From 76ccae8a2f7ce4ded27509249ef4b99199944fde Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 17 Nov 2023 18:17:32 +0900
Subject: [PATCH 001/435] chore(frontend): tweak rt style for safari

---
 packages/frontend/src/style.scss | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index 7bb443cece..978707140c 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -132,6 +132,10 @@ hr {
 	background: var(--divider);
 }
 
+rt {
+	text-align: center;
+}
+
 .ti {
 	width: 1.28em;
 	vertical-align: -12%;

From f007890e845361d8b9ceccae0e5318ec1a01fc42 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 17 Nov 2023 18:31:09 +0900
Subject: [PATCH 002/435] Revert "chore(frontend): tweak rt style for safari"

This reverts commit 76ccae8a2f7ce4ded27509249ef4b99199944fde.
---
 packages/frontend/src/style.scss | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index 978707140c..7bb443cece 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -132,10 +132,6 @@ hr {
 	background: var(--divider);
 }
 
-rt {
-	text-align: center;
-}
-
 .ti {
 	width: 1.28em;
 	vertical-align: -12%;

From 83ea0395f6a884b2de43b3e5bb93d1ceb107df64 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Fri, 17 Nov 2023 22:26:54 +0900
Subject: [PATCH 003/435] =?UTF-8?q?DeepL=20Translation=E3=81=AEPro=20accou?=
 =?UTF-8?q?nt=E3=83=88=E3=82=B0=E3=83=AB=E3=82=B9=E3=82=A4=E3=83=83?=
 =?UTF-8?q?=E3=83=81=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=A6?=
 =?UTF-8?q?=E3=81=84=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F=E3=81=AE=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#12355)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
---
 packages/frontend/src/pages/admin/external-services.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue
index e88860166c..e614bfeb1b 100644
--- a/packages/frontend/src/pages/admin/external-services.vue
+++ b/packages/frontend/src/pages/admin/external-services.vue
@@ -38,6 +38,7 @@ import { } from 'vue';
 import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import FormSection from '@/components/form/section.vue';
 import * as os from '@/os.js';

From 0a73973a7c6e6e95a5206bfc5388ff7f7a9ba8ed Mon Sep 17 00:00:00 2001
From: Nafu Satsuki <satsuki@nafusoft.dev>
Date: Sat, 18 Nov 2023 20:39:48 +0900
Subject: [PATCH 004/435] =?UTF-8?q?=E3=83=A1=E3=83=BC=E3=83=AB=E3=82=A2?=
 =?UTF-8?q?=E3=83=89=E3=83=AC=E3=82=B9=E3=81=AE=E8=AA=8D=E8=A8=BC=E3=81=AB?=
 =?UTF-8?q?verifymail.io=E3=82=92=E4=BD=BF=E3=81=88=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../1700303245007-supportVerifyMailApi.js     | 18 ++++
 packages/backend/src/core/EmailService.ts     | 92 +++++++++++++++++--
 packages/backend/src/models/Meta.ts           | 11 +++
 .../server/api/endpoints/admin/update-meta.ts | 14 +++
 .../frontend/src/pages/admin/security.vue     | 13 +++
 5 files changed, 140 insertions(+), 8 deletions(-)
 create mode 100644 packages/backend/migration/1700303245007-supportVerifyMailApi.js

diff --git a/packages/backend/migration/1700303245007-supportVerifyMailApi.js b/packages/backend/migration/1700303245007-supportVerifyMailApi.js
new file mode 100644
index 0000000000..3ac59ec37a
--- /dev/null
+++ b/packages/backend/migration/1700303245007-supportVerifyMailApi.js
@@ -0,0 +1,18 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class SupportVerifyMailApi1700303245007 {
+    name = 'SupportVerifyMailApi1700303245007'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "meta" ADD "verifymailAuthKey" character varying(1024)`);
+        await queryRunner.query(`ALTER TABLE "meta" ADD "enableVerifymailApi" boolean NOT NULL DEFAULT false`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableVerifymailApi"`);
+        await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "verifymailAuthKey"`);
+    }
+}
diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts
index c9da3f77c0..8f28197ebc 100644
--- a/packages/backend/src/core/EmailService.ts
+++ b/packages/backend/src/core/EmailService.ts
@@ -13,6 +13,9 @@ import type Logger from '@/logger.js';
 import type { UserProfilesRepository } from '@/models/_.js';
 import { LoggerService } from '@/core/LoggerService.js';
 import { bindThis } from '@/decorators.js';
+import {URLSearchParams} from "node:url";
+import { HttpRequestService } from '@/core/HttpRequestService.js';
+import {SubOutputFormat} from "deep-email-validator/dist/output/output.js";
 
 @Injectable()
 export class EmailService {
@@ -27,6 +30,7 @@ export class EmailService {
 
 		private metaService: MetaService,
 		private loggerService: LoggerService,
+		private httpRequestService: HttpRequestService,
 	) {
 		this.logger = this.loggerService.getLogger('email');
 	}
@@ -160,14 +164,25 @@ export class EmailService {
 			email: emailAddress,
 		});
 
-		const validated = meta.enableActiveEmailValidation ? await validateEmail({
-			email: emailAddress,
-			validateRegex: true,
-			validateMx: true,
-			validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので
-			validateDisposable: true, // 捨てアドかどうかチェック
-			validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので
-		}) : { valid: true, reason: null };
+		const verifymailApi = meta.enableVerifymailApi && meta.verifymailAuthKey != null;
+		let validated;
+
+		if (meta.enableActiveEmailValidation) {
+			if (verifymailApi) {
+				validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey);
+			} else {
+				validated = meta.enableActiveEmailValidation ? await validateEmail({
+					email: emailAddress,
+					validateRegex: true,
+					validateMx: true,
+					validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので
+					validateDisposable: true, // 捨てアドかどうかチェック
+					validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので
+				}) : { valid: true, reason: null };
+			}
+		} else {
+			validated = { valid: true, reason: null };
+		}
 
 		const available = exist === 0 && validated.valid;
 
@@ -182,4 +197,65 @@ export class EmailService {
 			null,
 		};
 	}
+
+	private async verifyMail(emailAddress: string, verifymailAuthKey: string): Promise<{
+		valid: boolean;
+		reason: 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | null;
+	}> {
+		const endpoint = 'https://verifymail.io/api/' + emailAddress + '?key=' + verifymailAuthKey;
+		const res = await this.httpRequestService.send(endpoint, {
+			method: 'GET',
+			headers: {
+				'Content-Type': 'application/x-www-form-urlencoded',
+				Accept: 'application/json, */*',
+			},
+		});
+
+		const json = (await res.json()) as {
+			block: boolean;
+			catch_all: boolean;
+			deliverable_email: boolean;
+			disposable: boolean;
+			domain: string;
+			email_address: string;
+			email_provider: string;
+			mx: boolean;
+			mx_fallback: boolean;
+			mx_host: string[];
+			mx_ip: string[];
+			mx_priority: { [key: string]: number };
+			privacy: boolean;
+			related_domains: string[];
+		};
+
+		if (json.email_address === undefined) {
+			return {
+				valid: false,
+				reason: 'format',
+			};
+		}
+		if (json.deliverable_email !== undefined && !json.deliverable_email) {
+			return {
+				valid: false,
+				reason: 'smtp',
+			};
+		}
+		if (json.disposable) {
+			return {
+				valid: false,
+				reason: 'disposable',
+			};
+		}
+		if (json.mx !== undefined && !json.mx) {
+			return {
+				valid: false,
+				reason: 'mx',
+			};
+		}
+
+		return {
+			valid: true,
+			reason: null,
+		};
+	}
 }
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 14a72add1d..83e8962f5d 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -446,6 +446,17 @@ export class MiMeta {
 	})
 	public enableActiveEmailValidation: boolean;
 
+	@Column('boolean', {
+		default: false,
+	})
+	public enableVerifymailApi: boolean;
+
+	@Column('varchar', {
+		length: 1024,
+		nullable: true,
+	})
+	public verifymailAuthKey: string | null;
+
 	@Column('boolean', {
 		default: true,
 	})
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 da3e5dd9ac..d6f9b2cd94 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -113,6 +113,8 @@ export const paramDef = {
 		objectStorageS3ForcePathStyle: { type: 'boolean' },
 		enableIpLogging: { type: 'boolean' },
 		enableActiveEmailValidation: { type: 'boolean' },
+		enableVerifymailApi: { type: 'boolean' },
+		verifymailAuthKey: { type: 'string', nullable: true },
 		enableChartsForRemoteUser: { type: 'boolean' },
 		enableChartsForFederatedInstances: { type: 'boolean' },
 		enableServerMachineStats: { type: 'boolean' },
@@ -454,6 +456,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				set.enableActiveEmailValidation = ps.enableActiveEmailValidation;
 			}
 
+			if (ps.enableVerifymailApi !== undefined) {
+				set.enableVerifymailApi = ps.enableVerifymailApi;
+			}
+
+			if (ps.verifymailAuthKey !== undefined) {
+				if (ps.verifymailAuthKey === '') {
+					set.verifymailAuthKey = null;
+				} else {
+					set.verifymailAuthKey = ps.verifymailAuthKey;
+				}
+			}
+
 			if (ps.enableChartsForRemoteUser !== undefined) {
 				set.enableChartsForRemoteUser = ps.enableChartsForRemoteUser;
 			}
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index a2594ee6c5..f7f76d910a 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -73,6 +73,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<MkSwitch v-model="enableActiveEmailValidation" @update:modelValue="save">
 							<template #label>Enable</template>
 						</MkSwitch>
+						<MkSwitch v-model="enableVerifymailApi" @update:modelValue="save">
+							<template #label>Use Verifymail API</template>
+						</MkSwitch>
+						<MkInput v-model="verifymailAuthKey" @update:modelValue="save">
+							<template #prefix><i class="ti ti-key"></i></template>
+							<template #label>Verifymail API Auth Key</template>
+						</MkInput>
 					</div>
 				</MkFolder>
 
@@ -132,6 +139,8 @@ let setSensitiveFlagAutomatically: boolean = $ref(false);
 let enableSensitiveMediaDetectionForVideos: boolean = $ref(false);
 let enableIpLogging: boolean = $ref(false);
 let enableActiveEmailValidation: boolean = $ref(false);
+let enableVerifymailApi: boolean = $ref(false);
+let verifymailAuthKey: string | null = $ref(null);
 
 async function init() {
 	const meta = await os.api('admin/meta');
@@ -150,6 +159,8 @@ async function init() {
 	enableSensitiveMediaDetectionForVideos = meta.enableSensitiveMediaDetectionForVideos;
 	enableIpLogging = meta.enableIpLogging;
 	enableActiveEmailValidation = meta.enableActiveEmailValidation;
+	enableVerifymailApi = meta.enableVerifymailApi;
+	verifymailAuthKey = meta.verifymailAuthKey;
 }
 
 function save() {
@@ -167,6 +178,8 @@ function save() {
 		enableSensitiveMediaDetectionForVideos,
 		enableIpLogging,
 		enableActiveEmailValidation,
+		enableVerifymailApi,
+		verifymailAuthKey,
 	}).then(() => {
 		fetchInstance();
 	});

From af668b15c447d1c175cd3669fe0025a3aff61d73 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 18 Nov 2023 21:03:01 +0900
Subject: [PATCH 005/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 92e02508fe..4b08d12093 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,17 @@
 
 -->
 
+## 2023.x.x (unreleased)
+
+### General
+- Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
+
+### Client
+-
+
+### Server
+-
+
 ## 2023.11.1
 
 ### General

From 30dc6e691d09073a904a40beb94370b6ad3c5c5c Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 18 Nov 2023 21:04:00 +0900
Subject: [PATCH 006/435] lint fix

---
 packages/backend/src/core/EmailService.ts | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts
index 8f28197ebc..f31cec2b3a 100644
--- a/packages/backend/src/core/EmailService.ts
+++ b/packages/backend/src/core/EmailService.ts
@@ -3,9 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { URLSearchParams } from 'node:url';
 import * as nodemailer from 'nodemailer';
 import { Inject, Injectable } from '@nestjs/common';
 import { validate as validateEmail } from 'deep-email-validator';
+import { SubOutputFormat } from 'deep-email-validator/dist/output/output.js';
 import { MetaService } from '@/core/MetaService.js';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
@@ -13,9 +15,7 @@ import type Logger from '@/logger.js';
 import type { UserProfilesRepository } from '@/models/_.js';
 import { LoggerService } from '@/core/LoggerService.js';
 import { bindThis } from '@/decorators.js';
-import {URLSearchParams} from "node:url";
 import { HttpRequestService } from '@/core/HttpRequestService.js';
-import {SubOutputFormat} from "deep-email-validator/dist/output/output.js";
 
 @Injectable()
 export class EmailService {
@@ -167,7 +167,7 @@ export class EmailService {
 		const verifymailApi = meta.enableVerifymailApi && meta.verifymailAuthKey != null;
 		let validated;
 
-		if (meta.enableActiveEmailValidation) {
+		if (meta.enableActiveEmailValidation && meta.verifymailAuthKey) {
 			if (verifymailApi) {
 				validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey);
 			} else {

From 2b6f789a5bc741d9092d29ea17d03be794ef5d51 Mon Sep 17 00:00:00 2001
From: Nafu Satsuki <satsuki@nafusoft.dev>
Date: Sat, 18 Nov 2023 05:20:11 +0900
Subject: [PATCH 007/435] =?UTF-8?q?feat(moderation):=20=E3=83=A2=E3=83=87?=
 =?UTF-8?q?=E3=83=AC=E3=83=BC=E3=82=BF=E3=83=BC=E3=81=8C=E3=83=A6=E3=83=BC?=
 =?UTF-8?q?=E3=82=B6=E3=83=BC=E3=81=AE=E3=82=A2=E3=82=A4=E3=82=B3=E3=83=B3?=
 =?UTF-8?q?=E3=82=82=E3=81=97=E3=81=8F=E3=81=AF=E3=83=90=E3=83=8A=E3=83=BC?=
 =?UTF-8?q?=E7=94=BB=E5=83=8F=E3=82=92=E6=9C=AA=E8=A8=AD=E5=AE=9A=E7=8A=B6?=
 =?UTF-8?q?=E6=85=8B=E3=81=AB=E3=81=A7=E3=81=8D=E3=82=8B=E6=A9=9F=E8=83=BD?=
 =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=20(MisskeyIO#222)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
---
 locales/en-US.yml                             |  4 ++
 locales/index.d.ts                            |  4 ++
 locales/ja-JP.yml                             |  4 ++
 .../backend/src/server/api/EndpointsModule.ts |  8 ++++
 packages/backend/src/server/api/endpoints.ts  |  4 ++
 .../api/endpoints/admin/delete-user-avatar.ts | 48 +++++++++++++++++++
 .../api/endpoints/admin/delete-user-banner.ts | 48 +++++++++++++++++++
 packages/frontend/src/pages/admin-user.vue    | 42 ++++++++++++++++
 packages/misskey-js/etc/misskey-js.api.md     | 12 +++++
 packages/misskey-js/src/api.types.ts          |  2 +
 10 files changed, 176 insertions(+)
 create mode 100644 packages/backend/src/server/api/endpoints/admin/delete-user-avatar.ts
 create mode 100644 packages/backend/src/server/api/endpoints/admin/delete-user-banner.ts

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 09fd726c9f..2aba028e45 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -564,6 +564,10 @@ output: "Output"
 script: "Script"
 disablePagesScript: "Disable AiScript on Pages"
 updateRemoteUser: "Update remote user information"
+deleteUserAvatar: "Delete user icon"
+deleteUserAvatarConfirm: "Are you sure that you want to delete this user's icon?"
+deleteUserBanner: "Delete user banner"
+deleteUserBannerConfirm: "Are you sure that you want to delete this user's banner?"
 deleteAllFiles: "Delete all files"
 deleteAllFilesConfirm: "Are you sure that you want to delete all files?"
 removeAllFollowing: "Unfollow all followed users"
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 6baed91c42..6fd6d3641a 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -567,6 +567,10 @@ export interface Locale {
     "script": string;
     "disablePagesScript": string;
     "updateRemoteUser": string;
+    "deleteUserAvatar": string;
+    "deleteUserAvatarConfirm": string;
+    "deleteUserBanner": string;
+    "deleteUserBannerConfirm": string;
     "deleteAllFiles": string;
     "deleteAllFilesConfirm": string;
     "removeAllFollowing": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index e59a550df5..9685e9c5a5 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -564,6 +564,10 @@ output: "出力"
 script: "スクリプト"
 disablePagesScript: "Pagesのスクリプトを無効にする"
 updateRemoteUser: "リモートユーザー情報の更新"
+deleteUserAvatar: "アイコンを削除"
+deleteUserAvatarConfirm: "アイコンを削除しますか?"
+deleteUserBanner: "バナーを削除"
+deleteUserBannerConfirm: "バナーを削除しますか?"
 deleteAllFiles: "すべてのファイルを削除"
 deleteAllFilesConfirm: "すべてのファイルを削除しますか?"
 removeAllFollowing: "フォローを全解除"
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index 6d813ae04e..3797b46d04 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -24,6 +24,8 @@ import * as ep___admin_avatarDecorations_delete from './endpoints/admin/avatar-d
 import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js';
 import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js';
 import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js';
+import * as ep___admin_deleteUserAvatar from './endpoints/admin/delete-user-avatar.js';
+import * as ep___admin_deleteUserBanner from './endpoints/admin/delete-user-banner.js';
 import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js';
 import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js';
 import * as ep___admin_drive_files from './endpoints/admin/drive/files.js';
@@ -383,6 +385,8 @@ const $admin_avatarDecorations_delete: Provider = { provide: 'ep:admin/avatar-de
 const $admin_avatarDecorations_list: Provider = { provide: 'ep:admin/avatar-decorations/list', useClass: ep___admin_avatarDecorations_list.default };
 const $admin_avatarDecorations_update: Provider = { provide: 'ep:admin/avatar-decorations/update', useClass: ep___admin_avatarDecorations_update.default };
 const $admin_deleteAllFilesOfAUser: Provider = { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default };
+const $admin_deleteUserAvatar: Provider = { provide: 'ep:admin/delete-user-avatar', useClass: ep___admin_deleteUserAvatar.default };
+const $admin_deleteUserBanner: Provider = { provide: 'ep:admin/delete-user-banner', useClass: ep___admin_deleteUserBanner.default };
 const $admin_drive_cleanRemoteFiles: Provider = { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default };
 const $admin_drive_cleanup: Provider = { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default };
 const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default };
@@ -746,6 +750,8 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
 		$admin_avatarDecorations_list,
 		$admin_avatarDecorations_update,
 		$admin_deleteAllFilesOfAUser,
+		$admin_deleteUserAvatar,
+		$admin_deleteUserBanner,
 		$admin_drive_cleanRemoteFiles,
 		$admin_drive_cleanup,
 		$admin_drive_files,
@@ -1103,6 +1109,8 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
 		$admin_avatarDecorations_list,
 		$admin_avatarDecorations_update,
 		$admin_deleteAllFilesOfAUser,
+		$admin_deleteUserAvatar,
+		$admin_deleteUserBanner,
 		$admin_drive_cleanRemoteFiles,
 		$admin_drive_cleanup,
 		$admin_drive_files,
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index ef4bd3e2b5..4162ace337 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -24,6 +24,8 @@ import * as ep___admin_avatarDecorations_delete from './endpoints/admin/avatar-d
 import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js';
 import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js';
 import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js';
+import * as ep___admin_deleteUserAvatar from './endpoints/admin/delete-user-avatar.js';
+import * as ep___admin_deleteUserBanner from './endpoints/admin/delete-user-banner.js';
 import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js';
 import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js';
 import * as ep___admin_drive_files from './endpoints/admin/drive/files.js';
@@ -381,6 +383,8 @@ const eps = [
 	['admin/avatar-decorations/list', ep___admin_avatarDecorations_list],
 	['admin/avatar-decorations/update', ep___admin_avatarDecorations_update],
 	['admin/delete-all-files-of-a-user', ep___admin_deleteAllFilesOfAUser],
+	['admin/delete-user-avatar', ep___admin_deleteUserAvatar],
+	['admin/delete-user-banner', ep___admin_deleteUserBanner],
 	['admin/drive/clean-remote-files', ep___admin_drive_cleanRemoteFiles],
 	['admin/drive/cleanup', ep___admin_drive_cleanup],
 	['admin/drive/files', ep___admin_drive_files],
diff --git a/packages/backend/src/server/api/endpoints/admin/delete-user-avatar.ts b/packages/backend/src/server/api/endpoints/admin/delete-user-avatar.ts
new file mode 100644
index 0000000000..d3c78d7fb6
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/delete-user-avatar.ts
@@ -0,0 +1,48 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import type { UsersRepository } from '@/models/_.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { DI } from '@/di-symbols.js';
+
+export const meta = {
+	tags: ['admin'],
+
+	requireCredential: true,
+	requireModerator: true,
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		userId: { type: 'string', format: 'misskey:id' },
+	},
+	required: ['userId'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+	constructor(
+		@Inject(DI.usersRepository)
+		private usersRepository: UsersRepository,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			const user = await this.usersRepository.findOneBy({ id: ps.userId });
+
+			if (user == null) {
+				throw new Error('user not found');
+			}
+
+			await this.usersRepository.update(user.id, {
+				avatar: null,
+				avatarId: null,
+				avatarUrl: null,
+				avatarBlurhash: null,
+			});
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/admin/delete-user-banner.ts b/packages/backend/src/server/api/endpoints/admin/delete-user-banner.ts
new file mode 100644
index 0000000000..e076cdcfc1
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/admin/delete-user-banner.ts
@@ -0,0 +1,48 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import type { UsersRepository } from '@/models/_.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { DI } from '@/di-symbols.js';
+
+export const meta = {
+	tags: ['admin'],
+
+	requireCredential: true,
+	requireModerator: true,
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		userId: { type: 'string', format: 'misskey:id' },
+	},
+	required: ['userId'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+	constructor(
+		@Inject(DI.usersRepository)
+		private usersRepository: UsersRepository,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			const user = await this.usersRepository.findOneBy({ id: ps.userId });
+
+			if (user == null) {
+				throw new Error('user not found');
+			}
+
+			await this.usersRepository.update(user.id, {
+				banner: null,
+				bannerId: null,
+				bannerUrl: null,
+				bannerBlurhash: null,
+			});
+		});
+	}
+}
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 5d671acf31..9f4975e888 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -122,6 +122,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 							</template>
 						</MkFolder>
 
+                        <div>
+							<MkButton v-if="iAmModerator" inline danger style="margin-right: 8px;" @click="deleteUserAvatar"><i class="ti ti-user-circle"></i> {{ i18n.ts.deleteUserAvatar }}</MkButton>
+							<MkButton v-if="iAmModerator" inline danger @click="deleteUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.deleteUserBanner }}</MkButton>
+						</div>
 						<MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton>
 					</div>
 				</FormSection>
@@ -320,6 +324,44 @@ async function toggleSuspend(v) {
 	}
 }
 
+async function deleteUserAvatar() {
+  const confirm = await os.confirm({
+    type: 'warning',
+    text: i18n.ts.deleteUserAvatarConfirm,
+  });
+  if (confirm.canceled) return;
+  const process = async () => {
+    await os.api('admin/delete-user-avatar', { userId: user.id });
+    os.success();
+  };
+  await process().catch(err => {
+    os.alert({
+      type: 'error',
+      text: err.toString(),
+    });
+  });
+  refreshUser();
+}
+
+async function deleteUserBanner() {
+  const confirm = await os.confirm({
+    type: 'warning',
+    text: i18n.ts.deleteUserBannerConfirm,
+  });
+  if (confirm.canceled) return;
+  const process = async () => {
+    await os.api('admin/delete-user-banner', { userId: user.id });
+    os.success();
+  };
+  await process().catch(err => {
+    os.alert({
+      type: 'error',
+      text: err.toString(),
+    });
+  });
+  refreshUser();
+}
+
 async function deleteAllFiles() {
 	const confirm = await os.confirm({
 		type: 'warning',
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 87922ba791..f4bcaa8066 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -345,6 +345,18 @@ export type Endpoints = {
         };
         res: null;
     };
+    'admin/delete-user-avatar': {
+        req: {
+            userId: User['id'];
+        };
+        res: null;
+    };
+    'admin/delete-user-banner': {
+        req: {
+            userId: User['id'];
+        };
+        res: null;
+    };
     'admin/delete-logs': {
         req: NoParams;
         res: null;
diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts
index 54b175fcf1..e3f28c644e 100644
--- a/packages/misskey-js/src/api.types.ts
+++ b/packages/misskey-js/src/api.types.ts
@@ -15,6 +15,8 @@ export type Endpoints = {
 	// admin
 	'admin/abuse-user-reports': { req: TODO; res: TODO; };
 	'admin/delete-all-files-of-a-user': { req: { userId: User['id']; }; res: null; };
+	'admin/delete-user-avatar': { req: { userId: User['id']; }; res: null; };
+	'admin/delete-user-banner': { req: { userId: User['id']; }; res: null; };
 	'admin/delete-logs': { req: NoParams; res: null; };
 	'admin/get-index-stats': { req: TODO; res: TODO; };
 	'admin/get-table-stats': { req: TODO; res: TODO; };

From 2f7d10bf2359823f5c37f8971bab287e7918a246 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 18 Nov 2023 21:08:32 +0900
Subject: [PATCH 008/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b08d12093..e2c226aec0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@
 
 ### General
 - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
+- Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
 
 ### Client
 -

From b65fd349812fe3c89b5face6ec5c12823459d7df Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 19 Nov 2023 10:18:57 +0900
Subject: [PATCH 009/435] tweak of 2b6f789a5b

---
 locales/en-US.yml                                |  8 ++++----
 locales/index.d.ts                               | 10 ++++++----
 locales/ja-JP.yml                                | 10 ++++++----
 .../backend/src/server/api/EndpointsModule.ts    | 16 ++++++++--------
 packages/backend/src/server/api/endpoints.ts     |  8 ++++----
 ...elete-user-avatar.ts => unset-user-avatar.ts} | 12 ++++++++++++
 ...elete-user-banner.ts => unset-user-banner.ts} | 12 ++++++++++++
 packages/backend/src/types.ts                    | 14 ++++++++++++++
 packages/frontend/src/pages/admin-user.vue       | 16 ++++++++--------
 packages/misskey-js/etc/misskey-js.api.md        |  4 ++--
 packages/misskey-js/src/api.types.ts             |  4 ++--
 packages/misskey-js/src/consts.ts                | 14 ++++++++++++++
 packages/misskey-js/src/entities.ts              |  6 ++++++
 13 files changed, 98 insertions(+), 36 deletions(-)
 rename packages/backend/src/server/api/endpoints/admin/{delete-user-avatar.ts => unset-user-avatar.ts} (77%)
 rename packages/backend/src/server/api/endpoints/admin/{delete-user-banner.ts => unset-user-banner.ts} (77%)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 2aba028e45..b14592b20a 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -564,10 +564,10 @@ output: "Output"
 script: "Script"
 disablePagesScript: "Disable AiScript on Pages"
 updateRemoteUser: "Update remote user information"
-deleteUserAvatar: "Delete user icon"
-deleteUserAvatarConfirm: "Are you sure that you want to delete this user's icon?"
-deleteUserBanner: "Delete user banner"
-deleteUserBannerConfirm: "Are you sure that you want to delete this user's banner?"
+unsetUserAvatar: "Delete user icon"
+unsetUserAvatarConfirm: "Are you sure that you want to delete this user's icon?"
+unsetUserBanner: "Delete user banner"
+unsetUserBannerConfirm: "Are you sure that you want to delete this user's banner?"
 deleteAllFiles: "Delete all files"
 deleteAllFilesConfirm: "Are you sure that you want to delete all files?"
 removeAllFollowing: "Unfollow all followed users"
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 6fd6d3641a..39fbb57799 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -567,10 +567,10 @@ export interface Locale {
     "script": string;
     "disablePagesScript": string;
     "updateRemoteUser": string;
-    "deleteUserAvatar": string;
-    "deleteUserAvatarConfirm": string;
-    "deleteUserBanner": string;
-    "deleteUserBannerConfirm": string;
+    "unsetUserAvatar": string;
+    "unsetUserAvatarConfirm": string;
+    "unsetUserBanner": string;
+    "unsetUserBannerConfirm": string;
     "deleteAllFiles": string;
     "deleteAllFilesConfirm": string;
     "removeAllFollowing": string;
@@ -2417,6 +2417,8 @@ export interface Locale {
         "createAvatarDecoration": string;
         "updateAvatarDecoration": string;
         "deleteAvatarDecoration": string;
+        "unsetUserAvatar": string;
+        "unsetUserBanner": string;
     };
     "_fileViewer": {
         "title": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 9685e9c5a5..3757715c0f 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -564,10 +564,10 @@ output: "出力"
 script: "スクリプト"
 disablePagesScript: "Pagesのスクリプトを無効にする"
 updateRemoteUser: "リモートユーザー情報の更新"
-deleteUserAvatar: "アイコンを削除"
-deleteUserAvatarConfirm: "アイコンを削除しますか?"
-deleteUserBanner: "バナーを削除"
-deleteUserBannerConfirm: "バナーを削除しますか?"
+unsetUserAvatar: "アイコンを解除"
+unsetUserAvatarConfirm: "アイコンを解除しますか?"
+unsetUserBanner: "バナーを解除"
+unsetUserBannerConfirm: "バナーを解除しますか?"
 deleteAllFiles: "すべてのファイルを削除"
 deleteAllFilesConfirm: "すべてのファイルを削除しますか?"
 removeAllFollowing: "フォローを全解除"
@@ -2318,6 +2318,8 @@ _moderationLogTypes:
   createAvatarDecoration: "アイコンデコレーションを作成"
   updateAvatarDecoration: "アイコンデコレーションを更新"
   deleteAvatarDecoration: "アイコンデコレーションを削除"
+  unsetUserAvatar: "ユーザーのアイコンを解除"
+  unsetUserBanner: "ユーザーのバナーを解除"
 
 _fileViewer:
   title: "ファイルの詳細"
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index 3797b46d04..86a64d7121 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -24,8 +24,8 @@ import * as ep___admin_avatarDecorations_delete from './endpoints/admin/avatar-d
 import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js';
 import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js';
 import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js';
-import * as ep___admin_deleteUserAvatar from './endpoints/admin/delete-user-avatar.js';
-import * as ep___admin_deleteUserBanner from './endpoints/admin/delete-user-banner.js';
+import * as ep___admin_unsetUserAvatar from './endpoints/admin/unset-user-avatar.js';
+import * as ep___admin_unsetUserBanner from './endpoints/admin/unset-user-banner.js';
 import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js';
 import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js';
 import * as ep___admin_drive_files from './endpoints/admin/drive/files.js';
@@ -385,8 +385,8 @@ const $admin_avatarDecorations_delete: Provider = { provide: 'ep:admin/avatar-de
 const $admin_avatarDecorations_list: Provider = { provide: 'ep:admin/avatar-decorations/list', useClass: ep___admin_avatarDecorations_list.default };
 const $admin_avatarDecorations_update: Provider = { provide: 'ep:admin/avatar-decorations/update', useClass: ep___admin_avatarDecorations_update.default };
 const $admin_deleteAllFilesOfAUser: Provider = { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default };
-const $admin_deleteUserAvatar: Provider = { provide: 'ep:admin/delete-user-avatar', useClass: ep___admin_deleteUserAvatar.default };
-const $admin_deleteUserBanner: Provider = { provide: 'ep:admin/delete-user-banner', useClass: ep___admin_deleteUserBanner.default };
+const $admin_unsetUserAvatar: Provider = { provide: 'ep:admin/unset-user-avatar', useClass: ep___admin_unsetUserAvatar.default };
+const $admin_unsetUserBanner: Provider = { provide: 'ep:admin/unset-user-banner', useClass: ep___admin_unsetUserBanner.default };
 const $admin_drive_cleanRemoteFiles: Provider = { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default };
 const $admin_drive_cleanup: Provider = { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default };
 const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default };
@@ -750,8 +750,8 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
 		$admin_avatarDecorations_list,
 		$admin_avatarDecorations_update,
 		$admin_deleteAllFilesOfAUser,
-		$admin_deleteUserAvatar,
-		$admin_deleteUserBanner,
+		$admin_unsetUserAvatar,
+		$admin_unsetUserBanner,
 		$admin_drive_cleanRemoteFiles,
 		$admin_drive_cleanup,
 		$admin_drive_files,
@@ -1109,8 +1109,8 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
 		$admin_avatarDecorations_list,
 		$admin_avatarDecorations_update,
 		$admin_deleteAllFilesOfAUser,
-		$admin_deleteUserAvatar,
-		$admin_deleteUserBanner,
+		$admin_unsetUserAvatar,
+		$admin_unsetUserBanner,
 		$admin_drive_cleanRemoteFiles,
 		$admin_drive_cleanup,
 		$admin_drive_files,
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index 4162ace337..e458d720ab 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -24,8 +24,8 @@ import * as ep___admin_avatarDecorations_delete from './endpoints/admin/avatar-d
 import * as ep___admin_avatarDecorations_list from './endpoints/admin/avatar-decorations/list.js';
 import * as ep___admin_avatarDecorations_update from './endpoints/admin/avatar-decorations/update.js';
 import * as ep___admin_deleteAllFilesOfAUser from './endpoints/admin/delete-all-files-of-a-user.js';
-import * as ep___admin_deleteUserAvatar from './endpoints/admin/delete-user-avatar.js';
-import * as ep___admin_deleteUserBanner from './endpoints/admin/delete-user-banner.js';
+import * as ep___admin_unsetUserAvatar from './endpoints/admin/unset-user-avatar.js';
+import * as ep___admin_unsetUserBanner from './endpoints/admin/unset-user-banner.js';
 import * as ep___admin_drive_cleanRemoteFiles from './endpoints/admin/drive/clean-remote-files.js';
 import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js';
 import * as ep___admin_drive_files from './endpoints/admin/drive/files.js';
@@ -383,8 +383,8 @@ const eps = [
 	['admin/avatar-decorations/list', ep___admin_avatarDecorations_list],
 	['admin/avatar-decorations/update', ep___admin_avatarDecorations_update],
 	['admin/delete-all-files-of-a-user', ep___admin_deleteAllFilesOfAUser],
-	['admin/delete-user-avatar', ep___admin_deleteUserAvatar],
-	['admin/delete-user-banner', ep___admin_deleteUserBanner],
+	['admin/unset-user-avatar', ep___admin_unsetUserAvatar],
+	['admin/unset-user-banner', ep___admin_unsetUserBanner],
 	['admin/drive/clean-remote-files', ep___admin_drive_cleanRemoteFiles],
 	['admin/drive/cleanup', ep___admin_drive_cleanup],
 	['admin/drive/files', ep___admin_drive_files],
diff --git a/packages/backend/src/server/api/endpoints/admin/delete-user-avatar.ts b/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts
similarity index 77%
rename from packages/backend/src/server/api/endpoints/admin/delete-user-avatar.ts
rename to packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts
index d3c78d7fb6..ac10f1b6fd 100644
--- a/packages/backend/src/server/api/endpoints/admin/delete-user-avatar.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts
@@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import type { 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'],
@@ -29,6 +30,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 	constructor(
 		@Inject(DI.usersRepository)
 		private usersRepository: UsersRepository,
+
+		private moderationLogService: ModerationLogService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const user = await this.usersRepository.findOneBy({ id: ps.userId });
@@ -36,6 +39,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			if (user == null) {
 				throw new Error('user not found');
 			}
+	
+			if (user.avatarId == null) return;
 
 			await this.usersRepository.update(user.id, {
 				avatar: null,
@@ -43,6 +48,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				avatarUrl: null,
 				avatarBlurhash: null,
 			});
+
+			this.moderationLogService.log(me, 'unsetUserAvatar', {
+				userId: user.id,
+				userUsername: user.username,
+				userHost: user.host,
+				fileId: user.avatarId,
+			});
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/delete-user-banner.ts b/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts
similarity index 77%
rename from packages/backend/src/server/api/endpoints/admin/delete-user-banner.ts
rename to packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts
index e076cdcfc1..66acd367df 100644
--- a/packages/backend/src/server/api/endpoints/admin/delete-user-banner.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts
@@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import type { 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'],
@@ -29,6 +30,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 	constructor(
 		@Inject(DI.usersRepository)
 		private usersRepository: UsersRepository,
+
+		private moderationLogService: ModerationLogService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const user = await this.usersRepository.findOneBy({ id: ps.userId });
@@ -37,12 +40,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				throw new Error('user not found');
 			}
 
+			if (user.bannerId == null) return;
+
 			await this.usersRepository.update(user.id, {
 				banner: null,
 				bannerId: null,
 				bannerUrl: null,
 				bannerBlurhash: null,
 			});
+
+			this.moderationLogService.log(me, 'unsetUserBanner', {
+				userId: user.id,
+				userUsername: user.username,
+				userHost: user.host,
+				fileId: user.bannerId,
+			});
 		});
 	}
 }
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index e6dfeb6f8c..1fb3d6a6ce 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -63,6 +63,8 @@ export const moderationLogTypes = [
 	'createAvatarDecoration',
 	'updateAvatarDecoration',
 	'deleteAvatarDecoration',
+	'unsetUserAvatar',
+	'unsetUserBanner',
 ] as const;
 
 export type ModerationLogPayloads = {
@@ -237,6 +239,18 @@ export type ModerationLogPayloads = {
 		avatarDecorationId: string;
 		avatarDecoration: any;
 	};
+	unsetUserAvatar: {
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
+		fileId: string;
+	};
+	unsetUserBanner: {
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
+		fileId: string;
+	};
 };
 
 export type Serialized<T> = {
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 9f4975e888..87ebedc296 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -123,8 +123,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</MkFolder>
 
                         <div>
-							<MkButton v-if="iAmModerator" inline danger style="margin-right: 8px;" @click="deleteUserAvatar"><i class="ti ti-user-circle"></i> {{ i18n.ts.deleteUserAvatar }}</MkButton>
-							<MkButton v-if="iAmModerator" inline danger @click="deleteUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.deleteUserBanner }}</MkButton>
+							<MkButton v-if="iAmModerator" inline danger style="margin-right: 8px;" @click="unsetUserAvatar"><i class="ti ti-user-circle"></i> {{ i18n.ts.unsetUserAvatar }}</MkButton>
+							<MkButton v-if="iAmModerator" inline danger @click="unsetUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.unsetUserBanner }}</MkButton>
 						</div>
 						<MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton>
 					</div>
@@ -324,14 +324,14 @@ async function toggleSuspend(v) {
 	}
 }
 
-async function deleteUserAvatar() {
+async function unsetUserAvatar() {
   const confirm = await os.confirm({
     type: 'warning',
-    text: i18n.ts.deleteUserAvatarConfirm,
+    text: i18n.ts.unsetUserAvatarConfirm,
   });
   if (confirm.canceled) return;
   const process = async () => {
-    await os.api('admin/delete-user-avatar', { userId: user.id });
+    await os.api('admin/unset-user-avatar', { userId: user.id });
     os.success();
   };
   await process().catch(err => {
@@ -343,14 +343,14 @@ async function deleteUserAvatar() {
   refreshUser();
 }
 
-async function deleteUserBanner() {
+async function unsetUserBanner() {
   const confirm = await os.confirm({
     type: 'warning',
-    text: i18n.ts.deleteUserBannerConfirm,
+    text: i18n.ts.unsetUserBannerConfirm,
   });
   if (confirm.canceled) return;
   const process = async () => {
-    await os.api('admin/delete-user-banner', { userId: user.id });
+    await os.api('admin/unset-user-banner', { userId: user.id });
     os.success();
   };
   await process().catch(err => {
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index f4bcaa8066..85907de665 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -345,13 +345,13 @@ export type Endpoints = {
         };
         res: null;
     };
-    'admin/delete-user-avatar': {
+    'admin/unset-user-avatar': {
         req: {
             userId: User['id'];
         };
         res: null;
     };
-    'admin/delete-user-banner': {
+    'admin/unset-user-banner': {
         req: {
             userId: User['id'];
         };
diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts
index e3f28c644e..1a75b7cf57 100644
--- a/packages/misskey-js/src/api.types.ts
+++ b/packages/misskey-js/src/api.types.ts
@@ -15,8 +15,8 @@ export type Endpoints = {
 	// admin
 	'admin/abuse-user-reports': { req: TODO; res: TODO; };
 	'admin/delete-all-files-of-a-user': { req: { userId: User['id']; }; res: null; };
-	'admin/delete-user-avatar': { req: { userId: User['id']; }; res: null; };
-	'admin/delete-user-banner': { req: { userId: User['id']; }; res: null; };
+	'admin/unset-user-avatar': { req: { userId: User['id']; }; res: null; };
+	'admin/unset-user-banner': { req: { userId: User['id']; }; res: null; };
 	'admin/delete-logs': { req: NoParams; res: null; };
 	'admin/get-index-stats': { req: TODO; res: TODO; };
 	'admin/get-table-stats': { req: TODO; res: TODO; };
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 48a36a31d6..a8f0b96d5d 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -81,6 +81,8 @@ export const moderationLogTypes = [
 	'createAvatarDecoration',
 	'updateAvatarDecoration',
 	'deleteAvatarDecoration',
+	'unsetUserAvatar',
+	'unsetUserBanner',
 ] as const;
 
 export type ModerationLogPayloads = {
@@ -255,4 +257,16 @@ export type ModerationLogPayloads = {
 		avatarDecorationId: string;
 		avatarDecoration: any;
 	};
+	unsetUserAvatar: {
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
+		fileId: string;
+	};
+	unsetUserBanner: {
+		userId: string;
+		userUsername: string;
+		userHost: string | null;
+		fileId: string;
+	};
 };
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index a0d0b7528d..a51315b13b 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -727,4 +727,10 @@ export type ModerationLog = {
 } | {
 	type: 'resolveAbuseReport';
 	info: ModerationLogPayloads['resolveAbuseReport'];
+} | {
+	type: 'unsetUserAvatar';
+	info: ModerationLogPayloads['unsetUserAvatar'];
+} | {
+	type: 'unsetUserBanner';
+	info: ModerationLogPayloads['unsetUserBanner'];
 });

From cbebe85ccfab582f9cdf6f3680673e80d708439c Mon Sep 17 00:00:00 2001
From: Lynx Kotoura <admin@sanin.link>
Date: Sun, 19 Nov 2023 11:43:04 +0900
Subject: [PATCH 010/435] =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E4=B8=80?=
 =?UTF-8?q?=E8=A6=A7=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AE=E8=A1=A8=E7=A4=BA?=
 =?UTF-8?q?=E3=81=8C=E3=83=A2=E3=83=90=E3=82=A4=E3=83=AB=E7=92=B0=E5=A2=83?=
 =?UTF-8?q?=E3=81=AB=E3=81=8A=E3=81=84=E3=81=A6=E5=B4=A9=E3=82=8C=E3=81=A6?=
 =?UTF-8?q?=E3=81=84=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1235?=
 =?UTF-8?q?4)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix style of list of pages on mobile

* overflow clip に変えた
---
 CHANGELOG.md                                       | 2 +-
 packages/frontend/src/components/MkPagePreview.vue | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e2c226aec0..d96af455f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@
 -
 
 ### Client
--
+- Fix: ページ一覧ページの表示がモバイル環境において崩れているのを修正
 
 ### Server
 -
diff --git a/packages/frontend/src/components/MkPagePreview.vue b/packages/frontend/src/components/MkPagePreview.vue
index 05b577c49c..6c8a0e56a6 100644
--- a/packages/frontend/src/components/MkPagePreview.vue
+++ b/packages/frontend/src/components/MkPagePreview.vue
@@ -114,7 +114,6 @@ const props = defineProps<{
 
 			& + article {
 				left: 0;
-				width: 100%;
 			}
 		}
 	}
@@ -124,6 +123,7 @@ const props = defineProps<{
 
 		> .thumbnail {
 			height: 80px;
+			overflow: clip;
 		}
 
 		> article {

From 02b0adf31facbe7eb6d5905e9670ee541e2585e6 Mon Sep 17 00:00:00 2001
From: yukineko <27853966+hideki0403@users.noreply.github.com>
Date: Sun, 19 Nov 2023 11:45:24 +0900
Subject: [PATCH 011/435] =?UTF-8?q?fix:=20=E3=80=8C=E8=A8=AD=E5=AE=9A?=
 =?UTF-8?q?=E3=81=AE=E3=83=90=E3=83=83=E3=82=AF=E3=82=A2=E3=83=83=E3=83=97?=
 =?UTF-8?q?=E3=80=8D=E3=81=AB=E4=B8=80=E9=83=A8=E3=81=AE=E8=A8=AD=E5=AE=9A?=
 =?UTF-8?q?=E9=A0=85=E7=9B=AE=E3=81=8C=E5=90=AB=E3=81=BE=E3=82=8C=E3=81=A6?=
 =?UTF-8?q?=E3=81=84=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#12366)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: 一部の設定項目がバックアップに含まれていなかったのを修正

* update: CHANGELOG.md

* remove: バックアップ不要な項目を削除
---
 CHANGELOG.md                                  |  2 +-
 .../pages/settings/preferences-backups.vue    | 25 +++++++++++++++++--
 2 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d96af455f1..0bdbb2aade 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,7 +19,7 @@
 - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
 
 ### Client
--
+- fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 
 ### Server
 -
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index 3b3a6bd07d..35435238fc 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -54,22 +54,24 @@ import { miLocalStorage } from '@/local-storage.js';
 const { t, ts } = i18n;
 
 const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
+	'collapseRenotes',
 	'menu',
 	'visibility',
 	'localOnly',
 	'statusbars',
 	'widgets',
 	'tl',
+	'pinnedUserLists',
 	'overridedDeviceKind',
 	'serverDisconnectedBehavior',
-	'collapseRenotes',
-	'showNoteActionsOnlyHover',
 	'nsfw',
+	'highlightSensitiveMedia',
 	'animation',
 	'animatedMfm',
 	'advancedMfm',
 	'loadRawImages',
 	'imageNewTab',
+	'enableDataSaverMode',
 	'disableShowingAnimatedImages',
 	'emojiStyle',
 	'disableDrawer',
@@ -89,9 +91,28 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
 	'menuDisplay',
 	'reportError',
 	'squareAvatars',
+	'showAvatarDecorations',
 	'numberOfPageCache',
+	'showNoteActionsOnlyHover',
+	'showClipButtonInNoteFooter',
+	'reactionsDisplaySize',
+	'forceShowAds',
 	'aiChanMode',
+	'devMode',
 	'mediaListWithOneImageAppearance',
+	'notificationPosition',
+	'notificationStackAxis',
+	'enableCondensedLineForAcct',
+	'keepScreenOn',
+	'defaultWithReplies',
+	'disableStreamingTimeline',
+	'useGroupedNotifications',
+	'sound_masterVolume',
+	'sound_note',
+	'sound_noteMy',
+	'sound_notification',
+	'sound_antenna',
+	'sound_channel',
 ];
 const coldDeviceStorageSaveKeys: (keyof typeof ColdDeviceStorage.default)[] = [
 	'lightTheme',

From e0de86359c9df510e02a2fa25643030d0517afdf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9E=9C=E7=89=A9=E3=83=AA=E3=83=B3?= <nassii74@gmail.com>
Date: Sun, 19 Nov 2023 13:39:25 +0900
Subject: [PATCH 012/435] =?UTF-8?q?backend=E3=81=AE=E3=83=97=E3=83=AD?=
 =?UTF-8?q?=E3=82=B8=E3=82=A7=E3=82=AF=E3=83=88=E3=81=A7=E5=8D=98=E4=BD=93?=
 =?UTF-8?q?=E3=81=A7=20start=20=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12371)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index a4856709c3..496c79c9c0 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -7,8 +7,8 @@
 		"node": ">=18.16.0"
 	},
 	"scripts": {
-		"start": "node ./built/index.js",
-		"start:test": "NODE_ENV=test node ./built/index.js",
+		"start": "node ./built/boot/entry.js",
+		"start:test": "NODE_ENV=test node ./built/boot/entry.js",
 		"migrate": "pnpm typeorm migration:run -d ormconfig.js",
 		"revert": "pnpm typeorm migration:revert -d ormconfig.js",
 		"check:connect": "node ./check_connect.js",

From ed0cc443ea8122d01488f427bcced457d3d65d4f Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 21 Nov 2023 09:55:49 +0900
Subject: [PATCH 013/435] =?UTF-8?q?fix(backend):=20=E3=83=AD=E3=83=BC?=
 =?UTF-8?q?=E3=83=AB=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?=
 =?UTF-8?q?=E3=81=8C=E4=BF=9D=E5=AD=98=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                             | 2 +-
 packages/backend/src/core/RoleService.ts | 5 ++++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e2c226aec0..aff2ed283e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,7 +22,7 @@
 -
 
 ### Server
--
+- Fix: ロールタイムラインが保存されない問題を修正
 
 ## 2023.11.1
 
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index d6a414694a..432887b3b7 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -87,6 +87,9 @@ export class RoleService implements OnApplicationShutdown {
 		@Inject(DI.redis)
 		private redisClient: Redis.Redis,
 
+		@Inject(DI.redisForTimelines)
+		private redisForTimelines: Redis.Redis,
+
 		@Inject(DI.redisForSub)
 		private redisForSub: Redis.Redis,
 
@@ -476,7 +479,7 @@ export class RoleService implements OnApplicationShutdown {
 	public async addNoteToRoleTimeline(note: Packed<'Note'>): Promise<void> {
 		const roles = await this.getUserRoles(note.userId);
 
-		const redisPipeline = this.redisClient.pipeline();
+		const redisPipeline = this.redisForTimelines.pipeline();
 
 		for (const role of roles) {
 			this.funoutTimelineService.push(`roleTimeline:${role.id}`, note.id, 1000, redisPipeline);

From 2ec3227012eecb4358feed6d16bf2b700b3ac9c7 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Tue, 21 Nov 2023 10:48:01 +0900
Subject: [PATCH 014/435] update api.md (#12379)

for API changes in b65fd349812fe3c89b5face6ec5c12823459d7df
---
 packages/misskey-js/etc/misskey-js.api.md | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 85907de665..63c3cb71a5 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2685,10 +2685,16 @@ type ModerationLog = {
 } | {
     type: 'resolveAbuseReport';
     info: ModerationLogPayloads['resolveAbuseReport'];
+} | {
+    type: 'unsetUserAvatar';
+    info: ModerationLogPayloads['unsetUserAvatar'];
+} | {
+    type: 'unsetUserBanner';
+    info: ModerationLogPayloads['unsetUserBanner'];
 });
 
 // @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", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration"];
+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", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"];
 
 // @public (undocumented)
 export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
@@ -3046,8 +3052,8 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u
 // Warnings were encountered during analysis:
 //
 // 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:632:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
+// src/api.types.ts:20:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
+// src/api.types.ts:634:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
 // src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts
 // src/entities.ts:627: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

From 8bd9077f77eaebaa4dff403e072ba49e4cab1326 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Tue, 21 Nov 2023 11:13:56 +0900
Subject: [PATCH 015/435] =?UTF-8?q?json-schema=E9=85=8D=E4=B8=8B=E3=81=AE?=
 =?UTF-8?q?=E6=9C=80=E6=96=B0=E5=8C=96=20(#12312)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* user.ts、page.ts、drive-folder.tsを各EntityServiceの戻り値をもとに最新化

* 再確認

* fix error

* note以外の残りのファイルを対応

* fix CHANGELOG.md

* fix CHANGELOG.md

* fix user.ts

* fix user.ts

* コメント対応

* fix note.ts

---------

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
---
 CHANGELOG.md                                  |   1 +
 .../entities/NotificationEntityService.ts     |  14 +-
 .../src/models/json-schema/announcement.ts    |   8 +-
 .../backend/src/models/json-schema/channel.ts |  63 ++--
 .../backend/src/models/json-schema/clip.ts    |   8 +-
 .../src/models/json-schema/drive-file.ts      |   2 +-
 .../src/models/json-schema/drive-folder.ts    |  12 +-
 .../models/json-schema/federation-instance.ts |   9 +-
 .../backend/src/models/json-schema/flash.ts   |  20 +-
 .../src/models/json-schema/following.ts       |  10 +-
 .../src/models/json-schema/gallery-post.ts    |  24 +-
 .../backend/src/models/json-schema/note.ts    |  28 +-
 .../src/models/json-schema/notification.ts    |  26 +-
 .../backend/src/models/json-schema/page.ts    |  68 +++-
 .../backend/src/models/json-schema/user.ts    | 313 ++++++++++++++++--
 15 files changed, 457 insertions(+), 149 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cc38356284..2efa1b230d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,7 @@
 - Feat: 管理者がコントロールパネルからメールアドレスの照会を行えるようになりました
 - Enhance: ローカリゼーションの更新
 - Enhance: 依存関係の更新
+- Enhance: json-schema(OpenAPIの戻り値として使用されるスキーマ定義)を出来る限り最新化 #12311
 
 ### Client
 - Enhance: MFMでルビを振れるように
diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts
index f74594ff0c..e723ea5a55 100644
--- a/packages/backend/src/core/entities/NotificationEntityService.ts
+++ b/packages/backend/src/core/entities/NotificationEntityService.ts
@@ -198,12 +198,14 @@ export class NotificationEntityService implements OnModuleInit {
 			});
 		} else if (notification.type === 'renote:grouped') {
 			const users = await Promise.all(notification.userIds.map(userId => {
-				const user = hint?.packedUsers != null
-					? hint.packedUsers.get(userId)
-					: this.userEntityService.pack(userId!, { id: meId }, {
-						detail: false,
-					});
-				return user;
+				const packedUser = hint?.packedUsers != null ? hint.packedUsers.get(userId) : null;
+				if (packedUser) {
+					return packedUser;
+				}
+
+				return this.userEntityService.pack(userId, { id: meId }, {
+					detail: false,
+				});
 			}));
 			return await awaitAll({
 				id: notification.id,
diff --git a/packages/backend/src/models/json-schema/announcement.ts b/packages/backend/src/models/json-schema/announcement.ts
index c7e24c7f29..78a98872b2 100644
--- a/packages/backend/src/models/json-schema/announcement.ts
+++ b/packages/backend/src/models/json-schema/announcement.ts
@@ -42,11 +42,15 @@ export const packedAnnouncementSchema = {
 			type: 'string',
 			optional: false, nullable: false,
 		},
-		forYou: {
+		needConfirmationToRead: {
 			type: 'boolean',
 			optional: false, nullable: false,
 		},
-		needConfirmationToRead: {
+		silence: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		forYou: {
 			type: 'boolean',
 			optional: false, nullable: false,
 		},
diff --git a/packages/backend/src/models/json-schema/channel.ts b/packages/backend/src/models/json-schema/channel.ts
index 8f9770cdc5..5b0fa0f15d 100644
--- a/packages/backend/src/models/json-schema/channel.ts
+++ b/packages/backend/src/models/json-schema/channel.ts
@@ -19,7 +19,7 @@ export const packedChannelSchema = {
 		},
 		lastNotedAt: {
 			type: 'string',
-			optional: false, nullable: true,
+			nullable: true, optional: false,
 			format: 'date-time',
 		},
 		name: {
@@ -28,38 +28,18 @@ export const packedChannelSchema = {
 		},
 		description: {
 			type: 'string',
-			nullable: true, optional: false,
-		},
-		bannerUrl: {
-			type: 'string',
-			format: 'url',
-			nullable: true, optional: false,
-		},
-		isArchived: {
-			type: 'boolean',
-			optional: false, nullable: false,
-		},
-		notesCount: {
-			type: 'number',
-			nullable: false, optional: false,
-		},
-		usersCount: {
-			type: 'number',
-			nullable: false, optional: false,
-		},
-		isFollowing: {
-			type: 'boolean',
-			optional: true, nullable: false,
-		},
-		isFavorited: {
-			type: 'boolean',
-			optional: true, nullable: false,
+			optional: false, nullable: true,
 		},
 		userId: {
 			type: 'string',
 			nullable: true, optional: false,
 			format: 'id',
 		},
+		bannerUrl: {
+			type: 'string',
+			format: 'url',
+			nullable: true, optional: false,
+		},
 		pinnedNoteIds: {
 			type: 'array',
 			nullable: false, optional: false,
@@ -72,6 +52,18 @@ export const packedChannelSchema = {
 			type: 'string',
 			optional: false, nullable: false,
 		},
+		isArchived: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+		usersCount: {
+			type: 'number',
+			nullable: false, optional: false,
+		},
+		notesCount: {
+			type: 'number',
+			nullable: false, optional: false,
+		},
 		isSensitive: {
 			type: 'boolean',
 			optional: false, nullable: false,
@@ -80,5 +72,22 @@ export const packedChannelSchema = {
 			type: 'boolean',
 			optional: false, nullable: false,
 		},
+		isFollowing: {
+			type: 'boolean',
+			optional: true, nullable: false,
+		},
+		isFavorited: {
+			type: 'boolean',
+			optional: true, nullable: false,
+		},
+		pinnedNotes: {
+			type: 'array',
+			optional: true, nullable: false,
+			items: {
+				type: 'object',
+				optional: false, nullable: false,
+				ref: 'Note',
+			},
+		},
 	},
 } as const;
diff --git a/packages/backend/src/models/json-schema/clip.ts b/packages/backend/src/models/json-schema/clip.ts
index 64f7a2ad9c..1ab96c2b3b 100644
--- a/packages/backend/src/models/json-schema/clip.ts
+++ b/packages/backend/src/models/json-schema/clip.ts
@@ -44,13 +44,13 @@ export const packedClipSchema = {
 			type: 'boolean',
 			optional: false, nullable: false,
 		},
-		isFavorited: {
-			type: 'boolean',
-			optional: true, nullable: false,
-		},
 		favoritedCount: {
 			type: 'number',
 			optional: false, nullable: false,
 		},
+		isFavorited: {
+			type: 'boolean',
+			optional: true, nullable: false,
+		},
 	},
 } as const;
diff --git a/packages/backend/src/models/json-schema/drive-file.ts b/packages/backend/src/models/json-schema/drive-file.ts
index 87f1340812..79f242a711 100644
--- a/packages/backend/src/models/json-schema/drive-file.ts
+++ b/packages/backend/src/models/json-schema/drive-file.ts
@@ -74,7 +74,7 @@ export const packedDriveFileSchema = {
 		},
 		url: {
 			type: 'string',
-			optional: false, nullable: true,
+			optional: false, nullable: false,
 			format: 'url',
 		},
 		thumbnailUrl: {
diff --git a/packages/backend/src/models/json-schema/drive-folder.ts b/packages/backend/src/models/json-schema/drive-folder.ts
index 51107d423f..aaad301303 100644
--- a/packages/backend/src/models/json-schema/drive-folder.ts
+++ b/packages/backend/src/models/json-schema/drive-folder.ts
@@ -21,6 +21,12 @@ export const packedDriveFolderSchema = {
 			type: 'string',
 			optional: false, nullable: false,
 		},
+		parentId: {
+			type: 'string',
+			optional: false, nullable: true,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
 		foldersCount: {
 			type: 'number',
 			optional: true, nullable: false,
@@ -29,12 +35,6 @@ export const packedDriveFolderSchema = {
 			type: 'number',
 			optional: true, nullable: false,
 		},
-		parentId: {
-			type: 'string',
-			optional: false, nullable: true,
-			format: 'id',
-			example: 'xxxxxxxxxx',
-		},
 		parent: {
 			type: 'object',
 			optional: true, nullable: true,
diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts
index 442e1076f2..3417314272 100644
--- a/packages/backend/src/models/json-schema/federation-instance.ts
+++ b/packages/backend/src/models/json-schema/federation-instance.ts
@@ -79,6 +79,10 @@ export const packedFederationInstanceSchema = {
 			type: 'string',
 			optional: false, nullable: true,
 		},
+		isSilenced: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
 		iconUrl: {
 			type: 'string',
 			optional: false, nullable: true,
@@ -93,11 +97,6 @@ export const packedFederationInstanceSchema = {
 			type: 'string',
 			optional: false, nullable: true,
 		},
-		isSilenced: {
-			type: "boolean",
-			optional: false,
-			nullable: false,
-		},
 		infoUpdatedAt: {
 			type: 'string',
 			optional: false, nullable: true,
diff --git a/packages/backend/src/models/json-schema/flash.ts b/packages/backend/src/models/json-schema/flash.ts
index 9453ba1dce..f08fa7a279 100644
--- a/packages/backend/src/models/json-schema/flash.ts
+++ b/packages/backend/src/models/json-schema/flash.ts
@@ -22,6 +22,16 @@ export const packedFlashSchema = {
 			optional: false, nullable: false,
 			format: 'date-time',
 		},
+		userId: {
+			type: 'string',
+			optional: false, nullable: false,
+			format: 'id',
+		},
+		user: {
+			type: 'object',
+			ref: 'UserLite',
+			optional: false, nullable: false,
+		},
 		title: {
 			type: 'string',
 			optional: false, nullable: false,
@@ -34,16 +44,6 @@ export const packedFlashSchema = {
 			type: 'string',
 			optional: false, nullable: false,
 		},
-		userId: {
-			type: 'string',
-			optional: false, nullable: false,
-			format: 'id',
-		},
-		user: {
-			type: 'object',
-			ref: 'UserLite',
-			optional: false, nullable: false,
-		},
 		likedCount: {
 			type: 'number',
 			optional: false, nullable: true,
diff --git a/packages/backend/src/models/json-schema/following.ts b/packages/backend/src/models/json-schema/following.ts
index 3a24ebb619..e92cff20a1 100644
--- a/packages/backend/src/models/json-schema/following.ts
+++ b/packages/backend/src/models/json-schema/following.ts
@@ -22,16 +22,16 @@ export const packedFollowingSchema = {
 			optional: false, nullable: false,
 			format: 'id',
 		},
-		followee: {
-			type: 'object',
-			optional: true, nullable: false,
-			ref: 'UserDetailed',
-		},
 		followerId: {
 			type: 'string',
 			optional: false, nullable: false,
 			format: 'id',
 		},
+		followee: {
+			type: 'object',
+			optional: true, nullable: false,
+			ref: 'UserDetailed',
+		},
 		follower: {
 			type: 'object',
 			optional: true, nullable: false,
diff --git a/packages/backend/src/models/json-schema/gallery-post.ts b/packages/backend/src/models/json-schema/gallery-post.ts
index cf260c0bf5..df7038950c 100644
--- a/packages/backend/src/models/json-schema/gallery-post.ts
+++ b/packages/backend/src/models/json-schema/gallery-post.ts
@@ -22,14 +22,6 @@ export const packedGalleryPostSchema = {
 			optional: false, nullable: false,
 			format: 'date-time',
 		},
-		title: {
-			type: 'string',
-			optional: false, nullable: false,
-		},
-		description: {
-			type: 'string',
-			optional: false, nullable: true,
-		},
 		userId: {
 			type: 'string',
 			optional: false, nullable: false,
@@ -40,6 +32,14 @@ export const packedGalleryPostSchema = {
 			ref: 'UserLite',
 			optional: false, nullable: false,
 		},
+		title: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		description: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
 		fileIds: {
 			type: 'array',
 			optional: true, nullable: false,
@@ -70,5 +70,13 @@ export const packedGalleryPostSchema = {
 			type: 'boolean',
 			optional: false, nullable: false,
 		},
+		likedCount: {
+			type: 'number',
+			optional: false, nullable: false,
+		},
+		isLiked: {
+			type: 'boolean',
+			optional: true, nullable: false,
+		},
 	},
 } as const;
diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts
index 38c0054b55..9d5d558f51 100644
--- a/packages/backend/src/models/json-schema/note.ts
+++ b/packages/backend/src/models/json-schema/note.ts
@@ -127,22 +127,18 @@ export const packedNoteSchema = {
 		channel: {
 			type: 'object',
 			optional: true, nullable: true,
-			items: {
-				type: 'object',
-				optional: false, nullable: false,
-				properties: {
-					id: {
-						type: 'string',
-						optional: false, nullable: false,
-					},
-					name: {
-						type: 'string',
-						optional: false, nullable: true,
-					},
-					isSensitive: {
-						type: 'boolean',
-						optional: true, nullable: false,
-					},
+			properties: {
+				id: {
+					type: 'string',
+					optional: false, nullable: false,
+				},
+				name: {
+					type: 'string',
+					optional: false, nullable: true,
+				},
+				isSensitive: {
+					type: 'boolean',
+					optional: true, nullable: false,
 				},
 			},
 		},
diff --git a/packages/backend/src/models/json-schema/notification.ts b/packages/backend/src/models/json-schema/notification.ts
index 27db3bb62c..c6d6e84317 100644
--- a/packages/backend/src/models/json-schema/notification.ts
+++ b/packages/backend/src/models/json-schema/notification.ts
@@ -42,13 +42,9 @@ export const packedNotificationSchema = {
 			type: 'string',
 			optional: true, nullable: true,
 		},
-		choice: {
-			type: 'number',
-			optional: true, nullable: true,
-		},
-		invitation: {
-			type: 'object',
-			optional: true, nullable: true,
+		achievement: {
+			type: 'string',
+			optional: true, nullable: false,
 		},
 		body: {
 			type: 'string',
@@ -81,14 +77,14 @@ export const packedNotificationSchema = {
 				required: ['user', 'reaction'],
 			},
 		},
-	},
-	users: {
-		type: 'array',
-		optional: true, nullable: true,
-		items: {
-			type: 'object',
-			ref: 'UserLite',
-			optional: false, nullable: false,
+		users: {
+			type: 'array',
+			optional: true, nullable: true,
+			items: {
+				type: 'object',
+				ref: 'UserLite',
+				optional: false, nullable: false,
+			},
 		},
 	},
 } as const;
diff --git a/packages/backend/src/models/json-schema/page.ts b/packages/backend/src/models/json-schema/page.ts
index 3f20a4b802..9baacd6884 100644
--- a/packages/backend/src/models/json-schema/page.ts
+++ b/packages/backend/src/models/json-schema/page.ts
@@ -22,6 +22,32 @@ export const packedPageSchema = {
 			optional: false, nullable: false,
 			format: 'date-time',
 		},
+		userId: {
+			type: 'string',
+			optional: false, nullable: false,
+			format: 'id',
+		},
+		user: {
+			type: 'object',
+			ref: 'UserLite',
+			optional: false, nullable: false,
+		},
+		content: {
+			type: 'array',
+			optional: false, nullable: false,
+			items: {
+				type: 'object',
+				optional: false, nullable: false,
+			},
+		},
+		variables: {
+			type: 'array',
+			optional: false, nullable: false,
+			items: {
+				type: 'object',
+				optional: false, nullable: false,
+			},
+		},
 		title: {
 			type: 'string',
 			optional: false, nullable: false,
@@ -34,23 +60,47 @@ export const packedPageSchema = {
 			type: 'string',
 			optional: false, nullable: true,
 		},
-		content: {
-			type: 'array',
+		hideTitleWhenPinned: {
+			type: 'boolean',
 			optional: false, nullable: false,
 		},
-		variables: {
-			type: 'array',
+		alignCenter: {
+			type: 'boolean',
 			optional: false, nullable: false,
 		},
-		userId: {
+		font: {
 			type: 'string',
 			optional: false, nullable: false,
-			format: 'id',
 		},
-		user: {
-			type: 'object',
-			ref: 'UserLite',
+		script: {
+			type: 'string',
 			optional: false, nullable: false,
 		},
+		eyeCatchingImageId: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		eyeCatchingImage: {
+			type: 'object',
+			optional: false, nullable: true,
+			ref: 'DriveFile',
+		},
+		attachedFiles: {
+			type: 'array',
+			optional: false, nullable: false,
+			items: {
+				type: 'object',
+				optional: false, nullable: false,
+				ref: 'DriveFile',
+			},
+		},
+		likedCount: {
+			type: 'number',
+			optional: false, nullable: false,
+		},
+		isLiked: {
+			type: 'boolean',
+			optional: true, nullable: false,
+		},
 	},
 } as const;
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index 37bdcbe281..b0e18db01a 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -49,11 +49,6 @@ export const packedUserLiteSchema = {
 						nullable: false, optional: false,
 						format: 'id',
 					},
-					url: {
-						type: 'string',
-						format: 'url',
-						nullable: false, optional: false,
-					},
 					angle: {
 						type: 'number',
 						nullable: false, optional: true,
@@ -62,19 +57,14 @@ export const packedUserLiteSchema = {
 						type: 'boolean',
 						nullable: false, optional: true,
 					},
+					url: {
+						type: 'string',
+						format: 'url',
+						nullable: false, optional: false,
+					},
 				},
 			},
 		},
-		isAdmin: {
-			type: 'boolean',
-			nullable: false, optional: true,
-			default: false,
-		},
-		isModerator: {
-			type: 'boolean',
-			nullable: false, optional: true,
-			default: false,
-		},
 		isBot: {
 			type: 'boolean',
 			nullable: false, optional: true,
@@ -83,12 +73,67 @@ export const packedUserLiteSchema = {
 			type: 'boolean',
 			nullable: false, optional: true,
 		},
+		instance: {
+			type: 'object',
+			nullable: false, optional: true,
+			properties: {
+				name: {
+					type: 'string',
+					nullable: true, optional: false,
+				},
+				softwareName: {
+					type: 'string',
+					nullable: true, optional: false,
+				},
+				softwareVersion: {
+					type: 'string',
+					nullable: true, optional: false,
+				},
+				iconUrl: {
+					type: 'string',
+					nullable: true, optional: false,
+				},
+				faviconUrl: {
+					type: 'string',
+					nullable: true, optional: false,
+				},
+				themeColor: {
+					type: 'string',
+					nullable: true, optional: false,
+				},
+			},
+		},
+		emojis: {
+			type: 'object',
+			nullable: false, optional: false,
+		},
 		onlineStatus: {
 			type: 'string',
-			format: 'url',
-			nullable: true, optional: false,
+			nullable: false, optional: false,
 			enum: ['unknown', 'online', 'active', 'offline'],
 		},
+		badgeRoles: {
+			type: 'array',
+			nullable: false, optional: true,
+			items: {
+				type: 'object',
+				nullable: false, optional: false,
+				properties: {
+					name: {
+						type: 'string',
+						nullable: false, optional: false,
+					},
+					iconUrl: {
+						type: 'string',
+						nullable: true, optional: false,
+					},
+					displayOrder: {
+						type: 'number',
+						nullable: false, optional: false,
+					},
+				},
+			},
+		},
 	},
 } as const;
 
@@ -105,21 +150,18 @@ export const packedUserDetailedNotMeOnlySchema = {
 			format: 'uri',
 			nullable: true, optional: false,
 		},
-		movedToUri: {
+		movedTo: {
 			type: 'string',
 			format: 'uri',
-			nullable: true,
-			optional: false,
+			nullable: true, optional: false,
 		},
 		alsoKnownAs: {
 			type: 'array',
-			nullable: true,
-			optional: false,
+			nullable: true, optional: false,
 			items: {
 				type: 'string',
 				format: 'id',
-				nullable: false,
-				optional: false,
+				nullable: false, optional: false,
 			},
 		},
 		createdAt: {
@@ -249,6 +291,11 @@ export const packedUserDetailedNotMeOnlySchema = {
 			type: 'boolean',
 			nullable: false, optional: false,
 		},
+		ffVisibility: {
+			type: 'string',
+			nullable: false, optional: false,
+			enum: ['public', 'followers', 'private'],
+		},
 		twoFactorEnabled: {
 			type: 'boolean',
 			nullable: false, optional: false,
@@ -264,6 +311,57 @@ export const packedUserDetailedNotMeOnlySchema = {
 			nullable: false, optional: false,
 			default: false,
 		},
+		roles: {
+			type: 'array',
+			nullable: false, optional: false,
+			items: {
+				type: 'object',
+				nullable: false, optional: false,
+				properties: {
+					id: {
+						type: 'string',
+						nullable: false, optional: false,
+						format: 'id',
+					},
+					name: {
+						type: 'string',
+						nullable: false, optional: false,
+					},
+					color: {
+						type: 'string',
+						nullable: true, optional: false,
+					},
+					iconUrl: {
+						type: 'string',
+						nullable: true, optional: false,
+					},
+					description: {
+						type: 'string',
+						nullable: false, optional: false,
+					},
+					isModerator: {
+						type: 'boolean',
+						nullable: false, optional: false,
+					},
+					isAdministrator: {
+						type: 'boolean',
+						nullable: false, optional: false,
+					},
+					displayOrder: {
+						type: 'number',
+						nullable: false, optional: false,
+					},
+				},
+			},
+		},
+		memo: {
+			type: 'string',
+			nullable: true, optional: false,
+		},
+		moderationNote: {
+			type: 'string',
+			nullable: false, optional: true,
+		},
 		//#region relations
 		isFollowing: {
 			type: 'boolean',
@@ -297,10 +395,6 @@ export const packedUserDetailedNotMeOnlySchema = {
 			type: 'boolean',
 			nullable: false, optional: true,
 		},
-		memo: {
-			type: 'string',
-			nullable: false, optional: true,
-		},
 		notify: {
 			type: 'string',
 			nullable: false, optional: true,
@@ -326,29 +420,37 @@ export const packedMeDetailedOnlySchema = {
 			nullable: true, optional: false,
 			format: 'id',
 		},
-		injectFeaturedNote: {
+		isModerator: {
 			type: 'boolean',
 			nullable: true, optional: false,
 		},
+		isAdmin: {
+			type: 'boolean',
+			nullable: true, optional: false,
+		},
+		injectFeaturedNote: {
+			type: 'boolean',
+			nullable: false, optional: false,
+		},
 		receiveAnnouncementEmail: {
 			type: 'boolean',
-			nullable: true, optional: false,
+			nullable: false, optional: false,
 		},
 		alwaysMarkNsfw: {
 			type: 'boolean',
-			nullable: true, optional: false,
+			nullable: false, optional: false,
 		},
 		autoSensitive: {
 			type: 'boolean',
-			nullable: true, optional: false,
+			nullable: false, optional: false,
 		},
 		carefulBot: {
 			type: 'boolean',
-			nullable: true, optional: false,
+			nullable: false, optional: false,
 		},
 		autoAcceptFollowed: {
 			type: 'boolean',
-			nullable: true, optional: false,
+			nullable: false, optional: false,
 		},
 		noCrawle: {
 			type: 'boolean',
@@ -387,10 +489,23 @@ export const packedMeDetailedOnlySchema = {
 			type: 'boolean',
 			nullable: false, optional: false,
 		},
+		unreadAnnouncements: {
+			type: 'array',
+			nullable: false, optional: false,
+			items: {
+				type: 'object',
+				nullable: false, optional: false,
+				ref: 'Announcement',
+			},
+		},
 		hasUnreadAntenna: {
 			type: 'boolean',
 			nullable: false, optional: false,
 		},
+		hasUnreadChannel: {
+			type: 'boolean',
+			nullable: false, optional: false,
+		},
 		hasUnreadNotification: {
 			type: 'boolean',
 			nullable: false, optional: false,
@@ -429,12 +544,132 @@ export const packedMeDetailedOnlySchema = {
 		},
 		emailNotificationTypes: {
 			type: 'array',
-			nullable: true, optional: false,
+			nullable: false, optional: false,
 			items: {
 				type: 'string',
 				nullable: false, optional: false,
 			},
 		},
+		achievements: {
+			type: 'array',
+			nullable: false, optional: false,
+			items: {
+				type: 'object',
+				nullable: false, optional: false,
+				properties: {
+					name: {
+						type: 'string',
+						nullable: false, optional: false,
+					},
+					unlockedAt: {
+						type: 'number',
+						nullable: false, optional: false,
+					},
+				},
+			},
+		},
+		loggedInDays: {
+			type: 'number',
+			nullable: false, optional: false,
+		},
+		policies: {
+			type: 'object',
+			nullable: false, optional: false,
+			properties: {
+				gtlAvailable: {
+					type: 'boolean',
+					nullable: false, optional: false,
+				},
+				ltlAvailable: {
+					type: 'boolean',
+					nullable: false, optional: false,
+				},
+				canPublicNote: {
+					type: 'boolean',
+					nullable: false, optional: false,
+				},
+				canInvite: {
+					type: 'boolean',
+					nullable: false, optional: false,
+				},
+				inviteLimit: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				inviteLimitCycle: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				inviteExpirationTime: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				canManageCustomEmojis: {
+					type: 'boolean',
+					nullable: false, optional: false,
+				},
+				canManageAvatarDecorations: {
+					type: 'boolean',
+					nullable: false, optional: false,
+				},
+				canSearchNotes: {
+					type: 'boolean',
+					nullable: false, optional: false,
+				},
+				canUseTranslator: {
+					type: 'boolean',
+					nullable: false, optional: false,
+				},
+				canHideAds: {
+					type: 'boolean',
+					nullable: false, optional: false,
+				},
+				driveCapacityMb: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				alwaysMarkNsfw: {
+					type: 'boolean',
+					nullable: false, optional: false,
+				},
+				pinLimit: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				antennaLimit: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				wordMuteLimit: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				webhookLimit: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				clipLimit: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				noteEachClipsLimit: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				userListLimit: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				userEachUserListsLimit: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+				rateLimitFactor: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
+			},
+		},
 		//#region secrets
 		email: {
 			type: 'string',
@@ -511,5 +746,13 @@ export const packedUserSchema = {
 			type: 'object',
 			ref: 'UserDetailed',
 		},
+		{
+			type: 'object',
+			ref: 'UserDetailedNotMe',
+		},
+		{
+			type: 'object',
+			ref: 'MeDetailed',
+		},
 	],
 } as const;

From 77ac51a680c352d5bed90a90379fcaf1454612a7 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 21 Nov 2023 11:32:13 +0900
Subject: [PATCH 016/435] update typescript to 5.3

---
 package.json                     |   2 +-
 packages/backend/package.json    |   2 +-
 packages/frontend/package.json   |   2 +-
 packages/misskey-js/package.json |   2 +-
 packages/sw/package.json         |   2 +-
 pnpm-lock.yaml                   | 172 +++++++++++++++----------------
 6 files changed, 91 insertions(+), 91 deletions(-)

diff --git a/package.json b/package.json
index d0fc867b3c..f51861401e 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,7 @@
 		"js-yaml": "4.1.0",
 		"postcss": "8.4.31",
 		"terser": "5.24.0",
-		"typescript": "5.2.2"
+		"typescript": "5.3.2"
 	},
 	"devDependencies": {
 		"@typescript-eslint/eslint-plugin": "6.11.0",
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 496c79c9c0..3b029a49d2 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -165,7 +165,7 @@
 		"tsconfig-paths": "4.2.0",
 		"twemoji-parser": "14.0.0",
 		"typeorm": "0.3.17",
-		"typescript": "5.2.2",
+		"typescript": "5.3.2",
 		"ulid": "2.3.0",
 		"vary": "1.1.2",
 		"web-push": "3.6.6",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 62192d0dab..c7736f7ac7 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -69,7 +69,7 @@
 		"tsc-alias": "1.8.8",
 		"tsconfig-paths": "4.2.0",
 		"twemoji-parser": "14.0.0",
-		"typescript": "5.2.2",
+		"typescript": "5.3.2",
 		"uuid": "9.0.1",
 		"v-code-diff": "1.7.2",
 		"vanilla-tilt": "1.8.1",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 0a4855874f..69ce173bf4 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -32,7 +32,7 @@
 		"jest-websocket-mock": "2.5.0",
 		"mock-socket": "9.3.1",
 		"tsd": "0.29.0",
-		"typescript": "5.2.2"
+		"typescript": "5.3.2"
 	},
 	"files": [
 		"built"
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 3259cae879..3c74ee8c78 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -18,7 +18,7 @@
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
 		"eslint": "8.53.0",
 		"eslint-plugin-import": "2.29.0",
-		"typescript": "5.2.2"
+		"typescript": "5.3.2"
 	},
 	"type": "module"
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 904150b075..7373d5f10b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -28,8 +28,8 @@ importers:
         specifier: 5.24.0
         version: 5.24.0
       typescript:
-        specifier: 5.2.2
-        version: 5.2.2
+        specifier: 5.3.2
+        version: 5.3.2
     optionalDependencies:
       '@tensorflow/tfjs-core':
         specifier: 4.4.0
@@ -37,10 +37,10 @@ importers:
     devDependencies:
       '@typescript-eslint/eslint-plugin':
         specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2)
+        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
         specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.2.2)
+        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
@@ -381,8 +381,8 @@ importers:
         specifier: 0.3.17
         version: 0.3.17(ioredis@5.3.2)(pg@8.11.3)
       typescript:
-        specifier: 5.2.2
-        version: 5.2.2
+        specifier: 5.3.2
+        version: 5.3.2
       ulid:
         specifier: 2.3.0
         version: 2.3.0
@@ -615,10 +615,10 @@ importers:
         version: 8.5.9
       '@typescript-eslint/eslint-plugin':
         specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2)
+        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
         specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.2.2)
+        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       aws-sdk-client-mock:
         specifier: 3.0.0
         version: 3.0.0
@@ -806,8 +806,8 @@ importers:
         specifier: 14.0.0
         version: 14.0.0
       typescript:
-        specifier: 5.2.2
-        version: 5.2.2
+        specifier: 5.3.2
+        version: 5.3.2
       uuid:
         specifier: 9.0.1
         version: 9.0.1
@@ -822,7 +822,7 @@ importers:
         version: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
       vue:
         specifier: 3.3.8
-        version: 3.3.8(typescript@5.2.2)
+        version: 3.3.8(typescript@5.3.2)
       vuedraggable:
         specifier: next
         version: 4.1.0(vue@3.3.8)
@@ -862,10 +862,10 @@ importers:
         version: 7.5.3
       '@storybook/react':
         specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
+        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)
       '@storybook/react-vite':
         specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.4.1)(typescript@5.2.2)(vite@4.5.0)
+        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.4.1)(typescript@5.3.2)(vite@4.5.0)
       '@storybook/testing-library':
         specifier: 0.2.2
         version: 0.2.2
@@ -880,7 +880,7 @@ importers:
         version: 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.8)
       '@storybook/vue3-vite':
         specifier: 7.5.3
-        version: 7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.5.0)(vue@3.3.8)
+        version: 7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@4.5.0)(vue@3.3.8)
       '@testing-library/vue':
         specifier: 8.0.0
         version: 8.0.0(@vue/compiler-sfc@3.3.8)(vue@3.3.8)
@@ -922,10 +922,10 @@ importers:
         version: 8.5.9
       '@typescript-eslint/eslint-plugin':
         specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2)
+        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
         specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.2.2)
+        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       '@vitest/coverage-v8':
         specifier: 0.34.6
         version: 0.34.6(vitest@0.34.6)
@@ -961,7 +961,7 @@ importers:
         version: 4.0.5
       msw:
         specifier: 1.3.2
-        version: 1.3.2(typescript@5.2.2)
+        version: 1.3.2(typescript@5.3.2)
       msw-storybook-addon:
         specifier: 1.10.0
         version: 1.10.0(msw@1.3.2)
@@ -1003,7 +1003,7 @@ importers:
         version: 9.3.2(eslint@8.53.0)
       vue-tsc:
         specifier: 1.8.22
-        version: 1.8.22(typescript@5.2.2)
+        version: 1.8.22(typescript@5.3.2)
 
   packages/misskey-js:
     dependencies:
@@ -1034,10 +1034,10 @@ importers:
         version: 20.9.1
       '@typescript-eslint/eslint-plugin':
         specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2)
+        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
         specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.2.2)
+        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       eslint:
         specifier: 8.53.0
         version: 8.53.0
@@ -1057,8 +1057,8 @@ importers:
         specifier: 0.29.0
         version: 0.29.0
       typescript:
-        specifier: 5.2.2
-        version: 5.2.2
+        specifier: 5.3.2
+        version: 5.3.2
 
   packages/sw:
     dependencies:
@@ -1074,7 +1074,7 @@ importers:
     devDependencies:
       '@typescript-eslint/parser':
         specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.2.2)
+        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: /@types/serviceworker@0.0.67
@@ -1085,8 +1085,8 @@ importers:
         specifier: 2.29.0
         version: 2.29.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)
       typescript:
-        specifier: 5.2.2
-        version: 5.2.2
+        specifier: 5.3.2
+        version: 5.3.2
 
 packages:
 
@@ -4254,7 +4254,7 @@ packages:
       chalk: 4.1.2
     dev: true
 
-  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.2.2)(vite@4.5.0):
+  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.2)(vite@4.5.0):
     resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==}
     peerDependencies:
       typescript: '>= 4.3.x'
@@ -4266,8 +4266,8 @@ packages:
       glob: 7.2.3
       glob-promise: 4.2.2(glob@7.2.3)
       magic-string: 0.27.0
-      react-docgen-typescript: 2.2.2(typescript@5.2.2)
-      typescript: 5.2.2
+      react-docgen-typescript: 2.2.2(typescript@5.3.2)
+      typescript: 5.3.2
       vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
     dev: true
 
@@ -6348,7 +6348,7 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-vite@7.5.3(typescript@5.2.2)(vite@4.5.0):
+  /@storybook/builder-vite@7.5.3(typescript@5.3.2)(vite@4.5.0):
     resolution: {integrity: sha512-c104V3O75OCVnfZj0Jr70V09g0KSbPGvQK2Zh31omXGvakG8XrhWolYxkmjOcForJmAqsXnKs/nw3F75Gp853g==}
     peerDependencies:
       '@preact/preset-vite': '*'
@@ -6379,7 +6379,7 @@ packages:
       fs-extra: 11.1.1
       magic-string: 0.30.5
       rollup: 3.29.4
-      typescript: 5.2.2
+      typescript: 5.3.2
       vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - encoding
@@ -6750,7 +6750,7 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/react-vite@7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.4.1)(typescript@5.2.2)(vite@4.5.0):
+  /@storybook/react-vite@7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.4.1)(typescript@5.3.2)(vite@4.5.0):
     resolution: {integrity: sha512-ArPyHgiPbT5YvcyK4xK/DfqBOpn4R4/EP3kfIGhx8QKJyOtxPEYFdkLIZ5xu3KnPX7/z7GT+4a6Rb+8sk9gliA==}
     engines: {node: '>=16'}
     peerDependencies:
@@ -6758,10 +6758,10 @@ packages:
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
       vite: ^3.0.0 || ^4.0.0 || ^5.0.0
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.2.2)(vite@4.5.0)
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.2)(vite@4.5.0)
       '@rollup/pluginutils': 5.0.5(rollup@4.4.1)
-      '@storybook/builder-vite': 7.5.3(typescript@5.2.2)(vite@4.5.0)
-      '@storybook/react': 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)
+      '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@4.5.0)
+      '@storybook/react': 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)
       '@vitejs/plugin-react': 3.1.0(vite@4.5.0)
       magic-string: 0.30.5
       react: 18.2.0
@@ -6777,7 +6777,7 @@ packages:
       - vite-plugin-glimmerx
     dev: true
 
-  /@storybook/react@7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2):
+  /@storybook/react@7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2):
     resolution: {integrity: sha512-dZILdM36xMFDjdmmy421G5X+sOIncB2qF3IPTooniG1i1Z6v/dVNo57ovdID9lDTNa+AWr2fLB9hANiISMqmjQ==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
@@ -6810,7 +6810,7 @@ packages:
       react-element-to-jsx-string: 15.0.0(react-dom@18.2.0)(react@18.2.0)
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      typescript: 5.2.2
+      typescript: 5.3.2
       util-deprecate: 1.0.2
     transitivePeerDependencies:
       - encoding
@@ -6892,7 +6892,7 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.2.2)(vite@4.5.0)(vue@3.3.8):
+  /@storybook/vue3-vite@7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@4.5.0)(vue@3.3.8):
     resolution: {integrity: sha512-gkNwDDn2AKthAtaoPrHb0+2gi33UluxpfSq/M5COoMEVFphj6y/jyDa+OEYlceXgnD8g2xvX4/yv2TbTNDzmcQ==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
@@ -6900,7 +6900,7 @@ packages:
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
       vite: ^3.0.0 || ^4.0.0 || ^5.0.0
     dependencies:
-      '@storybook/builder-vite': 7.5.3(typescript@5.2.2)(vite@4.5.0)
+      '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@4.5.0)
       '@storybook/core-server': 7.5.3
       '@storybook/vue3': 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.8)
       '@vitejs/plugin-vue': 4.5.0(vite@4.5.0)(vue@3.3.8)
@@ -6937,7 +6937,7 @@ packages:
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      vue: 3.3.8(typescript@5.2.2)
+      vue: 3.3.8(typescript@5.3.2)
       vue-component-type-helpers: 1.8.22
     transitivePeerDependencies:
       - encoding
@@ -7432,7 +7432,7 @@ packages:
       '@testing-library/dom': 9.3.3
       '@vue/compiler-sfc': 3.3.8
       '@vue/test-utils': 2.4.1(vue@3.3.8)
-      vue: 3.3.8(typescript@5.2.2)
+      vue: 3.3.8(typescript@5.3.2)
     transitivePeerDependencies:
       - '@vue/server-renderer'
     dev: true
@@ -8073,7 +8073,7 @@ packages:
     dev: true
     optional: true
 
-  /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.2.2):
+  /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2):
     resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8085,10 +8085,10 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.2.2)
+      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       '@typescript-eslint/scope-manager': 6.11.0
-      '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2)
-      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2)
+      '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       '@typescript-eslint/visitor-keys': 6.11.0
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.53.0
@@ -8096,13 +8096,13 @@ packages:
       ignore: 5.2.4
       natural-compare: 1.4.0
       semver: 7.5.4
-      ts-api-utils: 1.0.1(typescript@5.2.2)
-      typescript: 5.2.2
+      ts-api-utils: 1.0.1(typescript@5.3.2)
+      typescript: 5.3.2
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.2.2):
+  /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.2):
     resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8114,11 +8114,11 @@ packages:
     dependencies:
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
-      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2)
+      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
       '@typescript-eslint/visitor-keys': 6.11.0
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.53.0
-      typescript: 5.2.2
+      typescript: 5.3.2
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -8131,7 +8131,7 @@ packages:
       '@typescript-eslint/visitor-keys': 6.11.0
     dev: true
 
-  /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.2.2):
+  /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.2):
     resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8141,12 +8141,12 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2)
-      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.2.2)
+      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
+      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.53.0
-      ts-api-utils: 1.0.1(typescript@5.2.2)
-      typescript: 5.2.2
+      ts-api-utils: 1.0.1(typescript@5.3.2)
+      typescript: 5.3.2
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -8156,7 +8156,7 @@ packages:
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
-  /@typescript-eslint/typescript-estree@6.11.0(typescript@5.2.2):
+  /@typescript-eslint/typescript-estree@6.11.0(typescript@5.3.2):
     resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8171,13 +8171,13 @@ packages:
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
-      ts-api-utils: 1.0.1(typescript@5.2.2)
-      typescript: 5.2.2
+      ts-api-utils: 1.0.1(typescript@5.3.2)
+      typescript: 5.3.2
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.2.2):
+  /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.3.2):
     resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8188,7 +8188,7 @@ packages:
       '@types/semver': 7.5.5
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
-      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.2.2)
+      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
       eslint: 8.53.0
       semver: 7.5.4
     transitivePeerDependencies:
@@ -8232,7 +8232,7 @@ packages:
       vue: ^3.2.25
     dependencies:
       vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
-      vue: 3.3.8(typescript@5.2.2)
+      vue: 3.3.8(typescript@5.3.2)
 
   /@vitest/coverage-v8@0.34.6(vitest@0.34.6):
     resolution: {integrity: sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==}
@@ -8327,7 +8327,7 @@ packages:
       ast-kit: 0.11.2(rollup@4.4.1)
       local-pkg: 0.5.0
       magic-string-ast: 0.3.0
-      vue: 3.3.8(typescript@5.2.2)
+      vue: 3.3.8(typescript@5.3.2)
     transitivePeerDependencies:
       - rollup
     dev: false
@@ -8344,7 +8344,7 @@ packages:
       '@vue/shared': 3.3.8
       magic-string: 0.30.5
       unplugin: 1.5.1
-      vue: 3.3.8(typescript@5.2.2)
+      vue: 3.3.8(typescript@5.3.2)
     transitivePeerDependencies:
       - rollup
     dev: false
@@ -8415,7 +8415,7 @@ packages:
       '@vue/compiler-dom': 3.3.8
       '@vue/shared': 3.3.8
 
-  /@vue/language-core@1.8.22(typescript@5.2.2):
+  /@vue/language-core@1.8.22(typescript@5.3.2):
     resolution: {integrity: sha512-bsMoJzCrXZqGsxawtUea1cLjUT9dZnDsy5TuZ+l1fxRMzUGQUG9+Ypq4w//CqpWmrx7nIAJpw2JVF/t258miRw==}
     peerDependencies:
       typescript: '*'
@@ -8430,7 +8430,7 @@ packages:
       computeds: 0.0.1
       minimatch: 9.0.3
       muggle-string: 0.3.1
-      typescript: 5.2.2
+      typescript: 5.3.2
       vue-template-compiler: 2.7.14
     dev: true
 
@@ -8468,7 +8468,7 @@ packages:
     dependencies:
       '@vue/compiler-ssr': 3.3.8
       '@vue/shared': 3.3.8
-      vue: 3.3.8(typescript@5.2.2)
+      vue: 3.3.8(typescript@5.3.2)
 
   /@vue/shared@3.3.6:
     resolution: {integrity: sha512-Xno5pEqg8SVhomD0kTSmfh30ZEmV/+jZtyh39q6QflrjdJCXah5lrnOLi9KB6a5k5aAHXMXjoMnxlzUkCNfWLQ==}
@@ -8491,7 +8491,7 @@ packages:
         optional: true
     dependencies:
       js-beautify: 1.14.9
-      vue: 3.3.8(typescript@5.2.2)
+      vue: 3.3.8(typescript@5.3.2)
       vue-component-type-helpers: 1.8.4
     dev: true
 
@@ -11158,7 +11158,7 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.2.2)
+      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       debug: 3.2.7(supports-color@5.5.0)
       eslint: 8.53.0
       eslint-import-resolver-node: 0.3.9
@@ -11176,7 +11176,7 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.2.2)
+      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
@@ -14852,10 +14852,10 @@ packages:
       msw: '>=0.35.0 <2.0.0'
     dependencies:
       is-node-process: 1.2.0
-      msw: 1.3.2(typescript@5.2.2)
+      msw: 1.3.2(typescript@5.3.2)
     dev: true
 
-  /msw@1.3.2(typescript@5.2.2):
+  /msw@1.3.2(typescript@5.3.2):
     resolution: {integrity: sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==}
     engines: {node: '>=14'}
     hasBin: true
@@ -14884,7 +14884,7 @@ packages:
       path-to-regexp: 6.2.1
       strict-event-emitter: 0.4.6
       type-fest: 2.19.0
-      typescript: 5.2.2
+      typescript: 5.3.2
       yargs: 17.6.2
     transitivePeerDependencies:
       - encoding
@@ -16773,12 +16773,12 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /react-docgen-typescript@2.2.2(typescript@5.2.2):
+  /react-docgen-typescript@2.2.2(typescript@5.3.2):
     resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==}
     peerDependencies:
       typescript: '>= 4.3.x'
     dependencies:
-      typescript: 5.2.2
+      typescript: 5.3.2
     dev: true
 
   /react-docgen@6.0.4:
@@ -18627,13 +18627,13 @@ packages:
       escape-string-regexp: 5.0.0
     dev: false
 
-  /ts-api-utils@1.0.1(typescript@5.2.2):
+  /ts-api-utils@1.0.1(typescript@5.3.2):
     resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==}
     engines: {node: '>=16.13.0'}
     peerDependencies:
       typescript: '>=4.2.0'
     dependencies:
-      typescript: 5.2.2
+      typescript: 5.3.2
     dev: true
 
   /ts-dedent@2.2.0:
@@ -18897,8 +18897,8 @@ packages:
     hasBin: true
     dev: true
 
-  /typescript@5.2.2:
-    resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
+  /typescript@5.3.2:
+    resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==}
     engines: {node: '>=14.17'}
     hasBin: true
 
@@ -19186,7 +19186,7 @@ packages:
       diff: 5.1.0
       diff-match-patch: 1.0.5
       highlight.js: 11.8.0
-      vue: 3.3.8(typescript@5.2.2)
+      vue: 3.3.8(typescript@5.3.2)
       vue-demi: 0.13.11(vue@3.3.8)
     dev: false
 
@@ -19400,7 +19400,7 @@ packages:
       '@vue/composition-api':
         optional: true
     dependencies:
-      vue: 3.3.8(typescript@5.2.2)
+      vue: 3.3.8(typescript@5.3.2)
     dev: false
 
   /vue-docgen-api@4.64.1(vue@3.3.8):
@@ -19444,7 +19444,7 @@ packages:
     peerDependencies:
       vue: '>=2'
     dependencies:
-      vue: 3.3.8(typescript@5.2.2)
+      vue: 3.3.8(typescript@5.3.2)
     dev: true
 
   /vue-template-compiler@2.7.14:
@@ -19454,19 +19454,19 @@ packages:
       he: 1.2.0
     dev: true
 
-  /vue-tsc@1.8.22(typescript@5.2.2):
+  /vue-tsc@1.8.22(typescript@5.3.2):
     resolution: {integrity: sha512-j9P4kHtW6eEE08aS5McFZE/ivmipXy0JzrnTgbomfABMaVKx37kNBw//irL3+LlE3kOo63XpnRigyPC3w7+z+A==}
     hasBin: true
     peerDependencies:
       typescript: '*'
     dependencies:
       '@volar/typescript': 1.10.7
-      '@vue/language-core': 1.8.22(typescript@5.2.2)
+      '@vue/language-core': 1.8.22(typescript@5.3.2)
       semver: 7.5.4
-      typescript: 5.2.2
+      typescript: 5.3.2
     dev: true
 
-  /vue@3.3.8(typescript@5.2.2):
+  /vue@3.3.8(typescript@5.3.2):
     resolution: {integrity: sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==}
     peerDependencies:
       typescript: '*'
@@ -19479,7 +19479,7 @@ packages:
       '@vue/runtime-dom': 3.3.8
       '@vue/server-renderer': 3.3.8(vue@3.3.8)
       '@vue/shared': 3.3.8
-      typescript: 5.2.2
+      typescript: 5.3.2
 
   /vuedraggable@4.1.0(vue@3.3.8):
     resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
@@ -19487,7 +19487,7 @@ packages:
       vue: ^3.0.1
     dependencies:
       sortablejs: 1.14.0
-      vue: 3.3.8(typescript@5.2.2)
+      vue: 3.3.8(typescript@5.3.2)
     dev: false
 
   /w3c-xmlserializer@4.0.0:

From b5be0e5780453653ca37ea809c36757057a21758 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Tue, 21 Nov 2023 15:12:05 +0900
Subject: [PATCH 017/435] =?UTF-8?q?note.ts=E3=81=AEchannel=E3=82=92?=
 =?UTF-8?q?=E6=AD=A3=E3=81=97=E3=81=84=E5=BD=A2=E3=81=AB=E3=81=97=E3=81=9F?=
 =?UTF-8?q?=E3=81=93=E3=81=A8=E3=81=AB=E3=82=88=E3=82=8A=E8=A1=A8=E5=87=BA?=
 =?UTF-8?q?=E5=8C=96=E3=81=97=E3=81=9F=E5=9E=8B=E3=83=81=E3=82=A7=E3=83=83?=
 =?UTF-8?q?=E3=82=AF=E3=82=A8=E3=83=A9=E3=83=BC=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#12395)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
---
 packages/backend/src/models/json-schema/note.ts | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts
index 9d5d558f51..392fa7e1cb 100644
--- a/packages/backend/src/models/json-schema/note.ts
+++ b/packages/backend/src/models/json-schema/note.ts
@@ -134,11 +134,19 @@ export const packedNoteSchema = {
 				},
 				name: {
 					type: 'string',
-					optional: false, nullable: true,
+					optional: false, nullable: false,
+				},
+				color: {
+					type: 'string',
+					optional: false, nullable: false,
 				},
 				isSensitive: {
 					type: 'boolean',
-					optional: true, nullable: false,
+					optional: false, nullable: false,
+				},
+				allowRenoteToExternal: {
+					type: 'boolean',
+					optional: false, nullable: false,
 				},
 			},
 		},

From b3d1cc9525b44e37b983c3a97af4e2aea80ea735 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Tue, 21 Nov 2023 15:32:34 +0900
Subject: [PATCH 018/435] =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E8=B5=B7?=
 =?UTF-8?q?=E5=8B=95=E6=99=82=E3=81=AB=E3=82=A2=E3=83=B3=E3=83=86=E3=83=8A?=
 =?UTF-8?q?=E3=81=8C=E9=9D=9E=E3=82=A2=E3=82=AF=E3=83=86=E3=82=A3=E3=83=96?=
 =?UTF-8?q?=E3=81=A0=E3=81=A3=E3=81=9F=E5=A0=B4=E5=90=88=E3=80=81=E3=82=A2?=
 =?UTF-8?q?=E3=82=AF=E3=83=86=E3=82=A3=E3=83=96=E5=8C=96=E3=81=97=E3=81=A6?=
 =?UTF-8?q?=E3=82=82=E5=86=8D=E8=B5=B7=E5=8B=95=E3=81=99=E3=82=8B=E3=81=BE?=
 =?UTF-8?q?=E3=81=A7=E5=8F=8D=E6=98=A0=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=20(#12391)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* サーバ起動時にアンテナが非アクティブだった場合、アクティブ化しても再起動するまで反映されない

* Fix CHANGELOG.md

* lastUsedAtの更新に不備が出るので修正

---------

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
---
 CHANGELOG.md                                  |  1 +
 packages/backend/src/core/AntennaService.ts   | 20 ++++++++++++++-----
 .../server/api/endpoints/antennas/notes.ts    | 16 +++++++++++----
 3 files changed, 28 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2efa1b230d..900d042eaf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 
 ### Server
+- Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303
 - Fix: ロールタイムラインが保存されない問題を修正
 
 ## 2023.11.1
diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts
index 65be275548..2815d24734 100644
--- a/packages/backend/src/core/AntennaService.ts
+++ b/packages/backend/src/core/AntennaService.ts
@@ -60,11 +60,21 @@ export class AntennaService implements OnApplicationShutdown {
 						lastUsedAt: new Date(body.lastUsedAt),
 					});
 					break;
-				case 'antennaUpdated':
-					this.antennas[this.antennas.findIndex(a => a.id === body.id)] = {
-						...body,
-						lastUsedAt: new Date(body.lastUsedAt),
-					};
+				case 'antennaUpdated': {
+					const idx = this.antennas.findIndex(a => a.id === body.id);
+					if (idx >= 0) {
+						this.antennas[idx] = {
+							...body,
+							lastUsedAt: new Date(body.lastUsedAt),
+						};
+					} else {
+						// サーバ起動時にactiveじゃなかった場合、リストに持っていないので追加する必要あり
+						this.antennas.push({
+							...body,
+							lastUsedAt: new Date(body.lastUsedAt),
+						});
+					}
+				}
 					break;
 				case 'antennaDeleted':
 					this.antennas = this.antennas.filter(a => a.id !== body.id);
diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts
index 9b5911800c..29e56b1085 100644
--- a/packages/backend/src/server/api/endpoints/antennas/notes.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts
@@ -13,6 +13,7 @@ import { DI } from '@/di-symbols.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { IdService } from '@/core/IdService.js';
 import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -71,6 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private queryService: QueryService,
 		private noteReadService: NoteReadService,
 		private funoutTimelineService: FunoutTimelineService,
+		private globalEventService: GlobalEventService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
@@ -85,10 +87,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.noSuchAntenna);
 			}
 
-			this.antennasRepository.update(antenna.id, {
-				isActive: true,
-				lastUsedAt: new Date(),
-			});
+			// falseだった場合はアンテナの配信先が増えたことを通知したい
+			const needPublishEvent = !antenna.isActive;
+
+			antenna.isActive = true;
+			antenna.lastUsedAt = new Date();
+			this.antennasRepository.update(antenna.id, antenna);
+
+			if (needPublishEvent) {
+				this.globalEventService.publishInternalEvent('antennaUpdated', antenna);
+			}
 
 			let noteIds = await this.funoutTimelineService.get(`antennaTimeline:${antenna.id}`, untilId, sinceId);
 			noteIds = noteIds.slice(0, ps.limit);

From 481bca4cf2e8f13a46654770291b7ee31a1c361e Mon Sep 17 00:00:00 2001
From: nenohi <kimutipartylove@gmail.com>
Date: Tue, 21 Nov 2023 19:50:06 +0900
Subject: [PATCH 019/435] =?UTF-8?q?=E5=BA=83=E5=91=8A=E6=8E=B2=E8=BC=89?=
 =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AB=E3=81=A6filter=E3=82=92?=
 =?UTF-8?q?=E3=82=8F=E3=81=8B=E3=82=8A=E3=82=84=E3=81=99=E3=81=8F=20(#1238?=
 =?UTF-8?q?5)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/server/api/endpoints/admin/ad/list.ts |  6 ++-
 packages/frontend/src/pages/admin/ads.vue     | 50 ++++++++++++-------
 2 files changed, 35 insertions(+), 21 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
index 29eff89523..1366fbf76a 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
@@ -22,7 +22,7 @@ export const paramDef = {
 		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
 		sinceId: { type: 'string', format: 'misskey:id' },
 		untilId: { type: 'string', format: 'misskey:id' },
-		publishing: { type: 'boolean', default: false },
+		publishing: { type: 'boolean', default: null, nullable: true},
 	},
 	required: [],
 } as const;
@@ -37,8 +37,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId);
-			if (ps.publishing) {
+			if (ps.publishing === true) {
 				query.andWhere('ad.expiresAt > :now', { now: new Date() }).andWhere('ad.startsAt <= :now', { now: new Date() });
+			} else if (ps.publishing === false) {
+				query.andWhere('ad.expiresAt <= :now', { now: new Date() }).orWhere('ad.startsAt > :now', { now: new Date() });
 			}
 			const ads = await query.limit(ps.limit).getMany();
 
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 6e07585fdc..1ce99d4ba5 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -9,12 +9,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<XHeader :actions="headerActions" :tabs="headerTabs"/>
 	</template>
 	<MkSpacer :contentMax="900">
-		<MkSwitch :modelValue="publishing" @update:modelValue="onChangePublishing">
-			{{ i18n.ts.publishing }}
-		</MkSwitch>
+		<MkSelect v-model="type" :class="$style.input" @update:modelValue="onChangePublishing">
+			<template #label>{{ i18n.ts.state }}</template>
+			<option value="null">{{ i18n.ts.all }}</option>
+			<option value="true">{{ i18n.ts.publishing }}</option>
+			<option value="false">{{ i18n.ts.expired }}</option>
+		</MkSelect>
 		<div>
 			<div v-for="ad in ads" class="_panel _gaps_m" :class="$style.ad">
-				<MkAd v-if="ad.url" :specify="ad"/>
+				<MkAd v-if="ad.url" :key="ad.id" :specify="ad"/>
 				<MkInput v-model="ad.url" type="url">
 					<template #label>URL</template>
 				</MkInput>
@@ -82,14 +85,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkFolder from '@/components/MkFolder.vue';
-import MkSwitch from '@/components/MkSwitch.vue';
+import MkSelect from '@/components/MkSelect.vue';
 import FormSplit from '@/components/form/split.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
@@ -101,24 +104,28 @@ let ads: any[] = $ref([]);
 const localTime = new Date();
 const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000;
 const daysOfWeek: string[] = [i18n.ts._weekday.sunday, i18n.ts._weekday.monday, i18n.ts._weekday.tuesday, i18n.ts._weekday.wednesday, i18n.ts._weekday.thursday, i18n.ts._weekday.friday, i18n.ts._weekday.saturday];
-let publishing = false;
+let publishing: boolean | null = null;
+let type = ref('null');
 
 os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
-	ads = adsResponse.map(r => {
-		const exdate = new Date(r.expiresAt);
-		const stdate = new Date(r.startsAt);
-		exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
-		stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
-		return {
-			...r,
-			expiresAt: exdate.toISOString().slice(0, 16),
-			startsAt: stdate.toISOString().slice(0, 16),
-		};
-	});
+	if (adsResponse != null) {
+		ads = adsResponse.map(r => {
+			const exdate = new Date(r.expiresAt);
+			const stdate = new Date(r.startsAt);
+			exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
+			stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
+			return {
+				...r,
+				expiresAt: exdate.toISOString().slice(0, 16),
+				startsAt: stdate.toISOString().slice(0, 16),
+			};
+		});
+	}
 });
 
 const onChangePublishing = (v) => {
-	publishing = v;
+	console.log(v);
+	publishing = v === 'true' ? true : v === 'false' ? false : null;
 	refresh();
 };
 
@@ -197,6 +204,7 @@ function save(ad) {
 
 function more() {
 	os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
+		if (adsResponse == null) return;
 		ads = ads.concat(adsResponse.map(r => {
 			const exdate = new Date(r.expiresAt);
 			const stdate = new Date(r.startsAt);
@@ -213,6 +221,7 @@ function more() {
 
 function refresh() {
 	os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
+		if (adsResponse == null) return;
 		ads = adsResponse.map(r => {
 			const exdate = new Date(r.expiresAt);
 			const stdate = new Date(r.startsAt);
@@ -252,4 +261,7 @@ definePageMetadata({
 		margin-bottom: var(--margin);
 	}
 }
+.input {
+	margin-bottom: 32px;
+}
 </style>

From 4b13179ff919e4424eb60a37db0e72df4bd12101 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Tue, 21 Nov 2023 20:05:04 +0900
Subject: [PATCH 020/435] =?UTF-8?q?=E3=82=B5=E3=82=A6=E3=83=B3=E3=83=89?=
 =?UTF-8?q?=E5=86=8D=E7=94=9F=E6=96=B9=E6=B3=95=E3=81=AE=E5=A4=89=E6=9B=B4?=
 =?UTF-8?q?=E3=81=AB=E8=BF=BD=E5=BE=93=E3=81=A7=E3=81=8D=E3=81=A6=E3=81=84?=
 =?UTF-8?q?=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F=E6=89=80=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#12368)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
---
 CHANGELOG.md                                  |  1 +
 packages/frontend/src/scripts/sound.ts        | 20 +++++++++----------
 .../frontend/src/widgets/WidgetJobQueue.vue   | 14 ++++++++++---
 3 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 900d042eaf..7feb89b546 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@
 
 ### Client
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
+- Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 
 ### Server
 - Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 4b0cd0bb39..2b604bd98a 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -61,7 +61,7 @@ export const soundsTypes = [
 	'noizenecio/kick_gaba7',
 ] as const;
 
-export async function getAudio(file: string, useCache = true) {
+export async function loadAudio(file: string, useCache = true) {
 	if (useCache && cache.has(file)) {
 		return cache.get(file)!;
 	}
@@ -77,12 +77,6 @@ export async function getAudio(file: string, useCache = true) {
 	return audioBuffer;
 }
 
-export function setVolume(audio: HTMLAudioElement, volume: number): HTMLAudioElement {
-	const masterVolume = defaultStore.state.sound_masterVolume;
-	audio.volume = masterVolume - ((1 - volume) * masterVolume);
-	return audio;
-}
-
 export function play(type: 'noteMy' | 'note' | 'antenna' | 'channel' | 'notification') {
 	const sound = defaultStore.state[`sound_${type}`];
 	if (_DEV_) console.log('play', type, sound);
@@ -91,16 +85,22 @@ export function play(type: 'noteMy' | 'note' | 'antenna' | 'channel' | 'notifica
 }
 
 export async function playFile(file: string, volume: number) {
+	const buffer = await loadAudio(file);
+	createSourceNode(buffer, volume)?.start();
+}
+
+export function createSourceNode(buffer: AudioBuffer, volume: number) : AudioBufferSourceNode | null {
 	const masterVolume = defaultStore.state.sound_masterVolume;
 	if (masterVolume === 0 || volume === 0) {
-		return;
+		return null;
 	}
 
 	const gainNode = ctx.createGain();
 	gainNode.gain.value = masterVolume * volume;
 
 	const soundSource = ctx.createBufferSource();
-	soundSource.buffer = await getAudio(file);
+	soundSource.buffer = buffer;
 	soundSource.connect(gainNode).connect(ctx.destination);
-	soundSource.start();
+
+	return soundSource;
 }
diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue
index 89770b2216..fa82997570 100644
--- a/packages/frontend/src/widgets/WidgetJobQueue.vue
+++ b/packages/frontend/src/widgets/WidgetJobQueue.vue
@@ -99,7 +99,10 @@ const current = reactive({
 	},
 });
 const prev = reactive({} as typeof current);
-const jammedSound = sound.setVolume(sound.getAudio('syuilo/queue-jammed'), 1);
+let jammedAudioBuffer: AudioBuffer | null = $ref(null);
+let jammedSoundNodePlaying: boolean = $ref(false);
+
+sound.loadAudio('syuilo/queue-jammed').then(buf => jammedAudioBuffer = buf);
 
 for (const domain of ['inbox', 'deliver']) {
 	prev[domain] = deepClone(current[domain]);
@@ -113,8 +116,13 @@ const onStats = (stats) => {
 		current[domain].waiting = stats[domain].waiting;
 		current[domain].delayed = stats[domain].delayed;
 
-		if (current[domain].waiting > 0 && widgetProps.sound && jammedSound.paused) {
-			jammedSound.play();
+		if (current[domain].waiting > 0 && widgetProps.sound && jammedAudioBuffer && !jammedSoundNodePlaying) {
+			const soundNode = sound.createSourceNode(jammedAudioBuffer, 1);
+			if (soundNode) {
+				jammedSoundNodePlaying = true;
+				soundNode.onended = () => jammedSoundNodePlaying = false;
+				soundNode.start();
+			}
 		}
 	}
 };

From 18bdec9641b77e9b15e57e50c5fd688881cf2b80 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=9A=90=E6=9C=88=E3=81=AA=E3=81=B5=20=28Nafu=20Satsuki?=
 =?UTF-8?q?=29?= <satsuki@nafusoft.dev>
Date: Wed, 22 Nov 2023 10:13:46 +0900
Subject: [PATCH 021/435] =?UTF-8?q?fix:=20verifymail.io=20API=E3=81=AE?=
 =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E9=A0=85=E7=9B=AE=E3=81=8C=E5=8F=8D=E6=98=A0?=
 =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=20(#12399)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index cc9afaf7fd..1dddb166ae 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -267,6 +267,14 @@ export const meta = {
 				type: 'boolean',
 				optional: false, nullable: false,
 			},
+			enableVerifymailApi: {
+				type: 'boolean',
+				optional: false, nullable: false,
+			},
+			verifymailAuthKey: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
 			enableChartsForRemoteUser: {
 				type: 'boolean',
 				optional: false, nullable: false,
@@ -421,6 +429,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				deeplIsPro: instance.deeplIsPro,
 				enableIpLogging: instance.enableIpLogging,
 				enableActiveEmailValidation: instance.enableActiveEmailValidation,
+				enableVerifymailApi: instance.enableVerifymailApi,
+				verifymailAuthKey: instance.verifymailAuthKey,
 				enableChartsForRemoteUser: instance.enableChartsForRemoteUser,
 				enableChartsForFederatedInstances: instance.enableChartsForFederatedInstances,
 				enableServerMachineStats: instance.enableServerMachineStats,

From c6ed06d783a2d49ca029cdf5284150bbfd3c9976 Mon Sep 17 00:00:00 2001
From: "y.takahashi" <eai@mizle.net>
Date: Wed, 22 Nov 2023 10:19:30 +0900
Subject: [PATCH 022/435] =?UTF-8?q?twitter=E5=9F=8B=E3=82=81=E8=BE=BC?=
 =?UTF-8?q?=E3=81=BF=E3=81=AEsandbox=E5=B1=9E=E6=80=A7=E3=81=ABallow-popup?=
 =?UTF-8?q?s-to-escape-sandbox=E3=82=92=E8=BF=BD=E5=8A=A0=20(#12400)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: unarist <m.unarist@gmail.com>
---
 packages/frontend/src/components/MkUrlPreview.vue | 2 +-
 packages/frontend/test/url-preview.test.ts        | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index e2844f8fa1..a460f3ea07 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<iframe
 			ref="tweet"
 			allow="fullscreen;web-share"
-			sandbox="allow-popups allow-scripts allow-same-origin"
+			sandbox="allow-popups allow-popups-to-escape-sandbox allow-scripts allow-same-origin"
 			scrolling="no"
 			:style="{ position: 'relative', width: '100%', height: `${tweetHeight}px`, border: 0 }"
 			:src="`https://platform.twitter.com/embed/index.html?embedId=${embedId}&amp;hideCard=false&amp;hideThread=false&amp;lang=en&amp;theme=${defaultStore.state.darkMode ? 'dark' : 'light'}&amp;id=${tweetId}`"
diff --git a/packages/frontend/test/url-preview.test.ts b/packages/frontend/test/url-preview.test.ts
index 811f07d9c7..f760de9274 100644
--- a/packages/frontend/test/url-preview.test.ts
+++ b/packages/frontend/test/url-preview.test.ts
@@ -150,7 +150,7 @@ describe('MkUrlPreview', () => {
 		});
 		assert.exists(iframe, 'iframe should exist');
 		assert.strictEqual(iframe?.getAttribute('allow'), 'fullscreen;web-share');
-		assert.strictEqual(iframe?.getAttribute('sandbox'), 'allow-popups allow-scripts allow-same-origin');
+		assert.strictEqual(iframe?.getAttribute('sandbox'), 'allow-popups allow-popups-to-escape-sandbox allow-scripts allow-same-origin');
 	});
 
 	test('Loading a post in iframe', async () => {
@@ -159,6 +159,6 @@ describe('MkUrlPreview', () => {
 		});
 		assert.exists(iframe, 'iframe should exist');
 		assert.strictEqual(iframe?.getAttribute('allow'), 'fullscreen;web-share');
-		assert.strictEqual(iframe?.getAttribute('sandbox'), 'allow-popups allow-scripts allow-same-origin');
+		assert.strictEqual(iframe?.getAttribute('sandbox'), 'allow-popups allow-popups-to-escape-sandbox allow-scripts allow-same-origin');
 	});
 });

From a4f886378627811d7fb8a5f58700c5fb2a1084e4 Mon Sep 17 00:00:00 2001
From: taichan <40626578+tai-cha@users.noreply.github.com>
Date: Wed, 22 Nov 2023 12:20:07 +0900
Subject: [PATCH 023/435] taichanne30 -> tai-cha (#12405)

---
 packages/frontend/src/pages/about-misskey.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 9fe7f7f79c..b106806135 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -65,9 +65,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<img src="https://avatars.githubusercontent.com/u/67428053?v=4" :class="$style.contributorAvatar">
 							<span :class="$style.contributorUsername">@kakkokari-gtyih</span>
 						</a>
-						<a href="https://github.com/taichanNE30" target="_blank" :class="$style.contributor">
+						<a href="https://github.com/tai-cha" target="_blank" :class="$style.contributor">
 							<img src="https://avatars.githubusercontent.com/u/40626578?v=4" :class="$style.contributorAvatar">
-							<span :class="$style.contributorUsername">@taichanNE30</span>
+							<span :class="$style.contributorUsername">@tai-cha</span>
 						</a>
 					</div>
 				</FormSection>

From c284d41b5bff3244e3b79396aff8e87bc80425a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Wed, 22 Nov 2023 17:08:56 +0900
Subject: [PATCH 024/435] =?UTF-8?q?swagger-cli=20validate=E3=81=8Cvalid?=
 =?UTF-8?q?=E3=81=A8=E3=81=AA=E3=82=8Bapi.json=E3=82=92=E4=BD=9C=E3=82=8C?=
 =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#1240?=
 =?UTF-8?q?3)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* api.jsonがswagger-cli validateでエラーにならないように生成ロジックを修正

* フィールドの消し方に不備があったので変更

* バックエンドを起動しなくてもapi.jsonを作れるようにした

* deepCopyしてからレスポンス部分を作るようにした

* fix CHANGELOG.md

* securitySchemesの定義を復活&ApiCallServiceの実装的にベアラトークンなのでその形で

* bodyが無い(空オブジェクト)のときはrequestBodyを描画しないようにする

* allowGetがtrueな項目はget用の記載も作成

---------

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                  |  1 +
 packages/backend/generate_api_json.js         |  8 ++++
 packages/backend/package.json                 |  3 +-
 .../api/endpoints/federation/show-instance.ts |  9 ++--
 .../src/server/api/openapi/gen-spec.ts        | 42 +++++++++++++------
 .../backend/src/server/api/openapi/schemas.ts | 10 ++++-
 6 files changed, 51 insertions(+), 22 deletions(-)
 create mode 100644 packages/backend/generate_api_json.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7feb89b546..1113374595 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
 ### Server
 - Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303
 - Fix: ロールタイムラインが保存されない問題を修正
+- Fix: api.jsonの生成ロジックを改善 #12402
 
 ## 2023.11.1
 
diff --git a/packages/backend/generate_api_json.js b/packages/backend/generate_api_json.js
new file mode 100644
index 0000000000..5819c60a5f
--- /dev/null
+++ b/packages/backend/generate_api_json.js
@@ -0,0 +1,8 @@
+import { loadConfig } from './built/config.js'
+import { genOpenapiSpec } from './built/server/api/openapi/gen-spec.js'
+import { writeFileSync } from "node:fs";
+
+const config = loadConfig();
+const spec = genOpenapiSpec(config);
+
+writeFileSync('./built/api.json', JSON.stringify(spec), 'utf-8');
\ No newline at end of file
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 3b029a49d2..18c7818dcc 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -23,7 +23,8 @@
 		"jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit",
 		"jest-clear": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --clearCache",
 		"test": "pnpm jest",
-		"test-and-coverage": "pnpm jest-and-coverage"
+		"test-and-coverage": "pnpm jest-and-coverage",
+		"generate-api-json": "node ./generate_api_json.js"
 	},
 	"optionalDependencies": {
 		"@swc/core-android-arm64": "1.3.11",
diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts
index 71eec11235..781c15e742 100644
--- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts
+++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts
@@ -16,12 +16,9 @@ export const meta = {
 	requireCredential: false,
 
 	res: {
-		oneOf: [{
-			type: 'object',
-			ref: 'FederationInstance',
-		}, {
-			type: 'null',
-		}],
+		type: 'object',
+		optional: false, nullable: true,
+		ref: 'FederationInstance',
 	},
 } as const;
 
diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts
index 4f972d3f7e..30bf6b8b3e 100644
--- a/packages/backend/src/server/api/openapi/gen-spec.ts
+++ b/packages/backend/src/server/api/openapi/gen-spec.ts
@@ -4,7 +4,7 @@
  */
 
 import type { Config } from '@/config.js';
-import endpoints from '../endpoints.js';
+import endpoints, { IEndpoint } from '../endpoints.js';
 import { errors as basicErrors } from './errors.js';
 import { schemas, convertSchemaToOpenApiSchema } from './schemas.js';
 
@@ -33,16 +33,17 @@ export function genOpenapiSpec(config: Config) {
 			schemas: schemas,
 
 			securitySchemes: {
-				ApiKeyAuth: {
-					type: 'apiKey',
-					in: 'body',
-					name: 'i',
+				bearerAuth: {
+					type: 'http',
+					scheme: 'bearer',
 				},
 			},
 		},
 	};
 
-	for (const endpoint of endpoints.filter(ep => !ep.meta.secure)) {
+	// 書き換えたりするのでディープコピーしておく。そのまま編集するとメモリ上の値が汚れて次回以降の出力に影響する
+	const copiedEndpoints = JSON.parse(JSON.stringify(endpoints)) as IEndpoint[];
+	for (const endpoint of copiedEndpoints.filter(ep => !ep.meta.secure)) {
 		const errors = {} as any;
 
 		if (endpoint.meta.errors) {
@@ -79,6 +80,13 @@ export function genOpenapiSpec(config: Config) {
 			schema.required = [...schema.required ?? [], 'file'];
 		}
 
+		if (schema.required && schema.required.length <= 0) {
+			// 空配列は許可されない
+			schema.required = undefined;
+		}
+
+		const hasBody = (schema.type === 'object' && schema.properties && Object.keys(schema.properties).length >= 1);
+
 		const info = {
 			operationId: endpoint.name,
 			summary: endpoint.name,
@@ -92,17 +100,19 @@ export function genOpenapiSpec(config: Config) {
 			} : {}),
 			...(endpoint.meta.requireCredential ? {
 				security: [{
-					ApiKeyAuth: [],
+					bearerAuth: [],
 				}],
 			} : {}),
-			requestBody: {
-				required: true,
-				content: {
-					[requestType]: {
-						schema,
+			...(hasBody ? {
+				requestBody: {
+					required: true,
+					content: {
+						[requestType]: {
+							schema,
+						},
 					},
 				},
-			},
+			} : {}),
 			responses: {
 				...(endpoint.meta.res ? {
 					'200': {
@@ -118,6 +128,11 @@ export function genOpenapiSpec(config: Config) {
 						description: 'OK (without any results)',
 					},
 				}),
+				...(endpoint.meta.res?.optional === true || endpoint.meta.res?.nullable === true ? {
+					'204': {
+						description: 'OK (without any results)',
+					},
+				} : {}),
 				'400': {
 					description: 'Client error',
 					content: {
@@ -190,6 +205,7 @@ export function genOpenapiSpec(config: Config) {
 		};
 
 		spec.paths['/' + endpoint.name] = {
+			...(endpoint.meta.allowGet ? { get: info } : {}),
 			post: info,
 		};
 	}
diff --git a/packages/backend/src/server/api/openapi/schemas.ts b/packages/backend/src/server/api/openapi/schemas.ts
index 1a1d973e56..2716f5f162 100644
--- a/packages/backend/src/server/api/openapi/schemas.ts
+++ b/packages/backend/src/server/api/openapi/schemas.ts
@@ -7,10 +7,16 @@ import type { Schema } from '@/misc/json-schema.js';
 import { refs } from '@/misc/json-schema.js';
 
 export function convertSchemaToOpenApiSchema(schema: Schema) {
-	const res: any = schema;
+	// optional, refはスキーマ定義に含まれないので分離しておく
+	// eslint-disable-next-line @typescript-eslint/no-unused-vars
+	const { optional, ref, ...res }: any = schema;
 
 	if (schema.type === 'object' && schema.properties) {
-		res.required = Object.entries(schema.properties).filter(([k, v]) => !v.optional).map(([k]) => k);
+		const required = Object.entries(schema.properties).filter(([k, v]) => !v.optional).map(([k]) => k);
+		if (required.length > 0) {
+			// 空配列は許可されない
+			res.required = required;
+		}
 
 		for (const k of Object.keys(schema.properties)) {
 			res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k]);

From b15f293b82a7f3524f89d53d4e6373e219e7865f Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 22 Nov 2023 18:46:27 +0900
Subject: [PATCH 025/435] fix lint, resolve code smell
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-Authored-By: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
---
 .../src/server/api/endpoints/admin/ad/list.ts |  2 +-
 packages/frontend/src/pages/admin/ads.vue     | 22 ++++++++++++-------
 2 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
index 1366fbf76a..8cdeaae179 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
@@ -22,7 +22,7 @@ export const paramDef = {
 		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
 		sinceId: { type: 'string', format: 'misskey:id' },
 		untilId: { type: 'string', format: 'misskey:id' },
-		publishing: { type: 'boolean', default: null, nullable: true},
+		publishing: { type: 'boolean', default: null, nullable: true },
 	},
 	required: [],
 } as const;
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 1ce99d4ba5..1c15e32552 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -9,11 +9,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<XHeader :actions="headerActions" :tabs="headerTabs"/>
 	</template>
 	<MkSpacer :contentMax="900">
-		<MkSelect v-model="type" :class="$style.input" @update:modelValue="onChangePublishing">
+		<MkSelect v-model="filterType" :class="$style.input" @update:modelValue="filterItems">
 			<template #label>{{ i18n.ts.state }}</template>
-			<option value="null">{{ i18n.ts.all }}</option>
-			<option value="true">{{ i18n.ts.publishing }}</option>
-			<option value="false">{{ i18n.ts.expired }}</option>
+			<option value="all">{{ i18n.ts.all }}</option>
+			<option value="publishing">{{ i18n.ts.publishing }}</option>
+			<option value="expired">{{ i18n.ts.expired }}</option>
 		</MkSelect>
 		<div>
 			<div v-for="ad in ads" class="_panel _gaps_m" :class="$style.ad">
@@ -104,8 +104,8 @@ let ads: any[] = $ref([]);
 const localTime = new Date();
 const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000;
 const daysOfWeek: string[] = [i18n.ts._weekday.sunday, i18n.ts._weekday.monday, i18n.ts._weekday.tuesday, i18n.ts._weekday.wednesday, i18n.ts._weekday.thursday, i18n.ts._weekday.friday, i18n.ts._weekday.saturday];
+const filterType = ref('all');
 let publishing: boolean | null = null;
-let type = ref('null');
 
 os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
 	if (adsResponse != null) {
@@ -123,9 +123,15 @@ os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
 	}
 });
 
-const onChangePublishing = (v) => {
-	console.log(v);
-	publishing = v === 'true' ? true : v === 'false' ? false : null;
+const filterItems = (v) => {
+	if (v === 'publishing') {
+		publishing = true;
+	} else if (v === 'expired') {
+		publishing = false;
+	} else {
+		publishing = null;
+	}
+
 	refresh();
 };
 

From ded328fb43adda0ca61946de1ce1e94bc8e37b1d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Thu, 23 Nov 2023 08:13:51 +0900
Subject: [PATCH 026/435] =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE?=
 =?UTF-8?q?=E3=82=AA=E3=83=BC=E3=83=88=E3=82=B3=E3=83=B3=E3=83=97=E3=83=AA?=
 =?UTF-8?q?=E3=83=BC=E3=83=88=E5=BC=B7=E5=8C=96=E3=81=AE=E5=AF=BE=E5=BF=9C?=
 =?UTF-8?q?=20(#12365)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 前方一致・部分一致でなくても近似値でヒットするように

* fix CHANGELOG.md

* for of に変更

---------

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
---
 CHANGELOG.md                                  |   1 +
 .../src/components/MkAutocomplete.vue         | 100 ++++++++++++++----
 2 files changed, 78 insertions(+), 23 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1113374595..bc39e423c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
 - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
 
 ### Client
+- Enhance: 絵文字のオートコンプリート機能強化 #12364
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 
diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index 7e0c219045..a0f4961116 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -242,29 +242,7 @@ function exec() {
 			return;
 		}
 
-		const matched: EmojiDef[] = [];
-		const max = 30;
-
-		emojiDb.value.some(x => {
-			if (x.name.startsWith(props.q ?? '') && !x.aliasOf && !matched.some(y => y.emoji === x.emoji)) matched.push(x);
-			return matched.length === max;
-		});
-
-		if (matched.length < max) {
-			emojiDb.value.some(x => {
-				if (x.name.startsWith(props.q ?? '') && !matched.some(y => y.emoji === x.emoji)) matched.push(x);
-				return matched.length === max;
-			});
-		}
-
-		if (matched.length < max) {
-			emojiDb.value.some(x => {
-				if (x.name.includes(props.q ?? '') && !matched.some(y => y.emoji === x.emoji)) matched.push(x);
-				return matched.length === max;
-			});
-		}
-
-		emojis.value = matched;
+		emojis.value = emojiAutoComplete(props.q, emojiDb.value);
 	} else if (props.type === 'mfmTag') {
 		if (!props.q || props.q === '') {
 			mfmTags.value = MFM_TAGS;
@@ -275,6 +253,82 @@ function exec() {
 	}
 }
 
+type EmojiScore = { emoji: EmojiDef, score: number };
+
+function emojiAutoComplete(query: string | null, emojiDb: EmojiDef[], max = 30): EmojiDef[] {
+	if (!query) {
+		return [];
+	}
+
+	const matched = new Map<string, EmojiScore>();
+
+	// 前方一致(エイリアスなし)
+	emojiDb.some(x => {
+		if (x.name.startsWith(query) && !x.aliasOf) {
+			matched.set(x.name, { emoji: x, score: query.length });
+		}
+		return matched.size === max;
+	});
+
+	// 前方一致(エイリアス込み)
+	if (matched.size < max) {
+		emojiDb.some(x => {
+			if (x.name.startsWith(query)) {
+				matched.set(x.name, { emoji: x, score: query.length });
+			}
+			return matched.size === max;
+		});
+	}
+
+	// 部分一致(エイリアス込み)
+	if (matched.size < max) {
+		emojiDb.some(x => {
+			if (x.name.includes(query)) {
+				matched.set(x.name, { emoji: x, score: query.length });
+			}
+			return matched.size === max;
+		});
+	}
+
+	// 簡易あいまい検索
+	if (matched.size < max) {
+		const queryChars = [...query];
+		const hitEmojis = new Map<string, EmojiScore>();
+
+		for (const x of emojiDb) {
+			// クエリ文字列の1文字単位で絵文字名にヒットするかを見る
+			// ただし、過剰に検出されるのを防ぐためクエリ文字列に登場する順番で絵文字名を走査する
+
+			let queryCharHitPos = 0;
+			let queryCharHitCount = 0;
+			for (let idx = 0; idx < queryChars.length; idx++) {
+				queryCharHitPos = x.name.indexOf(queryChars[idx], queryCharHitPos);
+				if (queryCharHitPos <= -1) {
+					break;
+				}
+
+				queryCharHitCount++;
+			}
+
+			// ヒット数が少なすぎると検索結果が汚れるので調節する
+			if (queryCharHitCount > 2) {
+				hitEmojis.set(x.name, { emoji: x, score: queryCharHitCount });
+			}
+		}
+
+		// ヒットしたものを全部追加すると雑多になるので、先頭の6件程度だけにしておく(6件=オートコンプリートのポップアップのサイズ分)
+		[...hitEmojis.values()]
+			.sort((x, y) => y.score - x.score)
+			.slice(0, 6)
+			.forEach(it => matched.set(it.emoji.name, it));
+	}
+
+	return [...matched.values()]
+		.sort((x, y) => y.score - x.score)
+		.slice(0, max)
+		.map(it => it.emoji);
+}
+
 function onMousedown(event: Event) {
 	if (!contains(rootEl.value, event.target) && (rootEl.value !== event.target)) props.close();
 }

From 864827f788cd1671a4db2ebc159c1c8ab031b7ad Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 23 Nov 2023 18:56:20 +0900
Subject: [PATCH 027/435] Hard mute (#12376)

* feat(backend,misskey-js): hard mute storage in backend

* fix(backend,misskey-js): mute word record type

* chore(frontend): generalize XWordMute

* feat(frontend): configure hard mute

* feat(frontend): hard mute notes on the timelines

* lint(backend,frontend): fix lint failure

* chore(misskey-js): update api.md

* fix(backend): test failure

* chore(frontend): check word mute for reply

* chore: limit hard mute count
---
 locales/index.d.ts                            |  1 +
 locales/ja-JP.yml                             |  1 +
 .../migration/1700383825690-hard-mute.js      | 11 ++++++
 .../src/core/entities/UserEntityService.ts    |  1 +
 packages/backend/src/models/UserProfile.ts    |  7 +++-
 .../backend/src/models/json-schema/user.ts    | 12 +++++++
 .../src/server/api/endpoints/i/update.ts      | 34 +++++++++++++++----
 packages/backend/test/e2e/users.ts            |  1 +
 packages/frontend/src/components/MkNote.vue   | 17 ++++++++--
 packages/frontend/src/components/MkNotes.vue  |  2 +-
 .../src/components/MkNotifications.vue        |  2 +-
 .../src/pages/settings/mute-block.vue         | 18 +++++++++-
 .../pages/settings/mute-block.word-mute.vue   | 22 ++++++------
 packages/misskey-js/etc/misskey-js.api.md     | 12 ++++---
 packages/misskey-js/src/api.types.ts          |  3 +-
 packages/misskey-js/src/entities.ts           |  3 +-
 16 files changed, 114 insertions(+), 33 deletions(-)
 create mode 100644 packages/backend/migration/1700383825690-hard-mute.js

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 39fbb57799..3ed2706298 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -642,6 +642,7 @@ export interface Locale {
     "smtpSecureInfo": string;
     "testEmail": string;
     "wordMute": string;
+    "hardWordMute": string;
     "regexpError": string;
     "regexpErrorDescription": string;
     "instanceMute": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 3757715c0f..5982e71716 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -639,6 +639,7 @@ smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する"
 smtpSecureInfo: "STARTTLS使用時はオフにします。"
 testEmail: "配信テスト"
 wordMute: "ワードミュート"
+hardWordMute: "ハードワードミュート"
 regexpError: "正規表現エラー"
 regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが発生しました:"
 instanceMute: "サーバーミュート"
diff --git a/packages/backend/migration/1700383825690-hard-mute.js b/packages/backend/migration/1700383825690-hard-mute.js
new file mode 100644
index 0000000000..afd3247f5c
--- /dev/null
+++ b/packages/backend/migration/1700383825690-hard-mute.js
@@ -0,0 +1,11 @@
+export class HardMute1700383825690 {
+    name = 'HardMute1700383825690'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "user_profile" ADD "hardMutedWords" jsonb NOT NULL DEFAULT '[]'`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "hardMutedWords"`);
+    }
+}
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index 17e7988176..917f4e06d0 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -473,6 +473,7 @@ export class UserEntityService implements OnModuleInit {
 				hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id),
 				unreadNotificationsCount: notificationsInfo?.unreadCount,
 				mutedWords: profile!.mutedWords,
+				hardMutedWords: profile!.hardMutedWords,
 				mutedInstances: profile!.mutedInstances,
 				mutingNotificationTypes: [], // 後方互換性のため
 				notificationRecieveConfig: profile!.notificationRecieveConfig,
diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts
index d6d85c5609..8a43b60039 100644
--- a/packages/backend/src/models/UserProfile.ts
+++ b/packages/backend/src/models/UserProfile.ts
@@ -215,7 +215,12 @@ export class MiUserProfile {
 	@Column('jsonb', {
 		default: [],
 	})
-	public mutedWords: string[][];
+	public mutedWords: (string[] | string)[];
+
+	@Column('jsonb', {
+		default: [],
+	})
+	public hardMutedWords: (string[] | string)[];
 
 	@Column('jsonb', {
 		default: [],
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index b0e18db01a..a2ec203e96 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -530,6 +530,18 @@ export const packedMeDetailedOnlySchema = {
 				},
 			},
 		},
+		hardMutedWords: {
+			type: 'array',
+			nullable: false, optional: false,
+			items: {
+				type: 'array',
+				nullable: false, optional: false,
+				items: {
+					type: 'string',
+					nullable: false, optional: false,
+				},
+			},
+		},
 		mutedInstances: {
 			type: 'array',
 			nullable: true, optional: false,
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index b00aa87bee..8ba29c5658 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -123,6 +123,11 @@ export const meta = {
 	},
 } as const;
 
+const muteWords = { type: 'array', items: { oneOf: [
+	{ type: 'array', items: { type: 'string' } },
+	{ type: 'string' }
+] } } as const;
+
 export const paramDef = {
 	type: 'object',
 	properties: {
@@ -171,7 +176,8 @@ export const paramDef = {
 		autoSensitive: { type: 'boolean' },
 		ffVisibility: { type: 'string', enum: ['public', 'followers', 'private'] },
 		pinnedPageId: { type: 'string', format: 'misskey:id', nullable: true },
-		mutedWords: { type: 'array' },
+		mutedWords: muteWords,
+		hardMutedWords: muteWords,
 		mutedInstances: { type: 'array', items: {
 			type: 'string',
 		} },
@@ -234,16 +240,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			if (ps.location !== undefined) profileUpdates.location = ps.location;
 			if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
 			if (ps.ffVisibility !== undefined) profileUpdates.ffVisibility = ps.ffVisibility;
-			if (ps.mutedWords !== undefined) {
+
+			function checkMuteWordCount(mutedWords: (string[] | string)[], limit: number) {
 				// TODO: ちゃんと数える
 				const length = JSON.stringify(ps.mutedWords).length;
-				if (length > (await this.roleService.getUserPolicies(user.id)).wordMuteLimit) {
+				if (length > limit) {
 					throw new ApiError(meta.errors.tooManyMutedWords);
 				}
+			}
 
-				// validate regular expression syntax
-				ps.mutedWords.filter(x => !Array.isArray(x)).forEach(x => {
-					const regexp = x.match(/^\/(.+)\/(.*)$/);
+			function validateMuteWordRegex(mutedWords: (string[] | string)[]) {
+				for (const mutedWord of mutedWords) {
+					if (typeof mutedWord !== "string") continue;
+
+					const regexp = mutedWord.match(/^\/(.+)\/(.*)$/);
 					if (!regexp) throw new ApiError(meta.errors.invalidRegexp);
 
 					try {
@@ -251,11 +261,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					} catch (err) {
 						throw new ApiError(meta.errors.invalidRegexp);
 					}
-				});
+				}
+			}
+
+			if (ps.mutedWords !== undefined) {
+				checkMuteWordCount(ps.mutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit);
+				validateMuteWordRegex(ps.mutedWords);
 
 				profileUpdates.mutedWords = ps.mutedWords;
 				profileUpdates.enableWordMute = ps.mutedWords.length > 0;
 			}
+			if (ps.hardMutedWords !== undefined) {
+				checkMuteWordCount(ps.hardMutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit);
+				validateMuteWordRegex(ps.hardMutedWords);
+				profileUpdates.hardMutedWords = ps.hardMutedWords;
+			}
 			if (ps.mutedInstances !== undefined) profileUpdates.mutedInstances = ps.mutedInstances;
 			if (ps.notificationRecieveConfig !== undefined) profileUpdates.notificationRecieveConfig = ps.notificationRecieveConfig;
 			if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked;
diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts
index 1867525cc8..2ce8fbc129 100644
--- a/packages/backend/test/e2e/users.ts
+++ b/packages/backend/test/e2e/users.ts
@@ -168,6 +168,7 @@ describe('ユーザー', () => {
 			hasPendingReceivedFollowRequest: user.hasPendingReceivedFollowRequest,
 			unreadAnnouncements: user.unreadAnnouncements,
 			mutedWords: user.mutedWords,
+			hardMutedWords: user.hardMutedWords,
 			mutedInstances: user.mutedInstances,
 			mutingNotificationTypes: user.mutingNotificationTypes,
 			notificationRecieveConfig: user.notificationRecieveConfig,
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index e300ef88a5..6349df2e30 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div
-	v-if="!muted"
+	v-if="!hardMuted && !muted"
 	v-show="!isDeleted"
 	ref="el"
 	v-hotkey="keymap"
@@ -133,7 +133,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</div>
 	</article>
 </div>
-<div v-else :class="$style.muted" @click="muted = false">
+<div v-else-if="!hardMuted" :class="$style.muted" @click="muted = false">
 	<I18n :src="i18n.ts.userSaysSomething" tag="small">
 		<template #name>
 			<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
@@ -183,6 +183,7 @@ const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
 	pinned?: boolean;
 	mock?: boolean;
+	withHardMute?: boolean;
 }>(), {
 	mock: false,
 });
@@ -239,13 +240,23 @@ const urls = $computed(() => parsed ? extractUrlFromMfm(parsed) : null);
 const isLong = shouldCollapsed(appearNote, urls ?? []);
 const collapsed = ref(appearNote.cw == null && isLong);
 const isDeleted = ref(false);
-const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
+const muted = ref(checkMute(appearNote, $i?.mutedWords));
+const hardMuted = ref(props.withHardMute && checkMute(appearNote, $i?.hardMutedWords));
 const translation = ref<any>(null);
 const translating = ref(false);
 const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
 const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || (appearNote.visibility === 'followers' && appearNote.userId === $i.id));
 let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId || $i.id === appearNote.userId)) || (appearNote.myReaction != null)));
 
+function checkMute(note: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null): boolean {
+	if (mutedWords == null) return false;
+
+	if (checkWordMute(note, $i, mutedWords)) return true;
+	if (note.reply && checkWordMute(note.reply, $i, mutedWords)) return true;
+	if (note.renote && checkWordMute(note.renote, $i, mutedWords)) return true;
+	return false;
+}
+
 const keymap = {
 	'r': () => reply(true),
 	'e|a|plus': () => react(true),
diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue
index 89fd504dcc..7af31074db 100644
--- a/packages/frontend/src/components/MkNotes.vue
+++ b/packages/frontend/src/components/MkNotes.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				:ad="true"
 				:class="$style.notes"
 			>
-				<MkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note"/>
+				<MkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :withHardMute="true"/>
 			</MkDateSeparatedList>
 		</div>
 	</template>
diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index 0c817bd64c..7b072fa492 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 		<template #default="{ items: notifications }">
 			<MkDateSeparatedList v-slot="{ item: notification }" :class="$style.list" :items="notifications" :noGap="true">
-				<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id" :note="notification.note"/>
+				<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id" :note="notification.note" :withHardMute="true"/>
 				<XNotification v-else :key="notification.id" :notification="notification" :withTime="true" :full="true" class="_panel"/>
 			</MkDateSeparatedList>
 		</template>
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index c6cbd424ec..4883ca0df4 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -9,7 +9,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<template #icon><i class="ti ti-message-off"></i></template>
 		<template #label>{{ i18n.ts.wordMute }}</template>
 
-		<XWordMute/>
+		<XWordMute :muted="$i!.mutedWords" @save="saveMutedWords"/>
+	</MkFolder>
+
+	<MkFolder>
+		<template #icon><i class="ti ti-message-off"></i></template>
+		<template #label>{{ i18n.ts.hardWordMute }}</template>
+
+		<XWordMute :muted="$i!.hardMutedWords" @save="saveHardMutedWords"/>
 	</MkFolder>
 
 	<MkFolder>
@@ -129,6 +136,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import * as os from '@/os.js';
 import { infoImageUrl } from '@/instance.js';
+import { $i } from '@/account.js';
 import MkFolder from '@/components/MkFolder.vue';
 
 const renoteMutingPagination = {
@@ -207,6 +215,14 @@ async function toggleBlockItem(item) {
 	}
 }
 
+async function saveMutedWords(mutedWords: (string | string[])[]) {
+	await os.api('i/update', { mutedWords });
+}
+
+async function saveHardMutedWords(hardMutedWords: (string | string[])[]) {
+	await os.api('i/update', { hardMutedWords });
+}
+
 const headerActions = $computed(() => []);
 
 const headerTabs = $computed(() => []);
diff --git a/packages/frontend/src/pages/settings/mute-block.word-mute.vue b/packages/frontend/src/pages/settings/mute-block.word-mute.vue
index 25a836ea55..7328967c51 100644
--- a/packages/frontend/src/pages/settings/mute-block.word-mute.vue
+++ b/packages/frontend/src/pages/settings/mute-block.word-mute.vue
@@ -18,16 +18,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref, watch } from 'vue';
 import MkTextarea from '@/components/MkTextarea.vue';
-import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkButton from '@/components/MkButton.vue';
-import MkInfo from '@/components/MkInfo.vue';
-import MkTab from '@/components/MkTab.vue';
 import * as os from '@/os.js';
-import number from '@/filters/number.js';
-import { defaultStore } from '@/store.js';
-import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
-import { definePageMetadata } from '@/scripts/page-metadata.js';
+
+const props = defineProps<{
+	muted: (string[] | string)[];
+}>();
+
+const emit = defineEmits<{
+	(ev: 'save', value: (string[] | string)[]): void;
+}>();
 
 const render = (mutedWords) => mutedWords.map(x => {
 	if (Array.isArray(x)) {
@@ -37,8 +38,7 @@ const render = (mutedWords) => mutedWords.map(x => {
 	}
 }).join('\n');
 
-const tab = ref('soft');
-const mutedWords = ref(render($i!.mutedWords));
+const mutedWords = ref(render(props.muted));
 const changed = ref(false);
 
 watch(mutedWords, () => {
@@ -85,9 +85,7 @@ async function save() {
 		return;
 	}
 
-	await os.api('i/update', {
-		mutedWords: parsed,
-	});
+	emit('save', parsed);
 
 	changed.value = false;
 }
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 63c3cb71a5..dc93c4be3b 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1565,7 +1565,8 @@ export type Endpoints = {
             injectFeaturedNote?: boolean;
             receiveAnnouncementEmail?: boolean;
             alwaysMarkNsfw?: boolean;
-            mutedWords?: string[][];
+            mutedWords?: (string[] | string)[];
+            hardMutedWords?: (string[] | string)[];
             notificationRecieveConfig?: any;
             emailNotificationTypes?: string[];
             alsoKnownAs?: string[];
@@ -2516,7 +2517,8 @@ type MeDetailed = UserDetailed & {
     integrations: Record<string, any>;
     isDeleted: boolean;
     isExplorable: boolean;
-    mutedWords: string[][];
+    mutedWords: (string[] | string)[];
+    hardMutedWords: (string[] | string)[];
     notificationRecieveConfig: {
         [notificationType in typeof notificationTypes_2[number]]?: {
             type: 'all';
@@ -3053,9 +3055,9 @@ 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:20:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
-// src/api.types.ts:634:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
-// src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts
-// src/entities.ts:627:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
+// src/api.types.ts:635:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
+// src/entities.ts:117:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts
+// src/entities.ts:628: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/api.types.ts b/packages/misskey-js/src/api.types.ts
index 1a75b7cf57..ba333231e5 100644
--- a/packages/misskey-js/src/api.types.ts
+++ b/packages/misskey-js/src/api.types.ts
@@ -432,7 +432,8 @@ export type Endpoints = {
 		injectFeaturedNote?: boolean;
 		receiveAnnouncementEmail?: boolean;
 		alwaysMarkNsfw?: boolean;
-		mutedWords?: string[][];
+		mutedWords?: (string[] | string)[];
+		hardMutedWords?: (string[] | string)[];
 		notificationRecieveConfig?: any;
 		emailNotificationTypes?: string[];
 		alsoKnownAs?: string[];
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index a51315b13b..2ddcf93f5d 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -112,7 +112,8 @@ export type MeDetailed = UserDetailed & {
 	integrations: Record<string, any>;
 	isDeleted: boolean;
 	isExplorable: boolean;
-	mutedWords: string[][];
+	mutedWords: (string[] | string)[];
+	hardMutedWords: (string[] | string)[];
 	notificationRecieveConfig: {
 		[notificationType in typeof notificationTypes[number]]?: {
 			type: 'all';

From 4a2a44831babe450c1b5b10b3a43c843fa442dcd 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: Thu, 23 Nov 2023 19:34:14 +0900
Subject: [PATCH 028/435] =?UTF-8?q?fix(backend):=20=E6=8B=9B=E5=BE=85?=
 =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E3=81=8C=E4=BD=BF=E3=81=84=E5=9B=9E?=
 =?UTF-8?q?=E3=81=9B=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#12423)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (fix) 招待コードを一度のみ利用できるように

* Update Changelog
---
 CHANGELOG.md                                        | 1 +
 packages/backend/src/server/api/SignupApiService.ts | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc39e423c0..23bd05ea8b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@
 - Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303
 - Fix: ロールタイムラインが保存されない問題を修正
 - Fix: api.jsonの生成ロジックを改善 #12402
+- Fix: 招待コードが使い回せる問題を修正
 
 ## 2023.11.1
 
diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts
index d6f4df7f13..753984ef52 100644
--- a/packages/backend/src/server/api/SignupApiService.ts
+++ b/packages/backend/src/server/api/SignupApiService.ts
@@ -126,7 +126,7 @@ export class SignupApiService {
 				code: invitationCode,
 			});
 
-			if (ticket == null) {
+			if (ticket == null || ticket.usedById != null) {
 				reply.code(400);
 				return;
 			}

From ed6f866a4f6e32a7875951fe7ea358d5cd21c6ce 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: Thu, 23 Nov 2023 19:49:45 +0900
Subject: [PATCH 029/435] =?UTF-8?q?enhance/fix(AP/frontend):=20=E6=9C=80?=
 =?UTF-8?q?=E8=BF=91=E8=BF=BD=E5=8A=A0=E3=81=95=E3=82=8C=E3=81=9FMFM?=
 =?UTF-8?q?=E3=81=AE=E3=82=82=E3=82=8D=E3=82=82=E3=82=8D=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#12420)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (enhance) MFM rubyが連合されるように

* Update Changelog

* Update Changelog

* (fix) unixtimeのフォールバック (AP)

* (fix) unixtimeのフォールバック (frontend)

* Update Changelog
---
 CHANGELOG.md                                  |  2 +
 locales/ja-JP.yml                             |  2 +-
 packages/backend/src/core/MfmService.ts       | 79 ++++++++++++++++---
 .../frontend/src/components/global/MkTime.vue | 19 ++++-
 4 files changed, 87 insertions(+), 15 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 23bd05ea8b..eb06e52ecc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@
 ### General
 - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
 - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
+- Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
 
 ### Client
 - Enhance: 絵文字のオートコンプリート機能強化 #12364
@@ -24,6 +25,7 @@
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 
 ### Server
+- Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
 - Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303
 - Fix: ロールタイムラインが保存されない問題を修正
 - Fix: api.jsonの生成ロジックを改善 #12402
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 5982e71716..9b5dfce63d 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1856,7 +1856,7 @@ _ago:
   weeksAgo: "{n}週間前"
   monthsAgo: "{n}ヶ月前"
   yearsAgo: "{n}年前"
-  invalid: "ありません"
+  invalid: "日時の解析に失敗"
 
 _timeIn:
   seconds: "{n}秒後"
diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts
index af602168d4..e74c62e1a8 100644
--- a/packages/backend/src/core/MfmService.ts
+++ b/packages/backend/src/core/MfmService.ts
@@ -250,6 +250,12 @@ export class MfmService {
 			}
 		}
 
+		function fnDefault(node: mfm.MfmFn) {
+			const el = doc.createElement('i');
+			appendChildren(node.children, el);
+			return el;
+		}
+
 		const handlers: { [K in mfm.MfmNode['type']]: (node: mfm.NodeType<K>) => any } = {
 			bold: (node) => {
 				const el = doc.createElement('b');
@@ -276,17 +282,68 @@ export class MfmService {
 			},
 
 			fn: (node) => {
-				if (node.props.name === 'unixtime') {
-					const text = node.children[0]!.type === 'text' ? node.children[0].props.text : '';
-					const date = new Date(parseInt(text, 10) * 1000);
-					const el = doc.createElement('time');
-					el.setAttribute('datetime', date.toISOString());
-					el.textContent = date.toISOString();
-					return el;
-				} else {
-					const el = doc.createElement('i');
-					appendChildren(node.children, el);
-					return el;
+				switch (node.props.name) {
+					case 'unixtime': {
+						const text = node.children[0].type === 'text' ? node.children[0].props.text : '';
+						try {
+							const date = new Date(parseInt(text, 10) * 1000);
+							const el = doc.createElement('time');
+							el.setAttribute('datetime', date.toISOString());
+							el.textContent = date.toISOString();
+							return el;
+						} catch (err) {
+							return fnDefault(node);
+						}
+					}
+
+					case 'ruby': {
+						if (node.children.length === 1) {
+							const child = node.children[0];
+							const text = child.type === 'text' ? child.props.text : '';
+							const rubyEl = doc.createElement('ruby');
+							const rtEl = doc.createElement('rt');
+
+							// ruby未対応のHTMLサニタイザーを通したときにルビが「劉備(りゅうび)」となるようにする
+							const rpStartEl = doc.createElement('rp');
+							rpStartEl.appendChild(doc.createTextNode('('));
+							const rpEndEl = doc.createElement('rp');
+							rpEndEl.appendChild(doc.createTextNode(')'));
+
+							rubyEl.appendChild(doc.createTextNode(text.split(' ')[0]));
+							rtEl.appendChild(doc.createTextNode(text.split(' ')[1]));
+							rubyEl.appendChild(rpStartEl);
+							rubyEl.appendChild(rtEl);
+							rubyEl.appendChild(rpEndEl);
+							return rubyEl;
+						} else {
+							const rt = node.children.at(-1);
+
+							if (!rt) {
+								return fnDefault(node);
+							}
+
+							const text = rt.type === 'text' ? rt.props.text : '';
+							const rubyEl = doc.createElement('ruby');
+							const rtEl = doc.createElement('rt');
+
+							// ruby未対応のHTMLサニタイザーを通したときにルビが「劉備(りゅうび)」となるようにする
+							const rpStartEl = doc.createElement('rp');
+							rpStartEl.appendChild(doc.createTextNode('('));
+							const rpEndEl = doc.createElement('rp');
+							rpEndEl.appendChild(doc.createTextNode(')'));
+
+							appendChildren(node.children.slice(0, node.children.length - 1), rubyEl);
+							rtEl.appendChild(doc.createTextNode(text.trim()));
+							rubyEl.appendChild(rpStartEl);
+							rubyEl.appendChild(rtEl);
+							rubyEl.appendChild(rpEndEl);
+							return rubyEl;
+						}
+					}
+
+					default: {
+						return fnDefault(node);
+					}
 				}
 			},
 
diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue
index f08d538fc0..2eeab4d284 100644
--- a/packages/frontend/src/components/global/MkTime.vue
+++ b/packages/frontend/src/components/global/MkTime.vue
@@ -28,12 +28,25 @@ const props = withDefaults(defineProps<{
 	mode: 'relative',
 });
 
-const _time = props.time == null ? NaN :
-	typeof props.time === 'number' ? props.time :
-	(props.time instanceof Date ? props.time : new Date(props.time)).getTime();
+function getDateSafe(n: Date | string | number) {
+	try {
+		if (n instanceof Date) {
+			return n;
+		}
+		return new Date(n);
+	} catch (err) {
+		return {
+			getTime: () => NaN,
+		};
+	}
+}
+
+// eslint-disable-next-line vue/no-setup-props-destructure
+const _time = props.time == null ? NaN : getDateSafe(props.time).getTime();
 const invalid = Number.isNaN(_time);
 const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
 
+// eslint-disable-next-line vue/no-setup-props-destructure
 let now = $ref((props.origin ?? new Date()).getTime());
 const ago = $computed(() => (now - _time) / 1000/*ms*/);
 

From 44a378c46e1efac8a8f632f2d92da6a6dbad54f5 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 23 Nov 2023 20:17:21 +0900
Subject: [PATCH 030/435] Use generate-api-json for api.json diff GitHub
 comment (#12408)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* ci: use generate-api-json to get api.json changes

* restore copying default.yml

* refactor: get api.json with single workflow

* ci: api.jsonのdiffをbackendが変更されたときのみ取るように
---
 .github/workflows/get-api-diff.yml | 148 ++++-------------------------
 1 file changed, 19 insertions(+), 129 deletions(-)

diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index 8e3437ad86..e6de226e6b 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -6,9 +6,12 @@ on:
     branches:
       - master
       - develop
+    paths:
+      - packages/backend/**
+      - .github/workflows/get-api-diff.yml
 
 jobs:
-  get-base:
+  get-from-misskey:
     runs-on: ubuntu-latest
     permissions:
       contents: read
@@ -16,27 +19,20 @@ jobs:
     strategy:
       matrix:
         node-version: [20.5.1]
-
-    services:
-      db:
-        image: postgres:13
-        ports:
-          - 5432:5432
-        env:
-          POSTGRES_DB: misskey
-          POSTGRES_HOST_AUTH_METHOD: trust
-          POSTGRES_USER: example-misskey-user
-          POSTGRESS_PASS: example-misskey-pass
-      redis:
-        image: redis:7
-        ports:
-          - 6379:6379
+        api-json-name: [api-base.json, api-head.json]
+        include:
+          - api-json-name: api-base.json
+            repo-name: ${{ github.event.pull_request.base.repo.full_name }}
+            ref: ${{ github.base_ref }}
+          - api-json-name: api-head.json
+            repo-name: ${{ github.event.pull_request.head.repo.full_name }}
+            ref: ${{ github.head_ref }}
 
     steps:
     - uses: actions/checkout@v4.1.1
       with:
-        repository: ${{ github.event.pull_request.base.repo.full_name }}
-        ref: ${{ github.base_ref }}
+        repository: ${{ matrix.repo-name }}
+        ref: ${{ matrix.ref }}
         submodules: true
     - name: Install pnpm
       uses: pnpm/action-setup@v2
@@ -56,121 +52,15 @@ jobs:
       run: cp .config/example.yml .config/default.yml
     - name: Build
       run: pnpm build
-    - name : Migrate
-      run: pnpm migrate
-    - name: Launch misskey
-      run: |
-        screen -S misskey -dm pnpm run dev
-        sleep 30s
-    - name: Wait for Misskey to be ready
-      run: |
-        MAX_RETRIES=12
-        RETRY_DELAY=5
-        count=0
-        until $(curl --output /dev/null --silent --head --fail http://localhost:3000) || [[ $count -eq $MAX_RETRIES ]]; do
-          printf '.'
-          sleep $RETRY_DELAY
-          count=$((count + 1))
-        done
-
-        if [[ $count -eq $MAX_RETRIES ]]; then
-          echo "Failed to connect to Misskey after $MAX_RETRIES attempts."
-          exit 1
-        fi
-    - id: fetch
-      name: Get api.json from Misskey
-      run: |
-        RESULT=$(curl --retry 5 --retry-delay 5 --retry-max-time 60 http://localhost:3000/api.json)
-        echo $RESULT > api-base.json
+    - name: Generate API JSON
+      run: pnpm --filter backend generate-api-json
+    - name: Copy API.json
+      run: cp packages/backend/built/api.json ${{ matrix.api-json-name }}
     - name: Upload Artifact
       uses: actions/upload-artifact@v3
       with:
         name: api-artifact
-        path: api-base.json
-    - name: Kill Misskey Job
-      run: screen -S misskey -X quit
-
-  get-head:
-    runs-on: ubuntu-latest
-    permissions:
-      contents: read
-
-    strategy:
-      matrix:
-        node-version: [20.5.1]
-
-    services:
-      db:
-        image: postgres:13
-        ports:
-          - 5432:5432
-        env:
-          POSTGRES_DB: misskey
-          POSTGRES_HOST_AUTH_METHOD: trust
-          POSTGRES_USER: example-misskey-user
-          POSTGRESS_PASS: example-misskey-pass
-      redis:
-        image: redis:7
-        ports:
-          - 6379:6379
-
-    steps:
-    - uses: actions/checkout@v4.1.1
-      with:
-        repository: ${{ github.event.pull_request.head.repo.full_name }}
-        ref: ${{ github.head_ref }}
-        submodules: true
-    - name: Install pnpm
-      uses: pnpm/action-setup@v2
-      with:
-        version: 8
-        run_install: false
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.0
-      with:
-        node-version: ${{ matrix.node-version }}
-        cache: 'pnpm'
-    - run: corepack enable
-    - run: pnpm i --frozen-lockfile
-    - name: Check pnpm-lock.yaml
-      run: git diff --exit-code pnpm-lock.yaml
-    - name: Copy Configure
-      run: cp .config/example.yml .config/default.yml
-    - name: Build
-      run: pnpm build
-    - name : Migrate
-      run: pnpm migrate
-    - name: Launch misskey
-      run: |
-        screen -S misskey -dm pnpm run dev
-        sleep 30s
-    - name: Wait for Misskey to be ready
-      run: |
-        MAX_RETRIES=12
-        RETRY_DELAY=5
-        count=0
-        until $(curl --output /dev/null --silent --head --fail http://localhost:3000) || [[ $count -eq $MAX_RETRIES ]]; do
-          printf '.'
-          sleep $RETRY_DELAY
-          count=$((count + 1))
-        done
-
-        if [[ $count -eq $MAX_RETRIES ]]; then
-          echo "Failed to connect to Misskey after $MAX_RETRIES attempts."
-          exit 1
-        fi
-    - id: fetch
-      name: Get api.json from Misskey
-      run: |
-        RESULT=$(curl --retry 5 --retry-delay 5 --retry-max-time 60 http://localhost:3000/api.json)
-        echo $RESULT > api-head.json
-    - name: Upload Artifact
-      uses: actions/upload-artifact@v3
-      with:
-        name: api-artifact
-        path: api-head.json
-    - name: Kill Misskey Job
-      run: screen -S misskey -X quit
+        path: ${{ matrix.api-json-name }}
 
   save-pr-number:
     runs-on: ubuntu-latest

From cba66c921e3396beb298a17d9e8cb1eda508b84c 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: Thu, 23 Nov 2023 20:37:41 +0900
Subject: [PATCH 031/435] =?UTF-8?q?fix(frontend):=20=E3=82=B3=E3=83=BC?=
 =?UTF-8?q?=E3=83=89=E3=82=A8=E3=83=87=E3=82=A3=E3=82=BF=E3=81=8C=E6=AD=A3?=
 =?UTF-8?q?=E3=81=97=E3=81=8F=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=AA?=
 =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1241?=
 =?UTF-8?q?8)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (fix) コードエディタが正しく表示されない問題を修正

* Update Changelog
---
 CHANGELOG.md                                      | 1 +
 packages/frontend/src/components/MkCodeEditor.vue | 6 ++++++
 2 files changed, 7 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index eb06e52ecc..2b75fd629a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@
 - Enhance: 絵文字のオートコンプリート機能強化 #12364
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
+- Fix: コードエディタが正しく表示されない問題を修正
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/frontend/src/components/MkCodeEditor.vue b/packages/frontend/src/components/MkCodeEditor.vue
index 2d56a61963..03788af21e 100644
--- a/packages/frontend/src/components/MkCodeEditor.vue
+++ b/packages/frontend/src/components/MkCodeEditor.vue
@@ -139,6 +139,10 @@ watch(v, () => {
 	height: 100%;
 }
 
+.textarea, .codeEditorHighlighter {
+	margin: 0;
+}
+
 .textarea {
 	position: absolute;
 	top: 0;
@@ -154,6 +158,8 @@ watch(v, () => {
 	background-color: transparent;
 	border: 0;
 	outline: 0;
+	min-width: calc(100% - 24px);
+	height: calc(100% - 24px);
 	padding: 12px;
 	line-height: 1.5em;
 	font-size: 1em;

From bf2d2ff0ca29cbeba5897252fee1e6aaba2f8fee 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: Thu, 23 Nov 2023 21:18:24 +0900
Subject: [PATCH 032/435] =?UTF-8?q?fix(frontend):=20=E3=83=97=E3=83=AD?=
 =?UTF-8?q?=E3=83=95=E3=82=A3=E3=83=BC=E3=83=AB=E3=81=AE=E3=80=8C=E3=83=95?=
 =?UTF-8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E3=80=8D=E3=81=AB=E3=82=BB=E3=83=B3?=
 =?UTF-8?q?=E3=82=B7=E3=83=86=E3=82=A3=E3=83=96=E3=81=AA=E7=94=BB=E5=83=8F?=
 =?UTF-8?q?=E3=81=8C=E3=81=82=E3=82=8B=E9=9A=9B=E3=81=AE=E3=83=87=E3=82=B6?=
 =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12424)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (fix) 招待コードを一度のみ利用できるように

* Update Changelog

* (fix) profile media grid

* Update Changelog

* Change Changelog
---
 CHANGELOG.md                                  |  1 +
 .../frontend/src/pages/user/index.files.vue   | 29 ++++++++++++++++---
 2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2b75fd629a..c49aa0e899 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
+- Fix: プロフィールの「ファイル」にセンシティブな画像がある際のデザインを修正
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/frontend/src/pages/user/index.files.vue b/packages/frontend/src/pages/user/index.files.vue
index 43d6d91fc9..f9328b8d6b 100644
--- a/packages/frontend/src/pages/user/index.files.vue
+++ b/packages/frontend/src/pages/user/index.files.vue
@@ -11,10 +11,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkLoading v-if="fetching"/>
 		<div v-if="!fetching && files.length > 0" :class="$style.stream">
 			<template v-for="file in files" :key="file.note.id + file.file.id">
-				<div v-if="file.file.isSensitive && !showingFiles.includes(file.file.id)" :class="$style.sensitive" @click="showingFiles.push(file.file.id)">
-					<div>
-						<div><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}</div>
-						<div>{{ i18n.ts.clickToShow }}</div>
+				<div v-if="file.file.isSensitive && !showingFiles.includes(file.file.id)" :class="$style.img" @click="showingFiles.push(file.file.id)">
+					<!-- TODO: 画像以外のファイルに対応 -->
+					<ImgWithBlurhash :class="$style.sensitiveImg" :hash="file.file.blurhash" :src="thumbnail(file.file)" :title="file.file.name" :forceBlurhash="true"/>
+					<div :class="$style.sensitive">
+						<div>
+							<div><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}</div>
+							<div>{{ i18n.ts.clickToShow }}</div>
+						</div>
 					</div>
 				</div>
 				<MkA v-else :class="$style.img" :to="notePage(file.note)">
@@ -88,6 +92,7 @@ onMounted(() => {
 }
 
 .img {
+	position: relative;
 	height: 128px;
 	border-radius: 6px;
 	overflow: clip;
@@ -99,8 +104,24 @@ onMounted(() => {
 	text-align: center;
 }
 
+.sensitiveImg {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	filter: brightness(0.7);
+}
 .sensitive {
+	position: absolute;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
 	display: grid;
   place-items: center;
+	font-size: 0.8em;
+	color: #fff;
+	cursor: pointer;
 }
 </style>

From 521db37ca7356a19f923d9ef7d567df46264a34a Mon Sep 17 00:00:00 2001
From: mappi <mappi@mizuiromoon.com>
Date: Fri, 24 Nov 2023 08:36:55 +0900
Subject: [PATCH 033/435] feat 12325 (#12425)

---
 locales/index.d.ts                          | 2 ++
 locales/ja-JP.yml                           | 2 ++
 packages/frontend/src/pages/admin/roles.vue | 4 ++--
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 3ed2706298..654b64891c 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1642,7 +1642,9 @@ export interface Locale {
         "assignTarget": string;
         "descriptionOfAssignTarget": string;
         "manual": string;
+        "manualRoles": string;
         "conditional": string;
+        "conditionalRoles": string;
         "condition": string;
         "isConditionalRole": string;
         "isPublic": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 9b5dfce63d..26deccd365 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1552,7 +1552,9 @@ _role:
   assignTarget: "アサイン"
   descriptionOfAssignTarget: "<b>マニュアル</b>は誰がこのロールに含まれるかを手動で管理します。\n<b>コンディショナル</b>は条件を設定し、それに合致するユーザーが自動で含まれるようになります。"
   manual: "マニュアル"
+  manualRoles: "マニュアルロール"
   conditional: "コンディショナル"
+  conditionalRoles: "コンディショナルロール"
   condition: "条件"
   isConditionalRole: "これはコンディショナルロールです。"
   isPublic: "公開ロール"
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index d3f3773564..91cd86485f 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -198,13 +198,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkButton primary rounded @click="create"><i class="ti ti-plus"></i> {{ i18n.ts._role.new }}</MkButton>
 				<div class="_gaps_s">
 					<MkFoldableSection>
-						<template #header>Manual roles</template>
+						<template #header>{{ i18n.ts._role.manualRoles }}</template>
 						<div class="_gaps_s">
 							<MkRolePreview v-for="role in roles.filter(x => x.target === 'manual')" :key="role.id" :role="role" :forModeration="true"/>
 						</div>
 					</MkFoldableSection>
 					<MkFoldableSection>
-						<template #header>Conditional roles</template>
+						<template #header>{{ i18n.ts._role.conditionalRoles }}</template>
 						<div class="_gaps_s">
 							<MkRolePreview v-for="role in roles.filter(x => x.target === 'conditional')" :key="role.id" :role="role" :forModeration="true"/>
 						</div>

From 30b443de5594ef5b93717925961cd3ae290b67c1 Mon Sep 17 00:00:00 2001
From: Camilla Ett <camilla.ett@gmail.com>
Date: Fri, 24 Nov 2023 08:37:27 +0900
Subject: [PATCH 034/435] =?UTF-8?q?feat(frontend):=20=E3=83=AA=E3=82=A2?=
 =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E6=A8=AA=E5=B9=85?=
 =?UTF-8?q?=E3=82=92150px=E3=81=AB=E5=88=B6=E9=99=90=E3=81=99=E3=82=8B?=
 =?UTF-8?q?=E3=81=8B=E3=81=A9=E3=81=86=E3=81=8B=E3=83=A6=E3=83=BC=E3=82=B6?=
 =?UTF-8?q?=E3=83=BC=E3=81=8C=E9=81=B8=E3=81=B9=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#12416)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat(frontend): リアクションの横幅を150pxに制限するかどうかユーザーが選べるように

* localesの変更をjs-JP.ymlのみに修正し、日本語をより分かりやすく

* クラス名を.icon から .limitWidthに変更
---
 locales/index.d.ts                                            | 1 +
 locales/ja-JP.yml                                             | 1 +
 .../frontend/src/components/MkReactionsViewer.reaction.vue    | 4 ++--
 packages/frontend/src/pages/settings/general.vue              | 3 +++
 packages/frontend/src/store.ts                                | 4 ++++
 5 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 654b64891c..e6761641ab 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1040,6 +1040,7 @@ export interface Locale {
     "enableChartsForFederatedInstances": string;
     "showClipButtonInNoteFooter": string;
     "reactionsDisplaySize": string;
+		"limitWidthOfReaction": string;
     "noteIdOrUrl": string;
     "video": string;
     "videos": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 26deccd365..58e0dd0b19 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1037,6 +1037,7 @@ enableChartsForRemoteUser: "リモートユーザーのチャートを生成"
 enableChartsForFederatedInstances: "リモートサーバーのチャートを生成"
 showClipButtonInNoteFooter: "ノートのアクションにクリップを追加"
 reactionsDisplaySize: "リアクションの表示サイズ"
+limitWidthOfReaction: "リアクションの最大横幅を制限し、縮小して表示する"
 noteIdOrUrl: "ノートIDまたはURL"
 video: "動画"
 videos: "動画"
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 2b850016c5..9a107c3674 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]"
 	@click="toggleReaction()"
 >
-	<MkReactionIcon :class="$style.icon" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/>
+	<MkReactionIcon :class="defaultStore.state.limitWidthOfReaction ? $style.limitWidth : ''" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/>
 	<span :class="$style.count">{{ count }}</span>
 </button>
 </template>
@@ -188,7 +188,7 @@ if (!mock) {
 	}
 }
 
-.icon {
+.limitWidth {
 	max-width: 150px;
 }
 
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 06d3789829..313b5efc46 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -56,6 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<option value="medium">{{ i18n.ts.medium }}</option>
 					<option value="large">{{ i18n.ts.large }}</option>
 				</MkRadios>
+				<MkSwitch v-model="limitWidthOfReaction">{{ i18n.ts.limitWidthOfReaction }}</MkSwitch>
 			</div>
 
 			<MkSelect v-model="instanceTicker">
@@ -226,6 +227,7 @@ const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serve
 const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNoteActionsOnlyHover'));
 const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showClipButtonInNoteFooter'));
 const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize'));
+const limitWidthOfReaction = computed(defaultStore.makeGetterSetter('limitWidthOfReaction'));
 const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes'));
 const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v));
 const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal'));
@@ -290,6 +292,7 @@ watch([
 	overridedDeviceKind,
 	mediaListWithOneImageAppearance,
 	reactionsDisplaySize,
+	limitWidthOfReaction,
 	highlightSensitiveMedia,
 	keepScreenOn,
 	disableStreamingTimeline,
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 6d95ddba35..12660e9e8d 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -330,6 +330,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: 'medium' as 'small' | 'medium' | 'large',
 	},
+	limitWidthOfReaction: {
+		where: 'device',
+		default: true,
+	},
 	forceShowAds: {
 		where: 'device',
 		default: false,

From 97c10ed1e5ef5c010e8aae6042efeb6bd939757b Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 24 Nov 2023 09:20:34 +0900
Subject: [PATCH 035/435] Update index.d.ts

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

diff --git a/locales/index.d.ts b/locales/index.d.ts
index e6761641ab..042c7750e1 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1040,7 +1040,7 @@ export interface Locale {
     "enableChartsForFederatedInstances": string;
     "showClipButtonInNoteFooter": string;
     "reactionsDisplaySize": string;
-		"limitWidthOfReaction": string;
+    "limitWidthOfReaction": string;
     "noteIdOrUrl": string;
     "video": string;
     "videos": string;

From 06ed64f26f799ada108c8f2df315414b27681765 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 24 Nov 2023 09:20:41 +0900
Subject: [PATCH 036/435] update node to 20.10.0

---
 .devcontainer/devcontainer.json       | 2 +-
 .github/workflows/get-api-diff.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 +-
 .node-version                         | 2 +-
 Dockerfile                            | 2 +-
 8 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 0583a66960..e409adf644 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -8,7 +8,7 @@
 			"version": "8.9.2"
 		},
 		"ghcr.io/devcontainers/features/node:1": {
-			"version": "20.5.1"
+			"version": "20.10.0"
 		}
 	},
 	"forwardPorts": [3000],
diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index e6de226e6b..281e2058a6 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -18,7 +18,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.5.1]
+        node-version: [20.10.0]
         api-json-name: [api-base.json, api-head.json]
         include:
           - api-json-name: api-base.json
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index cbf0275620..6e8327ca07 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -13,7 +13,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.5.1]
+        node-version: [20.10.0]
 
     services:
       postgres:
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index 9575e22d02..50c225189d 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -13,7 +13,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.5.1]
+        node-version: [20.10.0]
 
     steps:
     - uses: actions/checkout@v4.1.1
@@ -51,7 +51,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        node-version: [20.5.1]
+        node-version: [20.10.0]
         browser: [chrome]
 
     services:
diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml
index ae1ddf31d1..76e170b3e3 100644
--- a/.github/workflows/test-misskey-js.yml
+++ b/.github/workflows/test-misskey-js.yml
@@ -16,7 +16,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.5.1]
+        node-version: [20.10.0]
         # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
 
     steps:
diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml
index d9bfb12be5..694fa1a8f5 100644
--- a/.github/workflows/test-production.yml
+++ b/.github/workflows/test-production.yml
@@ -16,7 +16,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [20.5.1]
+        node-version: [20.10.0]
 
     steps:
     - uses: actions/checkout@v4.1.1
diff --git a/.node-version b/.node-version
index 7cc2069986..d5a159609d 100644
--- a/.node-version
+++ b/.node-version
@@ -1 +1 @@
-20.5.1
+20.10.0
diff --git a/Dockerfile b/Dockerfile
index d397fe01cd..028a3976d2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
 # syntax = docker/dockerfile:1.4
 
-ARG NODE_VERSION=20.5.1-bullseye
+ARG NODE_VERSION=20.10.0-bullseye
 
 # build assets & compile TypeScript
 

From f7bdf5a2c0d69b3c7a18078ea7ca55106460a0a3 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Fri, 24 Nov 2023 09:48:36 +0900
Subject: [PATCH 037/435] Replace deprecated `Repository.findOneById()`
 (#12426)

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

diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 513b77b98a..c5d42dabe4 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -262,7 +262,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				if (renote.channelId && renote.channelId !== ps.channelId) {
 					// チャンネルのノートに対しリノート要求がきたとき、チャンネル外へのリノート可否をチェック
 					// リノートのユースケースのうち、チャンネル内→チャンネル外は少数だと考えられるため、JOINはせず必要な時に都度取得する
-					const renoteChannel = await this.channelsRepository.findOneById(renote.channelId);
+					const renoteChannel = await this.channelsRepository.findOneBy({ id: renote.channelId });
 					if (renoteChannel == null) {
 						// リノートしたいノートが書き込まれているチャンネルが無い
 						throw new ApiError(meta.errors.noSuchChannel);

From 536f08c401e82b9d34e2435f62ab2ac96473e77b Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Fri, 24 Nov 2023 15:09:25 +0900
Subject: [PATCH 038/435] fix: hard mute limit not applied (#12428)

* fix: hard mute limit not applied

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c49aa0e899..5678da25a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@
 ### General
 - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
 - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
+- Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加
 - Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
 
 ### Client
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index 8ba29c5658..b045c01189 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -243,7 +243,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			function checkMuteWordCount(mutedWords: (string[] | string)[], limit: number) {
 				// TODO: ちゃんと数える
-				const length = JSON.stringify(ps.mutedWords).length;
+				const length = JSON.stringify(mutedWords).length;
 				if (length > limit) {
 					throw new ApiError(meta.errors.tooManyMutedWords);
 				}

From 9c84055f504d89b0ec26d89589dcb3be1d395740 Mon Sep 17 00:00:00 2001
From: Camilla Ett <camilla.ett@gmail.com>
Date: Fri, 24 Nov 2023 16:19:37 +0900
Subject: [PATCH 039/435] =?UTF-8?q?Feat(frontend):=20=E3=82=B3=E3=83=B3?=
 =?UTF-8?q?=E3=83=88=E3=83=AD=E3=83=BC=E3=83=AB=E3=83=91=E3=83=8D=E3=83=AB?=
 =?UTF-8?q?=E3=80=8C=E9=80=9A=E5=A0=B1=E3=80=8D=E3=81=AB=E3=81=8A=E3=81=84?=
 =?UTF-8?q?=E3=81=A6=E3=80=81=E9=80=9A=E5=A0=B1=E8=80=85=E3=82=82=E3=83=AA?=
 =?UTF-8?q?=E3=83=B3=E3=82=AF=E3=81=A7=E9=A3=9B=E3=81=B9=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3=20(#12427)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue
index 66114b8734..2c7be319cb 100644
--- a/packages/frontend/src/components/MkAbuseReport.vue
+++ b/packages/frontend/src/components/MkAbuseReport.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<Mfm :text="report.comment"/>
 		</div>
 		<hr/>
-		<div>{{ i18n.ts.reporter }}: <MkAcct :user="report.reporter"/></div>
+		<div>{{ i18n.ts.reporter }}: <MkA :to="`/admin/user/${report.reporter.id}`" class="_link">@{{ report.reporter.username }}</MkA></div>
 		<div v-if="report.assignee">
 			{{ i18n.ts.moderator }}:
 			<MkAcct :user="report.assignee"/>

From 252efe8252ce5555ebc79b6a348610e9a37d9152 Mon Sep 17 00:00:00 2001
From: yukineko <27853966+hideki0403@users.noreply.github.com>
Date: Fri, 24 Nov 2023 20:19:46 +0900
Subject: [PATCH 040/435] =?UTF-8?q?fix:=20=E7=89=B9=E5=AE=9A=E3=81=AE?=
 =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E4=B8=8B=E3=81=A7=E3=83=81=E3=83=A3=E3=83=B3?=
 =?UTF-8?q?=E3=83=8D=E3=83=AB=E3=82=84=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC?=
 =?UTF-8?q?=E3=81=AE=E3=83=8E=E3=83=BC=E3=83=88=E4=B8=80=E8=A6=A7=E3=81=AB?=
 =?UTF-8?q?=E6=9C=80=E6=96=B0=E3=81=AE=E3=83=8E=E3=83=BC=E3=83=88=E3=81=8C?=
 =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=AA=E3=81=8F=E3=81=AA?=
 =?UTF-8?q?=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1243?=
 =?UTF-8?q?1)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: 特定の条件下でチャンネルやユーザーのノート一覧に最新のノートが表示されなくなる問題を修正

* update: CHANGELOG.md
---
 CHANGELOG.md                                                | 1 +
 .../backend/src/server/api/endpoints/channels/timeline.ts   | 6 +++++-
 packages/backend/src/server/api/endpoints/users/notes.ts    | 6 +++++-
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5678da25a4..64720ef482 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,7 @@
 - Fix: ロールタイムラインが保存されない問題を修正
 - Fix: api.jsonの生成ロジックを改善 #12402
 - Fix: 招待コードが使い回せる問題を修正
+- Fix: 特定の条件下でチャンネルやユーザーのノート一覧に最新のノートが表示されなくなる問題を修正
 
 ## 2023.11.1
 
diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts
index fae4249c8a..4ca7325f30 100644
--- a/packages/backend/src/server/api/endpoints/channels/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts
@@ -15,6 +15,7 @@ import { IdService } from '@/core/IdService.js';
 import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
 import { CacheService } from '@/core/CacheService.js';
+import { MetaService } from '@/core/MetaService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -72,12 +73,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private funoutTimelineService: FunoutTimelineService,
 		private cacheService: CacheService,
 		private activeUsersChart: ActiveUsersChart,
+		private metaService: MetaService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
 			const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null);
 			const isRangeSpecified = untilId != null && sinceId != null;
 
+			const serverSettings = await this.metaService.fetch();
+
 			const channel = await this.channelsRepository.findOneBy({
 				id: ps.channelId,
 			});
@@ -88,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			if (me) this.activeUsersChart.read(me);
 
-			if (isRangeSpecified || sinceId == null) {
+			if (serverSettings.enableFanoutTimeline && (isRangeSpecified || sinceId == null)) {
 				const [
 					userIdsWhoMeMuting,
 				] = me ? await Promise.all([
diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index a43e572922..2e7d939b12 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -15,6 +15,7 @@ import { IdService } from '@/core/IdService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
 import { QueryService } from '@/core/QueryService.js';
 import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { MetaService } from '@/core/MetaService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -71,6 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private cacheService: CacheService,
 		private idService: IdService,
 		private funoutTimelineService: FunoutTimelineService,
+		private metaService: MetaService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
@@ -78,7 +80,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const isRangeSpecified = untilId != null && sinceId != null;
 			const isSelf = me && (me.id === ps.userId);
 
-			if (isRangeSpecified || sinceId == null) {
+			const serverSettings = await this.metaService.fetch();
+
+			if (serverSettings.enableFanoutTimeline && (isRangeSpecified || sinceId == null)) {
 				const [
 					userIdsWhoMeMuting,
 				] = me ? await Promise.all([

From da3064343bfc76e8402adf5cd5ed68824c1e42da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?=
 =?UTF-8?q?=E3=81=AB=E3=82=85?=
 <17376330+u1-liquid@users.noreply.github.com>
Date: Fri, 24 Nov 2023 06:37:06 +0900
Subject: [PATCH 041/435] =?UTF-8?q?enhance(frontend):=20=E7=B5=B5=E6=96=87?=
 =?UTF-8?q?=E5=AD=97=E3=81=AE=E3=82=AA=E3=83=BC=E3=83=88=E3=82=B3=E3=83=B3?=
 =?UTF-8?q?=E3=83=97=E3=83=AA=E3=83=BC=E3=83=88=E3=81=AE=E3=82=A2=E3=83=AB?=
 =?UTF-8?q?=E3=82=B4=E3=83=AA=E3=82=BA=E3=83=A0=E3=81=AE=E6=94=B9=E5=96=84?=
 =?UTF-8?q?=20(MisskeyIO#261)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 実際は同じ絵文字なら重複してサジェストに出ないように
* エイリアスではない絵文字>前方一致>部分一致>あいまい検索順で表示されるようになるように
---
 .../src/components/MkAutocomplete.vue         | 38 +++++++++----------
 1 file changed, 17 insertions(+), 21 deletions(-)

diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index a0f4961116..c1fcbd7ac1 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -265,7 +265,7 @@ function emojiAutoComplete(query: string | null, emojiDb: EmojiDef[], max = 30):
 	// 前方一致(エイリアスなし)
 	emojiDb.some(x => {
 		if (x.name.startsWith(query) && !x.aliasOf) {
-			matched.set(x.name, { emoji: x, score: query.length });
+			matched.set(x.name, { emoji: x, score: query.length + 1 });
 		}
 		return matched.size === max;
 	});
@@ -273,8 +273,8 @@ function emojiAutoComplete(query: string | null, emojiDb: EmojiDef[], max = 30):
 	// 前方一致(エイリアス込み)
 	if (matched.size < max) {
 		emojiDb.some(x => {
-			if (x.name.startsWith(query)) {
-				matched.set(x.name, { emoji: x, score: query.length });
+			if (x.name.startsWith(query) && !matched.has(x.aliasOf ?? x.name)) {
+				matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length });
 			}
 			return matched.size === max;
 		});
@@ -283,36 +283,32 @@ function emojiAutoComplete(query: string | null, emojiDb: EmojiDef[], max = 30):
 	// 部分一致(エイリアス込み)
 	if (matched.size < max) {
 		emojiDb.some(x => {
-			if (x.name.includes(query)) {
-				matched.set(x.name, { emoji: x, score: query.length });
+			if (x.name.includes(query) && !matched.has(x.aliasOf ?? x.name)) {
+				matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length - 1 });
 			}
 			return matched.size === max;
 		});
 	}
 
-	// 簡易あいまい検索
-	if (matched.size < max) {
+	// 簡易あいまい検索(3文字以上)
+	if (matched.size < max && query.length > 3) {
 		const queryChars = [...query];
 		const hitEmojis = new Map<string, EmojiScore>();
 
 		for (const x of emojiDb) {
-			// クエリ文字列の1文字単位で絵文字名にヒットするかを見る
-			// ただし、過剰に検出されるのを防ぐためクエリ文字列に登場する順番で絵文字名を走査する
+			// 文字列の位置を進めながら、クエリの文字を順番に探す
 
-			let queryCharHitPos = 0;
-			let queryCharHitCount = 0;
-			for (let idx = 0; idx < queryChars.length; idx++) {
-				queryCharHitPos = x.name.indexOf(queryChars[idx], queryCharHitPos);
-				if (queryCharHitPos <= -1) {
-					break;
-				}
-
-				queryCharHitCount++;
+			let pos = 0;
+			let hit = 0;
+			for (const c of queryChars) {
+				pos = x.name.indexOf(c, pos);
+				if (pos <= -1) break;
+				hit++;
 			}
 
-			// ヒット数が少なすぎると検索結果が汚れるので調節する
-			if (queryCharHitCount > 2) {
-				hitEmojis.set(x.name, { emoji: x, score: queryCharHitCount });
+			// 半分以上の文字が含まれていればヒットとする
+			if (hit > Math.ceil(queryChars.length / 2) && hit - 2 > (matched.get(x.aliasOf ?? x.name)?.score ?? 0)) {
+				hitEmojis.set(x.aliasOf ?? x.name, { emoji: x, score: hit - 2 });
 			}
 		}
 

From 95095ee8d11a07938c183d4391de0a76ade31d44 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, 24 Nov 2023 21:11:18 +0900
Subject: [PATCH 042/435] =?UTF-8?q?enhance(frontend):=20=E3=83=A6=E3=83=BC?=
 =?UTF-8?q?=E3=82=B6=E3=83=BC=E3=81=AERaw=E3=83=87=E3=83=BC=E3=82=BF?=
 =?UTF-8?q?=E3=82=92=E8=AA=AD=E3=82=81=E3=82=8B=E3=83=9A=E3=83=BC=E3=82=B8?=
 =?UTF-8?q?=E3=82=92=E5=BE=A9=E6=B4=BB=E3=81=95=E3=81=9B=E3=82=8B=20(#1243?=
 =?UTF-8?q?6)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (add) User raw page

* Update Changelog

* fix lint
---
 CHANGELOG.md                               |   1 +
 packages/frontend/src/pages/user/index.vue |   6 +
 packages/frontend/src/pages/user/raw.vue   | 130 +++++++++++++++++++++
 3 files changed, 137 insertions(+)
 create mode 100644 packages/frontend/src/pages/user/raw.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 64720ef482..0215e7e735 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@
 
 ### Client
 - Enhance: 絵文字のオートコンプリート機能強化 #12364
+- Enhance: ユーザーのRawデータを表示するページが復活
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue
index 7550d5bcb2..50cc9a3311 100644
--- a/packages/frontend/src/pages/user/index.vue
+++ b/packages/frontend/src/pages/user/index.vue
@@ -18,6 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<XPages v-else-if="tab === 'pages'" :user="user"/>
 			<XFlashs v-else-if="tab === 'flashs'" :user="user"/>
 			<XGallery v-else-if="tab === 'gallery'" :user="user"/>
+			<XRaw v-else-if="tab === 'raw'" :user="user"/>
 		</div>
 		<MkError v-else-if="error" @retry="fetchUser()"/>
 		<MkLoading v-else/>
@@ -44,6 +45,7 @@ const XLists = defineAsyncComponent(() => import('./lists.vue'));
 const XPages = defineAsyncComponent(() => import('./pages.vue'));
 const XFlashs = defineAsyncComponent(() => import('./flashs.vue'));
 const XGallery = defineAsyncComponent(() => import('./gallery.vue'));
+const XRaw = defineAsyncComponent(() => import('./raw.vue'));
 
 const props = withDefaults(defineProps<{
 	acct: string;
@@ -112,6 +114,10 @@ const headerTabs = $computed(() => user ? [{
 	key: 'gallery',
 	title: i18n.ts.gallery,
 	icon: 'ti ti-icons',
+}, {
+	key: 'raw',
+	title: 'Raw',
+	icon: 'ti ti-code',
 }] : []);
 
 definePageMetadata(computed(() => user ? {
diff --git a/packages/frontend/src/pages/user/raw.vue b/packages/frontend/src/pages/user/raw.vue
new file mode 100644
index 0000000000..0c0bfc29ca
--- /dev/null
+++ b/packages/frontend/src/pages/user/raw.vue
@@ -0,0 +1,130 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkSpacer :contentMax="600" :marginMin="16" :marginMax="32">
+	<div class="_gaps_m">
+		<div :class="$style.userMInfoRoot">
+			<MkAvatar :class="$style.userMInfoAvatar" :user="user" indicator link preview/>
+			<div :class="$style.userMInfoMetaRoot">
+				<span :class="$style.userMInfoMetaName"><MkUserName :class="$style.userMInfoMetaName" :user="user"/></span>
+				<span :class="$style.userMInfoMetaSub"><span class="acct _monospace">@{{ acct(user) }}</span></span>
+				<span :class="$style.userMInfoMetaState">
+					<span v-if="suspended" :class="$style.suspended">Suspended</span>
+					<span v-if="silenced" :class="$style.silenced">Silenced</span>
+					<span v-if="moderator" :class="$style.moderator">Moderator</span>
+				</span>
+			</div>
+		</div>
+
+		<div style="display: flex; flex-direction: column; gap: 1em;">
+			<MkKeyValue :copy="user.id" oneline>
+				<template #key>ID</template>
+				<template #value><span class="_monospace">{{ user.id }}</span></template>
+			</MkKeyValue>
+			<MkKeyValue oneline>
+				<template #key>{{ i18n.ts.createdAt }}</template>
+				<template #value><span class="_monospace"><MkTime :time="user.createdAt" :mode="'detail'"/></span></template>
+			</MkKeyValue>
+		</div>
+
+		<FormSection>
+			<template #label>Raw</template>
+			<MkObjectView tall :value="user"></MkObjectView>
+		</FormSection>
+	</div>
+</MkSpacer>
+</template>
+
+<script lang="ts" setup>
+import { computed } from 'vue';
+import * as Misskey from 'misskey-js';
+import { acct } from '@/filters/user.js';
+import { i18n } from '@/i18n.js';
+import MkKeyValue from '@/components/MkKeyValue.vue';
+import FormSection from '@/components/form/section.vue';
+import MkObjectView from '@/components/MkObjectView.vue';
+
+const props = defineProps<{
+	user: Misskey.entities.User;
+}>();
+
+const moderator = computed(() => props.user.isModerator ?? false);
+const silenced = computed(() => props.user.isSilenced ?? false);
+const suspended = computed(() => props.user.isSuspended ?? false);
+</script>
+
+<style lang="scss" module>
+.userMInfoRoot {
+	display: flex;
+	align-items: center;
+}
+
+.userMInfoAvatar {
+	display: block;
+	width: 64px;
+	height: 64px;
+	margin-right: 16px;
+}
+
+.userMInfoMetaRoot {
+	flex: 1;
+	overflow: hidden;
+}
+
+.userMInfoMetaName {
+	display: block;
+	width: 100%;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+.userMInfoMetaSub {
+	display: block;
+	width: 100%;
+	font-size: 85%;
+	opacity: 0.7;
+	white-space: nowrap;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+.userMInfoMetaState {
+	display: flex;
+	gap: 8px;
+	flex-wrap: wrap;
+	margin-top: 4px;
+
+	&:empty {
+		display: none;
+	}
+
+	> .suspended,
+	> .silenced,
+	> .moderator {
+		display: inline-block;
+		border: solid 1px;
+		border-radius: 6px;
+		padding: 2px 6px;
+		font-size: 85%;
+	}
+
+	> .suspended {
+		color: var(--error);
+		border-color: var(--error);
+	}
+
+	> .silenced {
+		color: var(--warn);
+		border-color: var(--warn);
+	}
+
+	> .moderator {
+		color: var(--success);
+		border-color: var(--success);
+	}
+}
+</style>

From c8b85a98b807be7d7b4032cb2b9703c25665b1c5 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Sun, 26 Nov 2023 09:54:24 +0900
Subject: [PATCH 043/435] Add mocks for Web Audio API (#12457)

---
 packages/frontend/test/init.ts | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/packages/frontend/test/init.ts b/packages/frontend/test/init.ts
index 986fa99c17..ab5e84b53c 100644
--- a/packages/frontend/test/init.ts
+++ b/packages/frontend/test/init.ts
@@ -25,3 +25,21 @@ vi.mock('@/store.js', () => {
 		},
 	};
 });
+
+// Add mocks for Web Audio API
+const AudioNodeMock = vi.fn(() => ({
+	connect: vi.fn(() => ({ connect: vi.fn() })),
+	start: vi.fn(),
+}));
+
+const GainNodeMock = vi.fn(() => ({
+	gain: vi.fn(),
+}));
+
+const AudioContextMock = vi.fn(() => ({
+	createBufferSource: vi.fn(() => new AudioNodeMock()),
+	createGain: vi.fn(() => new GainNodeMock()),
+	decodeAudioData: vi.fn(),
+}));
+
+vi.stubGlobal('AudioContext', AudioContextMock);

From 3e0231d99501aba3b1f47431015c9b4a2f671e26 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sun, 26 Nov 2023 10:01:06 +0900
Subject: [PATCH 044/435] =?UTF-8?q?fix(backend):=20=E4=BD=95=E3=82=82?=
 =?UTF-8?q?=E3=83=8E=E3=83=BC=E3=83=88=E3=81=97=E3=81=A6=E3=81=84=E3=81=AA?=
 =?UTF-8?q?=E3=81=84=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AE=E3=83=95?=
 =?UTF-8?q?=E3=82=A3=E3=83=BC=E3=83=89=E3=81=AB=E3=82=A2=E3=82=AF=E3=82=BB?=
 =?UTF-8?q?=E3=82=B9=E3=81=99=E3=82=8B=E3=81=A8=E3=82=A8=E3=83=A9=E3=83=BC?=
 =?UTF-8?q?=E3=81=AB=E3=81=AA=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#12455)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(backend): 何もノートしていないユーザーのフィードにアクセスするとエラーになる問題を修正

* Update CHANGELOG.md

* add test

* fix: incorrect bob's username
---
 CHANGELOG.md                                   | 1 +
 packages/backend/src/server/web/FeedService.ts | 2 +-
 packages/backend/test/e2e/fetch-resource.ts    | 7 ++++++-
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0215e7e735..fdf6709117 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@
 - Fix: api.jsonの生成ロジックを改善 #12402
 - Fix: 招待コードが使い回せる問題を修正
 - Fix: 特定の条件下でチャンネルやユーザーのノート一覧に最新のノートが表示されなくなる問題を修正
+- Fix: 何もノートしていないユーザーのフィードにアクセスするとエラーになる問題を修正
 
 ## 2023.11.1
 
diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts
index 3ba26ad34a..dd4304e6ef 100644
--- a/packages/backend/src/server/web/FeedService.ts
+++ b/packages/backend/src/server/web/FeedService.ts
@@ -58,7 +58,7 @@ export class FeedService {
 		const feed = new Feed({
 			id: author.link,
 			title: `${author.name} (@${user.username}@${this.config.host})`,
-			updated: this.idService.parse(notes[0].id).date,
+			updated: notes.length !== 0 ? this.idService.parse(notes[0].id).date : undefined,
 			generator: 'Misskey',
 			description: `${user.notesCount} Notes, ${profile.ffVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.ffVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`,
 			link: author.link,
diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts
index 1cbfec3e5f..251d662760 100644
--- a/packages/backend/test/e2e/fetch-resource.ts
+++ b/packages/backend/test/e2e/fetch-resource.ts
@@ -93,7 +93,7 @@ describe('Webリソース', () => {
 		});
 		aliceChannel = await channel(alice, {});
 
-		bob = await signup({ username: 'alice' });
+		bob = await signup({ username: 'bob' });
 	}, 1000 * 60 * 2);
 
 	afterAll(async () => {
@@ -152,6 +152,11 @@ describe('Webリソース', () => {
 			type,
 		}));
 
+		test('がGETできる。(ノートが存在しない場合でも。)', async () => await ok({
+			path: path(bob.username),
+			type,
+		}));
+
 		test('は存在しないユーザーはGETできない。', async () => await notOk({
 			path: path('nonexisting'),
 			status: 404,

From 7a494b2aa7e0337a220b8988122839e9a7b75538 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sun, 26 Nov 2023 10:02:22 +0900
Subject: [PATCH 045/435] fix(backend): rename FunoutTimelineService to
 FanoutTimelineService (#12453)

---
 packages/backend/src/core/AntennaService.ts   |  6 ++--
 packages/backend/src/core/CoreModule.ts       | 12 +++----
 ...ineService.ts => FanoutTimelineService.ts} |  2 +-
 .../backend/src/core/NoteCreateService.ts     | 36 +++++++++----------
 packages/backend/src/core/RoleService.ts      |  6 ++--
 .../backend/src/core/UserFollowingService.ts  |  8 ++---
 .../server/api/endpoints/antennas/notes.ts    |  6 ++--
 .../server/api/endpoints/channels/timeline.ts |  6 ++--
 .../api/endpoints/notes/hybrid-timeline.ts    | 10 +++---
 .../api/endpoints/notes/local-timeline.ts     |  8 ++---
 .../server/api/endpoints/notes/timeline.ts    |  6 ++--
 .../api/endpoints/notes/user-list-timeline.ts |  6 ++--
 .../src/server/api/endpoints/roles/notes.ts   |  6 ++--
 .../src/server/api/endpoints/users/notes.ts   | 10 +++---
 14 files changed, 64 insertions(+), 64 deletions(-)
 rename packages/backend/src/core/{FunoutTimelineService.ts => FanoutTimelineService.ts} (98%)

diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts
index 2815d24734..2c27a02559 100644
--- a/packages/backend/src/core/AntennaService.ts
+++ b/packages/backend/src/core/AntennaService.ts
@@ -16,7 +16,7 @@ import type { AntennasRepository, UserListMembershipsRepository } from '@/models
 import { UtilityService } from '@/core/UtilityService.js';
 import { bindThis } from '@/decorators.js';
 import type { GlobalEvents } from '@/core/GlobalEventService.js';
-import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import type { OnApplicationShutdown } from '@nestjs/common';
 
 @Injectable()
@@ -39,7 +39,7 @@ export class AntennaService implements OnApplicationShutdown {
 
 		private utilityService: UtilityService,
 		private globalEventService: GlobalEventService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 	) {
 		this.antennasFetched = false;
 		this.antennas = [];
@@ -94,7 +94,7 @@ export class AntennaService implements OnApplicationShutdown {
 		const redisPipeline = this.redisForTimelines.pipeline();
 
 		for (const antenna of matchedAntennas) {
-			this.funoutTimelineService.push(`antennaTimeline:${antenna.id}`, note.id, 200, redisPipeline);
+			this.fanoutTimelineService.push(`antennaTimeline:${antenna.id}`, note.id, 200, redisPipeline);
 			this.globalEventService.publishAntennaStream(antenna.id, 'note', note);
 		}
 
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index 9fb29e0e68..bf6f0ef879 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -62,7 +62,7 @@ import { FileInfoService } from './FileInfoService.js';
 import { SearchService } from './SearchService.js';
 import { ClipService } from './ClipService.js';
 import { FeaturedService } from './FeaturedService.js';
-import { FunoutTimelineService } from './FunoutTimelineService.js';
+import { FanoutTimelineService } from './FanoutTimelineService.js';
 import { ChannelFollowingService } from './ChannelFollowingService.js';
 import { RegistryApiService } from './RegistryApiService.js';
 import { ChartLoggerService } from './chart/ChartLoggerService.js';
@@ -194,7 +194,7 @@ const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: Fi
 const $SearchService: Provider = { provide: 'SearchService', useExisting: SearchService };
 const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService };
 const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService };
-const $FunoutTimelineService: Provider = { provide: 'FunoutTimelineService', useExisting: FunoutTimelineService };
+const $FanoutTimelineService: Provider = { provide: 'FanoutTimelineService', useExisting: FanoutTimelineService };
 const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService };
 const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService };
 
@@ -330,7 +330,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		SearchService,
 		ClipService,
 		FeaturedService,
-		FunoutTimelineService,
+		FanoutTimelineService,
 		ChannelFollowingService,
 		RegistryApiService,
 		ChartLoggerService,
@@ -459,7 +459,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$SearchService,
 		$ClipService,
 		$FeaturedService,
-		$FunoutTimelineService,
+		$FanoutTimelineService,
 		$ChannelFollowingService,
 		$RegistryApiService,
 		$ChartLoggerService,
@@ -589,7 +589,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		SearchService,
 		ClipService,
 		FeaturedService,
-		FunoutTimelineService,
+		FanoutTimelineService,
 		ChannelFollowingService,
 		RegistryApiService,
 		FederationChart,
@@ -717,7 +717,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$SearchService,
 		$ClipService,
 		$FeaturedService,
-		$FunoutTimelineService,
+		$FanoutTimelineService,
 		$ChannelFollowingService,
 		$RegistryApiService,
 		$FederationChart,
diff --git a/packages/backend/src/core/FunoutTimelineService.ts b/packages/backend/src/core/FanoutTimelineService.ts
similarity index 98%
rename from packages/backend/src/core/FunoutTimelineService.ts
rename to packages/backend/src/core/FanoutTimelineService.ts
index c633c329e5..6a1b0aa879 100644
--- a/packages/backend/src/core/FunoutTimelineService.ts
+++ b/packages/backend/src/core/FanoutTimelineService.ts
@@ -10,7 +10,7 @@ import { bindThis } from '@/decorators.js';
 import { IdService } from '@/core/IdService.js';
 
 @Injectable()
-export class FunoutTimelineService {
+export class FanoutTimelineService {
 	constructor(
 		@Inject(DI.redisForTimelines)
 		private redisForTimelines: Redis.Redis,
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 86f220abd0..fd87edc28e 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -54,7 +54,7 @@ import { RoleService } from '@/core/RoleService.js';
 import { MetaService } from '@/core/MetaService.js';
 import { SearchService } from '@/core/SearchService.js';
 import { FeaturedService } from '@/core/FeaturedService.js';
-import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { UserBlockingService } from '@/core/UserBlockingService.js';
 
@@ -194,7 +194,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 		private idService: IdService,
 		private globalEventService: GlobalEventService,
 		private queueService: QueueService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 		private noteReadService: NoteReadService,
 		private notificationService: NotificationService,
 		private relayService: RelayService,
@@ -843,9 +843,9 @@ export class NoteCreateService implements OnApplicationShutdown {
 		const r = this.redisForTimelines.pipeline();
 
 		if (note.channelId) {
-			this.funoutTimelineService.push(`channelTimeline:${note.channelId}`, note.id, this.config.perChannelMaxNoteCacheCount, r);
+			this.fanoutTimelineService.push(`channelTimeline:${note.channelId}`, note.id, this.config.perChannelMaxNoteCacheCount, r);
 
-			this.funoutTimelineService.push(`userTimelineWithChannel:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
+			this.fanoutTimelineService.push(`userTimelineWithChannel:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
 
 			const channelFollowings = await this.channelFollowingsRepository.find({
 				where: {
@@ -855,9 +855,9 @@ export class NoteCreateService implements OnApplicationShutdown {
 			});
 
 			for (const channelFollowing of channelFollowings) {
-				this.funoutTimelineService.push(`homeTimeline:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r);
+				this.fanoutTimelineService.push(`homeTimeline:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r);
 				if (note.fileIds.length > 0) {
-					this.funoutTimelineService.push(`homeTimelineWithFiles:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
+					this.fanoutTimelineService.push(`homeTimelineWithFiles:${channelFollowing.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
 				}
 			}
 		} else {
@@ -895,9 +895,9 @@ export class NoteCreateService implements OnApplicationShutdown {
 					if (!following.withReplies) continue;
 				}
 
-				this.funoutTimelineService.push(`homeTimeline:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r);
+				this.fanoutTimelineService.push(`homeTimeline:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax, r);
 				if (note.fileIds.length > 0) {
-					this.funoutTimelineService.push(`homeTimelineWithFiles:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
+					this.fanoutTimelineService.push(`homeTimelineWithFiles:${following.followerId}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
 				}
 			}
 
@@ -913,36 +913,36 @@ export class NoteCreateService implements OnApplicationShutdown {
 					if (!userListMembership.withReplies) continue;
 				}
 
-				this.funoutTimelineService.push(`userListTimeline:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax, r);
+				this.fanoutTimelineService.push(`userListTimeline:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax, r);
 				if (note.fileIds.length > 0) {
-					this.funoutTimelineService.push(`userListTimelineWithFiles:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax / 2, r);
+					this.fanoutTimelineService.push(`userListTimelineWithFiles:${userListMembership.userListId}`, note.id, meta.perUserListTimelineCacheMax / 2, r);
 				}
 			}
 
 			if (note.visibility !== 'specified' || !note.visibleUserIds.some(v => v === user.id)) { // 自分自身のHTL
-				this.funoutTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r);
+				this.fanoutTimelineService.push(`homeTimeline:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax, r);
 				if (note.fileIds.length > 0) {
-					this.funoutTimelineService.push(`homeTimelineWithFiles:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
+					this.fanoutTimelineService.push(`homeTimelineWithFiles:${user.id}`, note.id, meta.perUserHomeTimelineCacheMax / 2, r);
 				}
 			}
 
 			// 自分自身以外への返信
 			if (note.replyId && note.replyUserId !== note.userId) {
-				this.funoutTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
+				this.fanoutTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
 
 				if (note.visibility === 'public' && note.userHost == null) {
-					this.funoutTimelineService.push('localTimelineWithReplies', note.id, 300, r);
+					this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r);
 				}
 			} else {
-				this.funoutTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
+				this.fanoutTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
 				if (note.fileIds.length > 0) {
-					this.funoutTimelineService.push(`userTimelineWithFiles:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax / 2 : meta.perRemoteUserUserTimelineCacheMax / 2, r);
+					this.fanoutTimelineService.push(`userTimelineWithFiles:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax / 2 : meta.perRemoteUserUserTimelineCacheMax / 2, r);
 				}
 
 				if (note.visibility === 'public' && note.userHost == null) {
-					this.funoutTimelineService.push('localTimeline', note.id, 1000, r);
+					this.fanoutTimelineService.push('localTimeline', note.id, 1000, r);
 					if (note.fileIds.length > 0) {
-						this.funoutTimelineService.push('localTimelineWithFiles', note.id, 500, r);
+						this.fanoutTimelineService.push('localTimelineWithFiles', note.id, 500, r);
 					}
 				}
 			}
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 432887b3b7..29e48aa8ca 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -20,7 +20,7 @@ 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 { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import type { OnApplicationShutdown } from '@nestjs/common';
 
 export type RolePolicies = {
@@ -108,7 +108,7 @@ export class RoleService implements OnApplicationShutdown {
 		private globalEventService: GlobalEventService,
 		private idService: IdService,
 		private moderationLogService: ModerationLogService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 	) {
 		//this.onMessage = this.onMessage.bind(this);
 
@@ -482,7 +482,7 @@ export class RoleService implements OnApplicationShutdown {
 		const redisPipeline = this.redisForTimelines.pipeline();
 
 		for (const role of roles) {
-			this.funoutTimelineService.push(`roleTimeline:${role.id}`, note.id, 1000, redisPipeline);
+			this.fanoutTimelineService.push(`roleTimeline:${role.id}`, note.id, 1000, redisPipeline);
 			this.globalEventService.publishRoleTimelineStream(role.id, 'note', note);
 		}
 
diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts
index bd7f298021..3062999c08 100644
--- a/packages/backend/src/core/UserFollowingService.ts
+++ b/packages/backend/src/core/UserFollowingService.ts
@@ -29,7 +29,7 @@ import { CacheService } from '@/core/CacheService.js';
 import type { Config } from '@/config.js';
 import { AccountMoveService } from '@/core/AccountMoveService.js';
 import { UtilityService } from '@/core/UtilityService.js';
-import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import Logger from '../logger.js';
 
 const logger = new Logger('following/create');
@@ -84,7 +84,7 @@ export class UserFollowingService implements OnModuleInit {
 		private webhookService: WebhookService,
 		private apRendererService: ApRendererService,
 		private accountMoveService: AccountMoveService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 		private perUserFollowingChart: PerUserFollowingChart,
 		private instanceChart: InstanceChart,
 	) {
@@ -305,7 +305,7 @@ export class UserFollowingService implements OnModuleInit {
 				}
 			});
 
-			this.funoutTimelineService.purge(`homeTimeline:${follower.id}`);
+			this.fanoutTimelineService.purge(`homeTimeline:${follower.id}`);
 		}
 
 		// Publish followed event
@@ -374,7 +374,7 @@ export class UserFollowingService implements OnModuleInit {
 				}
 			});
 
-			this.funoutTimelineService.purge(`homeTimeline:${follower.id}`);
+			this.fanoutTimelineService.purge(`homeTimeline:${follower.id}`);
 		}
 
 		if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts
index 29e56b1085..0bf2688b4a 100644
--- a/packages/backend/src/server/api/endpoints/antennas/notes.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts
@@ -12,7 +12,7 @@ import { NoteReadService } from '@/core/NoteReadService.js';
 import { DI } from '@/di-symbols.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { IdService } from '@/core/IdService.js';
-import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { ApiError } from '../../error.js';
 
@@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private noteEntityService: NoteEntityService,
 		private queryService: QueryService,
 		private noteReadService: NoteReadService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 		private globalEventService: GlobalEventService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
@@ -98,7 +98,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				this.globalEventService.publishInternalEvent('antennaUpdated', antenna);
 			}
 
-			let noteIds = await this.funoutTimelineService.get(`antennaTimeline:${antenna.id}`, untilId, sinceId);
+			let noteIds = await this.fanoutTimelineService.get(`antennaTimeline:${antenna.id}`, untilId, sinceId);
 			noteIds = noteIds.slice(0, ps.limit);
 			if (noteIds.length === 0) {
 				return [];
diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts
index 4ca7325f30..f9207199d6 100644
--- a/packages/backend/src/server/api/endpoints/channels/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts
@@ -12,7 +12,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import ActiveUsersChart from '@/core/chart/charts/active-users.js';
 import { DI } from '@/di-symbols.js';
 import { IdService } from '@/core/IdService.js';
-import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
 import { CacheService } from '@/core/CacheService.js';
 import { MetaService } from '@/core/MetaService.js';
@@ -70,7 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private idService: IdService,
 		private noteEntityService: NoteEntityService,
 		private queryService: QueryService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 		private cacheService: CacheService,
 		private activeUsersChart: ActiveUsersChart,
 		private metaService: MetaService,
@@ -99,7 +99,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					this.cacheService.userMutingsCache.fetch(me.id),
 				]) : [new Set<string>()];
 
-				let noteIds = await this.funoutTimelineService.get(`channelTimeline:${channel.id}`, untilId, sinceId);
+				let noteIds = await this.fanoutTimelineService.get(`channelTimeline:${channel.id}`, untilId, sinceId);
 				noteIds = noteIds.slice(0, ps.limit);
 
 				if (noteIds.length > 0) {
diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index 408c2fa371..372199844d 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -14,7 +14,7 @@ import { RoleService } from '@/core/RoleService.js';
 import { IdService } from '@/core/IdService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
 import { CacheService } from '@/core/CacheService.js';
-import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { QueryService } from '@/core/QueryService.js';
 import { UserFollowingService } from '@/core/UserFollowingService.js';
 import { MetaService } from '@/core/MetaService.js';
@@ -77,7 +77,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private activeUsersChart: ActiveUsersChart,
 		private idService: IdService,
 		private cacheService: CacheService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 		private queryService: QueryService,
 		private userFollowingService: UserFollowingService,
 		private metaService: MetaService,
@@ -120,20 +120,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			let shouldFallbackToDb = false;
 
 			if (ps.withFiles) {
-				const [htlNoteIds, ltlNoteIds] = await this.funoutTimelineService.getMulti([
+				const [htlNoteIds, ltlNoteIds] = await this.fanoutTimelineService.getMulti([
 					`homeTimelineWithFiles:${me.id}`,
 					'localTimelineWithFiles',
 				], untilId, sinceId);
 				noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds]));
 			} else if (ps.withReplies) {
-				const [htlNoteIds, ltlNoteIds, ltlReplyNoteIds] = await this.funoutTimelineService.getMulti([
+				const [htlNoteIds, ltlNoteIds, ltlReplyNoteIds] = await this.fanoutTimelineService.getMulti([
 					`homeTimeline:${me.id}`,
 					'localTimeline',
 					'localTimelineWithReplies',
 				], untilId, sinceId);
 				noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds, ...ltlReplyNoteIds]));
 			} else {
-				const [htlNoteIds, ltlNoteIds] = await this.funoutTimelineService.getMulti([
+				const [htlNoteIds, ltlNoteIds] = await this.fanoutTimelineService.getMulti([
 					`homeTimeline:${me.id}`,
 					'localTimeline',
 				], untilId, sinceId);
diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index 79baa6b285..7d8dec7b8f 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -14,7 +14,7 @@ import { RoleService } from '@/core/RoleService.js';
 import { IdService } from '@/core/IdService.js';
 import { CacheService } from '@/core/CacheService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
-import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { QueryService } from '@/core/QueryService.js';
 import { MetaService } from '@/core/MetaService.js';
 import { MiLocalUser } from '@/models/User.js';
@@ -69,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private activeUsersChart: ActiveUsersChart,
 		private idService: IdService,
 		private cacheService: CacheService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 		private queryService: QueryService,
 		private metaService: MetaService,
 	) {
@@ -107,9 +107,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			let noteIds: string[];
 
 			if (ps.withFiles) {
-				noteIds = await this.funoutTimelineService.get('localTimelineWithFiles', untilId, sinceId);
+				noteIds = await this.fanoutTimelineService.get('localTimelineWithFiles', untilId, sinceId);
 			} else {
-				const [nonReplyNoteIds, replyNoteIds] = await this.funoutTimelineService.getMulti([
+				const [nonReplyNoteIds, replyNoteIds] = await this.fanoutTimelineService.getMulti([
 					'localTimeline',
 					'localTimelineWithReplies',
 				], untilId, sinceId);
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index 8037d4862f..470abe0b14 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -14,7 +14,7 @@ import { DI } from '@/di-symbols.js';
 import { IdService } from '@/core/IdService.js';
 import { CacheService } from '@/core/CacheService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
-import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { UserFollowingService } from '@/core/UserFollowingService.js';
 import { MiLocalUser } from '@/models/User.js';
 import { MetaService } from '@/core/MetaService.js';
@@ -65,7 +65,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private activeUsersChart: ActiveUsersChart,
 		private idService: IdService,
 		private cacheService: CacheService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 		private userFollowingService: UserFollowingService,
 		private queryService: QueryService,
 		private metaService: MetaService,
@@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				this.cacheService.userBlockedCache.fetch(me.id),
 			]);
 
-			let noteIds = await this.funoutTimelineService.get(ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, untilId, sinceId);
+			let noteIds = await this.fanoutTimelineService.get(ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, untilId, sinceId);
 			noteIds = noteIds.slice(0, ps.limit);
 
 			let redisTimeline: MiNote[] = [];
diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
index dbc3875597..1ac1d37f48 100644
--- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -13,7 +13,7 @@ import { DI } from '@/di-symbols.js';
 import { CacheService } from '@/core/CacheService.js';
 import { IdService } from '@/core/IdService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
-import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { QueryService } from '@/core/QueryService.js';
 import { MiLocalUser } from '@/models/User.js';
 import { MetaService } from '@/core/MetaService.js';
@@ -81,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private activeUsersChart: ActiveUsersChart,
 		private cacheService: CacheService,
 		private idService: IdService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 		private queryService: QueryService,
 		private metaService: MetaService,
 	) {
@@ -123,7 +123,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				this.cacheService.userBlockedCache.fetch(me.id),
 			]);
 
-			let noteIds = await this.funoutTimelineService.get(ps.withFiles ? `userListTimelineWithFiles:${list.id}` : `userListTimeline:${list.id}`, untilId, sinceId);
+			let noteIds = await this.fanoutTimelineService.get(ps.withFiles ? `userListTimelineWithFiles:${list.id}` : `userListTimeline:${list.id}`, untilId, sinceId);
 			noteIds = noteIds.slice(0, ps.limit);
 
 			let redisTimeline: MiNote[] = [];
diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts
index daa9affc20..7010df22c9 100644
--- a/packages/backend/src/server/api/endpoints/roles/notes.ts
+++ b/packages/backend/src/server/api/endpoints/roles/notes.ts
@@ -11,7 +11,7 @@ import { QueryService } from '@/core/QueryService.js';
 import { DI } from '@/di-symbols.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { IdService } from '@/core/IdService.js';
-import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -66,7 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private idService: IdService,
 		private noteEntityService: NoteEntityService,
 		private queryService: QueryService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
@@ -84,7 +84,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				return [];
 			}
 
-			let noteIds = await this.funoutTimelineService.get(`roleTimeline:${role.id}`, untilId, sinceId);
+			let noteIds = await this.fanoutTimelineService.get(`roleTimeline:${role.id}`, untilId, sinceId);
 			noteIds = noteIds.slice(0, ps.limit);
 
 			if (noteIds.length === 0) {
diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index 2e7d939b12..a775b58f03 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -14,7 +14,7 @@ import { CacheService } from '@/core/CacheService.js';
 import { IdService } from '@/core/IdService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
 import { QueryService } from '@/core/QueryService.js';
-import { FunoutTimelineService } from '@/core/FunoutTimelineService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { MetaService } from '@/core/MetaService.js';
 import { ApiError } from '../../error.js';
 
@@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private queryService: QueryService,
 		private cacheService: CacheService,
 		private idService: IdService,
-		private funoutTimelineService: FunoutTimelineService,
+		private fanoutTimelineService: FanoutTimelineService,
 		private metaService: MetaService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
@@ -90,9 +90,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				]) : [new Set<string>()];
 
 				const [noteIdsRes, repliesNoteIdsRes, channelNoteIdsRes] = await Promise.all([
-					this.funoutTimelineService.get(ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, untilId, sinceId),
-					ps.withReplies ? this.funoutTimelineService.get(`userTimelineWithReplies:${ps.userId}`, untilId, sinceId) : Promise.resolve([]),
-					ps.withChannelNotes ? this.funoutTimelineService.get(`userTimelineWithChannel:${ps.userId}`, untilId, sinceId) : Promise.resolve([]),
+					this.fanoutTimelineService.get(ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, untilId, sinceId),
+					ps.withReplies ? this.fanoutTimelineService.get(`userTimelineWithReplies:${ps.userId}`, untilId, sinceId) : Promise.resolve([]),
+					ps.withChannelNotes ? this.fanoutTimelineService.get(`userTimelineWithChannel:${ps.userId}`, untilId, sinceId) : Promise.resolve([]),
 				]);
 
 				let noteIds = Array.from(new Set([

From 2ee48ae04da540384214ff0d7c8df2dfb18c88fc Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sun, 26 Nov 2023 10:05:56 +0900
Subject: [PATCH 046/435] =?UTF-8?q?fix(backend):=20=E3=82=AE=E3=83=A3?=
 =?UTF-8?q?=E3=83=A9=E3=83=AA=E3=83=BC=E3=81=AE=E4=BA=BA=E6=B0=97=E3=81=AE?=
 =?UTF-8?q?=E6=8A=95=E7=A8=BF=E3=81=AE=E9=81=B8=E5=87=BA=E3=81=ABid?=
 =?UTF-8?q?=E3=82=92=E7=94=A8=E3=81=84=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=20(#12448)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/core/FeaturedService.ts  | 13 ++++++-
 .../server/api/endpoints/gallery/featured.ts  | 37 ++++++++++++++++---
 .../api/endpoints/gallery/posts/like.ts       |  7 ++++
 .../api/endpoints/gallery/posts/unlike.ts     | 10 +++++
 4 files changed, 60 insertions(+), 7 deletions(-)

diff --git a/packages/backend/src/core/FeaturedService.ts b/packages/backend/src/core/FeaturedService.ts
index 9617f83880..507fc464ff 100644
--- a/packages/backend/src/core/FeaturedService.ts
+++ b/packages/backend/src/core/FeaturedService.ts
@@ -5,11 +5,12 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import * as Redis from 'ioredis';
-import type { MiNote, MiUser } from '@/models/_.js';
+import type { MiGalleryPost, MiNote, MiUser } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
 
 const GLOBAL_NOTES_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 3; // 3日ごと
+export const GALLERY_POSTS_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 3; // 3日ごと
 const PER_USER_NOTES_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 7; // 1週間ごと
 const HASHTAG_RANKING_WINDOW = 1000 * 60 * 60; // 1時間ごと
 
@@ -79,6 +80,11 @@ export class FeaturedService {
 		return this.updateRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, noteId, score);
 	}
 
+	@bindThis
+	public updateGalleryPostsRanking(galleryPostId: MiGalleryPost['id'], score = 1): Promise<void> {
+		return this.updateRankingOf('featuredGalleryPostsRanking', GALLERY_POSTS_RANKING_WINDOW, galleryPostId, score);
+	}
+
 	@bindThis
 	public updateInChannelNotesRanking(channelId: MiNote['channelId'], noteId: MiNote['id'], score = 1): Promise<void> {
 		return this.updateRankingOf(`featuredInChannelNotesRanking:${channelId}`, GLOBAL_NOTES_RANKING_WINDOW, noteId, score);
@@ -99,6 +105,11 @@ export class FeaturedService {
 		return this.getRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, threshold);
 	}
 
+	@bindThis
+	public getGalleryPostsRanking(threshold: number): Promise<MiGalleryPost['id'][]> {
+		return this.getRankingOf('featuredGalleryPostsRanking', GALLERY_POSTS_RANKING_WINDOW, threshold);
+	}
+
 	@bindThis
 	public getInChannelNotesRanking(channelId: MiNote['channelId'], threshold: number): Promise<MiNote['id'][]> {
 		return this.getRankingOf(`featuredInChannelNotesRanking:${channelId}`, GLOBAL_NOTES_RANKING_WINDOW, threshold);
diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts
index cbab3a83a4..cea4234065 100644
--- a/packages/backend/src/server/api/endpoints/gallery/featured.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts
@@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { GalleryPostsRepository } from '@/models/_.js';
 import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
 import { DI } from '@/di-symbols.js';
+import { FeaturedService } from '@/core/FeaturedService.js';
 
 export const meta = {
 	tags: ['gallery'],
@@ -27,25 +28,49 @@ export const meta = {
 
 export const paramDef = {
 	type: 'object',
-	properties: {},
+	properties: {
+		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
+		untilId: { type: 'string', format: 'misskey:id' },
+	},
 	required: [],
 } as const;
 
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	private galleryPostsRankingCache: string[] = [];
+	private galleryPostsRankingCacheLastFetchedAt = 0;
+
 	constructor(
 		@Inject(DI.galleryPostsRepository)
 		private galleryPostsRepository: GalleryPostsRepository,
 
 		private galleryPostEntityService: GalleryPostEntityService,
+		private featuredService: FeaturedService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const query = this.galleryPostsRepository.createQueryBuilder('post')
-				.andWhere('post.createdAt > :date', { date: new Date(Date.now() - (1000 * 60 * 60 * 24 * 3)) })
-				.andWhere('post.likedCount > 0')
-				.orderBy('post.likedCount', 'DESC');
+			let postIds: string[];
+			if (this.galleryPostsRankingCacheLastFetchedAt !== 0 && (Date.now() - this.galleryPostsRankingCacheLastFetchedAt < 1000 * 60 * 30)) {
+				postIds = this.galleryPostsRankingCache;
+			} else {
+				postIds = await this.featuredService.getGalleryPostsRanking(100);
+				this.galleryPostsRankingCache = postIds;
+				this.galleryPostsRankingCacheLastFetchedAt = Date.now();
+			}
 
-			const posts = await query.limit(10).getMany();
+			postIds.sort((a, b) => a > b ? -1 : 1);
+			if (ps.untilId) {
+				postIds = postIds.filter(id => id < ps.untilId!);
+			}
+			postIds = postIds.slice(0, ps.limit);
+
+			if (postIds.length === 0) {
+				return [];
+			}
+
+			const query = this.galleryPostsRepository.createQueryBuilder('post')
+				.where('post.id IN (:...postIds)', { postIds: postIds });
+
+			const posts = await query.getMany();
 
 			return await this.galleryPostEntityService.packMany(posts, me);
 		});
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
index 561b2492ab..cc424261b4 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts
@@ -6,6 +6,7 @@
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { GalleryLikesRepository, GalleryPostsRepository } from '@/models/_.js';
+import { FeaturedService, GALLERY_POSTS_RANKING_WINDOW } from '@/core/FeaturedService.js';
 import { IdService } from '@/core/IdService.js';
 import { DI } from '@/di-symbols.js';
 import { ApiError } from '../../../error.js';
@@ -57,6 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		@Inject(DI.galleryLikesRepository)
 		private galleryLikesRepository: GalleryLikesRepository,
 
+		private featuredService: FeaturedService,
 		private idService: IdService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
@@ -88,6 +90,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				userId: me.id,
 			});
 
+			// ランキング更新
+			if (Date.now() - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) {
+				await this.featuredService.updateGalleryPostsRanking(post.id, 1);
+			}
+
 			this.galleryPostsRepository.increment({ id: post.id }, 'likedCount', 1);
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
index 832b62282f..caa4d45553 100644
--- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
+++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts
@@ -6,6 +6,8 @@
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { GalleryPostsRepository, GalleryLikesRepository } from '@/models/_.js';
+import { FeaturedService, GALLERY_POSTS_RANKING_WINDOW } from '@/core/FeaturedService.js';
+import { IdService } from '@/core/IdService.js';
 import { DI } from '@/di-symbols.js';
 import { ApiError } from '../../../error.js';
 
@@ -49,6 +51,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 		@Inject(DI.galleryLikesRepository)
 		private galleryLikesRepository: GalleryLikesRepository,
+
+		private featuredService: FeaturedService,
+		private idService: IdService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const post = await this.galleryPostsRepository.findOneBy({ id: ps.postId });
@@ -68,6 +73,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			// Delete like
 			await this.galleryLikesRepository.delete(exist.id);
 
+			// ランキング更新
+			if (Date.now() - this.idService.parse(post.id).date.getTime() < GALLERY_POSTS_RANKING_WINDOW) {
+				await this.featuredService.updateGalleryPostsRanking(post.id, -1);
+			}
+
 			this.galleryPostsRepository.decrement({ id: post.id }, 'likedCount', 1);
 		});
 	}

From d32631d1590ffe40f051e7abf75faab7bbf9c7da Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sun, 26 Nov 2023 12:54:23 +0900
Subject: [PATCH 047/435] fix: query error in notes/featured (#12439)

---
 .../backend/src/server/api/endpoints/notes/featured.ts    | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts
index c456874309..bc6cbe242f 100644
--- a/packages/backend/src/server/api/endpoints/notes/featured.ts
+++ b/packages/backend/src/server/api/endpoints/notes/featured.ts
@@ -64,16 +64,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				}
 			}
 
-			if (noteIds.length === 0) {
-				return [];
-			}
-
 			noteIds.sort((a, b) => a > b ? -1 : 1);
 			if (ps.untilId) {
 				noteIds = noteIds.filter(id => id < ps.untilId!);
 			}
 			noteIds = noteIds.slice(0, ps.limit);
 
+			if (noteIds.length === 0) {
+				return [];
+			}
+
 			const query = this.notesRepository.createQueryBuilder('note')
 				.where('note.id IN (:...noteIds)', { noteIds: noteIds })
 				.innerJoinAndSelect('note.user', 'user')

From 5bdae9f6d03bbb1fc1cf341b2e6b3e5d15d03fad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 26 Nov 2023 13:04:44 +0900
Subject: [PATCH 048/435] =?UTF-8?q?enhance(frontend):=20=E3=83=AA=E3=82=A2?=
 =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E9=81=B8=E6=8A=9E=E6=99=82?=
 =?UTF-8?q?=E3=81=AB=E9=9F=B3=E3=82=92=E6=B5=81=E3=81=9B=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#12441)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (add) リアクション選択時に音を鳴らせるように

* Update Changelog

* tweak sound

* tweak sound

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                    |   1 +
 locales/index.d.ts                              |   1 +
 locales/ja-JP.yml                               |   1 +
 .../frontend/assets/sounds/syuilo/bubble1.mp3   | Bin 0 -> 19328 bytes
 .../frontend/assets/sounds/syuilo/bubble2.mp3   | Bin 0 -> 19328 bytes
 packages/frontend/src/components/MkNote.vue     |   5 +++++
 .../frontend/src/components/MkNoteDetailed.vue  |   5 +++++
 .../components/MkReactionsViewer.reaction.vue   |   7 +++++++
 packages/frontend/src/pages/settings/sounds.vue |   3 ++-
 packages/frontend/src/scripts/sound.ts          |   4 +++-
 packages/frontend/src/store.ts                  |   4 ++++
 11 files changed, 29 insertions(+), 2 deletions(-)
 create mode 100644 packages/frontend/assets/sounds/syuilo/bubble1.mp3
 create mode 100644 packages/frontend/assets/sounds/syuilo/bubble2.mp3

diff --git a/CHANGELOG.md b/CHANGELOG.md
index fdf6709117..7f02d462b2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@
 ### Client
 - Enhance: 絵文字のオートコンプリート機能強化 #12364
 - Enhance: ユーザーのRawデータを表示するページが復活
+- Enhance: リアクション選択時に音を鳴らせるように
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 042c7750e1..6e9fe311f1 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1943,6 +1943,7 @@ export interface Locale {
         "notification": string;
         "antenna": string;
         "channel": string;
+        "reaction": string;
     };
     "_ago": {
         "future": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 58e0dd0b19..0b051b6190 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1848,6 +1848,7 @@ _sfx:
   notification: "通知"
   antenna: "アンテナ受信"
   channel: "チャンネル通知"
+  reaction: "リアクション選択時"
 
 _ago:
   future: "未来"
diff --git a/packages/frontend/assets/sounds/syuilo/bubble1.mp3 b/packages/frontend/assets/sounds/syuilo/bubble1.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..05b8ef8b10056c66a6e1a9b401d86530ecdc3296
GIT binary patch
literal 19328
zcmeIZ1yEa2qplqS1PgA#JvdDW?xZ-uU7O(A;#Ldp?poYwu~MKGq&O5W))p^NXep(%
zrG;Gh&YW-N&Y3%N=l{?B@A>C^@668Ld3R>b+ADeXTI<~_x*7-~z_mimk;VpBdlCSE
z5H2Mp0Rxy@TU%cl;44G-`rQBIQeL@1uD*VE{%4!w%69Ve{dd#MO!KM)>MIk432~E_
zmync|fWu%{-oFd~XZzm||Cf8++irKRPF@uQpa%f-0RWb3L9d0lmfAmrx|Yqg+^!Y)
z4`HvBa;?H^)&4^r*TP+E;#$l9(C)RqUkiV&>&jf!+4-u@2-)j5|1N%F?teSf2fSX*
z8u)knU;BTxz<<XUxZ+pi3hz`J{ZdaXuu@}h>oFSOSgOs{a|9p)b*vAlwgL!<p=)!c
ztP&V%Hj-gF9-NO?I%J*l0Lsk6%fx%=klYpm2T&2=ex(_y-a2)B8S!)ABfFB45^O3y
z9t(K*3qbhds$~g2;sAi}cr0cMKQJ)x?$^e~C;Tuq7!W!P03eR9wku?*|FNw@!T<oh
zxE_P3{9RL1wkR6^k&OY4p&=AYxt|y#3|H``e}E2@!cPVBlk3B6kr1Xpz%VrALx(MD
zhXDM-i|XCpMqJ3$@@n4B^W(p-_xHE_yVq3N^5>QYsHe)xtZt-e@iFAVE<WjADUSW`
z@c_A=o*K!6t2%!A3xFN}T<7HERQ;=@jQ<@U2T&Zq0f?^70adRrLKO=DgkJ(coL3dj
zOw_v!HQ4=m|JN>WYmf_XhMPnaEVxTySIp%+3<--<@7F`$rq+U>xubSPFb~kSKsP=f
zEV>MILn;UiN8{L~PN>ARKXgOR)7hDExW>)&&+Vkm<R0sgLClD+X#i~dX|^EEq^hj`
zNKPclK`q3~Y;8ws$apl5ONj~OOrEc3|MqpM%x5#R2$KT7TB3?faQ=pI9J^rX>yN4y
zZNqkNXmhQ}N&92G#j$j>;D-edFQ2DhDqQSan=XTmEpVn;U+Perb`KrC;_;Ub7p|`t
z&oWx)S<}VHb{$35dLM=4v{l^tq@83$noe3_+E+oUGbr7dfa^1`AEL-8*uUTt8+jw-
z@z;cYeoTQ`qtBbXyx)#k2ge2w8*CzB$B_XN`a?l`xKW@K5<o|WBJcwgvAo4|Jt?mj
zQ<yEBIsFPbAxj1#tNmqyg<rTeX~Dc`0$IzSOY7@7dBnjY+=;6-xz&~%sT%^`Zdvis
zvB1^-sG||&^b(=pdq4bn>H5)W9eb>!1`!S{7wY`@^70#g9FJf4u6N^`=Ob(Gmt(6}
z-N2LUzQD6-)Xse26fK-I?q>V;^R;!Y3yiC&#n^^sak8HGjI3^{&g*~J|Mv3d8?^JQ
z7pu2vQ5pk6V_&7sn(RlsOA;(tT@I!lD+-f~?Ndq2=$Fwq41miZf5B<gU%{mMdSU+k
zG%!N~XDEppj*|d~t0NS`tpZzdEd<>-2f&CmU<;r_@I9`o%sKWZiua>7<Cs#lH<jCT
z(~Q*_U#fnWyXWn*r(^G$zxG&Cy#Nd4P_-~Vo4c;j#Us3Wj3ZNcWQ2-*s&T;Y7wW6<
z=<2&X*Zk@ve&1oikv!xEWDQz#e_aRAloyj3KE&#|(?9CFXoICJ@~B6<k-?+rsC%#@
z6ycF*if@qsP+R07F(%TFa05F5q{V6gkN}GAW>Rh`A)+^ahkSj$^pzps;|90yRLkF_
zUVOOuLm=Ndbcb$izF=(Ur`YYXH+LhdckV_!Yb?0Egay!2M*#!2(ntn)fm><p(AXCj
zLT0b=c=tcbXvQwS)Hf;LvSBxf4y5G68l?h~gkYvoED8>!OR+P31#(E2X;?dV5bw*F
zNaBtg0fRbmmbDXYnSDlCm=2KrB!u;W5H^F7k3QGCR^b=A@xqQJxulK7PTuKrX6q{-
z^_u6#d~KkS(vcD`b;=1N>nFB9lu^F#dd7o^Gqv?}m)<M<Jg)_+-2_Qmr-5ip8HcC1
z2HoMUkD)BsTW(M_dnie(1(eB82ue&UJWPo6o~Xls{IlJb4*XJ}-LKrW+zh3h_~Jwu
zoVb&FdT?|@(76ooAH}02a;KS3K?TySIxKYQM#Ht{s?Lkn;a`Vr0zU*71dqR@m)BN4
zK=5RZn@wd&iWpC?7gLfa(mgNK_5Eb~=)%A2^Uft6|EJRerEZ{!xE@tm{5KY?GA0lX
z7%YH)B^s4OeW+BClzfMHfjWWrM?Vyzg5ctbGH_8u8RAk~J_@?24K$Ue8>~`LcJJON
zHa$LXkhSff@0-m+%e)Pa%-k)!#0BOkdz-zPz8%MH%)q#<^$k+1d{*B{-21}hl5sFu
zCjXY#5S3FuEA@x%UFj5K%dr1w*h<zLsXZIqiZ^F9Y?>by0byZ|c=xI$vi|hWg(KjW
zHa;%Jiv~ZT0b~b9I60^N0<{<&yQYE3GoPRsBAHU;ac)#M+a{;rQ=%CYt0JjNW?);9
zi4HatgAn*004zX{QX)sm5@{nb6&g4gSdNvIm>~$p=622Ry@VgUQZJ*=_52!LL)RZR
ztI*CDGe7a&sodav6nP2zby$kv*)d}DNq;FM(U)o95fl-jXYXQTZ7bI8<J@S*(L4JS
zT{V5O8{QJp8NQ-4Wl1r((1#AMFs9`zH;;+u!M5gPoOZ4(wIqG?xEQ(o@b!)$9*@fV
z{K|uFLIyYg25<|kHq$(-i8J5{)>$ks0)OErG}fH;CyO&|WR_E?aL&d^x~b<NxM(CJ
z0qW>cQX+H#oPK&8#zbgbU83I3-Z%C@h(zbDeRggpLQa2<8>cW&SU#mV2Xw#Ly<rID
z6O-lWXj)^-HTHaOYj^uc>N<B=tGyg8OvFL2=e5J(k5K$Mwj!>;M~#Gqx89q};+Xl-
zYGGLss!F0DkHEP=V;#uGvFQD@V!{9fvMsES^Xx3gp@_QSVR8xfC;`3la)Sag@H9|}
zJi4H=K&nR0R+o$3OaB{=DCSsBnIPK_^`ibEyRIz>t4NOC{w7~Veu<K*s~%mLzCV9(
zJgXVO#~0g7y(1hyPJeB5HRn0s@w)7rzc^e@Z|^KQ^yMRoA@r4j3Vr)_D2KKXv6u{J
zw7R>tY5RD3EdTz89auu-7Azdw2-86_E8lB@ARI+1q=_E>k##q`NhclFw?d2^5Toes
zb0ztUGXu`!Jc-wFfq)#`3jh(LeT+>d*tMLLIN1WGnc~3*rp4&r9sp^e=n0s3n2dJz
z-mT0oxlhkZNiBJf^zO^0Vut)!p9y3YCN37*oW+aj@jrE4$Ehi)6A-69!X5xAgw#@9
z4ACa6+@5cmGT=U@oo3b1J-NQ1U=dQU4X{(z;OnsvvD}Ab>hFb-IQm2|F<41#L*p%(
z;CJFlrXF7F{mzB3j^LM_orzKRD0L&LSm4n<&Z7(bzcOcf*mzEm6)gFZ@ZT|FToo}g
zG^5xLz`j0h)>}fxF$*clajDfj1VEhg009Fd5z2%+24{KZ^^F8jv%W{<dE!9#Q}glD
zMZF%*#<znDts;(eT0o>L1vq24E?p-@?!cKp)|=Nq-U;=Y#5{n?c5|sbx*)3Q{s-rh
zsD+4#oC9fZThDApae3v!8B$kbLIW&>t=ySQZAg<w@+r?8_(RC+TYC7{HdsRd4>pF*
zGt$KmGh;^NKl95dGt)@{^WJ|M6BndqQUh~si(!DMr=A%X#(e?6qXFce*me?{X$6uS
z1TC%h#z3>zL6-7W#-tEW%fj!wL77;4q!5jc&K}V(x#N0`Z5BGEaAKz<a0e5wl3=!A
z)HT2O^s}cr%F*Zge$PnWO+WN8<Ti;orQT1SknHAEzkvS+uG=`#u@fO8GYQPuo=ahT
zCRc7}o<BxBO-Sb>;^w?TLlQ{?q8mnFg&}#(QBPw8;7W!t@p@j4ykng<PF_1N$o+ez
zh#ZYvHtiCRntNXCsehltT{@0n-;>RT2CcLy{xIMFnz1=Ha2Nzfg!_N&yj&E&V_j>?
zwj=WKZE|uXRvRGraaMkSw2U`BB3p1k8N!5ifr#N&3zD2K*xP#>IV_gh3Y9Vx9|e15
zu!-~=72R=-D(Kw$S_mZm4LQK-6)>PN5Pm>of#A9&09y~^z|mOJo0lWh=kI|W`Cv}*
ztU}S;xY^jq0t+6Z6kJRac_gm}hJ>$^3XAh#sTQb47+FBIP_o|Q3dtKH?jBt=jQh0e
zb8(MbQ*7sLe%D3Wx%qpQHnhtBUD+D?sRC7iqkq^6Xv92Fp*d0q^!gI`o6y_H+8m1B
z^n3k7C+<$HY%-&(&6{t|Qf@Qq7NM@+$3B1Ju%dfk9NaS*_gUui(xvs}=G5z1*7vRt
zj)&(P&w~-Jg|946-|gEz%Pk5zJPU^2G2FBISatHA$`wWe5QfP`dcnY2)bP;~U4=7p
zEqG3>FQSMnLD4bLQFiFhCnc9sA(?H6isbLeF8Dijf7m!c5B8)#5vGmfg9YK{VAKF*
zxE3OjPl11coJyvhUP%SWQ?z7Ll3eg@+G&L0UVGv_9R)0c985(BCx|(Nm9N8-90XzI
zq~_ADI;NRYLSRyY$FNY^jCgV_2>q|;*2s~<FzbjSo@zZ(ySC8A#IuK<%Y<?d)zs0V
z;!JUGW=O**(+OiWi1rBwLgG=}#f=)B9x#b!fl9|a7Q%_3JmE$RWk<rVF1M%OKQWcU
zf!|eoU&)P_jE;3>cV#|ry0yw>3H{)!y4?F@FZYk=hr-39mhDGx7n$+57aE=?fBndK
z*?T%5UHt+7@Zjf8=+3LPcUvFO%Gh6M!>C0k7J?y{M#7aabGjjnMdqHTiri3eK*-o_
zf>Bg>{cx8@d~%p;B}EKcl=K4z96gU-Cs0Bc5G0_LflxFu5{s6@=AaP-F=!bAn4j8^
z52^d7ib))wv42Gl8Bhbo9)=@KsYmybWO5G=nRT+WmPg`<fgYjaJg!7Q;ywwa8(fW>
zfoa5&qYOn&@X-l8!;!V|@}6a&kn7P4%^Xz}L6qdMNv~X{y!X*fhY{O=eY5hDH+-hU
z>6y$Av?aJmNFdBYP?g*A%}6<|fA9+?GYWkb{m1#6#8llCziF-u?w~v0#gfN_o%WBE
zI~Du=P$@fqkl*f2o@C`h#>q<!A4HXZyLaQZJWI)Y)khx<xr4SG&fe8IXr4W>t5^sO
zJbQS=WLil5I%Mh2$<(DI<Z1VryXcpYTX%l`xsdFTb?*B1(T%7dk<M)(%L)Pjh_wjU
zDdPxRsXz>^=1^za(_PUpqd3NTe~?*`7l?wTYM1%^Y^XJC47%xW=(Zr@@1|<4FvP89
z5#P^OID3H#dM2DqqUA<Z2-9*YR}(sP#tN@Q_%U&aJoYK~Q20!uAmxGH72&b&iv?0L
zYQP25tl&vPV&Ie^5dsbg7|VRIEs-8vQZ1|0$1IIPZ(zF8IuG?eNOJqPej9E@F)V)!
zT5g_V%K!djBn{1v5KZ50&k=P<2**CMPnOaCn*@ovH}@aE`z6mjqd&NPRlGwD|CF1k
z=KsuL{XVAQz{BfQz2!jY+@WtMUcj6-+=WqJWMRqcRCeNfn%!@FO~;Lz+5yXF;tf{e
z&Vf(m<9>g*^^MFW6_RSA%)in~Hzk|3P_?m`@8ss!1p^Qp>aubt08+Rjg>VzViJQoT
ziJY~G`(!b1mpZ{R*zK7HG1TV5`tB}iYB+nz#*WcMkoxpMD{bl^qS<e#S<zFSkSTkQ
z+!oyrB*d!aTXdvFTLX<f$dL?vw(FR!{$UVrFW1XNIWgk8IEVuQj?M-)3wZ{suBW6q
zQzJZ#(Lt;l`s?T=wPp%pK%`TGPDUaHp$Xq6N#7`2L_sEM&6jn=2WRMZdp#tEpUY48
zB3RO-ax7~3#3|i12kyv~bzm%a3+@au&$@0EFOt%~NjaHFGjjC&`J#;DNkQ@8JxlPV
z)Q5MA+{sj?G&;dd*-r^h2?56xTSTX9RiHtN4hY&ITI529FPv!VJ6#U610Ys$Q#Dn*
zO1oVj@udES^IdUagu_G%LS7e?Eg|IEK{)~s1ZA+95w|jS5TFR@3A;ga1Yty(pKG<$
z^LbsVN_6XrJ?9F@JBdl{-O5sj7zh%HYWACXh@7+_ew61cuubeuY(=u<rice+)MC~f
z8U_Hl?R>0w)5vFNqOWCB=j^x>)<Qh@Mg}+CN~6;<v?H1!C_k{Xr!?v<Q{ryAstJ<|
z)~)!id|g;XQXh9Upnjrn1YpF-ugn1)wu4*hwu*^0kx){bnQD60R?CCx%>Be>b=oEt
z`}_OHJM;z^M7&z`TD-jG8?#I-?r*3YRFYjfZPv!#bMGG!JsF}gxaKzlOnPxkCHq_>
zkQhvwdY3GnM#txn`gjt^0sf$ViAlH$Z@k{)Gp7Nvltj~{WGRLqz#AKYK~S2<pgI%f
zEjy3t@L@w`>giQn82G@1bgGkaVwmE^5L#{m7KQ2)qM4w<PT)|4C@Me-PKdxXY!P@;
z#Ol@1UmGO4+ZjDqKj9pCHlosol~Nqx5L(e@(Xru6jgB^1)LInTk+I^YtGe0O2Vj?L
zY<?CQE6v|43T_lA6dNncZfpoMC(Z?`#hU%~D>~WLbUn7=JyP{Am{8Zt05@P1(^W^B
zku0s`L_<YB#DoZelyI7cCp^Xtn^|U&=+>ItGNk?w3!As>s2J1Pwi8W>*lGjb6&pIC
zA4T2M^@SAhaSL<!Y;`?mi;ls-23roOh!O&KuS49NRtkoQ31C{1<pbKMN1)xedqqQ_
z->|shHjhu@cb^RD(@D82I<G;eXWDjEjiWyWJmyTT_kPaP%Ip>(Q*cbT*J{q7?%Mo%
z{Kc*M1^zWR-a%_&-n@fD9S8$3)$&XyL8?s%cbGl+`0YddRR)0n^zc0%|8Nb>09Zl;
zqS~bdbyHty^<@o$)RRsg@Wk&Y>5h*zvka+((kAc1!)ty_Ezh#p!^_%__UpeOg`@6K
ztAFSG<QD>|SaQqFtOaTjGm=^dV}SxVL>@!&&L^|6fJ^Z`FB_`f4&zB>kZCV!Z@)+X
zIroT7bu{K?1EHX6^Bw#T{2?A+ca{B|iu`lJ-S{v4r?r5jJYgt5OqakF)}l9npo%7i
zz3zW6#l>+`nkZsTS%>#nx@%xo@^MrPtT$>$?$?Hjskan%8&-eKFFW<gPlgwS5BHGw
zk?%6@o(oZvx|~oyjEf=wml3=^M&F0si-Og&!B9X;n4ij4V>G~4)L|uldCiGEd+g-s
zrNeuD+y+S_+K*(2bZZII%dkt7a2%SU>!y3ejp--?-u|RK<*9y_5^<xW-TQe{nh$5%
zyvJ<q3m0IVrIQuW3ObZX1WgZUN0VX`0Am;}R>mPxzktjw9>6@nI*CufaqGt4G(M5+
zH-Z%h7u7>$dog|L=$Kbu@qh4Fzbbe9`D^?c0T4B~&6ILOvw7Uc>TNH0Y20rUfavv$
z>2p&Y=c$`l(Hb7+TZ^-rk&O#6g=;yjKL)YXN7Sl4Vi|Js&XjLY(o&#e(s>G{r;J1j
zf4N}3kX@^5I1Uu}Qa%62x+Kl?-CGGG{HG>d)PNw<xx@LxE3B?k+qd6#bpgSr&^P3P
z6IxQgszLJ8N?P>4NZt4%`D@*cC2Ch0Wk3CqN`W@q@dY^{o<Ktjy5q!Q`XI5pTrrM!
zLe+8eLoRFuLWX>#5FrsDLtRBw-@cE7v~}YoWyoZgTiUsfUFTzmC9nLzcV8{qQ2Z1G
z_CZ!1SGkoc9)ENluNwT5rvJw2)&J2;N92HUlQ$hh-(IG`KWK7GvPH4N&LXSetD`8`
z)1h)nQF098Vc=gSyI*@sc>|ep+Bq$-x5EJ{s_e5Dgr2<yrZ;4GH3s_l8-RpE08A7;
zA(5_nG**Kk?+1^ysC-zdJ2M+y4_MijEYibc-|x$};rtu6V$Woo@E@;#V)%<)z#alb
z*Uc734$%V%^)kfWg8M_iZ-1-ZT&9QmUGr;1eM-o#82xA+*W1ig{?I9j*286<`lk=U
z9EEM<*S&(J19#DCIX53?1^0)c{Ga?Xj<k!vVzq&+w!5osDsC3m=UOyq`CWjJ!7d@h
z2ugzG00Dl1NDTM9#fFM9yuBhRcEi%p6>calXu61(p9esV;iTXxV_f#DC*##W6(y~i
zEWC=I`fSPi?Bv-j)_zzP1&g#)DzsS-{lTxEUOKm2T?PKUiRaO!`wjT(J!kV~2-&4h
ziuA!qX-64M5_<<`7Fhw~8bnDxAs>;vNnt0`(S2X$-efpp*qB{9*u7by6ke=gOZ`)Z
z|CWaoQqL3dobZ+;frg*zfTBzn1m;PmWJb6YDZ|JoAVol3aNOO>E36`326i=wcq#=P
zM+<|!cdWm6&whC;>a!;%vVJ&sNA$?u)hOv+{>3@|5@GbNI)AZ&%L~k;#?Ow}Wv!le
z33%#mH<{umN;1^@Q7;Z7`Fq#j=0;rBzWlELXq=WDsXa*2cmc{)udPLXOguCj{<Yj`
zxR4v9F(OB&z+cUj=nrlEjw{gI46}c3l%}+B80<{SDe))w;^XOMuNYqSyyfEZB{_cf
zEq?!B{Qe)M_dgU4Gf)$NIYoNIf=9w6C)XZG1tz0mK{nco9`aKvfp;ho<`X4|%ePMu
zOu81ZKn*j6C(ZAM`OLuC32(PD*ZZQiYN-K<)GRm#s<=JlF4l7$jra0zW=d2&mx}YO
zbLTc<+dK_uz}wc~alKH<iLYzB;8!Gnmw(!AK4{83yY<%Wvi-J|;$qp(s_#-f^-uUU
zzuM_P90c2=|FUjk2|=x)e<4)W_Dhs_3{squHS%&N!GCV?{<l)EN7KwtqnmqI&3sE%
zR4?bg{SI|J4Qf^EbU|vNXJ<WL%eci7!7$H>$}o=}ZTq;sJHCHo>G%Odqj9K-(YkjN
z_=Rgl&ZX1$xK2ztkT?8PeJC%EosU3!S4bA3=Ectzt)G04mH2k5vOnUv{wwoe=Akn9
z_tiaOf0i&!)c#Tp)hs-1AAd{XpX2d&RRrDts6KwZ8;{4NGnDOYU}wp6qt1D*o+SZ=
z;{dekX}eBsZARiV$3-KFQv+VYeM6a^u|k#=DMZA-;~$eH#xRh7Gxfl?rpAJGU%ZV1
zD>c(`rg)_~isxTEP+;8Ht8SMU%b%Ou5*^R|@x0GDp6kx_t#I@8A>Ii8W8`5uP6+_+
z<072SAe#gGv2W2y*${Hl(9)A*Wd{U0L4*tiir*dJb>FAQ1`+SgrUT_hXRB1%ukuT7
zHbTx#GvkB-fa!NdjmY(C9Yt8390jqL6O<Arrh^mms_jf;FqrV1yYM!(lcGBpI!dyC
zH7Ow-i7PH+Aa{9x$LPz^GQKhW`Nc#0T-RmVqtDm;!sxLtU=-L2m>eJlb|dPAB#G8C
ztRm7HW&qHFdk%k;3S?!Lj<d3rdvZ5L8MNG|*i;Wg@R6-b)(pK<GO&>%ZiAySk{$yH
z7ZMsm^(^4=3H(>Vyc=*%kI2|8mrv!lQv1)g-6o2%4oA+H`bM@tB&;tVlOh}KG7dsN
zpT9oBue`=L;j8eMJJV=7f?jkBs2_byj&^^W7vXvX2zGUiu)XsESL<|;I_7CRCl+V}
zH@#i)85>d6Z0${oc<dC-ZtVh3lW<0nGB|n1x?z%uh0yPajL|JowrE|zdc@S-2SnwZ
zwDmUJqmcBSne--#?v}~ko$T&*-J1%bpL{v2UHWpCgk}jC=-(8x3!Zr7(#q6V>{l56
zs+I^aq3PVDb0HQ5fzq;z=o41xMfio1+f$B<bWPz(qpMn4Olrc6LuRC=1=>yxIn=sj
zaQ-=U0e!6{V{<vAmc1p0bW0Q+G4fi5iu2Q^2e5iFj(aXYlK_xo9LOe8+p6p%W15im
z?2|JV#y;BbuNK~A{*LbPi_ExkA@em(HL-RA2Lxqe$OJ`e8dw|_o&ue*PKgbqLQQF_
z8@z*(fCm8(RN3)EN-lnHDx#a})OGk(%JH9H4I^-!X$PgNR3T=6QlIou;ronMv?qph
z!J{sd=CY`5!9gCAL|%fJYkn`%Pp#OVMqdojLhfj`J`|4Q__2+nKAnuAS9(WqS%X2F
zwn0F25FN$>V{l#4Ooo!E1v)vEO4V@Q0E6Do9oz0Un=)X2-(eU4W8fCbF?06GNN69S
z7YX@dT?f0ptMI(5WC$s=A*MQ1*l<o*r)u=;Pk>3OqT(3j!>;4buFsrQ`Quch*y=CI
z2!oxSTt)$V`ryXI<}dK&E+_g#ohmlInFgUI$)L^#JJk<eCmm#-U!@#iv#e+Z>?XPy
z$bhZ`^*VF#SUOWj2Vu7Oivpr_`|tT?W_Xo?6ntiGsJXb3vSNZFheGK&-HTeCoFtq)
zl`ya_a<P%90b&BQ4pZ@)7Z3vaSnkzmF8ABUL_`DYKAUmKsOUQIoC&wws!=kjk#2{>
zh$+|AOnF&^XwfIjRmW_BG^MPMuHST6zgWvW3h2S&8Usm*jj2u(uaBgU6?v*6TF5#b
zN#i4(^oSu>!1$<shenVk>wuqoepL67hA4S`u~&)VfJ^;IqCpiT9I@iKmPr84(<4dB
zk~3zQH_g&#s+s0>MVd9|bL;ww6>1ep^q;2~yB;)mWMr0G_f>^VH&lN}>AhN2Qv4AA
z=eCW>w0w+S-3c$p6LPm*+J2L%g579>WPfEy4p=><`*8srD<35U1YiY-GhHF2F~1k{
z{1wUebL<M4#owlx-jSek-N9sFO0<%g$&9njyZEQ9TsCN$+*C5+nWO`@`>==G!WCKV
zj`0$y0`#^DwZO~({VYCt=&dRK#<A~FN7P8NRZm}&4CG0Tvyx8=s*C+#fu6Sd76s<c
zEqRKQf^Q!0Dd|1^;v1P9>3}c&N+mo8Rg@Q|Q_Y}eewWJmFy_(hS?fyOe1X~7piS<Y
zU%B~PVyDI|Vs)nh&XTDC*47LoPRXpMOj|WMri8~^suB{-pU9J?DSp-bqF0{VqXF!q
zgRxi786xjtu#|L8s^n`JLy{a!ZZeZsud-Jd^zMoW=o0GVT9M){3&CMzvkjfUBqgCm
zfEpp^`>Atr{ZgzYj+k${aez2x+NdaXHSSh`2MzW#-M>g<jz8q1*Zh_mZnA<gs~A(y
zJm+aL?pw7lPG-s8MXuWbYoiPDLP*@F=Q0ypBkvWOhBf+|iFed}qbsWtS55a6s6Jy!
z)(x9|v(ei5;gnq3_hoZ{jCqAec~YrVO+(NPemxx~fzwX7LDbUz8%=2sVYl*1{v}!d
zqfWY@rLy9rpu>5`W#o&s1xZb!^{L8|mN}v4V(F+JA)=vce(?#v<j|eoW#KxfPo&o$
zCVDvL?)Fk2iGvlCtq3k^mPqO;Yl#7@yFi=7IA`m7wcz?0P*Cv`?V6H8lC9wS;UI5&
zsQTWhmfY1)kjI0zmKGoq2jm8jSV!GPgGz|Jb;gZp)Tk)cPxo9?AIP)b&Q9Z}Fe27;
zn=E%F<CPj`@odS@be9^ZtdQF+#@=txX$TtCB`z?r3ouTSVe6bLmY5SLHs~r}NhqKW
z(0s#R?IH46B-Qk=HO)gh5n?bxqsy|(sIbt9l#Cp)k%<L=Xg`pS8((8K-c&enF`7O4
zgCXzn6Ok%onfI)1V$IBJ_}g#%Ow!m=O)X_Jb@eZqZ5pzT;ry*2O>H9Cua|^Dy-_HF
zb&jD(PZ*V&8|+D66)cgIN0K1Y3KkA@frV?tBkqu8Bgk&y6#|9YRJxQ#<+VWLlB>E-
zu&;g5utaPuOhauR1_IP67Hw(LGs(l$B+G5Au`%E`k)!JLH5g0LlkJZD#A2B@V_D#A
z@|+}IGAYvpoO@QnkR(It+X+;O4$>*YK(`f!T-CIanjJ~b)qa_m!DgLr6^(qV8&A%o
zFxbu-&m86TI3d9}4m7y0Y=g-!U@V1TYig~S<?J<N+Tr@<*tNIS_GP9Pw0sPI-DsQ;
zZV#r*F(ZT9RgoGx%pNnG3aos_Y`hGlLIdz*vA_x2Cq8Cm+0^hhC2k@%wS;5y=PoYE
zYN<dv*N}e>(f>nQ1CYR<y;ByG*8@u}eK!JgLs*f4@owzSei)pCRw{q3i;_c}@c_;V
zO^G#@B?SaKnU4HOw53qj{NgQi@#6mT4?o$zHVjG~4c$Eu?f4Yu93uYP0s6YgeG<=;
zo8GQx+Lo(-6wf=dxGSBH_zHPlZ&Y&eHP~HyYH2xLn(;}n#I4y+Ev<)^cic+OziKWt
zasvR0A1@cb3a3bC^U?wtiibTXS__-xCpBU@432X~#*!*L3I^YgJb*)<M18*jOeb`7
zW{WW~AY378NMuz*irxqDvq!3V=D?b7Xo>?f;>bt`A-9;KB8Q*`1l%j%`;Ak6YZ}+x
zYF}Y!0W$5{yziK};Xvdo++sBIgw7_B(xv|W8!6+6h!(n^CbG#nA{Ng4VZX<}F3`v8
zfAA={SYa6rshh5m@6x0{Y_uoTzgSs=5ff9OsHo^Pis`d%^Rz|lyDi;GM5i)!!H)C-
z?QaGmObt6sqHExlfGRQ?4Hy7OgA)J%!XU&p!dZM-9h;Tc{PvL&g*nPAK7fxkAA;Wd
zOr*2+Vc!{sCu}2L=EWYtjYnrI+Z5!ebF*WrI+#ksYZu~s+e8H42koGb?V_Zh^uuiK
zW10PN=O^<DDb*W&vsIM`yCzHy>h|_>$~t_S0AuB$L|q3H<7ag`q9aqITW4J4G;cVp
znx)L|C1U#d78<f1aSJ)gjYC;&pIP`vjK5rnakMRx(q>S#o=J-RA-Y^RIjLyvf$FH<
z-4x8}%p-zOV0j|Z^aYx+@L10pNLxHlv`Jhs*64Q?*8?t)USAJntq=hAkEh9fBpa6)
zUPB|~6Jy%7WYf%9KeyGyqlgryPM7P>X&s`^-W(LPf6=R8AHf4dw^<(~M$P=rnu7B}
zhJsX7>=g_nbi}D1b0q45leU9*wY9%d?N|)qI%1N02t2Sa#AwL8(wXOxe$yJFoz3++
zVI#$As8(l!$SG=Kw**Q^X0Euz6QsQXLREs%<(!S#tVt&`I?Ho`aqz)iBKI6IWRKTL
z@kkbR?I%wr>Y=UD!|<^AC+}j2RF#mNbO^U%>6BIjen(Yx)B0iy<lL-9j`dRCf)L5$
zN6ji?)D7T7qRQT95&vtr{sV3QH;t=df}-!s#rK*Hqwwh}UU|7E^p;>@pU>eca=;GV
zp~4K!J{O}Dq-AQV1$1uZeOH5B?~7*Ns|7708F&0*w+Byum$RGJL2_(bykxfn%}d_#
zm-gl5hNeDJv(YGW802#=FU}5;Nl988Yd9tCQFzSn`*dW`1EM_Pl+>u~pn{Qxc%wFX
z!?<tjOGUE(DDvJfjyU%HYU)`EEhzCARY=AaW?VgH`LU|A_7qb;ioL0YAj<|uZJDu>
zO?ZVWew^MLHhb`Fl1@=0q?}UHP_Bi(rnJsh7IrQylt{-PrRv3QK(iTBo5@=mOMWx&
zO`ck?Zir5AggK)j`0kvb)HL_p2~wz2eoptX93?!0xW0{YZi4)VRHAtG8_c3cmHp;4
zX~m$Qn!I(vf$4;4j-ej6eLUE3goUMk1QtwQ4n@r+NX0P5s9p{N01j8lR>*9<ys#QE
z>>u;rW&kk*TIO?vu1+l&^wJ;d%_@vtc&wbc<<-g>Tn1Y!H-6wv-jE@t%_IdGX~wLR
zvx#cxHNDrDc%U*a^1YtYOm(%Zn@TU3FopmA`+hH<`P4%%mdt5xISGrglyGZiq1vG%
z>mAKfl1AT2%gyqrhDJd!jctDR_*18vrN34cDcK^0Dmxh^+(JWRiFge<b?)`Y8nJm7
z1S~y)bRv$l+4dp*#-|0XDWSKBopopOqWVtTZ4^aFs-I?NlO3_GFRD-{Yy5;){QVUu
zT1FaioOLf(J#b3jn>pQYUs?9@Exr{CAh^nGuO8`snQ8z4xP!*W$P#j@oy{YQSK{qD
z*$s?cjoXvjZJhs-IAsYFts}#ll7l_J`TLMCXjza`@QO2V(vY~sb#klSJ#eC*fWtN$
z)Q<0As((z`r?nPiP75-EWw^#jkO6IbNlvN^iSbR?(Hxo<;d~b(vSg^MMdOrC&G)A#
z=<YBl;K(m)y5V2(Cz)Bi8QV9Y)J=Ig8v?W+?=9r;Y7p;K^F|QW2^VH{&}2C4anQu%
z63qkaZ{KSU=A-V{jpVit^KmoOaNG3rkAzv_zwYndXZ$C>{~>Al-!RIr2H)>57vD!r
znyB4e@&gW((G*+#9zQGYE=+w?xYQegF!nX~3)w`3ql9O?C6*?sW+|5M{4h$hec+8b
zNb;;B>P=|0v?y$l7+01yH4zq1)l^*Z4Kbbk6JW0rXRO(H@5Dgb<@FcT<SB6|<Tki=
zEw3-{k5^ug5C25v%NDXXR<M2uqQ?DqQ2Bz?Skn8(-Z^!(RsbMv*|%+&ceJfVhDrU?
z=WRbS;Et+u{F=&k=-H9L#i{-r>j&N4918>oqro0ky+Y}IK|V(ZE!R~(3m%><3xdR~
zxYTSKK|z~%QzsAiH<ecGUHqq{N94j{D?WKh*NN&)H=0BiPoh!%>}RiPp|;y+ako`;
zcptK;laN%liMY+#f>D`3=$y+-<yY$LrXUf`N4ort41%m54wUK_$nB77S8Epj;DfUU
z66`OS{03V+;IuFyCXpmJ2i96%0e1;E_n$^k?$2><J++{007i-q*iEUd^KvYU(<^De
zp|h*#q61X6XbF<#FMP-z4yGD&qFkURehC)P;gjZu8TkiV3a0kmk)#M<_d{^GzL7N4
z_vF)+<T}WbtP@XjouB2bX8jc)l816-x$b{A07Ol<WIrqFHdcN<D^(?9<}0ioef%Q)
zP!MKwKlMc_uUrYZYqQdn-M+8dgZ?K!tFEOVDeZ}8b|vmsUuO_AVQXkc4VjassKFpO
ztq5FsxG2si)+4C3u{#z@&$DDtj_9M7n<D2NSwz}T=g2S1>qO*Z1m)gM^&iIbY<?*<
z3^;h8lOiE-E^mpZ<s%4)?=BuJXyrGl)xASMD_Q5~*7N+t)Toa!_Se`}Vs~O~#;qYV
z004DQ$M?Rk&7o=l9>(G@0>k7=x5;8Da3G2XwH62FsMDBdGHt?X(sPo4T~h(WN-cl3
z*c8oGRON9BER9%{*MWkrVIasZ$=o=WKl_MpIyPr()-j1N<0XOzv_+?@7R%eN{|QRe
z+y+6?#mLCMhH=V6b|N{`A$6jWS#sQ+rQ`u4awJX~?hRB~Zd=4aM+@T4Jqg|#6=@O^
z0s-N@=!{qF;>|(=U?<KPx=JczCNJa%Rm$}N#rH2YtCpjiyYo(hns4Cl2?)`i$1!Pk
zr}Oa2gfbVmm!%+0c%N&o|E8IX46>$h-ZHRkgu2t+SF_588*S~FFcY=-L=59!*xX?B
zws`;T{}sRg8;Jiu{s{K!-r&%+IBwfw!QshGUVHa;5|3MHdq`mrrIVd+HCH&YN%n`(
zXz!otdE?~G)%p-auReJAjJ!m2?Rk-FWHdIDEZN=msSwnkF^8bjDZ-_%M6N@Ox^uf0
zEM1+G7pAMkl^e;h`N$tkDEpv$S>vP-6mF#DCh^GNL2m}1Xs$0zgVKC6e+nAlQ#K|R
z$CLyE5CpG$70()T;yi30K7o>wW9W<j#CbNn)B~xku0Zn{B#%}<{O~WcP&fWTTwLHI
z;q0||E@}DLGNg`8G`>EstUaaCHVcnd$yH$yFwUbcWys6yO6FW@{G`*j(S{l%q}`4T
z*mFiii%{0J5NoShu2?sZv#ZR+Anwsf79m=v7fnn=h4eT<?^vu8a=vF4+YobQA**K%
zowSKpGjn4|-{=F2l#r{)D59`2S$HYg<=69?E<2yp=h_jL&vmm*0ucimx#fLx)N77w
zd3t>2j1AChemh8sl5WX-CPR!or8q0~OuWOI`e3}d?dRu;Ru<_vW#64|;0I~NyWVEA
zvnM*+u2HyT_n5Zn7;V0mrxX-6*;|C!A5vQC)z;Pwco?R}p+|%A$)n_Xg-nL5${2Gb
z%YEEP!mKzuS&8bSldQnuINUe;!R-sbZT4mEqI;dO<l8O-v&;7njnErBzbP{#$3jqc
zd=fDYgj6z=TEtBS`9zZ60ShLpn+uscE>4;iiOUSB(&82!*6$+<luQz16@|A)3{g(U
z;wSp&6@Naxr@p-3lRRCe24L*6YxbWC=?E+{ipv%yg%4@b13LK#HvP=cs|0zW;TD08
zode9JtyYy!>kthlmF$o1(rop!yO$(;$|C&w&FFG@UtB1rF90DilC}CB0WU#gowKHT
zy+#t{MWUaS0{c|z5Au@)Y(~t)tJ3A^N4$hKB}9VHs>O3Yr!~mYP&_qFOhiWH=Pcv0
z6c%z7(gFq4aPh(^fn^Pv%YR+l<Yk3s_8tft$-sgN_Zl;y)nPI9^9d&*LkVs-Xbd%y
zq)QEBg;@C%k-6!#lQhj^lbU-XYuk2U(w|190yHmJvRa>f2<Wx?FU#-0fxiTxdX2wa
zT<gmvN_A{3h1lOuFh5uv1lE^GOng2Rm#JwOw8s#1@Ye&!pCMN~_NRMCOzQm!hZ!@o
z$w9OHU|9LIP>H!v%IFM5uGoA6I)+lhgut)Q3D#jolM<WB#V01QJ4<9B(`OysUqcK-
zuGL0mByFN%JTiPNWH|)+w9L(8A@;HC9|;W^$(Go8N+P*A*vL2}ee?7-s&i2rA{Fg*
zG)b}iVJNclWF+sA)rB+w@XY(wqIR}uqSp`BnbhIM*s-RiNOCb%D<xfb`1a|{b4&S#
zDxVXX6bN&;u}xGR@1|Dp3`3T3q~6(W5_^p*7adB5x;OQilBu)`8r$Lb=?>YS*_@(^
zYNXyseaLzS7BjS=5Vz4>mAqM5R}rB(BTqJOZn$F5`GfWgvq|ky0|H9x4b@1qSCbp5
zSIIJiWi%JiYV0n83Y|~+qp}Xo0R;NKUE1vS;yN5HSNyuP0*JHG())5c-Q^RYmuzIr
zY%NjawVu&@H(FVPN?Kip1T|VK`D-0!mIpn&3P!YAZ;q)JI><-xi3r$Qhu`$pbGlhK
zTGhY5^hM9S*+Ya~hEB2j_t)NgLAR9n*`Bjz)yvS2Z^mig{8VMRMMAn~B*mUw`;8=l
zALl(Vn%Oj2rG((`oar$h$&$|bC`jF$yo%6Jh#j8*lhwI?KaSC+vtp0&ZZ=k8)i^mm
z==S91N+il~@w4SnHV)`K?zKy4Hl!PCZs)17Oxf5?-?ZP9nHYm8GiMa5Nz;I8lB6c?
zK4pA)6NhBW@V(nla21j7i{StUm-r)}?I?QeUtW?d$<47sFAiDA_b65pc?GVl!Wo{T
zJRx%<)@+Xj0n{5xT)}Bb&hJ=#y=`C-=?{&8VmHAAee&w6=&>xzG-94lgOS3Rgodid
zVSw-E$x5VfKv8j>oE$$5m#o&blHXh<AVPyJ!%0+9fsJAOlh>Xn<U~5li8VRXU)rOf
zyEDu{o8l{Hsao*oMf>&_Kl+HyO*<ogAN04`N-^hjI;mS$?|r0DU(e|5mb}5j{8+9$
zZ;k%&K<!uIGTy85<=8*@{l7Q6El?x)<NI>fmfQh380~tqXh`P)>p-xaE4_ZACO`7a
za<M1>sus=*SjAhN-t!!BwOn2I)utaV#Zm*M3D-v>=@qnQcb9xO`PVrtFb=T3{#K7R
ztyGAqAX7MU`=n5l93y{2YeTo2+m82yqf=u7yG7%XlL?})sG;Ry5-Xe!TqLV*D<?}J
zj$F#rVG(S7D`Z~rlU|i`r0(<0ZCz-=Np*qAOGbv5uamn-0lM~SQghjJWJauC-JI`L
zvh;gBVCHW0km`Rv%TQ(Na{i){WNZ!u^?n{oD9u0WhGrF;965^Y9U*EqGUk>1RD7q^
zi<gdj-Pb2zW_pw(Guz0>$3}`Qx{N_U!cRmkGFyFKCyfg=q98&#`ib$UE|ZC8yp~Aw
z_)Uhm@gIYPYPW}|#WMATtd?8=gdwBesU!Gs6oT1=RF#T8mqvWZ>~q+((i4jbk$L69
zL;kIMjKA%nZd?hb`{c%2Xg7DH;chIU7sG8)){pejnjU4E?brOi0uY;^r9JNGSSNk%
zDrMPaaoBGku6q&wc@}0Pb*6kjlR}H>UCq&8>r-Wo*ADFpUY@cGa6NjL?k<6JO`A{~
z#koVFyTxo$j5R&1Y$!x&>!p)9BdVR&lm1*jLd7D1v2XTC5SlsA71f~4{$kq^ssHUx
z+~&>k9zhxEtiy?0B^33uH+H;e)C+0dOjBx5@r2RacOyPirAj{d&~HZx{gt^exDScp
z(HRq2@G$X{w7)$7J*@he)Fk67QEtbORFN3RM-sZe)5Jq~`=@VkRufBc>hN}qJp-*~
zkDj`kSK3X1^+Ug;7SbpdA1**Y?QiZ^de{g~q-l2|>81nwO{_{^&xAlA;}^DY_Zy#V
z2_pHkp}KL~n>|V&3>Vx22MakJthRVJm?H%g=P5^@4erefk|cr!6TGd5Ri}w*zs4$b
zKBzP@b%d2@cFHlt72cC8EjylZU*Sj)v`u7nRg>_nxa<$$d1fX`(i<r!dpPR`U7n=m
zp>WeJn#g(}=5CX5COoo>VqP;D1v!)HxR!bifSU!jX<uYc#J-fJ-n!81bc~tk&e{Pz
zr;rK?>83ZIi#3an5BB*Z?tA;c0KfmdbF^VyyN-e~dmk?oZf73%$$dMKcs_5Iwc=S&
z=m|mjCE0)OfNLKXbTq#ZyF_uO6f3Y&x@?b24TSUYr>F-mwJem`k=%Np+qcoVt;=pg
z=Vo^kv{RX%<FC7hk#{QVDannw>C2s+=UVsnrTHDPDBG$j_YPCxph|!bnNU|wCVvJ{
zn?H9bEBs^T<Uzp@71B|KbUz26m9Qh2zhFRO5IB@Zm%Tdbl7Or4XUJw~I25rl`Geu?
zd{AzG)t|-=r5H9-Q%gmcBIAjJJaQ{t78tY1lJrP6;gYQ2tV$*;e!Wght(U2uM**95
zNp1HEd1Z<cCe8}%OYLfxAaF+TNDmK7Jilutp*OF}9Q`b#*a1ILA38)UUdP`4kBf-{
zGbGFDywgxBi2-(>N#J%DAY}PzyDq)8DI-C%;E3k~CK~#9-J$M;z{cFzkvynjusY_!
z?u#@9L4$@;*&$8Q;c~cHT|ZruEs`59*)*Q9Mjy<3&2JBu@Gn}%lTXsR<@1@Z(x@)y
z@2KILwoTEdR-2$R)s)!Nw+^*5^^L-YHM4uc@GPrO2t9eB$2TW(btj%<6iN!s8iUAQ
zcsFE@IGvZuRmvD@j+|B{ZcN!H^MWEo7F?GdTNLtjXJcMrOuJ$FCl(rw0;cqpn~gW7
zCvudI6$f1dva}@%jlZuOBqwDo${P~C59+;b#kA;Fr3HCJw+*$riW+t-$t1b|81kj^
z{?)l1$6tb~^~{7blL~#VQ>e-74bPd_^NvM#m=uZlEwc_DO{|k2ovqrry)w(B6sy+R
z`jr<)+pjyH5_9CSCq6p3KjZ*$MNY@~J!X>0BAUAWN`u*1_kO8~LSFDqa+8;I#>~x?
zRusD%Q4eaV=v!c^a>iqmxq7rQRnsY~Btz)q=KF-hBJn0{`H63S#u6Tw@Y4=3d@VrP
z<qmslM5B!=6tJC}g0<rIw4XNq##AlR3PtM-DCwFgo4?H2cj`GY$R!U=I>A0k$kJNd
zw-?B#$U}Idcf3knR|<BblHr&%X9KmVk%!rm{ni%779@0eu{{Ov6%)zPm+CO~dYulN
zUr+2-T#Oxt6P9<p|10tPPmuq0@c&f{T+e?2)MvsTV)|s#=ZW5n;@rYEY5;<2z*SBQ
z07zY>5JV=QAD3D-4teQ66OXFUF)*Pg&@mSCGc{dS%6YRL;7l1o7Kv%tOR?ySWA^|N
z$5k6oteaz{csLlj82XAddPLqLaWw!B6#)wRIDNQCv9R8BF1pZ4-#K$EtC>7ovMCel
zqLHGfk#)$*V?5jA(|p22tc|LOc}gBL+TN6DK(14(hr}*qlv9$T$k9nonb-_s!W4R?
zSb}P)BEN^fEj34ytcldt%|OxgWC|xWJI~L)!_9Jj1q$SR(DO!XjH=@C5VPcGT^n5=
z@4x<?%pV=8>()W0pm<4_6JQ%FtG8u`@6BpZCZkJ7W=~gpm<FBjzZV{|=(*^t>=4lX
zmZTL`@ttVIv`kkfL5HY6m$l1c^zNJ5I1fWM0h<f+WlbrmL`%Li6@g-glmh70|8r?d
zl|<}wGz?iN*kz7Vy+!#~MWKCgvJvSmP)2q4_8`N&ndBBdA^CsGcl<AY|5KXr|F4Do
E54tYUVgLXD

literal 0
HcmV?d00001

diff --git a/packages/frontend/assets/sounds/syuilo/bubble2.mp3 b/packages/frontend/assets/sounds/syuilo/bubble2.mp3
new file mode 100644
index 0000000000000000000000000000000000000000..8b4f8df6e9c17daec54da10bb50e790194c767d2
GIT binary patch
literal 19328
zcmeIZbx>Tv_U}8$z%aNEg9HZm8C(Ml?(UG_L4y-WNEl#ncX!v|36kIhcL)TM;1WU-
zf&>Y0;oLgEs$0LhzgP9@_wK9n-ue8~d#|p&cTdmy_FCP0x2l35HsBsV1Eh}TU5N_-
zU?GHr_}~Bo6BCm=1Hqj^^}g<Zu@c@{eeB&l-2YcI{+-#z!|k6&eSO8dI}qO)HLU#{
zgvI#;MEMYK_?_*a++J<{^YDLLsy=mazpK2v4S*Z~PzL~L?qRz}@E+NJpmC4sJr4JH
z{{z%LvG-)%Q~nQ{@9DW`{GR21uyfDxJ-7GVH|DO*ws&n76um$BCoDk5|9PkmfIVrh
z{-^oh<-dF2|BpRzr(Zh&07u43OWJ-7;ILDrTuT734agC{_>98r7PohigabgBo%LV4
zf0z>+e9Oz26DM<wv>=yG3D?ip4@;3Uxbnf!!tqFodfT30a+=t*gTxOm4CkOsfH`B0
zb{p5s?Y%ftv}rV{HR5Iv5bO>Ll=bha)h*h49+&@c@95F%CqXaQHyVlMr%$Abb!(Qt
z4wMPsJanqPyK>>%>GMXD;on)|L$BpY1`sB-0pvOhu?d{oneGdbHV{WBP6(thfZ=KY
zH-SNbdwKH*7G`5I>(3zNSnvRRPH6fR`tENK#-qubKVM4*pTBzqKsSADE8X#>4?~+I
z><O`hXcI|5l;$&*u#XlG5&IvXDJUo)KTq9${`~d&^XJdEmp^{&6<l?|*sqFVFc|42
zqbvaT8~}j4SOefjbRQEFKUlvfpMinl$qoYq<CYEpnCb!a-1!lWb<%?QZTHIO!%yps
z(?j#~eWX02Z&v$S4Q5k@r*!_VZ;sFS%7Sqq)zDaUxmZfLWK4*h*BZQ*(j(5f<pBE0
z`@IWuFt8l1VvDn5p-rT_qV&v+#ipP8f`er5WJXH5EBgR~1Y2X3gL$<L6K91l&)QuC
zf!I_y5yrF3MO+VjW>klYvSP>8JevoAKsg3#Ve+K(812vXqbo5ruLMbh%*Mx!)J|&_
z4LWrVZC28M>dbYz&`OR$e=SG}T**hD_P#P2yQVf;#2za>53+|-bib&Pd6CzzTXPww
z+b~dX%GbmxHcd`sGGYBezk=IxYdrf4G{o7yCTQ{}$mLnt<IAB|SI^NO!rioKuGiDX
z*Oczbw>Q%O0HYiqKu)d@1mMGkXkcXOJj@|`k3#F80c_bc$jKx1!--OGLmRhSWR)<Z
z<S}KjCt_(>xTy^EU}^Md1HdH_DNt|L9v_30pPnf)jlgcM_@(%nIUUQqrAoFaTNU+W
zX&yFDYbLX0Yg3|W)B!Dn$_P2LsC{W!=X`)DTV#pq`xApvtqN%)yKAMaf}e`Hi>1cP
zPR-9X%U}Odb^Gk{DTn2?Uia<6JLM+*$5#tnsrpzt^v%TnMuOA;v67ziH9<;Q`Vy*P
z#%62<lR0u8K0zFaA{J!JagMr6B?Z5W*c<CDQwg|%rynfF7z)$@$zv!G=ws#Lr{qEf
zsRZ2&p~eVg2^W-K6&(>AJ}=bMGhNrfqcTSP#Oplg4SD$IH?Ks=!@mxfOQb6C46L_5
z;Zk}q?3KJddRAjlny#nDO7Z)A^?gB5y7_DEd+zkBpc4s_u$0RPvcmkxz4ZB~6o#??
zCfUzZ&c3ozTj9fo0Pc#$7rB3rUq4FzpA7!EJpiX>T0i~V8L(UM;x<3v<K*W~N_an!
z_f<F*=RG|7AE#uly$KFJ-aqEidx-a~)bV7QT54)K0Pyh_CdT^u=He~!eMkqC-<DzZ
z_xInqJ?5MXObx<;>l8jiomIo`w74xrBODtQN8&)^lqwe`7L~<FZJM;K1^E-d9z7fb
z2|~}HEBPT*!KA)@D3}!lgAr6jqDK}xDz;0dC#$YD+sM&VtFMIaP<arse(uE-jV=A?
zMsiR4E_+y7_;%*!_w3Kd&OhIs&-{FCKirKAIbHro!FMAKTq@}0Pl_^_%&Y6GKSw{G
z2Y&hawfoi4=7-cW#5KoVnZO{E!0_jG_D=-i)t{f615uhmFTNx)NcrE@CH?G<)?-n=
zzvB9@{~AV+3k^#o@EBtlM-U5y!>qG~xrx21hy_?uB_~7CH-qM-m`P4lun5jYQif`e
z3t`88)W^#B<fJGpQbk*Gc^k$`Odte`$!+SazDdCKq`kCtUrJ!$2~+ixu+I+jR1Yc_
zzCo8@_!!8d!f8xEUkR?EbVw0NWhNRez=_<^KOSJ!NiIUFHL;Z$zpd7UnU<%g7-yZ8
z&dx+rngVsvy*i0P%DN<lLUgo4$#^K$?szOPk+MpA`0ec}0Kn%`zv99Kr^Jv4pnBmw
zh<>^s#Ny=rYU-jQo7xXWsIUb1!BLO^xUDV<qeKNn^Rhn8)*i(6Dz}{O8YG)^Kxtv;
zW2Yv-7R8b>_i2*P%**B<c%7El)`=q!0s;aLE!q#XOg(32l9G~=7wSKjFIu!ZMMXs~
z)EIr5Ypvlns<&wO+O2cXYOP(!;B#oLa}ks@TdeIk5CI1bg73hiyuB^Eb0fCThmna^
zS{Qy-zM&0Pfn;AC_u+lErx9PQB+%2=H@FSfVwfE^@F*LbZoEtzPGF_ga}1=*j9gcl
zGF<>|AB!h0q9#r$4W4NLx82Z9hzM7JLL5mhAavRGvz+O`Ar^A#7BD3|Qfh$l_j(te
z6dg4cM*X~omFlOd+5I^JV3_Q&Bby_rA!l-V_C=ZcDY(B8pW$0cn1cG`e6s(3=12B;
z#f9-jy*xbDGgkZ%Q?0cZ8ddfZ6l1O2LO*iYp3cQO_zRe3|Hxr(`yu?eH+jZaswI7<
zc%Y>{ZZ|!##$@V{&o-d63%V$L${yoXM=Wxz?`8A$UcZ4TP_hxy<?r*~&lEWFf;2cd
zct`NGw|}bJW~^V|x_YwE%U9Z@3v+@A-I*LE+p9y{%hZ70IZS>%7;jfQUqG24xK<<(
z8o<L$1d|XAbxk=$1%-7dCd&LaoWv|D*}?@YXMAuTmTbnV#1&1dH9hu_T6zCvu^9~)
zW^i9qXUt>XCXb|rvQW_I!Bj4fErIXjpv<zeUP$1~5mv~7KZe~{3_|2{f+54y?X~gi
z*-8LL{hvFp;*-6A%k;6sTce}|f)WaZGt_F}d8)FA$^IAtjtF;nX4rScY@|El1Sbh$
zjvWO*#*%^8gwVj9F=XJ<s8%>NiW7{|aDN(L=EvtsRHF2Js6(k*ULFy$j*E?xWlFY9
zWD96OvILh>VOlvB(5Xd4;V%}Su@QDdDd?gAT#-!jJn7)|T8?$faCKEyzDkofVDG{e
zW?PizsO+j`bkS2G?0HjDFe#HgdB2!#yg-A(lr@s2tgD+skCx4AMn)qYi}4ti5m|Kd
zih4E-eWD>L1Bn+lgD+SVJP~*)^#b`=r9*~VWe~Ys+x7B6wa?K3Fo5Oztwj+S7`p#X
zBnZx~D^R6v(!i3yz+5Zw;@NZ~&SN5zMD}M<1$w;RPEsxJPRXa-%;sx5Q>e>-=DGZQ
z5r-BwLm!d00RW(<6AxUVEScCnq?uHV`VjqI<xI_CWWG3cFbY6O1I^4zH+hvgMb?`$
z5|h(i>J;oWMRUnaS{(d|#*QE<T+T5YT&SAu|2>l#k0v(rU1og9NR2gv&w2%M_R7WA
zmdN?I>%W9JcG2M$I;a$CdMqFc1wc~GhYE$#r1&JhF^T!H-v@OdlrCBgEuJHD^b5Bn
zE3Dya?J#>>CtN+h@Wv>^uxZ=*bEnZ6jcGr(B~Sq$uTe!2NFYj%>~J-v{~D7tlkY~H
zQQmjdXd#-AGdVV~dpGO48*y6OTzuQ$R!EQ%bq|k~SDf;rwhkMq9@$bPLJCWc(O6<C
zZnNYjkg(@Nq-e-c2_O+O86zb$Dzjm|U!N}Xs@hUdgS{X01L~Z_h%4^S8}dbqaz_rN
z?j?T2Cl%vaTn5h}<3cV)D<-t`H@>fu0ll!UZSpBh`M||cZ%h|W#f*?BgFm2;JYDuj
z=OCd}(<jf%Vu$R-bxO{6o{Bu}3aVM5*xfjs33Re7<jIMxH>n{D;T8;u`nD~#nv_M#
zpz!d-whLE8)Yma@l!ux}Sj=x1w;?;n<0F}c)P;kko_j&<jKBSwg~q3qWR4E9@O%CG
zqi|dG`VT$@%#n}z8^0ikw`#8-e(koaj>~Cxz1nc{wa(bdJrg4*$j+)7s~ljRXuXz-
zQ>C8m3+lw#og{NP0kc&-wR&2Aq|_9%;X+a15Ung?-y_4u+LLYdiFyc9WMcIm69)?r
zZRL!GD#V8qVdz^M`1i3)s!n@a`3#yErJ_qy9Gq=tRii&uE0|dFFnK)`YkhN6DDTQ?
z&iZuSg<b^H{494>{bmzk7@=eMqjq8t@Kc_N*E|i%(?5DZ&6M49XoLJD=YDJvIhtu_
zl;z#`u5NIVz6SmrX8Kpr>!sEQEe#gB61bYVT^AvdPB8E_mZBPV^M>{EuPFT_dVQ8w
z3%|IxFzpk`#}PP`R(<aSs%f1@n$w!Dy#30z1<n2cey+L<!~vLrDr0epl_Mf{UcG<?
zdZGasz%kyqVVn?)UPj_riN0T-x#Z%#zIdD&VvXklV@K!RH4}gJ#b`?hE>{vH#-bgS
zh*%x~Ted#PK9^3ws!S1fQ4)WInK=e{h>?i`uBqWh{hjoT_m1;X$CO2hYuJ#q1Sk-#
zgLrV(11bA@&<|tPC}P!%BGn1dYVWjO`!D5+lZ4OS1QlI*#cyLuO1#9+yO<1XS56`F
zcaxcHmC@p$B(pL4`&rh9DI)4TR}|);9F2<|9LOG_;A^_rikBQc2%Ou=+NE##8pQKj
z|J#c<+4d7M_BLb8u|V2~hbpAP(fWUFvdb7Wf4;na?lMMnG4V6#xM1_cTUp#2SsYow
z=}GX+he7};w~+Gfp&J`Uhyx)`j}GIHG_mars{tH}+7*O9qp+JCO`&K0@bY1jDQOrN
z<vTA6lUNC%FPy2?n|eI!^_Vc(bmIpYSss<V5^B(D{sf*h;)GS=spSVr7X%{#S+i~6
zU6!^fTZM0SRHOS@9=$2bnIhKKCUa1Jg@#~nH1in+ak_AIadqq9VEuIc=x;O5R4VN+
zd|ewGW}*qR!r_#UF4I1M@#p&~<9Xp9EqG)b7IyW9!o!C3PQIkI)>81v0>B^LTH=w0
z*s%<FNK|hvt7`MuNJ`Vl_G9t4Z+iF1t$S?tvj>a9F!9hZ(l8Vai9+%kBD9>?<P+wu
z+$kS^7=AxHu|@>_GV=Eyv}F!;Qr&BRj<?%m^7a7?;cMk=ByBBAxr+sWQk%qpCB-Lo
z2~{?GW{uAPo#k`rWISKxEZ{ZcOCj<<E+I~K>u$We7jJ8|=w151A3O3#E|9&o;(7oj
z)O>Q}!2&ikgJfttz;0H$jN+=H58t@guO$jpV5M^K_QmthH5b~?cr2L}gL&(&*tSUF
ztgWl>y5QzJIus2bdOO03wn&B0>8BXX<_KWVcHlW$*I$;#prW_X68~c85lM66cb4cT
zDtpBz^ih*jTiL$^AAZpU{#}ke+M?ZIv*O}@M*Ji?A~0v8Eimhb;?GGBfYGk*!A}-9
zOnE^zBnF7ggK^eYXVm`1cMqOD&mIjsu_afoK0AmIn36zD1)EP6q{Q_JBbGl0hD4fV
zG%}ez2T<0&iQ{B@kOA}($e3C6d-7N@`cN1uMbgvjYnAw1m?0@OH@L5mz@pbt9)X8X
zz~+mKZX_#HG3IBD-=C+~>oYszXHppj(@3XG=h>={I-yg9-ghf2)M(7r^B2Bt<#g-i
zxrYA!CZh4de`1n#v)Nm4f2^JPx;7v42h5rIkJd_&uaDE*>$<b~r-#?qW$CYOR_R@T
zyv)yg^%j76<^I)dB{s1CF1ch=Tnrde8c%&8%@&2UR$ye~+sd>Nk!Pa_^>;eq9^zvb
zvEBf00kW+mNF2axQqopWlwm{kZ2p|6R{X9i!#Fs}j;v<`@&i!GY!SPk1eT`F`Fu+b
zQ>9R(>G3vWfwNpXm6jsgqbXR0OUh*S)}t04(yS(s2wt%hU!YuY6C3R#Inb-z$d$sf
zdMUlEuc-=ODuF5W8b3yPx@XPev1~Ku&%5x=Uap7dO^jHNTvRFwWtDvn;asole^&T0
zgh>>f7pKPh`jkLMYD?wf<0R=T1c%R|)_7-<q`}eo@dZ_3$fk~Ol%X=eUe+ENKYN`#
zw#jz{@n~_n&3w~EC12)9nHHz;?Ee0sbnP2M0C2n0eM?cc5>pC`AXMo|ku0-LZwSSh
z9nUzm$<(sdoQ=mN$-rSX=wd(iB*1RMBbO{7N7X~4Ou-$qU1Z&Kw8tW~MWeKj3<%gE
zl?#c-B%yF-i>%CV2V&R^uVyC;E$Rr7G4K25N3e7|6O_j>)y7eLQKk{7Mkp8u6MC5(
zDo2!OLoCK@;w3yyDrA_6LwEde_kUkg>Dz;4L@qR{f+PZhFRLY^za+o)HWq6@ydAV1
z+URsCum63L*QZTPvK4`mFOw9`d~#|R092P;+%bB|zc@jEAUUYMTLUsbecr@HP1*i#
zBiNVp(JLvf4?bBw1^W0!Q(pKz0lgN;kFC@T26wAY@;T2MKhXFKfvk1f^OSa6qq0gl
z?8r$w_dgJTpiF2Rtc$ofw%{as#lH>>GZo|E;)GIKlB&vf-&|eWe3M(zZr(Ka`ejDp
zz^&A-Vukl=5B3Y(!ebsGHhvL*pHh1iZz*@PkGdjtje$Pq{oPcd6|<)BE4M0Tu3`yi
zun}8vF>$0xtPr~=0Pnj%0s~j5(MZ==Tk4P&jWWNRDI#`Tvn`I#@`LgUMbn{u3N`i1
z4Cy$sq})!P&KfOkeVV6ygJbbLSUFPBf`E~S)+6phY<Mr2(-d0bM@FDgO<ufqR%!aV
z?H(8aOvSw0HWv+m0uMo75585CGKvhxA+cNz$EQn4`jZuWh<4ShDx3_!gI%jj6KJRr
z!U!ZaiZ5(9AmMr#f)wK0=QNLwASnao=~c6SL2GHT%wla-G`05W)ku<koxtN|a2Bq`
zgmas+D{0Ph{OM2r;mJPfMD;_N+l0o)lQmmOQ(>0Hz>}qlIf`sfWh!}iypiP{g7@#N
zxW&A=$)U)v^hu30>7C@m3LYypj7uvXj1#LYOf&myf@rIq2U=DuCnw%dpb&crtXg;5
zx$mBRk6?kHoGza{Hu~<d(y{1N%ExH$gBfV88mean4k59U27GZuD~)dPiFkr`(mm`$
z%d&arn4cEv0DoBZl<gQ9%z747j@d-@*ItS&F<#Wk%t{JWy=3Wf8c4uPX7*KC;?S}u
zD;5@0OD;Z1UyvGs7SE(S`J=GlLYMZWB+95OH!X-QNx3U2>rg4Fz(AGj#FM3FggnYP
znMGwPMJ1^~nOSgeL!pLaL)AE0D9s{UA@)KjT6}3g>rf%eNSI8bb!#j6HCqz1<&Mgv
z5>N$8iL^o6fsvCIr*$LriRwqLU4lyc@Q2F~B``!l3Aa&T$4R^&hXj$%W>fTSN53z0
z$k(w22lJd^SAAj#O20!)h^q?cBRy{u@c57p6-s%{{QFFjhD8YH$EKg9oU|logj2Wl
z?Cf%**B6S<@62X43-^RrSquU7rUNQD`V<XDP47)4b!fXPoejT!KMm==4ElJGqamlM
zD0u&}#qj?ua3b&~Rgpd1hnd-#K2ms8pPcBXR_NHgLS}>Si;y~0O#Hws3Nfr^Akc}&
z2ak-n6}XA{iiic=$Y?Ab^a!8hsAU-lS+8*z(1Gz<$QIa2p6*B=vgMTxZYJ{WbEz>!
zUu;h<O`R4zHR-j*USGf0uV}&vm{tnw>pJ`?@{d(etr0n>^f&PlFOrlfU+#uAQ+=X}
z$ot37Pu(X$UvF;`Zf`GRu^A1=84^QdDLuvsO`0?bisqQ&H*dxghO4a!pA_cD7U<e(
zs2#bDn{5DMWo1n#9ySrG+r~+gk5}SipiD!8o0u?Bt;7z9Ay`vy4hw=nM}e)vXmqS$
zN<Z(m<9C?+rb!5%CEo4L*Sks0O*3E|XA_5xWfQxHULW6;C<V61gN4t-+6*PJ@TRXz
zwax103r*o@-8__rVf6&-GRqCl3hLypkTcS8AD2&R!N`SmS;k=A!dL`1E|hRcrVxY~
zt^(r1(4nwb*qASc5(!hukoIwGze}JWYUF-h%sRnj(YV`oO1k?z-&2wHkIt{v&WZYD
zV=HpC<vGQI?Vummk8iKKD|-Zh2EC=&=RF9#(viP}*u9378XsIqQisUd>5d|EYA;rD
zx;CKBjw?DQj30E!0za(dZuQsVko1;e8?49otG8$*lL^Y64_lE{YJkC1B(bPoS@@cS
zQ$6T8i~qj0%6u>=C6<ALOjKXD!)I;iR%AlTpgnNb=Jk`?lc3w%le^CO`Tv%m{-0IF
zf9KQS)EL5WElfUma(@xL1n-d`t>Gcud_Yw|Uqt~iMsN!c57897oH!GS;W!i|;KF>^
zd#)i>wGR;bGn@~f>a&Nx1Q^13f~RB*$t?^JIpin(@X``<9{v<<xa@p~GnXp?AsHnm
zik+f7tWI&4**R_6QtvO1CX|LO5<NP>)|}YDm4Bhl3sx}X%J>u6XeBs};TxvVwa;6{
zsu{0;`>4?AhQ7ahZSLantpp~(u~!Ui(E9_lG>9ke(Yr<D-@`&=&}Tw(K8cHv9V5Hf
zuS3G=W6n3BSL!`^=no!2PX44QqiNzRr9R!*9E`8O;2GZ!-i=MKP9@J@E?F8JxJmVV
z4Utpn1K<btd1LzY{K6h~q!&bHi8+!|A{yJW3)d4-G1!%A#D#R&cW+1fWY-qMAJe$`
zbxqn%9)|)Y1c8G{Qh?JgXoRp>6GJp*)rqXO&g;l5#Q_~hfooH*!b?`kHRgQhoVT~v
zO29&*9ni3GI5`{#$5@YG#|d3fG4#wU0-ITMD7nxLXK`c00V=@;{9t0|h}<D{Sah{m
zF83eQv9AZ03-tI|y(g+L@)u!b<*-7ow+m@wyUh>rlZ986oSeBC^=Gd3K*+luh>&^8
z<2|gZJ`tVfD1Y>;dY_{C<>1u~S}5Yz1))#LVwnGzwx%gV2U}u?;u$3f3Dki<$9*XM
zh_9T_;}(bb+`pXBq?nDnVGoDdBL|1jglk7W(mMQLaew!tQ0@-V`gl0U+kqVjiH8!o
zqvVq~jT|TV6NBXf0ALysE{Bpm2Z0JVP(o;az105HqkZxe&fWE=6T&<TW6$ag2_iqV
zGckD#t5@M>nM>5YS%1AzaHGCk#k%e9QgORi^l$zCcWV3p`d)C%o*TG!@H(6f`3WIE
z$%s$|6bRy5>PQxTPL(-{{~;8qq%NFi79`Z)dkUXNe;0lBQs5O-Xo(uGGtB^u^&?G|
zi^Xoi($Sw_Wug@^P7u-!Q=tuG{s|q;k4Zhw`O=WG`DOj|D#-k+tHJO(V41CPcWaV<
zd6fP3WMS@P|0?VG^}CmSUxy(?0Q^k3f5czqgcCW2R;=s3#1}T<sGxQs3dYjK`}j*w
zU?z9tB%1&s3Dt)T^$F)@zUQVt)qY0v8?zf?1FO%BdrBwF0d>d7f&8RtcmRRRLDYzH
zsA#tGjX*3yibz!wjq(Hh?MoF+$}Gii3zedEpXi9wIp(NZ*&{TeCe^_1xnipc^N&A$
zuVa6&Kz~lmrI)|E3NwB7^kxnt70(tpz+#K-Pd7){MIFj`F4T`lfh4DfNXa94uS)8k
zyy>5jD;HcVSM7iQb{5O#aN66LZsM5Qj+y*h>!eq@AVO-Ua8s6G;-L(pSI4o#N{0iD
zE%iVSWP$luZd#I$3cwk|7c=m9(NgT|0NpbEaVXlJ))k)Z#1dAY9k1=M`_n1bQtFSn
zNv$uVKtz|`yFV{)>94!T_isyfbt|e%0J$_J_$VT8Tvu5v?$xhYBpeOQ&a1y{uhva$
zv90k#Eqhj(!9C6;^r<D}M6qJbvHFureS$LsYp=lFf{CGc*>C`?Xka^X2mJ=gjw=|A
ztM{5!S7osJk6=#5ZICw=93KOVku59<VrR32rP6#eM%ffYx^eq{a{k)<wk<3D;dS+#
zUqadcUH!tzgAs6JOdWXc06*L?Dq2v_qynL!6el3AZUrZc`H0xU5fm8jlN4Pqdxnto
zNtF2=pd(Fb7cY3E%n0}BTZg-#V&D=ODN-K&B7E?=<FgdR8M8J-hgA@PLHX(<^#vM~
zA=}eKw>K8@Q)5oZ@LNx|FTaicNz0FM>n|1hddW$}D7|d;R6<$WF3MMheLCt|x^*li
zXPHAzK6!tq^15y{KYy^vu}luU1=}^wV0Lc*)L5DL<K_EIFzvU1dPwx8Lj{muSbKRY
z(C(YrGVSo9tv5g6>XD$tm*Ss~C~jA~+UMGWn*bPq?{HZ;Vz?x77v4Z92=|tsL6C;p
z2@!q8k+||}6Q%Y4EUbCnEEQ?TE=s+`BwR7$DUdJs65fG13{R&ROen!)(D0yvU#yPX
zAJat06S{yYaHzsyWA&66^}i2`pP%LW?PB;?H&<o??3_3?dUTz>2}6Q$d|vlc^d{Bv
z>W>VGY2k)LJ057P+{C?Jn7i_NaQhAMHyBlkK@;3Zj1iiMZJp(#mzCP^%e1>_E_i&Y
zc_zV%A*#%z1i}^`5Z`kF1;d!io#bgAvx#RhF4HJS_J*U#GlfwqYZRHp_wjc#;g6g@
zJoHL%HUllv5ESLe*uM9c_<~*jIghE_b=*a8q6tXPG(?t{1qs3#ZQvDP^r#k)$C<?H
zi)5BXB8Mwlm4O;XR=Eje_M}j%8N|Gy4H=q{3BJswG7}xHItaEA9Qq^>lR{3xVyBdS
zfQn<v4kuaf8TW}yvBV}07Wa!LQSmH^VG0j7W>f{CtHg9Jwaio!Wqob11YIvJbXrLy
zEwnhkbwC2GQ?WT2WP_pTC+2LXO4x#Qe6NU)t=2VWOBEl8ftSg~{X@7Dg*0)gNk=ve
zAE^vXWJO`&V1ej|@RrzyKzlMu%%l(5LL8s75iDstFafmLsC&hBim=Yu+Qt4V<DE@c
zY^U=%Or)~no>uRKdjYl_h~e@|lpJz6z;n>9a;_`865p!NWyA{#=RzbSTm=q!Zf%mB
z+k;wgv;g(Wrk%o43%~7KR94s96FQB?cJc_<PwI5?PmE~u&2HukWSm*nR@E84`_wX9
z?d-fz9nufRWLmCJ8FT@XhLV-O;IuT7$P}o!SXSLk?=+YZ)C>2x_<cDZJEbQalNc(x
zH*r0F>^fEi)}FZ9TEL=w18QvNjAg|_^~M8ZNyujsiOj8I&}^#VlTo@QR|Wxll<VuC
z_X#{ZM|HE381-g?18JjLep`o=w3o3QWrplW6=ek%I_ltHql!?|C<0{2FPNm6iIQ#%
zi+x<F{n(^Iq(YJJNNJS!;aV7_oa4xF>s$6pC4JJK{_H@tq#X88SI?1?2`7U9vrm{U
zrvj~_Y*BW=Xw9;h(KPm1r2bG!IdeTlo2T1r*~wP9eNucpQBR7u$BTL4M~jcR808@3
zH6juUYK5T6q7<NkmcFKFa!sA8F6NfGSyHOOdr}wTBuzzLB_-GRr=^0X2zhdozxvi>
zOh|>L{v!Z@$o-~mV;h7j?O9VN&<my;Rn#Cz4CVNk<Ufuk7z<wQvDWy&JTS#{zP|LU
z$29jl4qbm!WKZHegaR8ITXyLeI8$rB(*dTMvLMYf(5^ej-Yld}|M!Nuq?cm^I!6@e
zBy?@h{Lt(rXl?tDYmrAyrsTtyV6W4vS)sDzPwe<(iwxBGQsvqox!9|se^D!7A!oO@
z`g0&1#?p+RUQ_7}ctx%Uj05#9{I^;xkFEuY=Ks!$ec+(6_(o?Rs#5%>HduatfCI{r
z5xcBmDB&q^5?d`3>u!6>55cw)fQVY%K%{$OGI>idGJgddLL994GZ(_9vmT%=p(g7?
zxvN0|kkxjgobFwxd;Q)fL{$3MhhD4Ae&WIDcu~vCu)ono{A(iSbJs_Vn=($?Xo4fC
zAod0%xu=3b&R=O7QMEUM6)KsfA?qi|NJ7Sh6Cx`|_LW0|Y-~8x(WpFncI-XJEbEU{
ziKD*88jS7}(v@cFsvm{cHfs1RUIeciQx#H<^($ez8phd*z_qP5uf*9{g-US(HiycR
zy*h7qz@$-<yv$%C%pu>iE(Srai3WPi0EJElnB2v(4i<`a$vA@Xm=)&RF*VT15^;h$
zA(_k4B>nJtehfL~2s(}p52YMipL`sg240m;cIaZN97{BZ#SYLBO9{M&EC;@g$3azD
zI4J1zRScq8aH>NL!p#enhIsrX93T+6C+uEB-&xAn;v6IhhZxXB9`rhIVhF&P`{|Oy
zot#E2WozkTXu9>efoQ&Ls}wGB5<RTGt)3+0Bc#8Q>)d`sN)3pVgUy8;=DOh|TN2AJ
zV2{Pj!o$&sPdCX2lGRzUJ_gH1kETg6*hTt(i+-HFtD2hp8X4D&Y;7&&FtMizA5BH$
zi=cA<(kzVTnY?7oC()SMC&)&kpS3*PV=`ICy!Kt85U>=>wL-uKbqYuekrPaA(7~-D
zsoV|`5;h~dGRg8!<RcmrOI0*#dQa2;BSKgIlRNh}f0}H#5gm3bM4B>mB+?klkb+T5
zQ||_eeoNb=PrHzi*Z44ZX)sHcxvKB8R^^0bG(Lv;0@KM6sElnS$Cj8>n!wH0FEheR
zrMrcz+^C&)3KmE^AXPoB%oAPuL&*qRA;ptNYJy4bQ&NFb+3O<;7Oi$$>fCP>BFS4X
zjWAb(OP8sXbd3kDlvqX<KWZ@4*<3ws)nJmNb0dQ^1#`-o5VSk2s9-zIxELI>E7;yG
zxu5{}owsG}iXVbC@syZQN>&8GaK3T_$M-bhW4Pbl2N(l2N1ZvQ?HM6Nw6j(exMd}8
zrjgzcl3O0@z6<@KX-M-oT}Yi>WrGY?6CGEt*6*3Cx!N&7I>xd}R`tM5Kyk%Ll#Q!O
zDaqJ@hgmIYkTlQFCV{6meE=ALQ7-b@P`iph%G}-j8DchFZGJeQJX%3ptmDLVdNwCN
zzd0M3`$Lfv-_ERrI0Svrg?HMm&Qb$5Ey2KO7QzjsnBLr35JYGuZAm@H;sMeUgnO5S
z(9r6j3SYM<yd#<S^kkgY{4-A2+(erm9UTNiox3`uRYk>qm;Qio+{dHF#F=%i*0NEk
zH|Ep(9wukwCQXj3pdW4E0_|E5y4UY^WLUA4@Z3w);p$kNZ!fAunJn8z>i2gJ&bl&!
zZ&<5RfsHDkUWz8_?;A+OV1_dpmD-7|CmIWJ(aODi!atp%5#9xR;pN~>VWXv*wzHPS
zt;CHe?>YBOEERu*W{O15bOe@a8>a8b4_3}2()hC1sQsGr{CindNio$qEtfjj)8qZD
z8D+FsOd~a7u8O!`73@U45>Hc|i{nZZ9|E*J09w1{1!x_Ays~+FRsQPGHR$$BW5kW~
z`HCL>Nage%?hZBy-4KpH42NmiZioh;Iz$3;EEk#f+R>Jj#1*hS8@lbI!yV`B{@{lB
zeJHZVnyq8xpj_tdGj`pEY{F{iV(U=C9UNXr%bBfCV<r|7HKXH<CIn;^dCa5sfC22G
zm)#BV;{1QbK5r1twu>*~M-C>9q(v~OAe}Jla?42%7QEcH9&x9AW7Vz@?E^g#$+Sco
zz{^w>j+HYNMKeuHSu<@MNZ8`bI|<cR0z}e=_*P0|%KO172dS2sVe@^aZB56kh9;Gn
zxh0=?M#T#MI3~WqNxKkGW%{(}l^lJ<Ze74UZS74UNIh{uqm)<c45ZBG7&Z2IL9W`y
z^`F)GPnG^Fly_g|-z|xrebyuQ$L34f@#T8~j5s)Z#?6S)X#M7Bt~&=Sl_^zoFCt)v
zL}XkJ@1<@PB%tTV$Y6}bBu_q&%?UYNrZ5u?t_huGRWTzVigawS+yZH9vz^mMgv3ef
z@(H9b9BjR?&<L4pPVXOC$!o|VU@O{BW~KQ~Fx9Xnkw3At^M~>|`rEGVySx#F)xCZl
z60T-~LZ@q$^1DyEaD%Q}+n4P$#(Q1khg(e0Q&X`ct^?8Wz_-&lEF)}U+aG&Awwg>N
zK`1b)DG>BiB)yclU%f_>oHL}0PFwNtoGCZG!u&a|r%c9M>W7k$$M_`G&L`}98I{FD
z3{l^!b7c0os8bV1*pup!K5yv3DvF3fHFYHsw*8MDf1F*pN9cp}7OB${%>=|vT&oW9
z>$zIv2F7(QX>>Q7#HuGxH8b_@^;;N;{%9rY;-K!|$AdHBUaJTRc>b~06TNZV0QK!^
z$ekUlHrw#C%~NsE^D+{RoSS8NYr)SOAbz(7x{p2e{`Rx4pq5j3eaL$fEQ)ks><&{N
z!wg?QTR=(P(6k1Pm23)Y5|dX6Ncmaqr_kfy#jvaxWsipE;hjVZej4L3+#lPvXUe#*
za25S5Jho1eE(x5_2yOf=<c!a1x1Kf7+BxiTk;(T!8|E?O3sPS}Wu1nubx!MI3+|aT
zt0*Stt>l1)OAkj`;yi0rvC4;o{&6abqvAHIEFY=5D>bO_%*SBt?1IA@n{1Fe8Q<-=
z29`W{v_=W5^`=o=Kv!b0OeyYR=L%257#2Q8jw9k~{XsEPyAz75no&3x!pJmllDv-L
zc@)5NbVO3=_r`s(|2R&{TA-x$Lj^~1)b7Vl=ovhB7Fwxncf`jd^=u1QgIO8p{m4dg
zJBPJTI4@<w*TrQOm2i-$eZYlk_-+!fm}f7c+_n+1LNmtFxt^7c$xA`yQLULlykrAr
z%7!-xdU5)X507)T#d#Sh`MM^&jjJxYd|tS}hOV;#_0q`%@I`~I1<4AiqlnwvA2e(J
zhkpOl(#xF#8ZEoUi|0x#kx9vNJPKiCCLxt2O)CL0PIgdCrcF$*ic!`NdrmrABxc0D
zEREGlQ5AVs9X=XkMn-NASKUkGT?2JX7QRo{ea=Z**aki^g)%7qU^-%oYo*-v%Y(yI
z(`QB$GW7Tj(ZElRT_jGs4OtY%95JBEgF(yhB589PqN)f>FytG3h@QQ)sS>?v&&ekK
z6CgZB+VR_$cil&Bz9;~e`<q)A`%zAx!QfClJdI@;cyZxquV9GlKrlj)q;;sMgbai?
zLef&XPO)E*YlF>N+&@|zO$z2H;p4Oeb0}^)r;+L&IbfF@lT>3C$-97w+9ZLBnoLq?
z$NgF)>Q{c?#V7wyJW9X$I=&GI1(Wfq3q^$y7Ji3PQ${A5mBjUK@-w+6ktK;S=P@EY
z)xmOYF|?g<Zm<0)2e-(%NS(q)T^0?lj9-s<oP?6$G1|A+5fwjc^IJp(Osr~QuXg21
zVgQ9|q)gJxF{L(0u_T>-#;~@}1s&5q(nN}t1w*ZPxf!}&BW05^>JHKeAH2B%`!O0X
z_<f0ZjmU+Qa~JQJ6VA62vzNu~kB4)IukIoZ@uk-ilZU@2;os$Cd35%b+Oy`chCfKs
zhRF!TY__D0vP=Tdn%7NyMzuDh^<KZfBg>eqgk2mo{980|-g&DE!?wS*O-hRoC^gXd
zzVFKQ(`mXE;6iXSp;=Yzw7l{NwTWzbaUR=(RFm*j3bd6NS~x+vDh@?lyIOoX*(85P
z?bV?pw?GE9LeYf2y>Qc(E{OdE(u5=WA&r`+s}c7q^@Na3fQ;P+r+;5$Ks{PR$&s%&
zLo=*zKoQpuHnz5m!-Tt}1=rCFQH(ATY4n^{-P}oiIE%RlnPxp3E7%B5Y|yPMrXeLw
zw#tq@&KJ+jnhSqGi<8|^kwa9URZN)K00RNwXLtJ>vI6N~I2P04Ax|GgrYwUba<a@4
z%9pI!Bm#=7UD-c4B?eNR>VjFTV0!B6>+`RrVj?4&D)pW&<H>srvi9fA2=Za71eAhg
zZ(;+J7dTPr_UvkdDw|3TqN%+5Tfxb2qFIV6{=tS#`qK4*XryZ52{nlA_x`f5afO~*
zKjW<or2~@5>`}(2IOaCkMf7|@yk|a}E}<N$mg0G8rQzO&SY9d3OiAE(a)pL;Wyug<
zPJJbeA8lSts>dFAR2ZVt3myYe7R*lA@G_PNaVD{`s%Ne_a<ZA9U>q+Qi_%vY%D1Zj
zL%;tD;r4&qMik)9*Pl0+pKE9%9Pr1P0$k?bM>CDYJ1StrL`VvUnUO-5;L3=SWM*VG
z+lyxHvWD!0U*1>wBa1eR7v($4UKG`;7aXmzFvWy}>flm*sA2#!OGp;2`_182=gQIf
zo`mDJTdST@pD;n4s=yv?as|z=*~)M8D}JgJjH5r?t<||r;;NcI-S79q$PKzV`&#Yn
z*_VXrfXU*N3eKD}9Y$u$lGZkZh^I|3RmWh83a?wMRk844LsSMMd%1&!#`XyMgcLGw
z>O<vlQBCp0hZ_$SG9DlMuoAK=CN~?EYHIw^r*_}$mHb0|DtTO5%!;QnGU!tN^5dG`
z2n~zrhQ3*r_<4OLzCN>i?Bm>Ty>ArUFm@_Yce@WK9t54fc~M~+p!;wT#pcvrD&`$U
zCz<;_tFY#Bpy%jbzqu$D=|^u4u+&ZB)pv<)9fZx?Ud<iaN7^>RO;2mPa*O`HZB*x1
zD8pV;ePH{Li;;;Cr=HJ--*jJ7Yrut4U89yE=8)3>eH}AEw(K)WzEQ-<nNAwRd^Dm-
zkj1HvrX)_ABhe;)AVbIMT}$ebEs+U<lt#@7Vqhl{l9k3L?j}Wr_C;XIC!2XU@1|r&
zGZHVfy+WChn1XQo%d$CHqH0DXs5#RxilR5?k_$ar%4;1S_nvP<yJZT`5mxHxUO}vO
zkv$S-=NWcH=f{MR7ajlr|K;bx49z(%a@;hn2<Zvy8T61T3lGBuz6gij3@NV5W}VnI
zvikYz;RaNOCb^b(FHI9fb6%;d=U2fJ2Fn9`t>}mSF4M|0^>{2Yo@PCat};*tQwz2R
z@F!^%+d?#U6a-tt$ih-pwv&5a$%@T=x0$XLo5NS5{>4PkQ6_#FUQ|7AOKK3MNVU0L
zXLMZ!OJxH$H!8$ymoI8<*>t7U;LQXW<wtv`)rYomO3D$-jr!4rwQ6aqU)&s))3^#O
zZ9ZVXd@jL|&e}*(*63txCr*&X=U$;7u|fK^-1H_)8NZ?D@R{pI=H<VJehUG>wiWw&
zN$dry5K=6q0uSb{^ugc$OEzcw6!chWvlUIZ#wsZ<zOr`&CJCvq<rn)U+ks^S%l(!d
zvD-t*z4VAYT7-YsE<&AhBtD_*EiI``PjZ5#%guFbAS9lsM4`>W92A;>NVg=3y`@2A
zsp;SrS1Mo3+6nRQH@RsuknYvSJjj9JS5DY+ReTQsxAX93@lfU5oP35(im@i9;l@Ra
zx$PU<^>kWvC{!}CaI<i()O<TN48uC!o>cKk%4HJ4K4lmtCB}SmwcR}8Xdk4sKl**3
zWHR%&#c{8<HBlLo$_p1y(>36f)vt|*Fs{$3Zh_J-ofRqZJcR=M`_d!X%Al#iuaa4b
zkI@-~UH|~N-8(+lrW;J|k(yKj=pf5`Ldv~*<4#@;yqm7;mZcpR%CDEI1ff~(v>(7C
zhX&y#Ei)JA72R58^cUuAdIFp_>@!aU@8hpsEehMwN_fv7ZW4O1b1miIVkKQTQYRH~
z5c7KZG+o-~lgB1<X2h^RYH=We$$_N&fxRaODviGc{aH~IMvH%$`i^D1#j!1=Nntgt
zVq~Plf`H;;sn}X8<%IFK@lq;0WBr$Z4pPGe=&{LL0p0o~t%cRQEUZ-=&%zSQum`Iw
zXv{{&2K6XZT0!xUThhc+c0G2lbD!X;*@-5%l6iy_C#Hqchz$i*!*aIAo032r1>8}y
zuZ(pQeE$es3Q;QaSYK8daICy!_<NRWyT^M%rP!W}rf*ED&_@BD1+`tSc!!UOblN+8
zb9wHNNw)^D7fXu3yxn7JKXPnfPhVn86x)+2YL-uW`wCJo`{?a?^1<1cfmkPf0bvTz
zwnUmhd>HI#Z5spovDx~As1*M%_K)m^wE8bGDGFWPJlu4k9(!3&Y@ZKj>~DN4cJwSJ
z>v?2|H9{nA03IZRpGlD0?H`a;K@rwml<KWxgQIUNB&HnLPTF<iB@@mnn+jY?#g#HG
znYawYIA;rGi06|7D46zI)SJsk+f4C$$2F5i(MZ;ZnwmhE!jTvHNWzne2Uq-$^>)#h
z`uw$o{~~|>PYLzEj=_o%0P;(?y*%usc8LPR-vIasrxh`R%}Ns9PSg==WtF7b!Abzv
zqUJ7o&G}zp*;$ajl*}M;vlXJ|`7<SkgCjw57v7m?)?sd3zdh0rk4>a%ujisTPG2-`
zrRq{FqN~3@w-*>mRYq*(H;s_0znf`k2G=qv?zX0gw>wA{#$*2iV4Pjuws!mwtUSG_
z(4>`(Vn7JV_CL!T=@XCxUaNm=sMoB2S!tGp5YFp(#X$b5;d6wvGLWQ+^huf?e+;iy
zLS+eO+n6xqqB2-i__x<@WewHg#S}+Im_4{zA{toP$FZ8deWF%W`K|MA2O={&OV&o9
zi8UL!Hn9^60Y7HP+#-weX4PegnB4crAx@UULi;FL0L>TC+ZS!2Ey&np38*)%txp5s
zUcYGoZ15wofOWy_<XD_Ts}XmIFlu4zT`2Pa!c;h1y6ii~j0iZsKI*fKU@K*c<*A1t
z={1*MlxN_p6FoaQt<vEzj#L&6GXyDlukJ~kheLnZ(fQ$k8_DDc=FRWCWJSaq3yJBA
zZ0t}7ixbCrr!JJr4Ba6~8KbCUQKZ)!%qg~^mpt3cQ~9Wf$!0)Jetzd^Iv;9AOon{0
z*tDjsNUFm?QqasHR%dUkCtkU)t*DqGiJ{FvO<som;Y**0lBje#rU$+3td98Kt{YGQ
zzkR=SQJ%3X6Mgo4d%j|BSf*tp-_zv-m@1@BTIKg~$c&vY9cpj>a%;S)6?>_R9;6rM
zb*h%HmCLc9f);G*7obaPw}9by7=oA=_$I4n__*fPiev}km03tmkt<x#L?K2B`D<^l
zYsjeRB=%)qqlAg_ug!;e;MiII(f*0pE&06?-iapaCW=BAefB8*GaMW$+g@_)?&?%R
zH|LLP2DTy4({EZ*VmL8gY3evw@KYg;Od*0a&Mn8r%7*uSxgJ)=MUgEVCwtzYO}^3Q
zB;qHECuAg_9;ds-I@4!@4uT37*)HB+o;uH5y8l0xKL=yIY=<h;1@ft7h|Uye=n6BW
z+TF~2{i65)KWSe3AjO+qPrf{oq*8fEkz;{HPg|s#Y^)ub3-V%#<>H{Uoh<#v{7{wx
zrV|Qp?0fbZCsXO+OGuwj090{SQy{4p+iJfA_5~rx=UyMxnkZiGwbL?u)D^oI8Q=Hw
za>Xf=^r1Ho%F;rkh^*m<W9~AQ5tD-xm$z1+%7@qU95&THRFA6{+F##38(3LEVRr5(
z<y3q}@~o|WsiG^aBANX)N2M=JsBvn=<%^#9hRdL0)ilh3jl(hwmFx?zkBCwc(yA8I
z%xX}HQm9rKs84G1w3nr6@>fZ>6Jy{K)>V>qrKR^w*N31o#fo7VNk~GjMkr-rX_Caq
zzW9M=la`%Pk?*QP2cVG~U!atO=cT?Kjy4&MHA8*u1S8D4IF=kqC}didFYA*^;G+~l
zSTx9gw>@skmkJhR<+8q2k6Vs<yS*#mC;L?8ph;ylWyr&jZ=@Q}gqtkGAH?qHL@}aU
zcs0Z0Jo1==>pTk*US1!`KBhsu{?f_*4UL+Po;2}hp@A7fz#I<_yVvg=3V8PmXLi&j
z+T+;7rAFK=XwpAaltp|8G?=uUydyL-6$C3M9g)qRC+ZgK>71|&$jHh9N8%qq($ujo
zNIix#D^IP`EwGr*rytSQ5`8Fg;w>(meXAxT#h@2oteVtoSIvg4m@0_gnr-}D7Qv)j
zgk4IP&`?Q@@lk<F*+YOVGa0nOA>3%;!C#b2u=qfNv0+a{hWN1rwt5Y?(4djzk%|cg
zwwlcAmAst#`728Z^NTWS(k3-qzflP{SbOc_p<4!dpV7;hs6f1p)?91OF<Yb^8&{yW
zY<Qt<RzMlD*pMR(y(vmny;b|%E1QdQ1W(?9_k%!K=1bn(8NAuZ)T)#S@plwSq>idj
zZ6C&T^g%c)nu_$$RdL<`zc(x(cw2!og`%FSUj$zDp$AJfB2sN;eXCzQeWvf^2`<<7
zH8OjQPZ}|^LYn#C-ymfYo}stADIB7N`35NF$m-QwF`mjym+St;z>i{LDoM?)9wOcH
zDv5H58W2ci=Z;;|N`O?R!P0aIyY|F~ba%dJ3;~|CGO)vMAd2Jej5F%3u*h~<Z=;H_
z^2M6RKR)ZO=7et!hY!($p8;hI|093?*NLwGZs~tt58Ue)KztVC$~Cg{;<DTo7$7zB
zSQda$dN-421OSBYrqI}CWx9rCD+U~;QZ{HY`?$gdn$XJB8cNMZYbCK0mkamhdB5yJ
z5)zLTJgTUVS|xdgym+*<!o27Ng_H{kJZN}}uQO-jH_IaS-?jUe;zRRni&z}_MPVbT
zP@1iI)tK`^z99sc#0NY9;SZ%J%5<-%7YFsyHfCEjmVWRRJ>ucr5Of*T^9>#er?XJ1
zZ=^A6xAS_zx*^DJ%m-#ZHK~44^F%v)Rex%M$Do<)Qs~WttyJUqzLS3Q$B+L$<>4M2
zy5Xmy-0184roPRluEz5^RX1b2CPuHZ%acg&7ewyT9ItV7$fJ>GRbs|R&7#!=o4^rt
z;$q)?%iBHn$hGZ*PVFa(Y-8968GLy9946Gq7O8_5sUy7oYHgO1MkSL=mJq0dm;Rix
zzI?l?%(}6n$cQ+hA~`Y<2hD+(7U$Xu)<&i%2--iX!AVnR4QQ;3wVJKc9FEjVCMT(!
zeH;BoW@&Lm1U#xX`bE#NoU4zs?shAkThDSZ2>}$Q7=Js^)B9ij-T&6_f7LtxpRfBL
DcMQD(

literal 0
HcmV?d00001

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 6349df2e30..d047495dc9 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -163,6 +163,7 @@ import { focusPrev, focusNext } from '@/scripts/focus.js';
 import { checkWordMute } from '@/scripts/check-word-mute.js';
 import { userPage } from '@/filters/user.js';
 import * as os from '@/os.js';
+import * as sound from '@/scripts/sound.js';
 import { defaultStore, noteViewInterruptors } from '@/store.js';
 import { reactionPicker } from '@/scripts/reaction-picker.js';
 import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
@@ -336,6 +337,8 @@ function react(viaKeyboard = false): void {
 	pleaseLogin();
 	showMovedDialog();
 	if (appearNote.reactionAcceptance === 'likeOnly') {
+		sound.play('reaction');
+
 		if (props.mock) {
 			return;
 		}
@@ -354,6 +357,8 @@ function react(viaKeyboard = false): void {
 	} else {
 		blur();
 		reactionPicker.show(reactButton.value, reaction => {
+			sound.play('reaction');
+
 			if (props.mock) {
 				emit('reaction', reaction);
 				return;
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index d1bc3f676f..d8089ac36f 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -210,6 +210,7 @@ import { checkWordMute } from '@/scripts/check-word-mute.js';
 import { userPage } from '@/filters/user.js';
 import { notePage } from '@/filters/note.js';
 import * as os from '@/os.js';
+import * as sound from '@/scripts/sound.js';
 import { defaultStore, noteViewInterruptors } from '@/store.js';
 import { reactionPicker } from '@/scripts/reaction-picker.js';
 import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
@@ -369,6 +370,8 @@ function react(viaKeyboard = false): void {
 	pleaseLogin();
 	showMovedDialog();
 	if (appearNote.reactionAcceptance === 'likeOnly') {
+		sound.play('reaction');
+
 		os.api('notes/reactions/create', {
 			noteId: appearNote.id,
 			reaction: '❤️',
@@ -383,6 +386,8 @@ function react(viaKeyboard = false): void {
 	} else {
 		blur();
 		reactionPicker.show(reactButton.value, reaction => {
+			sound.play('reaction');
+
 			os.api('notes/reactions/create', {
 				noteId: appearNote.id,
 				reaction: reaction,
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 9a107c3674..65a5c2374e 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -28,6 +28,7 @@ import MkReactionEffect from '@/components/MkReactionEffect.vue';
 import { claimAchievement } from '@/scripts/achievements.js';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
+import * as sound from '@/scripts/sound.js';
 
 const props = defineProps<{
 	reaction: string;
@@ -59,6 +60,10 @@ async function toggleReaction() {
 		});
 		if (confirm.canceled) return;
 
+		if (oldReaction !== props.reaction) {
+			sound.play('reaction');
+		}
+
 		if (mock) {
 			emit('reactionToggled', props.reaction, (props.count - 1));
 			return;
@@ -75,6 +80,8 @@ async function toggleReaction() {
 			}
 		});
 	} else {
+		sound.play('reaction');
+
 		if (mock) {
 			emit('reactionToggled', props.reaction, (props.count + 1));
 			return;
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index cd1707a594..244bb1e0e2 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -38,7 +38,7 @@ import { defaultStore } from '@/store.js';
 
 const masterVolume = computed(defaultStore.makeGetterSetter('sound_masterVolume'));
 
-const soundsKeys = ['note', 'noteMy', 'notification', 'antenna', 'channel'] as const;
+const soundsKeys = ['note', 'noteMy', 'notification', 'antenna', 'channel', 'reaction'] as const;
 
 const sounds = ref<Record<typeof soundsKeys[number], Ref<any>>>({
 	note: defaultStore.reactiveState.sound_note,
@@ -46,6 +46,7 @@ const sounds = ref<Record<typeof soundsKeys[number], Ref<any>>>({
 	notification: defaultStore.reactiveState.sound_notification,
 	antenna: defaultStore.reactiveState.sound_antenna,
 	channel: defaultStore.reactiveState.sound_channel,
+	reaction: defaultStore.reactiveState.sound_reaction,
 });
 
 async function updated(type: keyof typeof sounds.value, sound) {
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 2b604bd98a..4d7ef9bdee 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -38,6 +38,8 @@ export const soundsTypes = [
 	'syuilo/waon',
 	'syuilo/popo',
 	'syuilo/triple',
+	'syuilo/bubble1',
+	'syuilo/bubble2',
 	'syuilo/poi1',
 	'syuilo/poi2',
 	'syuilo/pirori',
@@ -77,7 +79,7 @@ export async function loadAudio(file: string, useCache = true) {
 	return audioBuffer;
 }
 
-export function play(type: 'noteMy' | 'note' | 'antenna' | 'channel' | 'notification') {
+export function play(type: 'noteMy' | 'note' | 'antenna' | 'channel' | 'notification' | 'reaction') {
 	const sound = defaultStore.state[`sound_${type}`];
 	if (_DEV_) console.log('play', type, sound);
 	if (sound.type == null) return;
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 12660e9e8d..f2ed4e7c0b 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -411,6 +411,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: { type: 'syuilo/square-pico', volume: 1 },
 	},
+	sound_reaction: {
+		where: 'device',
+		default: { type: 'syuilo/bubble2', volume: 1 },
+	},
 }));
 
 // TODO: 他のタブと永続化されたstateを同期

From 755ca9785779eaa120c7e4372b61979362e7ceb5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 26 Nov 2023 13:20:46 +0900
Subject: [PATCH 049/435] =?UTF-8?q?fix(frontend):=20=E9=80=9A=E7=9F=A5?=
 =?UTF-8?q?=E9=9F=B3=E3=81=8C=E3=81=BB=E3=81=BC=E5=90=8C=E6=99=82=E3=81=AB?=
 =?UTF-8?q?=E9=B3=B4=E3=81=A3=E3=81=9F=E5=A0=B4=E5=90=88=E3=81=AF=E5=86=8D?=
 =?UTF-8?q?=E7=94=9F=E3=82=92=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF=E3=81=99?=
 =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=EF=BC=88=E9=9F=B3=E5=89=B2?=
 =?UTF-8?q?=E3=82=8C=E9=98=B2=E6=AD=A2=EF=BC=89=20(#12433)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (fix) 通知音がダブって音割れしないように

* Update Changelog
---
 CHANGELOG.md                           |  1 +
 packages/frontend/src/scripts/sound.ts | 12 ++++++++++--
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f02d462b2..7a1bdd233d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
 - Fix: プロフィールの「ファイル」にセンシティブな画像がある際のデザインを修正
+- Fix: 一度に大量の通知が入った際に通知音が音割れする問題を修正
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 4d7ef9bdee..47ec4171af 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -7,6 +7,7 @@ import { defaultStore } from '@/store.js';
 
 const ctx = new AudioContext();
 const cache = new Map<string, AudioBuffer>();
+let canPlay = true;
 
 export const soundsTypes = [
 	null,
@@ -82,8 +83,15 @@ export async function loadAudio(file: string, useCache = true) {
 export function play(type: 'noteMy' | 'note' | 'antenna' | 'channel' | 'notification' | 'reaction') {
 	const sound = defaultStore.state[`sound_${type}`];
 	if (_DEV_) console.log('play', type, sound);
-	if (sound.type == null) return;
-	playFile(sound.type, sound.volume);
+	if (sound.type == null || !canPlay) return;
+
+	canPlay = false;
+	playFile(sound.type, sound.volume).then(() => {
+		// ごく短時間に音が重複しないように
+		setTimeout(() => {
+			canPlay = true;
+		}, 25);
+	});
 }
 
 export async function playFile(file: string, volume: number) {

From ccb951f11e8cc3c884eef799bef82d09f138d28c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Sun, 26 Nov 2023 14:38:34 +0900
Subject: [PATCH 050/435] chore: create AudioContext when it is needed (#12460)

---
 packages/frontend/src/scripts/sound.ts           | 5 ++++-
 packages/frontend/src/widgets/WidgetJobQueue.vue | 5 ++++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 47ec4171af..d28d629227 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -5,7 +5,7 @@
 
 import { defaultStore } from '@/store.js';
 
-const ctx = new AudioContext();
+let ctx: AudioContext;
 const cache = new Map<string, AudioBuffer>();
 let canPlay = true;
 
@@ -65,6 +65,9 @@ export const soundsTypes = [
 ] as const;
 
 export async function loadAudio(file: string, useCache = true) {
+	if (ctx == null) {
+		ctx = new AudioContext();
+	}
 	if (useCache && cache.has(file)) {
 		return cache.get(file)!;
 	}
diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue
index fa82997570..8c990e8e49 100644
--- a/packages/frontend/src/widgets/WidgetJobQueue.vue
+++ b/packages/frontend/src/widgets/WidgetJobQueue.vue
@@ -58,6 +58,7 @@ import { useStream } from '@/stream.js';
 import number from '@/filters/number.js';
 import * as sound from '@/scripts/sound.js';
 import { deepClone } from '@/scripts/clone.js';
+import { defaultStore } from '@/store.js';
 
 const name = 'jobQueue';
 
@@ -102,7 +103,9 @@ const prev = reactive({} as typeof current);
 let jammedAudioBuffer: AudioBuffer | null = $ref(null);
 let jammedSoundNodePlaying: boolean = $ref(false);
 
-sound.loadAudio('syuilo/queue-jammed').then(buf => jammedAudioBuffer = buf);
+if (defaultStore.state.sound_masterVolume) {
+	sound.loadAudio('syuilo/queue-jammed').then(buf => jammedAudioBuffer = buf);
+}
 
 for (const domain of ['inbox', 'deliver']) {
 	prev[domain] = deepClone(current[domain]);

From c9503da8f8e4a67cbe7ba88722b3c3893f9ab4e4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sun, 26 Nov 2023 16:12:02 +0900
Subject: [PATCH 051/435] =?UTF-8?q?=E3=82=B5=E3=82=A6=E3=83=B3=E3=83=89?=
 =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E3=81=AB=E3=80=8C=E3=82=B5=E3=82=A6=E3=83=B3?=
 =?UTF-8?q?=E3=83=89=E3=82=92=E5=87=BA=E5=8A=9B=E3=81=97=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E3=80=8D=E3=81=A8=E3=80=8CMisskey=E3=81=8C=E3=82=A2=E3=82=AF?=
 =?UTF-8?q?=E3=83=86=E3=82=A3=E3=83=96=E3=81=AA=E6=99=82=E3=81=AE=E3=81=BF?=
 =?UTF-8?q?=E3=82=B5=E3=82=A6=E3=83=B3=E3=83=89=E3=82=92=E5=87=BA=E5=8A=9B?=
 =?UTF-8?q?=E3=81=99=E3=82=8B=E3=80=8D=E3=82=92=E8=BF=BD=E5=8A=A0=20(#1234?=
 =?UTF-8?q?2)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
---
 CHANGELOG.md                                    |  1 +
 locales/index.d.ts                              |  2 ++
 locales/ja-JP.yml                               |  2 ++
 packages/frontend/src/pages/settings/sounds.vue |  9 +++++++++
 packages/frontend/src/scripts/sound.ts          | 17 ++++++++++++++++-
 packages/frontend/src/store.ts                  |  8 ++++++++
 6 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7a1bdd233d..bf3fecb5b8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -54,6 +54,7 @@
 	- 例: `$[unixtime 1701356400]`
 - Enhance: プラグインでエラーが発生した場合のハンドリングを強化
 - Enhance: 細かなUIのブラッシュアップ
+- Enhance: サウンド設定に「サウンドを出力しない」と「Misskeyがアクティブな時のみサウンドを出力する」を追加
 - Fix: 効果音が再生されるとデバイスで再生している動画や音声が停止する問題を修正 #12339
 - Fix: デッキに表示されたチャンネルの表示先チャンネルを切り替えた際、即座に反映されない問題を修正 #12236
 - Fix: プラグインでノートの表示を書き換えられない問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 6e9fe311f1..6097ae130e 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -547,6 +547,8 @@ export interface Locale {
     "popout": string;
     "volume": string;
     "masterVolume": string;
+    "notUseSound": string;
+    "useSoundOnlyWhenActive": string;
     "details": string;
     "chooseEmoji": string;
     "unableToProcess": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0b051b6190..1f6695b3e3 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -544,6 +544,8 @@ showInPage: "ページで表示"
 popout: "ポップアウト"
 volume: "音量"
 masterVolume: "マスター音量"
+notUseSound: "サウンドを出力しない"
+useSoundOnlyWhenActive: "Misskeyがアクティブな時のみサウンドを出力する"
 details: "詳細"
 chooseEmoji: "絵文字を選択"
 unableToProcess: "操作を完了できません"
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index 244bb1e0e2..05e4b0d14c 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -5,6 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div class="_gaps_m">
+	<MkSwitch v-model="notUseSound">
+		<template #label>{{ i18n.ts.notUseSound }}</template>
+	</MkSwitch>
+	<MkSwitch v-model="useSoundOnlyWhenActive">
+		<template #label>{{ i18n.ts.useSoundOnlyWhenActive }}</template>
+	</MkSwitch>
 	<MkRange v-model="masterVolume" :min="0" :max="1" :step="0.05" :textConverter="(v) => `${Math.floor(v * 100)}%`">
 		<template #label>{{ i18n.ts.masterVolume }}</template>
 	</MkRange>
@@ -35,7 +41,10 @@ import MkFolder from '@/components/MkFolder.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { defaultStore } from '@/store.js';
+import MkSwitch from '@/components/MkSwitch.vue';
 
+const notUseSound = computed(defaultStore.makeGetterSetter('sound_notUseSound'));
+const useSoundOnlyWhenActive = computed(defaultStore.makeGetterSetter('sound_useSoundOnlyWhenActive'));
 const masterVolume = computed(defaultStore.makeGetterSetter('sound_masterVolume'));
 
 const soundsKeys = ['note', 'noteMy', 'notification', 'antenna', 'channel', 'reaction'] as const;
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index d28d629227..a3cddba1f4 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -104,7 +104,7 @@ export async function playFile(file: string, volume: number) {
 
 export function createSourceNode(buffer: AudioBuffer, volume: number) : AudioBufferSourceNode | null {
 	const masterVolume = defaultStore.state.sound_masterVolume;
-	if (masterVolume === 0 || volume === 0) {
+	if (isMute() || masterVolume === 0 || volume === 0) {
 		return null;
 	}
 
@@ -117,3 +117,18 @@ export function createSourceNode(buffer: AudioBuffer, volume: number) : AudioBuf
 
 	return soundSource;
 }
+
+export function isMute(): boolean {
+	if (defaultStore.state.sound_notUseSound) {
+		// サウンドを出力しない
+		return true;
+	}
+
+	// noinspection RedundantIfStatementJS
+	if (defaultStore.state.sound_useSoundOnlyWhenActive && document.visibilityState === 'hidden') {
+		// ブラウザがアクティブな時のみサウンドを出力する
+		return true;
+	}
+
+	return false;
+}
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index f2ed4e7c0b..40fb1dde76 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -391,6 +391,14 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: 0.3,
 	},
+	sound_notUseSound: {
+		where: 'device',
+		default: false,
+	},
+	sound_useSoundOnlyWhenActive: {
+		where: 'device',
+		default: false,
+	},
 	sound_note: {
 		where: 'device',
 		default: { type: 'syuilo/n-aec', volume: 1 },

From d60f645d1dc731d7ef6eff16081d2b5615412690 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Sun, 26 Nov 2023 16:15:24 +0900
Subject: [PATCH 052/435] chore(frontend/MkMediaVideo): loop and autoplay
 silent videos (#12392)

---
 packages/frontend/src/components/MkMediaVideo.vue | 7 +++++++
 packages/frontend/src/scripts/media-has-audio.ts  | 9 +++++++++
 2 files changed, 16 insertions(+)
 create mode 100644 packages/frontend/src/scripts/media-has-audio.ts

diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 43c64b4c85..b6e8f1ff22 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -37,6 +37,7 @@ import * as Misskey from 'misskey-js';
 import bytes from '@/filters/bytes.js';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
+import hasAudio from '@/scripts/media-has-audio.js';
 
 const props = defineProps<{
 	video: Misskey.entities.DriveFile;
@@ -49,6 +50,12 @@ const videoEl = shallowRef<HTMLVideoElement>();
 watch(videoEl, () => {
 	if (videoEl.value) {
 		videoEl.value.volume = 0.3;
+		hasAudio(videoEl.value).then(had => {
+			if (!had) {
+				videoEl.value.loop = videoEl.value.muted = true;
+				videoEl.value.play();
+			}
+		});
 	}
 });
 </script>
diff --git a/packages/frontend/src/scripts/media-has-audio.ts b/packages/frontend/src/scripts/media-has-audio.ts
new file mode 100644
index 0000000000..3421a38a76
--- /dev/null
+++ b/packages/frontend/src/scripts/media-has-audio.ts
@@ -0,0 +1,9 @@
+export default async function hasAudio(media: HTMLMediaElement) {
+	const cloned = media.cloneNode() as HTMLMediaElement;
+	cloned.muted = (cloned as typeof cloned & Partial<HTMLVideoElement>).playsInline = true;
+	cloned.play();
+	await new Promise((resolve) => cloned.addEventListener('playing', resolve));
+	const result = !!(cloned as any).audioTracks?.length || (cloned as any).mozHasAudio || !!(cloned as any).webkitAudioDecodedByteCount;
+	cloned.remove();
+	return result;
+}

From 780b120c64a8554cfe0fc44962e36124d3f45210 Mon Sep 17 00:00:00 2001
From: ragujp <fumon@shimadate.com>
Date: Sun, 26 Nov 2023 23:35:53 +0900
Subject: [PATCH 053/435] fix: wake lock error in safari etc (#12464)

---
 packages/frontend/src/boot/common.ts | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index 12bb56a874..594fe64230 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -204,12 +204,16 @@ export async function common(createVue: () => App<Element>) {
 
 	if (defaultStore.state.keepScreenOn) {
 		if ('wakeLock' in navigator) {
-			navigator.wakeLock.request('screen');
-
-			document.addEventListener('visibilitychange', async () => {
-				if (document.visibilityState === 'visible') {
-					navigator.wakeLock.request('screen');
-				}
+			navigator.wakeLock.request('screen')
+			.then(() => {
+				document.addEventListener('visibilitychange', async () => {
+					if (document.visibilityState === 'visible') {
+						navigator.wakeLock.request('screen');
+					}
+				});
+			})
+			.catch(() => {
+				// If Permission fails on an AppleDevice such as Safari
 			});
 		}
 	}

From 01d06e7121c907dca82a2d402b3e86124793c945 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Mon, 27 Nov 2023 08:06:47 +0900
Subject: [PATCH 054/435] Fix a frontend testing script (#12471)

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

diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index c7736f7ac7..9156ab0391 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -9,7 +9,7 @@
 		"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
 		"build-storybook": "pnpm build-storybook-pre && storybook build",
 		"chromatic": "chromatic",
-		"test": "vitest --run",
+		"test": "vitest --run --globals",
 		"test-and-coverage": "vitest --run --coverage --globals",
 		"typecheck": "vue-tsc --noEmit",
 		"eslint": "eslint --quiet \"src/**/*.{ts,vue}\"",

From 6acaded89810fa24f56b7910265913a7e5d151af Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 27 Nov 2023 14:47:25 +0900
Subject: [PATCH 055/435] fix: error can be happened if animation is on and
 hard mute matches (#12474)

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

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index d047495dc9..4916d59d00 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -142,6 +142,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</template>
 	</I18n>
 </div>
+<div v-else>
+	<!--
+	  MkDateSeparatedList uses TransitionGroup which requires single element in the child elements
+	  so MkNote create empty div instead of no elements
+	-->
+</div>
 </template>
 
 <script lang="ts" setup>

From 8f1da036f42b99220600cb38a93bd7c0a4158a7c Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 27 Nov 2023 15:29:39 +0900
Subject: [PATCH 056/435] style: fix lint error of 6acaded8 (#12476)

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

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 4916d59d00..04fe8d52fb 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -144,8 +144,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 </div>
 <div v-else>
 	<!--
-	  MkDateSeparatedList uses TransitionGroup which requires single element in the child elements
-	  so MkNote create empty div instead of no elements
+		MkDateSeparatedList uses TransitionGroup which requires single element in the child elements
+		so MkNote create empty div instead of no elements
 	-->
 </div>
 </template>

From 2a451ebb572531f5dfa34a3f863e2b15d29e7355 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: Mon, 27 Nov 2023 17:33:42 +0900
Subject: [PATCH 057/435] =?UTF-8?q?enhance(frontend):=20=E9=80=9A=E7=9F=A5?=
 =?UTF-8?q?=E9=9F=B3=E3=81=AB=E3=83=89=E3=83=A9=E3=82=A4=E3=83=96=E3=81=AE?=
 =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92=E4=BD=BF=E7=94=A8?=
 =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1244?=
 =?UTF-8?q?7)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (enhance) サウンドにドライブのファイルを使用できるように

* Update Changelog

* fix

* fix design

* fix design

* Update store.ts

* (fix) ファイル名表示

* refactor

* (refactor) better types

* operationTypeとsoundTypeの混同を防止

* (refactor)

* (fix)

* enhance jsdoc

* driveFile -> _driveFile_
---
 CHANGELOG.md                                  |   1 +
 locales/index.d.ts                            |   8 +
 locales/ja-JP.yml                             |   8 +
 .../src/pages/settings/sounds.sound.vue       | 141 ++++++++++++++++--
 .../frontend/src/pages/settings/sounds.vue    |  28 +++-
 packages/frontend/src/scripts/sound.ts        | 124 +++++++++++++--
 packages/frontend/src/store.ts                |  29 +++-
 .../frontend/src/widgets/WidgetJobQueue.vue   |   8 +-
 8 files changed, 311 insertions(+), 36 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf3fecb5b8..d59307caae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@
 - Enhance: 絵文字のオートコンプリート機能強化 #12364
 - Enhance: ユーザーのRawデータを表示するページが復活
 - Enhance: リアクション選択時に音を鳴らせるように
+- Enhance: サウンドにドライブのファイルを使用できるように
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 6097ae130e..64ee30410e 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1947,6 +1947,14 @@ export interface Locale {
         "channel": string;
         "reaction": string;
     };
+    "_soundSettings": {
+        "driveFile": string;
+        "driveFileWarn": string;
+        "driveFileTypeWarn": string;
+        "driveFileTypeWarnDescription": string;
+        "driveFileDurationWarn": string;
+        "driveFileDurationWarnDescription": string;
+    };
     "_ago": {
         "future": string;
         "justNow": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 1f6695b3e3..f4daefa978 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1852,6 +1852,14 @@ _sfx:
   channel: "チャンネル通知"
   reaction: "リアクション選択時"
 
+_soundSettings:
+  driveFile: "ドライブの音声を使用"
+  driveFileWarn: "ドライブのファイルを選択してください"
+  driveFileTypeWarn: "このファイルは対応していません"
+  driveFileTypeWarnDescription: "音声ファイルを選択してください"
+  driveFileDurationWarn: "音声が長すぎます"
+  driveFileDurationWarnDescription: "長い音声を使用するとMisskeyの使用に支障をきたす可能性があります。それでも続行しますか?"
+
 _ago:
   future: "未来"
   justNow: "たった今"
diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue
index 08a923e104..2f4cd1be2c 100644
--- a/packages/frontend/src/pages/settings/sounds.sound.vue
+++ b/packages/frontend/src/pages/settings/sounds.sound.vue
@@ -7,8 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 <div class="_gaps_m">
 	<MkSelect v-model="type">
 		<template #label>{{ i18n.ts.sound }}</template>
-		<option v-for="x in soundsTypes" :key="x" :value="x">{{ x == null ? i18n.ts.none : x }}</option>
+		<option v-for="x in soundsTypes" :key="x ?? 'null'" :value="x">{{ getSoundTypeName(x) }}</option>
 	</MkSelect>
+	<div v-if="type === '_driveFile_'" :class="$style.fileSelectorRoot">
+		<MkButton :class="$style.fileSelectorButton" inline rounded primary @click="selectSound">{{ i18n.ts.selectFile }}</MkButton>
+		<div :class="['_nowrap', !fileUrl && $style.fileNotSelected]">{{ friendlyFileName }}</div>
+	</div>
 	<MkRange v-model="volume" :min="0" :max="1" :step="0.05" :textConverter="(v) => `${Math.floor(v * 100)}%`">
 		<template #label>{{ i18n.ts.volume }}</template>
 	</MkRange>
@@ -21,30 +25,149 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
+import type { SoundType } from '@/scripts/sound.js';
 import MkSelect from '@/components/MkSelect.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkRange from '@/components/MkRange.vue';
 import { i18n } from '@/i18n.js';
-import { playFile, soundsTypes } from '@/scripts/sound.js';
+import * as os from '@/os.js';
+import { playFile, soundsTypes, getSoundDuration } from '@/scripts/sound.js';
+import { selectFile } from '@/scripts/select-file.js';
 
 const props = defineProps<{
-	type: string;
+	type: SoundType;
+	fileId?: string;
+	fileUrl?: string;
 	volume: number;
 }>();
 
 const emit = defineEmits<{
-	(ev: 'update', result: { type: string; volume: number; }): void;
+	(ev: 'update', result: { type: SoundType; fileId?: string; fileUrl?: string; volume: number; }): void;
 }>();
 
-let type = $ref(props.type);
-let volume = $ref(props.volume);
+const type = ref<SoundType>(props.type);
+const fileId = ref(props.fileId);
+const fileUrl = ref(props.fileUrl);
+const fileName = ref<string>('');
+const volume = ref(props.volume);
+
+if (type.value === '_driveFile_' && fileId.value) {
+	const apiRes = await os.api('drive/files/show', {
+		fileId: fileId.value,
+	});
+	fileName.value = apiRes.name;
+}
+
+function getSoundTypeName(f: SoundType): string {
+	switch (f) {
+		case null:
+			return i18n.ts.none;
+		case '_driveFile_':
+			return i18n.ts._soundSettings.driveFile;
+		default:
+			return f;
+	}
+}
+
+const friendlyFileName = computed<string>(() => {
+	if (fileName.value) {
+		return fileName.value;
+	}
+	if (fileUrl.value) {
+		return fileUrl.value;
+	}
+
+	return i18n.ts._soundSettings.driveFileWarn;
+});
+
+function selectSound(ev) {
+	selectFile(ev.currentTarget ?? ev.target, i18n.ts._soundSettings.driveFile).then(async (file) => {
+		if (!file.type.startsWith('audio')) {
+			os.alert({
+				type: 'warning',
+				title: i18n.ts._soundSettings.driveFileTypeWarn,
+				text: i18n.ts._soundSettings.driveFileTypeWarnDescription,
+			});
+			return;
+		}
+		const duration = await getSoundDuration(file.url);
+		if (duration >= 2000) {
+			const { canceled } = await os.confirm({
+				type: 'warning',
+				title: i18n.ts._soundSettings.driveFileDurationWarn,
+				text: i18n.ts._soundSettings.driveFileDurationWarnDescription,
+				okText: i18n.ts.continue,
+				cancelText: i18n.ts.cancel,
+			});
+			if (canceled) return;
+		}
+
+		fileUrl.value = file.url;
+		fileName.value = file.name;
+		fileId.value = file.id;
+	});
+}
 
 function listen() {
-	playFile(type, volume);
+	if (type.value === '_driveFile_' && (!fileUrl.value || !fileId.value)) {
+		os.alert({
+			type: 'warning',
+			text: i18n.ts._soundSettings.driveFileWarn,
+		});
+		return;
+	}
+
+	playFile(type.value === '_driveFile_' ? {
+		type: '_driveFile_',
+		fileId: fileId.value as string,
+		fileUrl: fileUrl.value as string,
+		volume: volume.value,
+	} : {
+		type: type.value,
+		volume: volume.value,
+	});
 }
 
 function save() {
-	emit('update', { type, volume });
+	if (type.value === '_driveFile_' && !fileUrl.value) {
+		os.alert({
+			type: 'warning',
+			text: i18n.ts._soundSettings.driveFileWarn,
+		});
+		return;
+	}
+
+	if (type.value !== '_driveFile_') {
+		fileUrl.value = undefined;
+		fileName.value = '';
+		fileId.value = undefined;
+	}
+
+	emit('update', {
+		type: type.value,
+		fileId: fileId.value,
+		fileUrl: fileUrl.value,
+		volume: volume.value,
+	});
+
+	os.success();
 }
 </script>
+
+<style module>
+.fileSelectorRoot {
+	display: flex;
+	align-items: center;
+	gap: 8px;
+}
+
+.fileSelectorButton {
+	flex-shrink: 0;
+}
+
+.fileNotSelected {
+	font-weight: 700;
+	color: var(--infoWarnFg);
+}
+</style>
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index 05e4b0d14c..e549901f05 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -18,11 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<FormSection>
 		<template #label>{{ i18n.ts.sounds }}</template>
 		<div class="_gaps_s">
-			<MkFolder v-for="type in soundsKeys" :key="type">
+			<MkFolder v-for="type in operationTypes" :key="type">
 				<template #label>{{ i18n.t('_sfx.' + type) }}</template>
-				<template #suffix>{{ sounds[type].type ?? i18n.ts.none }}</template>
+				<template #suffix>{{ getSoundTypeName(sounds[type].type) }}</template>
 
-				<XSound :type="sounds[type].type" :volume="sounds[type].volume" @update="(res) => updated(type, res)"/>
+				<XSound :type="sounds[type].type" :volume="sounds[type].volume" :fileId="sounds[type].fileId" :fileUrl="sounds[type].fileUrl" @update="(res) => updated(type, res)"/>
 			</MkFolder>
 		</div>
 	</FormSection>
@@ -33,6 +33,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { Ref, computed, ref } from 'vue';
+import type { SoundType, OperationType } from '@/scripts/sound.js';
+import type { SoundStore } from '@/store.js';
 import XSound from './sounds.sound.vue';
 import MkRange from '@/components/MkRange.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -40,6 +42,7 @@ import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { operationTypes } from '@/scripts/sound.js';
 import { defaultStore } from '@/store.js';
 import MkSwitch from '@/components/MkSwitch.vue';
 
@@ -47,9 +50,7 @@ const notUseSound = computed(defaultStore.makeGetterSetter('sound_notUseSound'))
 const useSoundOnlyWhenActive = computed(defaultStore.makeGetterSetter('sound_useSoundOnlyWhenActive'));
 const masterVolume = computed(defaultStore.makeGetterSetter('sound_masterVolume'));
 
-const soundsKeys = ['note', 'noteMy', 'notification', 'antenna', 'channel', 'reaction'] as const;
-
-const sounds = ref<Record<typeof soundsKeys[number], Ref<any>>>({
+const sounds = ref<Record<OperationType, Ref<SoundStore>>>({
 	note: defaultStore.reactiveState.sound_note,
 	noteMy: defaultStore.reactiveState.sound_noteMy,
 	notification: defaultStore.reactiveState.sound_notification,
@@ -58,9 +59,22 @@ const sounds = ref<Record<typeof soundsKeys[number], Ref<any>>>({
 	reaction: defaultStore.reactiveState.sound_reaction,
 });
 
+function getSoundTypeName(f: SoundType): string {
+	switch (f) {
+		case null:
+			return i18n.ts.none;
+		case '_driveFile_':
+			return i18n.ts._soundSettings.driveFile;
+		default:
+			return f;
+	}
+}
+
 async function updated(type: keyof typeof sounds.value, sound) {
-	const v = {
+	const v: SoundStore = {
 		type: sound.type,
+		fileId: sound.fileId,
+		fileUrl: sound.fileUrl,
 		volume: sound.volume,
 	};
 
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index a3cddba1f4..a4c6967d18 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -3,14 +3,22 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import type { SoundStore } from '@/store.js';
 import { defaultStore } from '@/store.js';
+import * as os from '@/os.js';
 
 let ctx: AudioContext;
 const cache = new Map<string, AudioBuffer>();
 let canPlay = true;
 
 export const soundsTypes = [
+	// 音声なし
 	null,
+
+	// ドライブの音声
+	'_driveFile_',
+
+	// プリインストール
 	'syuilo/n-aec',
 	'syuilo/n-aec-4va',
 	'syuilo/n-aec-4vb',
@@ -64,32 +72,96 @@ export const soundsTypes = [
 	'noizenecio/kick_gaba7',
 ] as const;
 
-export async function loadAudio(file: string, useCache = true) {
+export const operationTypes = [
+	'noteMy',
+	'note',
+	'antenna',
+	'channel',
+	'notification',
+	'reaction',
+] as const;
+
+/** サウンドの種類 */
+export type SoundType = typeof soundsTypes[number];
+
+/** スプライトの種類 */
+export type OperationType = typeof operationTypes[number];
+
+/**
+ * 音声を読み込む
+ * @param soundStore サウンド設定
+ * @param options `useCache`: デフォルトは`true` 一度再生した音声はキャッシュする
+ */
+export async function loadAudio(soundStore: SoundStore, options?: { useCache?: boolean; }) {
+	if (_DEV_) console.log('loading audio. opts:', options);
+	// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+	if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) {
+		return;
+	}
+	// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 	if (ctx == null) {
 		ctx = new AudioContext();
 	}
-	if (useCache && cache.has(file)) {
-		return cache.get(file)!;
+	if (options?.useCache ?? true) {
+		if (soundStore.type === '_driveFile_' && cache.has(soundStore.fileId)) {
+			if (_DEV_) console.log('use cache');
+			return cache.get(soundStore.fileId) as AudioBuffer;
+		} else if (cache.has(soundStore.type)) {
+			if (_DEV_) console.log('use cache');
+			return cache.get(soundStore.type) as AudioBuffer;
+		}
+	}
+
+	let response: Response;
+
+	if (soundStore.type === '_driveFile_') {
+		try {
+			response = await fetch(soundStore.fileUrl);
+		} catch (err) {
+			try {
+				// URLが変わっている可能性があるのでドライブ側からURLを取得するフォールバック
+				const apiRes = await os.api('drive/files/show', {
+					fileId: soundStore.fileId,
+				});
+				response = await fetch(apiRes.url);
+			} catch (fbErr) {
+				// それでも無理なら諦める
+				return;
+			}
+		}
+	} else {
+		try {
+			response = await fetch(`/client-assets/sounds/${soundStore.type}.mp3`);
+		} catch (err) {
+			return;
+		}
 	}
 
-	const response = await fetch(`/client-assets/sounds/${file}.mp3`);
 	const arrayBuffer = await response.arrayBuffer();
 	const audioBuffer = await ctx.decodeAudioData(arrayBuffer);
 
-	if (useCache) {
-		cache.set(file, audioBuffer);
+	if (options?.useCache ?? true) {
+		if (soundStore.type === '_driveFile_') {
+			cache.set(soundStore.fileId, audioBuffer);
+		} else {
+			cache.set(soundStore.type, audioBuffer);
+		}
 	}
 
 	return audioBuffer;
 }
 
-export function play(type: 'noteMy' | 'note' | 'antenna' | 'channel' | 'notification' | 'reaction') {
-	const sound = defaultStore.state[`sound_${type}`];
-	if (_DEV_) console.log('play', type, sound);
+/**
+ * 既定のスプライトを再生する
+ * @param type スプライトの種類を指定
+ */
+export function play(operationType: OperationType) {
+	const sound = defaultStore.state[`sound_${operationType}`];
+	if (_DEV_) console.log('play', operationType, sound);
 	if (sound.type == null || !canPlay) return;
 
 	canPlay = false;
-	playFile(sound.type, sound.volume).then(() => {
+	playFile(sound).then(() => {
 		// ごく短時間に音が重複しないように
 		setTimeout(() => {
 			canPlay = true;
@@ -97,9 +169,14 @@ export function play(type: 'noteMy' | 'note' | 'antenna' | 'channel' | 'notifica
 	});
 }
 
-export async function playFile(file: string, volume: number) {
-	const buffer = await loadAudio(file);
-	createSourceNode(buffer, volume)?.start();
+/**
+ * サウンド設定形式で指定された音声を再生する
+ * @param soundStore サウンド設定
+ */
+export async function playFile(soundStore: SoundStore) {
+	const buffer = await loadAudio(soundStore);
+	if (!buffer) return;
+	createSourceNode(buffer, soundStore.volume)?.start();
 }
 
 export function createSourceNode(buffer: AudioBuffer, volume: number) : AudioBufferSourceNode | null {
@@ -118,6 +195,27 @@ export function createSourceNode(buffer: AudioBuffer, volume: number) : AudioBuf
 	return soundSource;
 }
 
+/**
+ * 音声の長さをミリ秒で取得する
+ * @param file ファイルのURL(ドライブIDではない)
+ */
+export async function getSoundDuration(file: string): Promise<number> {
+	const audioEl = document.createElement('audio');
+	audioEl.src = file;
+	return new Promise((resolve) => {
+		const si = setInterval(() => {
+			if (audioEl.readyState > 0) {
+				resolve(audioEl.duration * 1000);
+				clearInterval(si);
+				audioEl.remove();
+			}
+		}, 100);
+	});
+}
+
+/**
+ * ミュートすべきかどうかを判断する
+ */
 export function isMute(): boolean {
 	if (defaultStore.state.sound_notUseSound) {
 		// サウンドを出力しない
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 40fb1dde76..70d2cf402d 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -6,6 +6,7 @@
 import { markRaw, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { miLocalStorage } from './local-storage.js';
+import type { SoundType } from '@/scripts/sound.js';
 import { Storage } from '@/pizzax.js';
 
 interface PostFormAction {
@@ -35,6 +36,22 @@ interface PageViewInterruptor {
 	handler: (page: Misskey.entities.Page) => unknown;
 }
 
+/** サウンド設定 */
+export type SoundStore = {
+	type: Exclude<SoundType, '_driveFile_'>;
+	volume: number;
+} | {
+	type: '_driveFile_';
+
+	/** ドライブのファイルID */
+	fileId: string;
+
+	/** ファイルURL(こちらが優先される) */
+	fileUrl: string;
+
+	volume: number;
+}
+
 export const postFormActions: PostFormAction[] = [];
 export const userActions: UserAction[] = [];
 export const noteActions: NoteAction[] = [];
@@ -401,27 +418,27 @@ export const defaultStore = markRaw(new Storage('base', {
 	},
 	sound_note: {
 		where: 'device',
-		default: { type: 'syuilo/n-aec', volume: 1 },
+		default: { type: 'syuilo/n-aec', volume: 1 } as SoundStore,
 	},
 	sound_noteMy: {
 		where: 'device',
-		default: { type: 'syuilo/n-cea-4va', volume: 1 },
+		default: { type: 'syuilo/n-cea-4va', volume: 1 } as SoundStore,
 	},
 	sound_notification: {
 		where: 'device',
-		default: { type: 'syuilo/n-ea', volume: 1 },
+		default: { type: 'syuilo/n-ea', volume: 1 } as SoundStore,
 	},
 	sound_antenna: {
 		where: 'device',
-		default: { type: 'syuilo/triple', volume: 1 },
+		default: { type: 'syuilo/triple', volume: 1 } as SoundStore,
 	},
 	sound_channel: {
 		where: 'device',
-		default: { type: 'syuilo/square-pico', volume: 1 },
+		default: { type: 'syuilo/square-pico', volume: 1 } as SoundStore,
 	},
 	sound_reaction: {
 		where: 'device',
-		default: { type: 'syuilo/bubble2', volume: 1 },
+		default: { type: 'syuilo/bubble2', volume: 1 } as SoundStore,
 	},
 }));
 
diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue
index 8c990e8e49..5531794569 100644
--- a/packages/frontend/src/widgets/WidgetJobQueue.vue
+++ b/packages/frontend/src/widgets/WidgetJobQueue.vue
@@ -104,7 +104,13 @@ let jammedAudioBuffer: AudioBuffer | null = $ref(null);
 let jammedSoundNodePlaying: boolean = $ref(false);
 
 if (defaultStore.state.sound_masterVolume) {
-	sound.loadAudio('syuilo/queue-jammed').then(buf => jammedAudioBuffer = buf);
+	sound.loadAudio({
+		type: 'syuilo/queue-jammed',
+		volume: 1,
+	}).then(buf => {
+		if (!buf) throw new Error('[WidgetJobQueue] Failed to initialize AudioBuffer');
+		jammedAudioBuffer = buf;
+	});
 }
 
 for (const domain of ['inbox', 'deliver']) {

From 51cf906b250f9166e1cafef0a32223fd05cba1b5 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 27 Nov 2023 21:05:20 +0900
Subject: [PATCH 058/435] update deps

---
 package.json                     |    8 +-
 packages/backend/package.json    |   54 +-
 packages/frontend/package.json   |   38 +-
 packages/misskey-js/package.json |   12 +-
 packages/sw/package.json         |    6 +-
 pnpm-lock.yaml                   | 1444 +++++++++++++++---------------
 6 files changed, 791 insertions(+), 771 deletions(-)

diff --git a/package.json b/package.json
index f51861401e..7db2b0e7fc 100644
--- a/package.json
+++ b/package.json
@@ -52,11 +52,11 @@
 		"typescript": "5.3.2"
 	},
 	"devDependencies": {
-		"@typescript-eslint/eslint-plugin": "6.11.0",
-		"@typescript-eslint/parser": "6.11.0",
+		"@typescript-eslint/eslint-plugin": "6.12.0",
+		"@typescript-eslint/parser": "6.12.0",
 		"cross-env": "7.0.3",
-		"cypress": "13.5.1",
-		"eslint": "8.53.0",
+		"cypress": "13.6.0",
+		"eslint": "8.54.0",
 		"start-server-and-test": "2.0.3"
 	},
 	"optionalDependencies": {
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 18c7818dcc..b17ce904f6 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -60,9 +60,9 @@
 	"dependencies": {
 		"@aws-sdk/client-s3": "3.412.0",
 		"@aws-sdk/lib-storage": "3.412.0",
-		"@bull-board/api": "5.9.1",
-		"@bull-board/fastify": "5.9.1",
-		"@bull-board/ui": "5.9.1",
+		"@bull-board/api": "5.9.2",
+		"@bull-board/fastify": "5.9.2",
+		"@bull-board/ui": "5.9.2",
 		"@discordapp/twemoji": "14.1.2",
 		"@fastify/accepts": "4.2.0",
 		"@fastify/cookie": "9.2.0",
@@ -72,15 +72,15 @@
 		"@fastify/multipart": "8.0.0",
 		"@fastify/static": "6.12.0",
 		"@fastify/view": "8.2.0",
-		"@nestjs/common": "10.2.8",
-		"@nestjs/core": "10.2.8",
-		"@nestjs/testing": "10.2.8",
+		"@nestjs/common": "10.2.10",
+		"@nestjs/core": "10.2.10",
+		"@nestjs/testing": "10.2.10",
 		"@peertube/http-signature": "1.7.0",
 		"@simplewebauthn/server": "8.3.5",
 		"@sinonjs/fake-timers": "11.2.2",
-		"@smithy/node-http-handler": "2.1.5",
+		"@smithy/node-http-handler": "2.1.10",
 		"@swc/cli": "0.1.63",
-		"@swc/core": "1.3.96",
+		"@swc/core": "1.3.99",
 		"accepts": "1.3.8",
 		"ajv": "8.12.0",
 		"archiver": "6.0.1",
@@ -88,7 +88,7 @@
 		"bcryptjs": "2.4.3",
 		"blurhash": "2.0.5",
 		"body-parser": "1.20.2",
-		"bullmq": "4.13.3",
+		"bullmq": "4.14.2",
 		"cacheable-lookup": "7.0.0",
 		"cbor": "9.0.1",
 		"chalk": "5.3.0",
@@ -114,11 +114,11 @@
 		"ipaddr.js": "2.1.0",
 		"is-svg": "5.0.0",
 		"js-yaml": "4.1.0",
-		"jsdom": "22.1.0",
+		"jsdom": "23.0.0",
 		"json5": "2.2.3",
 		"jsonld": "8.3.1",
-		"jsrsasign": "10.8.6",
-		"meilisearch": "0.35.0",
+		"jsrsasign": "10.9.0",
+		"meilisearch": "0.36.0",
 		"mfm-js": "0.23.3",
 		"microformats-parser": "1.5.2",
 		"mime-types": "2.1.35",
@@ -145,7 +145,7 @@
 		"qrcode": "1.5.3",
 		"random-seed": "0.3.0",
 		"ratelimiter": "3.4.1",
-		"re2": "1.20.8",
+		"re2": "1.20.9",
 		"redis-lock": "0.1.4",
 		"reflect-metadata": "0.1.13",
 		"rename": "1.0.4",
@@ -159,7 +159,7 @@
 		"strict-event-emitter-types": "2.0.0",
 		"stringz": "2.1.0",
 		"summaly": "github:misskey-dev/summaly",
-		"systeminformation": "5.21.17",
+		"systeminformation": "5.21.18",
 		"tinycolor2": "1.6.0",
 		"tmp": "0.2.1",
 		"tsc-alias": "1.8.8",
@@ -178,7 +178,7 @@
 		"@simplewebauthn/typescript-types": "8.3.4",
 		"@swc/jest": "0.2.29",
 		"@types/accepts": "1.3.7",
-		"@types/archiver": "6.0.1",
+		"@types/archiver": "6.0.2",
 		"@types/bcryptjs": "2.4.6",
 		"@types/body-parser": "1.19.5",
 		"@types/cbor": "6.0.0",
@@ -186,28 +186,28 @@
 		"@types/content-disposition": "0.5.8",
 		"@types/fluent-ffmpeg": "2.1.24",
 		"@types/http-link-header": "1.0.5",
-		"@types/jest": "29.5.8",
+		"@types/jest": "29.5.10",
 		"@types/js-yaml": "4.0.9",
-		"@types/jsdom": "21.1.5",
-		"@types/jsonld": "1.5.12",
+		"@types/jsdom": "21.1.6",
+		"@types/jsonld": "1.5.13",
 		"@types/jsrsasign": "10.5.12",
 		"@types/mime-types": "2.1.4",
 		"@types/ms": "0.7.34",
-		"@types/node": "20.9.1",
+		"@types/node": "20.10.0",
 		"@types/node-fetch": "3.0.3",
 		"@types/nodemailer": "6.4.14",
 		"@types/oauth": "0.9.4",
 		"@types/oauth2orize": "1.11.3",
 		"@types/oauth2orize-pkce": "0.1.2",
 		"@types/pg": "8.10.9",
-		"@types/pug": "2.0.9",
-		"@types/punycode": "2.1.2",
+		"@types/pug": "2.0.10",
+		"@types/punycode": "2.1.3",
 		"@types/qrcode": "1.5.5",
 		"@types/random-seed": "0.3.5",
 		"@types/ratelimiter": "3.4.6",
 		"@types/rename": "1.0.7",
-		"@types/sanitize-html": "2.9.4",
-		"@types/semver": "7.5.5",
+		"@types/sanitize-html": "2.9.5",
+		"@types/semver": "7.5.6",
 		"@types/sharp": "0.32.0",
 		"@types/simple-oauth2": "5.0.7",
 		"@types/sinonjs__fake-timers": "8.1.5",
@@ -215,12 +215,12 @@
 		"@types/tmp": "0.2.6",
 		"@types/vary": "1.1.3",
 		"@types/web-push": "3.6.3",
-		"@types/ws": "8.5.9",
-		"@typescript-eslint/eslint-plugin": "6.11.0",
-		"@typescript-eslint/parser": "6.11.0",
+		"@types/ws": "8.5.10",
+		"@typescript-eslint/eslint-plugin": "6.12.0",
+		"@typescript-eslint/parser": "6.12.0",
 		"aws-sdk-client-mock": "3.0.0",
 		"cross-env": "7.0.3",
-		"eslint": "8.53.0",
+		"eslint": "8.54.0",
 		"eslint-plugin-import": "2.29.0",
 		"execa": "8.0.1",
 		"jest": "29.7.0",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 9156ab0391..fab12df575 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -18,7 +18,7 @@
 	"dependencies": {
 		"@discordapp/twemoji": "14.1.2",
 		"@github/webauthn-json": "2.1.1",
-		"@rollup/plugin-alias": "5.0.1",
+		"@rollup/plugin-alias": "5.1.0",
 		"@rollup/plugin-json": "6.0.1",
 		"@rollup/plugin-replace": "5.0.5",
 		"@rollup/pluginutils": "5.0.5",
@@ -26,7 +26,7 @@
 		"@tabler/icons-webfont": "2.37.0",
 		"@vitejs/plugin-vue": "4.5.0",
 		"@vue-macros/reactivity-transform": "0.4.0",
-		"@vue/compiler-sfc": "3.3.8",
+		"@vue/compiler-sfc": "3.3.9",
 		"astring": "1.8.6",
 		"autosize": "6.0.1",
 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
@@ -39,7 +39,7 @@
 		"chartjs-chart-matrix": "2.0.1",
 		"chartjs-plugin-gradient": "0.6.1",
 		"chartjs-plugin-zoom": "2.0.1",
-		"chromatic": "9.0.0",
+		"chromatic": "9.1.0",
 		"compare-versions": "6.1.0",
 		"cropperjs": "2.0.0-beta.4",
 		"date-fns": "2.30.0",
@@ -57,7 +57,7 @@
 		"photoswipe": "5.4.2",
 		"punycode": "2.3.1",
 		"querystring": "0.2.1",
-		"rollup": "4.4.1",
+		"rollup": "4.6.0",
 		"sanitize-html": "2.11.0",
 		"shiki": "^0.14.5",
 		"sass": "1.69.5",
@@ -73,8 +73,8 @@
 		"uuid": "9.0.1",
 		"v-code-diff": "1.7.2",
 		"vanilla-tilt": "1.8.1",
-		"vite": "4.5.0",
-		"vue": "3.3.8",
+		"vite": "5.0.2",
+		"vue": "3.3.9",
 		"vuedraggable": "next"
 	},
 	"devDependencies": {
@@ -96,27 +96,27 @@
 		"@storybook/types": "7.5.3",
 		"@storybook/vue3": "7.5.3",
 		"@storybook/vue3-vite": "7.5.3",
-		"@testing-library/vue": "8.0.0",
+		"@testing-library/vue": "8.0.1",
 		"@types/escape-regexp": "0.0.3",
 		"@types/estree": "1.0.5",
-		"@types/matter-js": "0.19.4",
-		"@types/micromatch": "4.0.5",
-		"@types/node": "20.9.1",
-		"@types/punycode": "2.1.2",
-		"@types/sanitize-html": "2.9.4",
+		"@types/matter-js": "0.19.5",
+		"@types/micromatch": "4.0.6",
+		"@types/node": "20.10.0",
+		"@types/punycode": "2.1.3",
+		"@types/sanitize-html": "2.9.5",
 		"@types/throttle-debounce": "5.0.2",
 		"@types/tinycolor2": "1.4.6",
 		"@types/uuid": "9.0.7",
-		"@types/websocket": "1.0.9",
-		"@types/ws": "8.5.9",
-		"@typescript-eslint/eslint-plugin": "6.11.0",
-		"@typescript-eslint/parser": "6.11.0",
+		"@types/websocket": "1.0.10",
+		"@types/ws": "8.5.10",
+		"@typescript-eslint/eslint-plugin": "6.12.0",
+		"@typescript-eslint/parser": "6.12.0",
 		"@vitest/coverage-v8": "0.34.6",
-		"@vue/runtime-core": "3.3.8",
+		"@vue/runtime-core": "3.3.9",
 		"acorn": "8.11.2",
 		"cross-env": "7.0.3",
-		"cypress": "13.5.1",
-		"eslint": "8.53.0",
+		"cypress": "13.6.0",
+		"eslint": "8.54.0",
 		"eslint-plugin-import": "2.29.0",
 		"eslint-plugin-vue": "9.18.1",
 		"fast-glob": "3.3.2",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 69ce173bf4..865d25146a 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -22,11 +22,11 @@
 	"devDependencies": {
 		"@microsoft/api-extractor": "7.38.3",
 		"@swc/jest": "0.2.29",
-		"@types/jest": "29.5.8",
-		"@types/node": "20.9.1",
-		"@typescript-eslint/eslint-plugin": "6.11.0",
-		"@typescript-eslint/parser": "6.11.0",
-		"eslint": "8.53.0",
+		"@types/jest": "29.5.10",
+		"@types/node": "20.10.0",
+		"@typescript-eslint/eslint-plugin": "6.12.0",
+		"@typescript-eslint/parser": "6.12.0",
+		"eslint": "8.54.0",
 		"jest": "29.7.0",
 		"jest-fetch-mock": "3.0.3",
 		"jest-websocket-mock": "2.5.0",
@@ -39,7 +39,7 @@
 	],
 	"dependencies": {
 		"@swc/cli": "0.1.63",
-		"@swc/core": "1.3.96",
+		"@swc/core": "1.3.99",
 		"eventemitter3": "5.0.1",
 		"reconnecting-websocket": "4.4.0"
 	}
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 3c74ee8c78..8ece5baf21 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -9,14 +9,14 @@
 		"lint": "pnpm typecheck && pnpm eslint"
 	},
 	"dependencies": {
-		"esbuild": "0.19.5",
+		"esbuild": "0.19.8",
 		"idb-keyval": "6.2.1",
 		"misskey-js": "workspace:*"
 	},
 	"devDependencies": {
-		"@typescript-eslint/parser": "6.11.0",
+		"@typescript-eslint/parser": "6.12.0",
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
-		"eslint": "8.53.0",
+		"eslint": "8.54.0",
 		"eslint-plugin-import": "2.29.0",
 		"typescript": "5.3.2"
 	},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7373d5f10b..68d70ba4c1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -36,20 +36,20 @@ importers:
         version: 4.4.0
     devDependencies:
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2)
+        specifier: 6.12.0
+        version: 6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
-        specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+        specifier: 6.12.0
+        version: 6.12.0(eslint@8.54.0)(typescript@5.3.2)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.5.1
-        version: 13.5.1
+        specifier: 13.6.0
+        version: 13.6.0
       eslint:
-        specifier: 8.53.0
-        version: 8.53.0
+        specifier: 8.54.0
+        version: 8.54.0
       start-server-and-test:
         specifier: 2.0.3
         version: 2.0.3
@@ -63,14 +63,14 @@ importers:
         specifier: 3.412.0
         version: 3.412.0(@aws-sdk/client-s3@3.412.0)
       '@bull-board/api':
-        specifier: 5.9.1
-        version: 5.9.1(@bull-board/ui@5.9.1)
+        specifier: 5.9.2
+        version: 5.9.2(@bull-board/ui@5.9.2)
       '@bull-board/fastify':
-        specifier: 5.9.1
-        version: 5.9.1
+        specifier: 5.9.2
+        version: 5.9.2
       '@bull-board/ui':
-        specifier: 5.9.1
-        version: 5.9.1
+        specifier: 5.9.2
+        version: 5.9.2
       '@discordapp/twemoji':
         specifier: 14.1.2
         version: 14.1.2
@@ -99,14 +99,14 @@ importers:
         specifier: 8.2.0
         version: 8.2.0
       '@nestjs/common':
-        specifier: 10.2.8
-        version: 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1)
+        specifier: 10.2.10
+        version: 10.2.10(reflect-metadata@0.1.13)(rxjs@7.8.1)
       '@nestjs/core':
-        specifier: 10.2.8
-        version: 10.2.8(@nestjs/common@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1)
+        specifier: 10.2.10
+        version: 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.13)(rxjs@7.8.1)
       '@nestjs/testing':
-        specifier: 10.2.8
-        version: 10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)
+        specifier: 10.2.10
+        version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
       '@peertube/http-signature':
         specifier: 1.7.0
         version: 1.7.0
@@ -117,14 +117,14 @@ importers:
         specifier: 11.2.2
         version: 11.2.2
       '@smithy/node-http-handler':
-        specifier: 2.1.5
-        version: 2.1.5
+        specifier: 2.1.10
+        version: 2.1.10
       '@swc/cli':
         specifier: 0.1.63
-        version: 0.1.63(@swc/core@1.3.96)(chokidar@3.5.3)
+        version: 0.1.63(@swc/core@1.3.99)(chokidar@3.5.3)
       '@swc/core':
-        specifier: 1.3.96
-        version: 1.3.96
+        specifier: 1.3.99
+        version: 1.3.99
       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.13.3
-        version: 4.13.3
+        specifier: 4.14.2
+        version: 4.14.2
       cacheable-lookup:
         specifier: 7.0.0
         version: 7.0.0
@@ -225,8 +225,8 @@ importers:
         specifier: 4.1.0
         version: 4.1.0
       jsdom:
-        specifier: 22.1.0
-        version: 22.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+        specifier: 23.0.0
+        version: 23.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       json5:
         specifier: 2.2.3
         version: 2.2.3
@@ -234,11 +234,11 @@ importers:
         specifier: 8.3.1
         version: 8.3.1
       jsrsasign:
-        specifier: 10.8.6
-        version: 10.8.6
+        specifier: 10.9.0
+        version: 10.9.0
       meilisearch:
-        specifier: 0.35.0
-        version: 0.35.0
+        specifier: 0.36.0
+        version: 0.36.0
       mfm-js:
         specifier: 0.23.3
         version: 0.23.3
@@ -318,8 +318,8 @@ importers:
         specifier: 3.4.1
         version: 3.4.1
       re2:
-        specifier: 1.20.8
-        version: 1.20.8
+        specifier: 1.20.9
+        version: 1.20.9
       redis-lock:
         specifier: 0.1.4
         version: 0.1.4
@@ -360,8 +360,8 @@ importers:
         specifier: github:misskey-dev/summaly
         version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7
       systeminformation:
-        specifier: 5.21.17
-        version: 5.21.17
+        specifier: 5.21.18
+        version: 5.21.18
       tinycolor2:
         specifier: 1.6.0
         version: 1.6.0
@@ -495,13 +495,13 @@ importers:
         version: 8.3.4
       '@swc/jest':
         specifier: 0.2.29
-        version: 0.2.29(@swc/core@1.3.96)
+        version: 0.2.29(@swc/core@1.3.99)
       '@types/accepts':
         specifier: 1.3.7
         version: 1.3.7
       '@types/archiver':
-        specifier: 6.0.1
-        version: 6.0.1
+        specifier: 6.0.2
+        version: 6.0.2
       '@types/bcryptjs':
         specifier: 2.4.6
         version: 2.4.6
@@ -524,17 +524,17 @@ importers:
         specifier: 1.0.5
         version: 1.0.5
       '@types/jest':
-        specifier: 29.5.8
-        version: 29.5.8
+        specifier: 29.5.10
+        version: 29.5.10
       '@types/js-yaml':
         specifier: 4.0.9
         version: 4.0.9
       '@types/jsdom':
-        specifier: 21.1.5
-        version: 21.1.5
+        specifier: 21.1.6
+        version: 21.1.6
       '@types/jsonld':
-        specifier: 1.5.12
-        version: 1.5.12
+        specifier: 1.5.13
+        version: 1.5.13
       '@types/jsrsasign':
         specifier: 10.5.12
         version: 10.5.12
@@ -545,8 +545,8 @@ importers:
         specifier: 0.7.34
         version: 0.7.34
       '@types/node':
-        specifier: 20.9.1
-        version: 20.9.1
+        specifier: 20.10.0
+        version: 20.10.0
       '@types/node-fetch':
         specifier: 3.0.3
         version: 3.0.3
@@ -566,11 +566,11 @@ importers:
         specifier: 8.10.9
         version: 8.10.9
       '@types/pug':
-        specifier: 2.0.9
-        version: 2.0.9
+        specifier: 2.0.10
+        version: 2.0.10
       '@types/punycode':
-        specifier: 2.1.2
-        version: 2.1.2
+        specifier: 2.1.3
+        version: 2.1.3
       '@types/qrcode':
         specifier: 1.5.5
         version: 1.5.5
@@ -584,11 +584,11 @@ importers:
         specifier: 1.0.7
         version: 1.0.7
       '@types/sanitize-html':
-        specifier: 2.9.4
-        version: 2.9.4
+        specifier: 2.9.5
+        version: 2.9.5
       '@types/semver':
-        specifier: 7.5.5
-        version: 7.5.5
+        specifier: 7.5.6
+        version: 7.5.6
       '@types/sharp':
         specifier: 0.32.0
         version: 0.32.0
@@ -611,14 +611,14 @@ importers:
         specifier: 3.6.3
         version: 3.6.3
       '@types/ws':
-        specifier: 8.5.9
-        version: 8.5.9
+        specifier: 8.5.10
+        version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2)
+        specifier: 6.12.0
+        version: 6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
-        specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+        specifier: 6.12.0
+        version: 6.12.0(eslint@8.54.0)(typescript@5.3.2)
       aws-sdk-client-mock:
         specifier: 3.0.0
         version: 3.0.0
@@ -626,17 +626,17 @@ importers:
         specifier: 7.0.3
         version: 7.0.3
       eslint:
-        specifier: 8.53.0
-        version: 8.53.0
+        specifier: 8.54.0
+        version: 8.54.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)
+        version: 2.29.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)
       execa:
         specifier: 8.0.1
         version: 8.0.1
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.9.1)
+        version: 29.7.0(@types/node@20.10.0)
       jest-mock:
         specifier: 29.7.0
         version: 29.7.0
@@ -653,17 +653,17 @@ importers:
         specifier: 2.1.1
         version: 2.1.1
       '@rollup/plugin-alias':
-        specifier: 5.0.1
-        version: 5.0.1(rollup@4.4.1)
+        specifier: 5.1.0
+        version: 5.1.0(rollup@4.6.0)
       '@rollup/plugin-json':
         specifier: 6.0.1
-        version: 6.0.1(rollup@4.4.1)
+        version: 6.0.1(rollup@4.6.0)
       '@rollup/plugin-replace':
         specifier: 5.0.5
-        version: 5.0.5(rollup@4.4.1)
+        version: 5.0.5(rollup@4.6.0)
       '@rollup/pluginutils':
         specifier: 5.0.5
-        version: 5.0.5(rollup@4.4.1)
+        version: 5.0.5(rollup@4.6.0)
       '@syuilo/aiscript':
         specifier: 0.16.0
         version: 0.16.0
@@ -672,13 +672,13 @@ importers:
         version: 2.37.0
       '@vitejs/plugin-vue':
         specifier: 4.5.0
-        version: 4.5.0(vite@4.5.0)(vue@3.3.8)
+        version: 4.5.0(vite@5.0.2)(vue@3.3.9)
       '@vue-macros/reactivity-transform':
         specifier: 0.4.0
-        version: 0.4.0(rollup@4.4.1)(vue@3.3.8)
+        version: 0.4.0(rollup@4.6.0)(vue@3.3.9)
       '@vue/compiler-sfc':
-        specifier: 3.3.8
-        version: 3.3.8
+        specifier: 3.3.9
+        version: 3.3.9
       aiscript-vscode:
         specifier: github:aiscript-dev/aiscript-vscode#v0.0.6
         version: github.com/aiscript-dev/aiscript-vscode/b5a8aa0ad927831a0b867d1c183460a14e6c48cd
@@ -716,8 +716,8 @@ importers:
         specifier: 2.0.1
         version: 2.0.1(chart.js@4.4.0)
       chromatic:
-        specifier: 9.0.0
-        version: 9.0.0
+        specifier: 9.1.0
+        version: 9.1.0
       compare-versions:
         specifier: 6.1.0
         version: 6.1.0
@@ -770,8 +770,8 @@ importers:
         specifier: 0.2.1
         version: 0.2.1
       rollup:
-        specifier: 4.4.1
-        version: 4.4.1
+        specifier: 4.6.0
+        version: 4.6.0
       sanitize-html:
         specifier: 2.11.0
         version: 2.11.0
@@ -813,19 +813,19 @@ importers:
         version: 9.0.1
       v-code-diff:
         specifier: 1.7.2
-        version: 1.7.2(vue@3.3.8)
+        version: 1.7.2(vue@3.3.9)
       vanilla-tilt:
         specifier: 1.8.1
         version: 1.8.1
       vite:
-        specifier: 4.5.0
-        version: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
+        specifier: 5.0.2
+        version: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
       vue:
-        specifier: 3.3.8
-        version: 3.3.8(typescript@5.3.2)
+        specifier: 3.3.9
+        version: 3.3.9(typescript@5.3.2)
       vuedraggable:
         specifier: next
-        version: 4.1.0(vue@3.3.8)
+        version: 4.1.0(vue@3.3.9)
     devDependencies:
       '@storybook/addon-actions':
         specifier: 7.5.3
@@ -865,7 +865,7 @@ importers:
         version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)
       '@storybook/react-vite':
         specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.4.1)(typescript@5.3.2)(vite@4.5.0)
+        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.6.0)(typescript@5.3.2)(vite@5.0.2)
       '@storybook/testing-library':
         specifier: 0.2.2
         version: 0.2.2
@@ -877,13 +877,13 @@ importers:
         version: 7.5.3
       '@storybook/vue3':
         specifier: 7.5.3
-        version: 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.8)
+        version: 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.9)
       '@storybook/vue3-vite':
         specifier: 7.5.3
-        version: 7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@4.5.0)(vue@3.3.8)
+        version: 7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9)
       '@testing-library/vue':
-        specifier: 8.0.0
-        version: 8.0.0(@vue/compiler-sfc@3.3.8)(vue@3.3.8)
+        specifier: 8.0.1
+        version: 8.0.1(@vue/compiler-sfc@3.3.9)(vue@3.3.9)
       '@types/escape-regexp':
         specifier: 0.0.3
         version: 0.0.3
@@ -891,20 +891,20 @@ importers:
         specifier: 1.0.5
         version: 1.0.5
       '@types/matter-js':
-        specifier: 0.19.4
-        version: 0.19.4
+        specifier: 0.19.5
+        version: 0.19.5
       '@types/micromatch':
-        specifier: 4.0.5
-        version: 4.0.5
+        specifier: 4.0.6
+        version: 4.0.6
       '@types/node':
-        specifier: 20.9.1
-        version: 20.9.1
+        specifier: 20.10.0
+        version: 20.10.0
       '@types/punycode':
-        specifier: 2.1.2
-        version: 2.1.2
+        specifier: 2.1.3
+        version: 2.1.3
       '@types/sanitize-html':
-        specifier: 2.9.4
-        version: 2.9.4
+        specifier: 2.9.5
+        version: 2.9.5
       '@types/throttle-debounce':
         specifier: 5.0.2
         version: 5.0.2
@@ -915,23 +915,23 @@ importers:
         specifier: 9.0.7
         version: 9.0.7
       '@types/websocket':
-        specifier: 1.0.9
-        version: 1.0.9
+        specifier: 1.0.10
+        version: 1.0.10
       '@types/ws':
-        specifier: 8.5.9
-        version: 8.5.9
+        specifier: 8.5.10
+        version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2)
+        specifier: 6.12.0
+        version: 6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
-        specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+        specifier: 6.12.0
+        version: 6.12.0(eslint@8.54.0)(typescript@5.3.2)
       '@vitest/coverage-v8':
         specifier: 0.34.6
         version: 0.34.6(vitest@0.34.6)
       '@vue/runtime-core':
-        specifier: 3.3.8
-        version: 3.3.8
+        specifier: 3.3.9
+        version: 3.3.9
       acorn:
         specifier: 8.11.2
         version: 8.11.2
@@ -939,17 +939,17 @@ importers:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.5.1
-        version: 13.5.1
+        specifier: 13.6.0
+        version: 13.6.0
       eslint:
-        specifier: 8.53.0
-        version: 8.53.0
+        specifier: 8.54.0
+        version: 8.54.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)
+        version: 2.29.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)
       eslint-plugin-vue:
         specifier: 9.18.1
-        version: 9.18.1(eslint@8.53.0)
+        version: 9.18.1(eslint@8.54.0)
       fast-glob:
         specifier: 3.3.2
         version: 3.3.2
@@ -1000,7 +1000,7 @@ importers:
         version: 0.2.2(vitest@0.34.6)
       vue-eslint-parser:
         specifier: 9.3.2
-        version: 9.3.2(eslint@8.53.0)
+        version: 9.3.2(eslint@8.54.0)
       vue-tsc:
         specifier: 1.8.22
         version: 1.8.22(typescript@5.3.2)
@@ -1009,10 +1009,10 @@ importers:
     dependencies:
       '@swc/cli':
         specifier: 0.1.63
-        version: 0.1.63(@swc/core@1.3.96)(chokidar@3.5.3)
+        version: 0.1.63(@swc/core@1.3.99)(chokidar@3.5.3)
       '@swc/core':
-        specifier: 1.3.96
-        version: 1.3.96
+        specifier: 1.3.99
+        version: 1.3.99
       eventemitter3:
         specifier: 5.0.1
         version: 5.0.1
@@ -1022,28 +1022,28 @@ importers:
     devDependencies:
       '@microsoft/api-extractor':
         specifier: 7.38.3
-        version: 7.38.3(@types/node@20.9.1)
+        version: 7.38.3(@types/node@20.10.0)
       '@swc/jest':
         specifier: 0.2.29
-        version: 0.2.29(@swc/core@1.3.96)
+        version: 0.2.29(@swc/core@1.3.99)
       '@types/jest':
-        specifier: 29.5.8
-        version: 29.5.8
+        specifier: 29.5.10
+        version: 29.5.10
       '@types/node':
-        specifier: 20.9.1
-        version: 20.9.1
+        specifier: 20.10.0
+        version: 20.10.0
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2)
+        specifier: 6.12.0
+        version: 6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
-        specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+        specifier: 6.12.0
+        version: 6.12.0(eslint@8.54.0)(typescript@5.3.2)
       eslint:
-        specifier: 8.53.0
-        version: 8.53.0
+        specifier: 8.54.0
+        version: 8.54.0
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.9.1)
+        version: 29.7.0(@types/node@20.10.0)
       jest-fetch-mock:
         specifier: 3.0.3
         version: 3.0.3
@@ -1063,8 +1063,8 @@ importers:
   packages/sw:
     dependencies:
       esbuild:
-        specifier: 0.19.5
-        version: 0.19.5
+        specifier: 0.19.8
+        version: 0.19.8
       idb-keyval:
         specifier: 6.2.1
         version: 6.2.1
@@ -1073,17 +1073,17 @@ importers:
         version: link:../misskey-js
     devDependencies:
       '@typescript-eslint/parser':
-        specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+        specifier: 6.12.0
+        version: 6.12.0(eslint@8.54.0)(typescript@5.3.2)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: /@types/serviceworker@0.0.67
       eslint:
-        specifier: 8.53.0
-        version: 8.53.0
+        specifier: 8.54.0
+        version: 8.54.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)
+        version: 2.29.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)
       typescript:
         specifier: 5.3.2
         version: 5.3.2
@@ -1225,7 +1225,7 @@ packages:
       '@smithy/middleware-serde': 2.0.8
       '@smithy/middleware-stack': 2.0.1
       '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.1.5
+      '@smithy/node-http-handler': 2.1.10
       '@smithy/protocol-http': 3.0.5
       '@smithy/smithy-client': 2.1.5
       '@smithy/types': 2.3.3
@@ -1269,7 +1269,7 @@ packages:
       '@smithy/middleware-serde': 2.0.8
       '@smithy/middleware-stack': 2.0.1
       '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.1.5
+      '@smithy/node-http-handler': 2.1.10
       '@smithy/protocol-http': 3.0.5
       '@smithy/smithy-client': 2.1.5
       '@smithy/types': 2.3.3
@@ -1313,7 +1313,7 @@ packages:
       '@smithy/middleware-serde': 2.0.8
       '@smithy/middleware-stack': 2.0.1
       '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.1.5
+      '@smithy/node-http-handler': 2.1.10
       '@smithy/protocol-http': 3.0.5
       '@smithy/smithy-client': 2.1.5
       '@smithy/types': 2.3.3
@@ -1594,7 +1594,7 @@ packages:
       '@smithy/middleware-serde': 2.0.8
       '@smithy/middleware-stack': 2.0.1
       '@smithy/node-config-provider': 2.0.11
-      '@smithy/node-http-handler': 2.1.5
+      '@smithy/node-http-handler': 2.1.10
       '@smithy/property-provider': 2.0.9
       '@smithy/protocol-http': 3.0.5
       '@smithy/shared-ini-file-loader': 2.0.10
@@ -3025,7 +3025,7 @@ packages:
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/code-frame': 7.22.13
-      '@babel/parser': 7.23.0
+      '@babel/parser': 7.23.3
       '@babel/types': 7.22.17
     dev: true
 
@@ -3039,7 +3039,7 @@ packages:
       '@babel/helper-function-name': 7.22.5
       '@babel/helper-hoist-variables': 7.22.5
       '@babel/helper-split-export-declaration': 7.22.6
-      '@babel/parser': 7.23.0
+      '@babel/parser': 7.23.3
       '@babel/types': 7.22.17
       debug: 4.3.4(supports-color@8.1.1)
       globals: 11.12.0
@@ -3081,29 +3081,29 @@ packages:
     resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
     dev: true
 
-  /@bull-board/api@5.9.1(@bull-board/ui@5.9.1):
-    resolution: {integrity: sha512-xZzPYNw9Dp46It4vvZv3tmFScmUu/UT/jWQxYK9cvbkJRXh15rsZrbbR+/phUqou0NxRQGiHoFSZ1y5D107dIA==}
+  /@bull-board/api@5.9.2(@bull-board/ui@5.9.2):
+    resolution: {integrity: sha512-x89+CsOZHgQnnUFmC0NRgBJNJtgvQSmrpvAb4SA/tX2ftJ9PSxobLhAx/mvL9Ekdi6U1FZD2uA2JH2nsVVzwCg==}
     peerDependencies:
-      '@bull-board/ui': 5.9.1
+      '@bull-board/ui': 5.9.2
     dependencies:
-      '@bull-board/ui': 5.9.1
+      '@bull-board/ui': 5.9.2
       redis-info: 3.1.0
     dev: false
 
-  /@bull-board/fastify@5.9.1:
-    resolution: {integrity: sha512-bMktkLGd83K2Wimv+ASBhjgBTOR7ZKQ6OsZsFoaV5xH0CbPlIZ/S0apwTlSSfT/tfBbilWKX9BrE0xta22mkrA==}
+  /@bull-board/fastify@5.9.2:
+    resolution: {integrity: sha512-K5jR3H/lTQqehxznDMFyqD6xejxlPXuerVXGGcFMmRrz1LFDsbbZYe4y4YE+sGN/+HmaQQjjWWQDRtYQmAlOBQ==}
     dependencies:
-      '@bull-board/api': 5.9.1(@bull-board/ui@5.9.1)
-      '@bull-board/ui': 5.9.1
+      '@bull-board/api': 5.9.2(@bull-board/ui@5.9.2)
+      '@bull-board/ui': 5.9.2
       '@fastify/static': 6.12.0
       '@fastify/view': 8.2.0
       ejs: 3.1.9
     dev: false
 
-  /@bull-board/ui@5.9.1:
-    resolution: {integrity: sha512-lL93KVRTpLSl73KUFBw7sXOcCrqddGBbpiMKWEbViXxObIq68yFuhRrcfR7JeTSocLF5GsnqSVdSiNCZHmdNpw==}
+  /@bull-board/ui@5.9.2:
+    resolution: {integrity: sha512-wNlY8l+x4rhwoiGIpZNk/FrCrNzKcBeLqMcZTRC3DIBkp91njMkx+MJeG6EYlENcsN5EQk3I6IwUgvTXcS315g==}
     dependencies:
-      '@bull-board/api': 5.9.1(@bull-board/ui@5.9.1)
+      '@bull-board/api': 5.9.2(@bull-board/ui@5.9.2)
     dev: false
 
   /@canvas/image-data@1.0.0:
@@ -3325,15 +3325,15 @@ packages:
     cpu: [arm64]
     os: [android]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/android-arm64@0.19.5:
-    resolution: {integrity: sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==}
+  /@esbuild/android-arm64@0.19.8:
+    resolution: {integrity: sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/android-arm@0.18.17:
@@ -3342,15 +3342,15 @@ packages:
     cpu: [arm]
     os: [android]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/android-arm@0.19.5:
-    resolution: {integrity: sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==}
+  /@esbuild/android-arm@0.19.8:
+    resolution: {integrity: sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==}
     engines: {node: '>=12'}
     cpu: [arm]
     os: [android]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/android-x64@0.18.17:
@@ -3359,15 +3359,15 @@ packages:
     cpu: [x64]
     os: [android]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/android-x64@0.19.5:
-    resolution: {integrity: sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==}
+  /@esbuild/android-x64@0.19.8:
+    resolution: {integrity: sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [android]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/darwin-arm64@0.18.17:
@@ -3376,15 +3376,15 @@ packages:
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/darwin-arm64@0.19.5:
-    resolution: {integrity: sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==}
+  /@esbuild/darwin-arm64@0.19.8:
+    resolution: {integrity: sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/darwin-x64@0.18.17:
@@ -3393,15 +3393,15 @@ packages:
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/darwin-x64@0.19.5:
-    resolution: {integrity: sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==}
+  /@esbuild/darwin-x64@0.19.8:
+    resolution: {integrity: sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/freebsd-arm64@0.18.17:
@@ -3410,15 +3410,15 @@ packages:
     cpu: [arm64]
     os: [freebsd]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/freebsd-arm64@0.19.5:
-    resolution: {integrity: sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==}
+  /@esbuild/freebsd-arm64@0.19.8:
+    resolution: {integrity: sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [freebsd]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/freebsd-x64@0.18.17:
@@ -3427,15 +3427,15 @@ packages:
     cpu: [x64]
     os: [freebsd]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/freebsd-x64@0.19.5:
-    resolution: {integrity: sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==}
+  /@esbuild/freebsd-x64@0.19.8:
+    resolution: {integrity: sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [freebsd]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/linux-arm64@0.18.17:
@@ -3444,15 +3444,15 @@ packages:
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/linux-arm64@0.19.5:
-    resolution: {integrity: sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==}
+  /@esbuild/linux-arm64@0.19.8:
+    resolution: {integrity: sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/linux-arm@0.18.17:
@@ -3461,15 +3461,15 @@ packages:
     cpu: [arm]
     os: [linux]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/linux-arm@0.19.5:
-    resolution: {integrity: sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==}
+  /@esbuild/linux-arm@0.19.8:
+    resolution: {integrity: sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==}
     engines: {node: '>=12'}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/linux-ia32@0.18.17:
@@ -3478,15 +3478,15 @@ packages:
     cpu: [ia32]
     os: [linux]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/linux-ia32@0.19.5:
-    resolution: {integrity: sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==}
+  /@esbuild/linux-ia32@0.19.8:
+    resolution: {integrity: sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [linux]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/linux-loong64@0.18.17:
@@ -3495,15 +3495,15 @@ packages:
     cpu: [loong64]
     os: [linux]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/linux-loong64@0.19.5:
-    resolution: {integrity: sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==}
+  /@esbuild/linux-loong64@0.19.8:
+    resolution: {integrity: sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==}
     engines: {node: '>=12'}
     cpu: [loong64]
     os: [linux]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/linux-mips64el@0.18.17:
@@ -3512,15 +3512,15 @@ packages:
     cpu: [mips64el]
     os: [linux]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/linux-mips64el@0.19.5:
-    resolution: {integrity: sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==}
+  /@esbuild/linux-mips64el@0.19.8:
+    resolution: {integrity: sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==}
     engines: {node: '>=12'}
     cpu: [mips64el]
     os: [linux]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/linux-ppc64@0.18.17:
@@ -3529,15 +3529,15 @@ packages:
     cpu: [ppc64]
     os: [linux]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/linux-ppc64@0.19.5:
-    resolution: {integrity: sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==}
+  /@esbuild/linux-ppc64@0.19.8:
+    resolution: {integrity: sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==}
     engines: {node: '>=12'}
     cpu: [ppc64]
     os: [linux]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/linux-riscv64@0.18.17:
@@ -3546,15 +3546,15 @@ packages:
     cpu: [riscv64]
     os: [linux]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/linux-riscv64@0.19.5:
-    resolution: {integrity: sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==}
+  /@esbuild/linux-riscv64@0.19.8:
+    resolution: {integrity: sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==}
     engines: {node: '>=12'}
     cpu: [riscv64]
     os: [linux]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/linux-s390x@0.18.17:
@@ -3563,15 +3563,15 @@ packages:
     cpu: [s390x]
     os: [linux]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/linux-s390x@0.19.5:
-    resolution: {integrity: sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==}
+  /@esbuild/linux-s390x@0.19.8:
+    resolution: {integrity: sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==}
     engines: {node: '>=12'}
     cpu: [s390x]
     os: [linux]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/linux-x64@0.18.17:
@@ -3580,15 +3580,15 @@ packages:
     cpu: [x64]
     os: [linux]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/linux-x64@0.19.5:
-    resolution: {integrity: sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==}
+  /@esbuild/linux-x64@0.19.8:
+    resolution: {integrity: sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/netbsd-x64@0.18.17:
@@ -3597,15 +3597,15 @@ packages:
     cpu: [x64]
     os: [netbsd]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/netbsd-x64@0.19.5:
-    resolution: {integrity: sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==}
+  /@esbuild/netbsd-x64@0.19.8:
+    resolution: {integrity: sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [netbsd]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/openbsd-x64@0.18.17:
@@ -3614,15 +3614,15 @@ packages:
     cpu: [x64]
     os: [openbsd]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/openbsd-x64@0.19.5:
-    resolution: {integrity: sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==}
+  /@esbuild/openbsd-x64@0.19.8:
+    resolution: {integrity: sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [openbsd]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/sunos-x64@0.18.17:
@@ -3631,15 +3631,15 @@ packages:
     cpu: [x64]
     os: [sunos]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/sunos-x64@0.19.5:
-    resolution: {integrity: sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==}
+  /@esbuild/sunos-x64@0.19.8:
+    resolution: {integrity: sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [sunos]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/win32-arm64@0.18.17:
@@ -3648,15 +3648,15 @@ packages:
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/win32-arm64@0.19.5:
-    resolution: {integrity: sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==}
+  /@esbuild/win32-arm64@0.19.8:
+    resolution: {integrity: sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/win32-ia32@0.18.17:
@@ -3665,15 +3665,15 @@ packages:
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/win32-ia32@0.19.5:
-    resolution: {integrity: sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==}
+  /@esbuild/win32-ia32@0.19.8:
+    resolution: {integrity: sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
-    dev: false
     optional: true
 
   /@esbuild/win32-x64@0.18.17:
@@ -3682,24 +3682,24 @@ packages:
     cpu: [x64]
     os: [win32]
     requiresBuild: true
+    dev: true
     optional: true
 
-  /@esbuild/win32-x64@0.19.5:
-    resolution: {integrity: sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==}
+  /@esbuild/win32-x64@0.19.8:
+    resolution: {integrity: sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
-    dev: false
     optional: true
 
-  /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0):
+  /@eslint-community/eslint-utils@4.4.0(eslint@8.54.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.53.0
+      eslint: 8.54.0
       eslint-visitor-keys: 3.4.3
     dev: true
 
@@ -3725,8 +3725,8 @@ packages:
       - supports-color
     dev: true
 
-  /@eslint/js@8.53.0:
-    resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==}
+  /@eslint/js@8.54.0:
+    resolution: {integrity: sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
@@ -4020,7 +4020,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
@@ -4041,14 +4041,14 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       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.9.1)
+      jest-config: 29.7.0(@types/node@20.10.0)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -4083,7 +4083,7 @@ packages:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       jest-mock: 29.7.0
     dev: true
 
@@ -4110,7 +4110,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -4143,7 +4143,7 @@ packages:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.18
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       chalk: 4.1.2
       collect-v8-coverage: 1.0.1
       exit: 0.1.2
@@ -4237,7 +4237,7 @@ packages:
     dependencies:
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       '@types/yargs': 16.0.5
       chalk: 4.1.2
     dev: true
@@ -4249,12 +4249,12 @@ packages:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       '@types/yargs': 17.0.19
       chalk: 4.1.2
     dev: true
 
-  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.2)(vite@4.5.0):
+  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.2)(vite@5.0.2):
     resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==}
     peerDependencies:
       typescript: '>= 4.3.x'
@@ -4268,7 +4268,7 @@ packages:
       magic-string: 0.27.0
       react-docgen-typescript: 2.2.2(typescript@5.3.2)
       typescript: 5.3.2
-      vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
     dev: true
 
   /@jridgewell/gen-mapping@0.3.2:
@@ -4353,24 +4353,24 @@ packages:
       react: 18.2.0
     dev: true
 
-  /@microsoft/api-extractor-model@7.28.2(@types/node@20.9.1):
+  /@microsoft/api-extractor-model@7.28.2(@types/node@20.10.0):
     resolution: {integrity: sha512-vkojrM2fo3q4n4oPh4uUZdjJ2DxQ2+RnDQL/xhTWSRUNPF6P4QyrvY357HBxbnltKcYu+nNNolVqc6TIGQ73Ig==}
     dependencies:
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.61.0(@types/node@20.9.1)
+      '@rushstack/node-core-library': 3.61.0(@types/node@20.10.0)
     transitivePeerDependencies:
       - '@types/node'
     dev: true
 
-  /@microsoft/api-extractor@7.38.3(@types/node@20.9.1):
+  /@microsoft/api-extractor@7.38.3(@types/node@20.10.0):
     resolution: {integrity: sha512-xt9iYyC5f39281j77JTA9C3ISJpW1XWkCcnw+2vM78CPnro6KhPfwQdPDfwS5JCPNuq0grm8cMdPUOPvrchDWw==}
     hasBin: true
     dependencies:
-      '@microsoft/api-extractor-model': 7.28.2(@types/node@20.9.1)
+      '@microsoft/api-extractor-model': 7.28.2(@types/node@20.10.0)
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.61.0(@types/node@20.9.1)
+      '@rushstack/node-core-library': 3.61.0(@types/node@20.10.0)
       '@rushstack/rig-package': 0.5.1
       '@rushstack/ts-command-line': 4.17.1
       colors: 1.2.5
@@ -4490,8 +4490,8 @@ packages:
       tar-fs: 2.1.1
     dev: true
 
-  /@nestjs/common@10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1):
-    resolution: {integrity: sha512-rmpwcdvq2IWMmsUVP8rsdKub6uDWk7dwCYo0aif50JTwcvcxzaP3iKVFKoSgvp0RKYu8h15+/AEOfaInmPpl0Q==}
+  /@nestjs/common@10.2.10(reflect-metadata@0.1.13)(rxjs@7.8.1):
+    resolution: {integrity: sha512-fwAk931rjW8CNH2Mgwawq/7HWHH1dxkOLdcgs7U52ddLk8CtHXjejm1cbNahewlSbNhvlOl7y1STLHutE6sUqw==}
     peerDependencies:
       class-transformer: '*'
       class-validator: '*'
@@ -4510,8 +4510,8 @@ packages:
       uid: 2.0.2
     dev: false
 
-  /@nestjs/core@10.2.8(@nestjs/common@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1):
-    resolution: {integrity: sha512-9+MZ2s8ixfY9Bl/M9ofChiyYymcwdK9ZWNH4GDMF7Am7XRAQ1oqde6MYGG05rhQwiVXuTwaYLlXciJKfsrg5qg==}
+  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.13)(rxjs@7.8.1):
+    resolution: {integrity: sha512-+ckOI6BPi2ZMHikT9MCG4ctHDc4OnjhoIytrn7f2AYMMXI4bnutJhqyQKc30VDka5x3Wq6QAD57pgSP7y+JjJg==}
     requiresBuild: true
     peerDependencies:
       '@nestjs/common': ^10.0.0
@@ -4528,7 +4528,7 @@ packages:
       '@nestjs/websockets':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1)
+      '@nestjs/common': 10.2.10(reflect-metadata@0.1.13)(rxjs@7.8.1)
       '@nuxtjs/opencollective': 0.3.2
       fast-safe-stringify: 2.1.1
       iterare: 1.2.1
@@ -4541,8 +4541,8 @@ packages:
       - encoding
     dev: false
 
-  /@nestjs/testing@10.2.8(@nestjs/common@10.2.8)(@nestjs/core@10.2.8):
-    resolution: {integrity: sha512-9Kj5IQhM67/nj/MT6Wi2OmWr5YQnCMptwKVFrX1TDaikpY12196v7frk0jVjdT7wms7rV07GZle9I2z0aSjqtQ==}
+  /@nestjs/testing@10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10):
+    resolution: {integrity: sha512-IVLUnPz/+fkBtPATYfqTIP+phN9yjkXejmj+JyhmcfPJZpxBmD1i9VSMqa4u54l37j0xkGPscQ0IXpbhqMYUKw==}
     peerDependencies:
       '@nestjs/common': ^10.0.0
       '@nestjs/core': ^10.0.0
@@ -4554,8 +4554,8 @@ packages:
       '@nestjs/platform-express':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.8(reflect-metadata@0.1.13)(rxjs@7.8.1)
-      '@nestjs/core': 10.2.8(@nestjs/common@10.2.8)(reflect-metadata@0.1.13)(rxjs@7.8.1)
+      '@nestjs/common': 10.2.10(reflect-metadata@0.1.13)(rxjs@7.8.1)
+      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.13)(rxjs@7.8.1)
       tslib: 2.6.2
     dev: false
 
@@ -5218,8 +5218,8 @@ packages:
       '@babel/runtime': 7.23.2
     dev: true
 
-  /@rollup/plugin-alias@5.0.1(rollup@4.4.1):
-    resolution: {integrity: sha512-JObvbWdOHoMy9W7SU0lvGhDtWq9PllP5mjpAy+TUslZG/WzOId9u80Hsqq1vCUn9pFJ0cxpdcnAv+QzU2zFH3Q==}
+  /@rollup/plugin-alias@5.1.0(rollup@4.6.0):
+    resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
       rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
@@ -5227,11 +5227,11 @@ packages:
       rollup:
         optional: true
     dependencies:
-      rollup: 4.4.1
+      rollup: 4.6.0
       slash: 4.0.0
     dev: false
 
-  /@rollup/plugin-json@6.0.1(rollup@4.4.1):
+  /@rollup/plugin-json@6.0.1(rollup@4.6.0):
     resolution: {integrity: sha512-RgVfl5hWMkxN1h/uZj8FVESvPuBJ/uf6ly6GTj0GONnkfoBN5KC0MSz+PN2OLDgYXMhtG0mWpTrkiOjoxAIevw==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5240,11 +5240,11 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.0.5(rollup@4.4.1)
-      rollup: 4.4.1
+      '@rollup/pluginutils': 5.0.5(rollup@4.6.0)
+      rollup: 4.6.0
     dev: false
 
-  /@rollup/plugin-replace@5.0.5(rollup@4.4.1):
+  /@rollup/plugin-replace@5.0.5(rollup@4.6.0):
     resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5253,12 +5253,12 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.0.5(rollup@4.4.1)
+      '@rollup/pluginutils': 5.0.5(rollup@4.6.0)
       magic-string: 0.30.5
-      rollup: 4.4.1
+      rollup: 4.6.0
     dev: false
 
-  /@rollup/pluginutils@5.0.5(rollup@4.4.1):
+  /@rollup/pluginutils@5.0.5(rollup@4.6.0):
     resolution: {integrity: sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5270,93 +5270,93 @@ packages:
       '@types/estree': 1.0.5
       estree-walker: 2.0.2
       picomatch: 2.3.1
-      rollup: 4.4.1
+      rollup: 4.6.0
 
-  /@rollup/rollup-android-arm-eabi@4.4.1:
-    resolution: {integrity: sha512-Ss4suS/sd+6xLRu+MLCkED2mUrAyqHmmvZB+zpzZ9Znn9S8wCkTQCJaQ8P8aHofnvG5L16u9MVnJjCqioPErwQ==}
+  /@rollup/rollup-android-arm-eabi@4.6.0:
+    resolution: {integrity: sha512-keHkkWAe7OtdALGoutLY3utvthkGF+Y17ws9LYT8pxMBYXaCoH/8dXS2uzo6e8+sEhY7y/zi5RFo22Dy2lFpDw==}
     cpu: [arm]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-android-arm64@4.4.1:
-    resolution: {integrity: sha512-sRSkGTvGsARwWd7TzC8LKRf8FiPn7257vd/edzmvG4RIr9x68KBN0/Ek48CkuUJ5Pj/Dp9vKWv6PEupjKWjTYA==}
+  /@rollup/rollup-android-arm64@4.6.0:
+    resolution: {integrity: sha512-y3Kt+34smKQNWilicPbBz/MXEY7QwDzMFNgwEWeYiOhUt9MTWKjHqe3EVkXwT2fR7izOvHpDWZ0o2IyD9SWX7A==}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-arm64@4.4.1:
-    resolution: {integrity: sha512-nz0AiGrrXyaWpsmBXUGOBiRDU0wyfSXbFuF98pPvIO8O6auQsPG6riWsfQqmCCC5FNd8zKQ4JhgugRNAkBJ8mQ==}
+  /@rollup/rollup-darwin-arm64@4.6.0:
+    resolution: {integrity: sha512-oLzzxcUIHltHxOCmaXl+pkIlU+uhSxef5HfntW7RsLh1eHm+vJzjD9Oo4oUKso4YuP4PpbFJNlZjJuOrxo8dPg==}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-x64@4.4.1:
-    resolution: {integrity: sha512-Ogqvf4/Ve/faMaiPRvzsJEqajbqs00LO+8vtrPBVvLgdw4wBg6ZDXdkDAZO+4MLnrc8mhGV6VJAzYScZdPLtJg==}
+  /@rollup/rollup-darwin-x64@4.6.0:
+    resolution: {integrity: sha512-+ANnmjkcOBaV25n0+M0Bere3roeVAnwlKW65qagtuAfIxXF9YxUneRyAn/RDcIdRa7QrjRNJL3jR7T43ObGe8Q==}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm-gnueabihf@4.4.1:
-    resolution: {integrity: sha512-9zc2tqlr6HfO+hx9+wktUlWTRdje7Ub15iJqKcqg5uJZ+iKqmd2CMxlgPpXi7+bU7bjfDIuvCvnGk7wewFEhCg==}
+  /@rollup/rollup-linux-arm-gnueabihf@4.6.0:
+    resolution: {integrity: sha512-tBTSIkjSVUyrekddpkAqKOosnj1Fc0ZY0rJL2bIEWPKqlEQk0paORL9pUIlt7lcGJi3LzMIlUGXvtNi1Z6MOCQ==}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-gnu@4.4.1:
-    resolution: {integrity: sha512-phLb1fN3rq2o1j1v+nKxXUTSJnAhzhU0hLrl7Qzb0fLpwkGMHDem+o6d+ZI8+/BlTXfMU4kVWGvy6g9k/B8L6Q==}
+  /@rollup/rollup-linux-arm64-gnu@4.6.0:
+    resolution: {integrity: sha512-Ed8uJI3kM11de9S0j67wAV07JUNhbAqIrDYhQBrQW42jGopgheyk/cdcshgGO4fW5Wjq97COCY/BHogdGvKVNQ==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-musl@4.4.1:
-    resolution: {integrity: sha512-M2sDtw4tf57VPSjbTAN/lz1doWUqO2CbQuX3L9K6GWIR5uw9j+ROKCvvUNBY8WUbMxwaoc8mH9HmmBKsLht7+w==}
+  /@rollup/rollup-linux-arm64-musl@4.6.0:
+    resolution: {integrity: sha512-mZoNQ/qK4D7SSY8v6kEsAAyDgznzLLuSFCA3aBHZTmf3HP/dW4tNLTtWh9+LfyO0Z1aUn+ecpT7IQ3WtIg3ViQ==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-gnu@4.4.1:
-    resolution: {integrity: sha512-mHIlRLX+hx+30cD6c4BaBOsSqdnCE4ok7/KDvjHYAHoSuveoMMxIisZFvcLhUnyZcPBXDGZTuBoalcuh43UfQQ==}
+  /@rollup/rollup-linux-x64-gnu@4.6.0:
+    resolution: {integrity: sha512-rouezFHpwCqdEXsqAfNsTgSWO0FoZ5hKv5p+TGO5KFhyN/dvYXNMqMolOb8BkyKcPqjYRBeT+Z6V3aM26rPaYg==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-musl@4.4.1:
-    resolution: {integrity: sha512-tB+RZuDi3zxFx7vDrjTNGVLu2KNyzYv+UY8jz7e4TMEoAj7iEt8Qk6xVu6mo3pgjnsHj6jnq3uuRsHp97DLwOA==}
+  /@rollup/rollup-linux-x64-musl@4.6.0:
+    resolution: {integrity: sha512-Bbm+fyn3S6u51urfj3YnqBXg5vI2jQPncRRELaucmhBVyZkbWClQ1fEsRmdnCPpQOQfkpg9gZArvtMVkOMsh1w==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-arm64-msvc@4.4.1:
-    resolution: {integrity: sha512-Hdn39PzOQowK/HZzYpCuZdJC91PE6EaGbTe2VCA9oq2u18evkisQfws0Smh9QQGNNRa/T7MOuGNQoLeXhhE3PQ==}
+  /@rollup/rollup-win32-arm64-msvc@4.6.0:
+    resolution: {integrity: sha512-+MRMcyx9L2kTrTUzYmR61+XVsliMG4odFb5UmqtiT8xOfEicfYAGEuF/D1Pww1+uZkYhBqAHpvju7VN+GnC3ng==}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-ia32-msvc@4.4.1:
-    resolution: {integrity: sha512-tLpKb1Elm9fM8c5w3nl4N1eLTP4bCqTYw9tqUBxX8/hsxqHO3dxc2qPbZ9PNkdK4tg4iLEYn0pOUnVByRd2CbA==}
+  /@rollup/rollup-win32-ia32-msvc@4.6.0:
+    resolution: {integrity: sha512-rxfeE6K6s/Xl2HGeK6cO8SiQq3k/3BYpw7cfhW5Bk2euXNEpuzi2cc7llxx1si1QgwfjNtdRNTGqdBzGlFZGFw==}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-x64-msvc@4.4.1:
-    resolution: {integrity: sha512-eAhItDX9yQtZVM3yvXS/VR3qPqcnXvnLyx1pLXl4JzyNMBNO3KC986t/iAg2zcMzpAp9JSvxB5VZGnBiNoA98w==}
+  /@rollup/rollup-win32-x64-msvc@4.6.0:
+    resolution: {integrity: sha512-QqmCsydHS172Y0Kc13bkMXvipbJSvzeglBncJG3LsYJSiPlxYACz7MmJBs4A8l1oU+jfhYEIC/+AUSlvjmiX/g==}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rushstack/node-core-library@3.61.0(@types/node@20.9.1):
+  /@rushstack/node-core-library@3.61.0(@types/node@20.10.0):
     resolution: {integrity: sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==}
     peerDependencies:
       '@types/node': '*'
@@ -5364,7 +5364,7 @@ packages:
       '@types/node':
         optional: true
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       colors: 1.2.5
       fs-extra: 7.0.1
       import-lazy: 4.0.0
@@ -5488,6 +5488,14 @@ packages:
     resolution: {integrity: sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==}
     dev: true
 
+  /@smithy/abort-controller@2.0.14:
+    resolution: {integrity: sha512-zXtteuYLWbSXnzI3O6xq3FYvigYZFW8mdytGibfarLL2lxHto9L3ILtGVnVGmFZa7SDh62l39EnU5hesLN87Fw==}
+    engines: {node: '>=14.0.0'}
+    dependencies:
+      '@smithy/types': 2.6.0
+      tslib: 2.6.2
+    dev: false
+
   /@smithy/abort-controller@2.0.9:
     resolution: {integrity: sha512-8liHOEbx99xcy4VndeQNQhyA0LS+e7UqsuRnDTSIA26IKBv/7vA9w09KOd4fgNULrvX0r3WpA6cwsQTRJpSWkg==}
     engines: {node: '>=14.0.0'}
@@ -5695,14 +5703,14 @@ packages:
       tslib: 2.6.2
     dev: false
 
-  /@smithy/node-http-handler@2.1.5:
-    resolution: {integrity: sha512-52uF+BrZaFiBh+NT/bADiVDCQO91T+OwDRsuaAeWZC1mlCXFjAPPQdxeQohtuYOe9m7mPP/xIMNiqbe8jvndHA==}
+  /@smithy/node-http-handler@2.1.10:
+    resolution: {integrity: sha512-lkALAwtN6odygIM4nB8aHDahINM6WXXjNrZmWQAh0RSossySRT2qa31cFv0ZBuAYVWeprskRk13AFvvLmf1WLw==}
     engines: {node: '>=14.0.0'}
     dependencies:
-      '@smithy/abort-controller': 2.0.9
-      '@smithy/protocol-http': 3.0.5
-      '@smithy/querystring-builder': 2.0.9
-      '@smithy/types': 2.3.3
+      '@smithy/abort-controller': 2.0.14
+      '@smithy/protocol-http': 3.0.10
+      '@smithy/querystring-builder': 2.0.14
+      '@smithy/types': 2.6.0
       tslib: 2.6.2
     dev: false
 
@@ -5714,6 +5722,14 @@ packages:
       tslib: 2.6.2
     dev: false
 
+  /@smithy/protocol-http@3.0.10:
+    resolution: {integrity: sha512-6+tjNk7rXW7YTeGo9qwxXj/2BFpJTe37kTj3EnZCoX/nH+NP/WLA7O83fz8XhkGqsaAhLUPo/bB12vvd47nsmg==}
+    engines: {node: '>=14.0.0'}
+    dependencies:
+      '@smithy/types': 2.6.0
+      tslib: 2.6.2
+    dev: false
+
   /@smithy/protocol-http@3.0.5:
     resolution: {integrity: sha512-3t3fxj+ip4EPHRC2fQ0JimMxR/qCQ1LSQJjZZVZFgROnFLYWPDgUZqpoi7chr+EzatxJVXF/Rtoi5yLHOWCoZQ==}
     engines: {node: '>=14.0.0'}
@@ -5722,6 +5738,15 @@ packages:
       tslib: 2.6.2
     dev: false
 
+  /@smithy/querystring-builder@2.0.14:
+    resolution: {integrity: sha512-lQ4pm9vTv9nIhl5jt6uVMPludr6syE2FyJmHsIJJuOD7QPIJnrf9HhUGf1iHh9KJ4CUv21tpOU3X6s0rB6uJ0g==}
+    engines: {node: '>=14.0.0'}
+    dependencies:
+      '@smithy/types': 2.6.0
+      '@smithy/util-uri-escape': 2.0.0
+      tslib: 2.6.2
+    dev: false
+
   /@smithy/querystring-builder@2.0.9:
     resolution: {integrity: sha512-Yt6CPF4j3j1cuwod/DRflbuXxBFjJm7gAjy6W1RE21Rz5/kfGFqiZBXWmmXwGtnnhiLThYwoHK4S6/TQtnx0Fg==}
     engines: {node: '>=14.0.0'}
@@ -5785,6 +5810,13 @@ packages:
       tslib: 2.6.2
     dev: false
 
+  /@smithy/types@2.6.0:
+    resolution: {integrity: sha512-PgqxJq2IcdMF9iAasxcqZqqoOXBHufEfmbEUdN1pmJrJltT42b0Sc8UiYSWWzKkciIp9/mZDpzYi4qYG1qqg6g==}
+    engines: {node: '>=14.0.0'}
+    dependencies:
+      tslib: 2.6.2
+    dev: false
+
   /@smithy/url-parser@2.0.8:
     resolution: {integrity: sha512-wQw7j004ScCrBRJ+oNPXlLE9mtofxyadSZ9D8ov/rHkyurS7z1HTNuyaGRj6OvKsEk0SVQsuY0C9+EfM75XTkw==}
     dependencies:
@@ -5882,7 +5914,7 @@ packages:
     engines: {node: '>=14.0.0'}
     dependencies:
       '@smithy/fetch-http-handler': 2.1.4
-      '@smithy/node-http-handler': 2.1.5
+      '@smithy/node-http-handler': 2.1.10
       '@smithy/types': 2.3.3
       '@smithy/util-base64': 2.0.0
       '@smithy/util-buffer-from': 2.0.0
@@ -6348,7 +6380,7 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-vite@7.5.3(typescript@5.3.2)(vite@4.5.0):
+  /@storybook/builder-vite@7.5.3(typescript@5.3.2)(vite@5.0.2):
     resolution: {integrity: sha512-c104V3O75OCVnfZj0Jr70V09g0KSbPGvQK2Zh31omXGvakG8XrhWolYxkmjOcForJmAqsXnKs/nw3F75Gp853g==}
     peerDependencies:
       '@preact/preset-vite': '*'
@@ -6380,7 +6412,7 @@ packages:
       magic-string: 0.30.5
       rollup: 3.29.4
       typescript: 5.3.2
-      vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -6413,7 +6445,7 @@ packages:
       '@storybook/node-logger': 7.5.3
       '@storybook/telemetry': 7.5.3
       '@storybook/types': 7.5.3
-      '@types/semver': 7.5.5
+      '@types/semver': 7.5.6
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
       chalk: 4.1.2
@@ -6564,7 +6596,7 @@ packages:
       '@types/detect-port': 1.3.2
       '@types/node': 18.17.15
       '@types/pretty-hrtime': 1.0.1
-      '@types/semver': 7.5.5
+      '@types/semver': 7.5.6
       better-opn: 3.0.2
       chalk: 4.1.2
       cli-table3: 0.6.3
@@ -6750,7 +6782,7 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/react-vite@7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.4.1)(typescript@5.3.2)(vite@4.5.0):
+  /@storybook/react-vite@7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.6.0)(typescript@5.3.2)(vite@5.0.2):
     resolution: {integrity: sha512-ArPyHgiPbT5YvcyK4xK/DfqBOpn4R4/EP3kfIGhx8QKJyOtxPEYFdkLIZ5xu3KnPX7/z7GT+4a6Rb+8sk9gliA==}
     engines: {node: '>=16'}
     peerDependencies:
@@ -6758,16 +6790,16 @@ packages:
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
       vite: ^3.0.0 || ^4.0.0 || ^5.0.0
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.2)(vite@4.5.0)
-      '@rollup/pluginutils': 5.0.5(rollup@4.4.1)
-      '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@4.5.0)
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.2)(vite@5.0.2)
+      '@rollup/pluginutils': 5.0.5(rollup@4.6.0)
+      '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@5.0.2)
       '@storybook/react': 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)
-      '@vitejs/plugin-react': 3.1.0(vite@4.5.0)
+      '@vitejs/plugin-react': 3.1.0(vite@5.0.2)
       magic-string: 0.30.5
       react: 18.2.0
       react-docgen: 6.0.4
       react-dom: 18.2.0(react@18.2.0)
-      vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - encoding
@@ -6892,7 +6924,7 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@4.5.0)(vue@3.3.8):
+  /@storybook/vue3-vite@7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9):
     resolution: {integrity: sha512-gkNwDDn2AKthAtaoPrHb0+2gi33UluxpfSq/M5COoMEVFphj6y/jyDa+OEYlceXgnD8g2xvX4/yv2TbTNDzmcQ==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
@@ -6900,15 +6932,15 @@ packages:
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
       vite: ^3.0.0 || ^4.0.0 || ^5.0.0
     dependencies:
-      '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@4.5.0)
+      '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@5.0.2)
       '@storybook/core-server': 7.5.3
-      '@storybook/vue3': 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.8)
-      '@vitejs/plugin-vue': 4.5.0(vite@4.5.0)(vue@3.3.8)
+      '@storybook/vue3': 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.9)
+      '@vitejs/plugin-vue': 4.5.0(vite@5.0.2)(vue@3.3.9)
       magic-string: 0.30.5
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
-      vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
-      vue-docgen-api: 4.64.1(vue@3.3.8)
+      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+      vue-docgen-api: 4.64.1(vue@3.3.9)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - '@vue/compiler-core'
@@ -6921,7 +6953,7 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.8):
+  /@storybook/vue3@7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.9):
     resolution: {integrity: sha512-JaxtOl3UD9YhPrOqHuKtpqHMnFril3sBUxx/no2yM/mZYmNpAVd/C6PFM839WCay1mAywPuUoebJvmwWxWijkw==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
@@ -6937,14 +6969,14 @@ packages:
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      vue: 3.3.8(typescript@5.3.2)
+      vue: 3.3.9(typescript@5.3.2)
       vue-component-type-helpers: 1.8.22
     transitivePeerDependencies:
       - encoding
       - supports-color
     dev: true
 
-  /@swc/cli@0.1.63(@swc/core@1.3.96)(chokidar@3.5.3):
+  /@swc/cli@0.1.63(@swc/core@1.3.99)(chokidar@3.5.3):
     resolution: {integrity: sha512-EM9oxxHzmmsprYRbGqsS2M4M/Gr5Gkcl0ROYYIdlUyTkhOiX822EQiRCpPCwdutdnzH2GyaTN7wc6i0Y+CKd3A==}
     engines: {node: '>= 12.13'}
     hasBin: true
@@ -6956,7 +6988,7 @@ packages:
         optional: true
     dependencies:
       '@mole-inc/bin-wrapper': 8.0.1
-      '@swc/core': 1.3.96
+      '@swc/core': 1.3.99
       chokidar: 3.5.3
       commander: 7.2.0
       fast-glob: 3.3.2
@@ -6985,8 +7017,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-darwin-arm64@1.3.96:
-    resolution: {integrity: sha512-8hzgXYVd85hfPh6mJ9yrG26rhgzCmcLO0h1TIl8U31hwmTbfZLzRitFQ/kqMJNbIBCwmNH1RU2QcJnL3d7f69A==}
+  /@swc/core-darwin-arm64@1.3.99:
+    resolution: {integrity: sha512-Qj7Jct68q3ZKeuJrjPx7k8SxzWN6PqLh+VFxzA+KwLDpQDPzOlKRZwkIMzuFjLhITO4RHgSnXoDk/Syz0ZeN+Q==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [darwin]
@@ -7002,8 +7034,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-darwin-x64@1.3.96:
-    resolution: {integrity: sha512-mFp9GFfuPg+43vlAdQZl0WZpZSE8sEzqL7sr/7Reul5McUHP0BaLsEzwjvD035ESfkY8GBZdLpMinblIbFNljQ==}
+  /@swc/core-darwin-x64@1.3.99:
+    resolution: {integrity: sha512-wR7m9QVJjgiBu1PSOHy7s66uJPa45Kf9bZExXUL+JAa9OQxt5y+XVzr+n+F045VXQOwdGWplgPnWjgbUUHEVyw==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [darwin]
@@ -7030,14 +7062,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-arm-gnueabihf@1.3.96:
-    resolution: {integrity: sha512-8UEKkYJP4c8YzYIY/LlbSo8z5Obj4hqcv/fUTHiEePiGsOddgGf7AWjh56u7IoN/0uEmEro59nc1ChFXqXSGyg==}
-    engines: {node: '>=10'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-linux-arm64-gnu@1.3.56:
     resolution: {integrity: sha512-GzsUy/4egJ4cMlxbM+Ub7AMi5CKAc+pxBxrh8MUPQbyStW8jGgnQsJouTnGy0LHawtdEnsCOl6PcO6OgvktXuQ==}
     engines: {node: '>=10'}
@@ -7047,8 +7071,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-arm64-gnu@1.3.96:
-    resolution: {integrity: sha512-c/IiJ0s1y3Ymm2BTpyC/xr6gOvoqAVETrivVXHq68xgNms95luSpbYQ28rqaZC8bQC8M5zdXpSc0T8DJu8RJGw==}
+  /@swc/core-linux-arm64-gnu@1.3.99:
+    resolution: {integrity: sha512-gcGv1l5t0DScEONmw5OhdVmEI/o49HCe9Ik38zzH0NtDkc+PDYaCcXU5rvfZP2qJFaAAr8cua8iJcOunOSLmnA==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
@@ -7064,8 +7088,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-arm64-musl@1.3.96:
-    resolution: {integrity: sha512-i5/UTUwmJLri7zhtF6SAo/4QDQJDH2fhYJaBIUhrICmIkRO/ltURmpejqxsM/ye9Jqv5zG7VszMC0v/GYn/7BQ==}
+  /@swc/core-linux-arm64-musl@1.3.99:
+    resolution: {integrity: sha512-XL1/eUsTO8BiKsWq9i3iWh7H99iPO61+9HYiWVKhSavknfj4Plbn+XyajDpxsauln5o8t+BRGitymtnAWJM4UQ==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
@@ -7081,8 +7105,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-x64-gnu@1.3.96:
-    resolution: {integrity: sha512-USdaZu8lTIkm4Yf9cogct/j5eqtdZqTgcTib4I+NloUW0E/hySou3eSyp3V2UAA1qyuC72ld1otXuyKBna0YKQ==}
+  /@swc/core-linux-x64-gnu@1.3.99:
+    resolution: {integrity: sha512-fGrXYE6DbTfGNIGQmBefYxSk3rp/1lgbD0nVg4rl4mfFRQPi7CgGhrrqSuqZ/ezXInUIgoCyvYGWFSwjLXt/Qg==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
@@ -7098,8 +7122,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-x64-musl@1.3.96:
-    resolution: {integrity: sha512-QYErutd+G2SNaCinUVobfL7jWWjGTI0QEoQ6hqTp7PxCJS/dmKmj3C5ZkvxRYcq7XcZt7ovrYCTwPTHzt6lZBg==}
+  /@swc/core-linux-x64-musl@1.3.99:
+    resolution: {integrity: sha512-kvgZp/mqf3IJ806gUOL6gN6VU15+DfzM1Zv4Udn8GqgXiUAvbQehrtruid4Snn5pZTLj4PEpSCBbxgxK1jbssA==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
@@ -7115,8 +7139,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-arm64-msvc@1.3.96:
-    resolution: {integrity: sha512-hjGvvAduA3Un2cZ9iNP4xvTXOO4jL3G9iakhFsgVhpkU73SGmK7+LN8ZVBEu4oq2SUcHO6caWvnZ881cxGuSpg==}
+  /@swc/core-win32-arm64-msvc@1.3.99:
+    resolution: {integrity: sha512-yt8RtZ4W/QgFF+JUemOUQAkVW58cCST7mbfKFZ1v16w3pl3NcWd9OrtppFIXpbjU1rrUX2zp2R7HZZzZ2Zk/aQ==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [win32]
@@ -7132,8 +7156,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-ia32-msvc@1.3.96:
-    resolution: {integrity: sha512-Far2hVFiwr+7VPCM2GxSmbh3ikTpM3pDombE+d69hkedvYHYZxtTF+2LTKl/sXtpbUnsoq7yV/32c9R/xaaWfw==}
+  /@swc/core-win32-ia32-msvc@1.3.99:
+    resolution: {integrity: sha512-62p5fWnOJR/rlbmbUIpQEVRconICy5KDScWVuJg1v3GPLBrmacjphyHiJC1mp6dYvvoEWCk/77c/jcQwlXrDXw==}
     engines: {node: '>=10'}
     cpu: [ia32]
     os: [win32]
@@ -7149,16 +7173,16 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-x64-msvc@1.3.96:
-    resolution: {integrity: sha512-4VbSAniIu0ikLf5mBX81FsljnfqjoVGleEkCQv4+zRlyZtO3FHoDPkeLVoy6WRlj7tyrRcfUJ4mDdPkbfTO14g==}
+  /@swc/core-win32-x64-msvc@1.3.99:
+    resolution: {integrity: sha512-PdppWhkoS45VGdMBxvClVgF1hVjqamtvYd82Gab1i4IV45OSym2KinoDCKE1b6j3LwBLOn2J9fvChGSgGfDCHQ==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@swc/core@1.3.96:
-    resolution: {integrity: sha512-zwE3TLgoZwJfQygdv2SdCK9mRLYluwDOM53I+dT6Z5ZvrgVENmY3txvWDvduzkV+/8IuvrRbVezMpxcojadRdQ==}
+  /@swc/core@1.3.99:
+    resolution: {integrity: sha512-8O996RfuPC4ieb4zbYMfbyCU9k4gSOpyCNnr7qBQ+o7IEmh8JCV6B8wwu+fT/Om/6Lp34KJe1IpJ/24axKS6TQ==}
     engines: {node: '>=10'}
     requiresBuild: true
     peerDependencies:
@@ -7170,28 +7194,27 @@ packages:
       '@swc/counter': 0.1.1
       '@swc/types': 0.1.5
     optionalDependencies:
-      '@swc/core-darwin-arm64': 1.3.96
-      '@swc/core-darwin-x64': 1.3.96
-      '@swc/core-linux-arm-gnueabihf': 1.3.96
-      '@swc/core-linux-arm64-gnu': 1.3.96
-      '@swc/core-linux-arm64-musl': 1.3.96
-      '@swc/core-linux-x64-gnu': 1.3.96
-      '@swc/core-linux-x64-musl': 1.3.96
-      '@swc/core-win32-arm64-msvc': 1.3.96
-      '@swc/core-win32-ia32-msvc': 1.3.96
-      '@swc/core-win32-x64-msvc': 1.3.96
+      '@swc/core-darwin-arm64': 1.3.99
+      '@swc/core-darwin-x64': 1.3.99
+      '@swc/core-linux-arm64-gnu': 1.3.99
+      '@swc/core-linux-arm64-musl': 1.3.99
+      '@swc/core-linux-x64-gnu': 1.3.99
+      '@swc/core-linux-x64-musl': 1.3.99
+      '@swc/core-win32-arm64-msvc': 1.3.99
+      '@swc/core-win32-ia32-msvc': 1.3.99
+      '@swc/core-win32-x64-msvc': 1.3.99
 
   /@swc/counter@0.1.1:
     resolution: {integrity: sha512-xVRaR4u9hcYjFvcSg71Lz5Bo4//CyjAAfMxa7UsaDSYxAshflUkVJWiyVWrfxC59z2kP1IzI4/1BEpnhI9o3Mw==}
 
-  /@swc/jest@0.2.29(@swc/core@1.3.96):
+  /@swc/jest@0.2.29(@swc/core@1.3.99):
     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.96
+      '@swc/core': 1.3.99
       jsonc-parser: 3.2.0
     dev: true
 
@@ -7421,8 +7444,8 @@ packages:
       '@testing-library/dom': 9.2.0
     dev: true
 
-  /@testing-library/vue@8.0.0(@vue/compiler-sfc@3.3.8)(vue@3.3.8):
-    resolution: {integrity: sha512-SP0qEY/SGpdT9+bPuHxYD3P/HCG0ZY8GlGJocVqdLn9EojbdQu69x06trJi1V7RW9tAZai/wwy+ZFcRsTp47kg==}
+  /@testing-library/vue@8.0.1(@vue/compiler-sfc@3.3.9)(vue@3.3.9):
+    resolution: {integrity: sha512-l51ZEpjTQ6glq3wM+asQ1GbKJMGcxwgHEygETx0aCRN4TjFEGvMZy4YdWKs/y7bu4bmLrxcxhbEPP7iPSW/2OQ==}
     engines: {node: '>=14'}
     peerDependencies:
       '@vue/compiler-sfc': '>= 3'
@@ -7430,9 +7453,9 @@ packages:
     dependencies:
       '@babel/runtime': 7.23.2
       '@testing-library/dom': 9.3.3
-      '@vue/compiler-sfc': 3.3.8
-      '@vue/test-utils': 2.4.1(vue@3.3.8)
-      vue: 3.3.8(typescript@5.3.2)
+      '@vue/compiler-sfc': 3.3.9
+      '@vue/test-utils': 2.4.1(vue@3.3.9)
+      vue: 3.3.9(typescript@5.3.2)
     transitivePeerDependencies:
       - '@vue/server-renderer'
     dev: true
@@ -7441,11 +7464,6 @@ packages:
     resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==}
     dev: false
 
-  /@tootallnate/once@2.0.0:
-    resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
-    engines: {node: '>= 10'}
-    dev: false
-
   /@trysound/sax@0.2.0:
     resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
     engines: {node: '>=10.13.0'}
@@ -7459,11 +7477,11 @@ packages:
   /@types/accepts@1.3.7:
     resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
-  /@types/archiver@6.0.1:
-    resolution: {integrity: sha512-F2+JkmDYvtQrtb2YldwL0apRB1/WB6ub+1zVF/bKp3TOygUMFqfOLuw5Fj62Q+DPwJUFz1eocMxJMu7yVpplZA==}
+  /@types/archiver@6.0.2:
+    resolution: {integrity: sha512-KmROQqbQzKGuaAbmK+ZcytkJ51+YqDa7NmbXjmtC5YBLSyQYo21YaUnQ3HbaPFKL1ooo6RQ6OPYPIDyxfpDDXw==}
     dependencies:
       '@types/readdir-glob': 1.1.1
     dev: true
@@ -7513,7 +7531,7 @@ packages:
     resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
     dependencies:
       '@types/connect': 3.4.35
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/braces@3.0.1:
@@ -7525,7 +7543,7 @@ packages:
     dependencies:
       '@types/http-cache-semantics': 4.0.1
       '@types/keyv': 3.1.4
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       '@types/responselike': 1.0.0
     dev: false
 
@@ -7558,7 +7576,7 @@ packages:
   /@types/connect@3.4.35:
     resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/content-disposition@0.5.8:
@@ -7572,7 +7590,7 @@ packages:
   /@types/cross-spawn@6.0.2:
     resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/debug@4.1.7:
@@ -7630,7 +7648,7 @@ packages:
   /@types/express-serve-static-core@4.17.33:
     resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       '@types/qs': 6.9.7
       '@types/range-parser': 1.2.4
     dev: true
@@ -7651,20 +7669,20 @@ packages:
   /@types/fluent-ffmpeg@2.1.24:
     resolution: {integrity: sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/glob@7.2.0:
     resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/graceful-fs@4.1.6:
     resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/hast@2.3.4:
@@ -7679,7 +7697,7 @@ packages:
   /@types/http-link-header@1.0.5:
     resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/istanbul-lib-coverage@2.0.4:
@@ -7705,8 +7723,8 @@ packages:
       pretty-format: 28.1.3
     dev: true
 
-  /@types/jest@29.5.8:
-    resolution: {integrity: sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==}
+  /@types/jest@29.5.10:
+    resolution: {integrity: sha512-tE4yxKEphEyxj9s4inideLHktW/x6DwesIwWZ9NN1FKf9zbJYsnhBoA9vrHA/IuIOKwPa5PcFBNV4lpMIOEzyQ==}
     dependencies:
       expect: 29.7.0
       pretty-format: 29.7.0
@@ -7720,10 +7738,10 @@ packages:
     resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
     dev: true
 
-  /@types/jsdom@21.1.5:
-    resolution: {integrity: sha512-sBK/3YjS3uuPj+HzZyhB4GGTnFmk0mdyQfhzZ/sqs9ciyG41QJdZZdwcPa6OfW97OTNTwl5tBAsfEOm/dui9pQ==}
+  /@types/jsdom@21.1.6:
+    resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       '@types/tough-cookie': 4.0.2
       parse5: 7.1.2
     dev: true
@@ -7736,8 +7754,8 @@ packages:
     resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
     dev: true
 
-  /@types/jsonld@1.5.12:
-    resolution: {integrity: sha512-y2EDlpPhuifmqcijoLV0zu9Pw3fd40RIZqpX4V0v7cq6vVFXjBOMhCGe2SlfTPzTZBJLZUFBidWshTYFfInvDQ==}
+  /@types/jsonld@1.5.13:
+    resolution: {integrity: sha512-n7fUU6W4kSYK8VQlf/LsE9kddBHPKhODoVOjsZswmve+2qLwBy6naWxs/EiuSZN9NU0N06Ra01FR+j87C62T0A==}
     dev: true
 
   /@types/jsrsasign@10.5.12:
@@ -7747,7 +7765,7 @@ packages:
   /@types/keyv@3.1.4:
     resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: false
 
   /@types/lodash@4.14.191:
@@ -7759,16 +7777,16 @@ packages:
     requiresBuild: true
     dev: false
 
-  /@types/matter-js@0.19.4:
-    resolution: {integrity: sha512-CHKobJ2Kr9GJqr1uvoL4v3DCCgf44b0qJcOctbHtkmPBDMMN0ORnIwNS0WNFxiD0YqtySZH7IgaefGFZ0NUcMA==}
+  /@types/matter-js@0.19.5:
+    resolution: {integrity: sha512-pTVB5krRGb01hr8L6BJqWGoSriqUbbvJ9Fd0Qp0eAOE//w/lFvkaVHkVB8J3wXr9U3lZDzmAjJPPQn7x4wzbWg==}
     dev: true
 
   /@types/mdx@2.0.3:
     resolution: {integrity: sha512-IgHxcT3RC8LzFLhKwP3gbMPeaK7BM9eBH46OdapPA7yvuIUJ8H6zHZV53J8hGZcTSnt95jANt+rTBNUUc22ACQ==}
     dev: true
 
-  /@types/micromatch@4.0.5:
-    resolution: {integrity: sha512-B1o0zVdb9GsbKT4Fucy3oeG9G1qy/TOHrYM+NsEPazT+ktsGXOJSb1+Bg9hP7BH14Bv4dd5m7r+FohwXkY/39A==}
+  /@types/micromatch@4.0.6:
+    resolution: {integrity: sha512-2eulCHWqjEpk9/vyic4tBhI8a9qQEl6DaK2n/sF7TweX9YESlypgKyhXMDGt4DAOy/jhLPvVrZc8pTDAMsplJA==}
     dependencies:
       '@types/braces': 3.0.1
     dev: true
@@ -7796,7 +7814,7 @@ packages:
   /@types/node-fetch@2.6.4:
     resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       form-data: 3.0.1
 
   /@types/node-fetch@3.0.3:
@@ -7809,15 +7827,15 @@ packages:
     resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==}
     dev: true
 
-  /@types/node@20.9.1:
-    resolution: {integrity: sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==}
+  /@types/node@20.10.0:
+    resolution: {integrity: sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==}
     dependencies:
       undici-types: 5.26.5
 
   /@types/nodemailer@6.4.14:
     resolution: {integrity: sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/normalize-package-data@2.4.1:
@@ -7834,13 +7852,13 @@ packages:
     resolution: {integrity: sha512-Ali0fUUn+zgr4Yy/pCTFbuiaiJpq7l7OQwFnxYVchNbNGIx0c4Wkcdje6WO89I91RAaYF+gVc1pOaizA4YKZmA==}
     dependencies:
       '@types/express': 4.17.17
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/oauth@0.9.4:
     resolution: {integrity: sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/offscreencanvas@2019.3.0:
@@ -7856,7 +7874,7 @@ packages:
   /@types/pg@8.10.9:
     resolution: {integrity: sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       pg-protocol: 1.6.0
       pg-types: 4.0.1
     dev: true
@@ -7869,18 +7887,18 @@ packages:
     resolution: {integrity: sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==}
     dev: true
 
-  /@types/pug@2.0.9:
-    resolution: {integrity: sha512-Yg4LkgFYvn1faISbDNWmcAC1XoDT8IoMUFspp5mnagKk+UvD2N0IWt5A7GRdMubsNWqgCLmrkf8rXkzNqb4szA==}
+  /@types/pug@2.0.10:
+    resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==}
     dev: true
 
-  /@types/punycode@2.1.2:
-    resolution: {integrity: sha512-KKQ4GjRCpswGPA6ZfvPrC+7h84bAvPkU1kFGJ3FuQOgZIEc8JmO1jcDCaxSiYcN3qzOOp9YqHq+njKEO3Q4BnA==}
+  /@types/punycode@2.1.3:
+    resolution: {integrity: sha512-dFkH9Mz0yY5UfQVSrpj1grQyqRwe4TohTLlHFx4Gli8/fsaNyoOVUAsiEBZk5JBwbEJVZ49W6st8D5g6dRJb/w==}
     dev: true
 
   /@types/qrcode@1.5.5:
     resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/qs@6.9.7:
@@ -7910,7 +7928,7 @@ packages:
   /@types/readdir-glob@1.1.1:
     resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/rename@1.0.7:
@@ -7924,11 +7942,11 @@ packages:
   /@types/responselike@1.0.0:
     resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: false
 
-  /@types/sanitize-html@2.9.4:
-    resolution: {integrity: sha512-Ym4hjmAFxF/eux7nW2yDPAj2o9RYh0vP/9V5ECoHtgJ/O9nPGslUd20CMn6WatRMlFVfjMTg3lMcWq8YyO6QnA==}
+  /@types/sanitize-html@2.9.5:
+    resolution: {integrity: sha512-2Sr1vd8Dw+ypsg/oDDfZ57OMSG2Befs+l2CMyCC5bVSK3CpE7lTB2aNlbbWzazgVA+Qqfuholwom6x/mWd1qmw==}
     dependencies:
       htmlparser2: 8.0.1
     dev: true
@@ -7942,15 +7960,15 @@ packages:
     requiresBuild: true
     dev: false
 
-  /@types/semver@7.5.5:
-    resolution: {integrity: sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==}
+  /@types/semver@7.5.6:
+    resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==}
     dev: true
 
   /@types/serve-static@1.15.1:
     resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
     dependencies:
       '@types/mime': 3.0.1
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/serviceworker@0.0.67:
@@ -7960,7 +7978,7 @@ packages:
   /@types/set-cookie-parser@2.4.3:
     resolution: {integrity: sha512-7QhnH7bi+6KAhBB+Auejz1uV9DHiopZqu7LfR/5gZZTkejJV5nYeZZpgfFoE0N8aDsXuiYpfKyfyMatCwQhyTQ==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/sharp@0.32.0:
@@ -8023,13 +8041,13 @@ packages:
   /@types/vary@1.1.3:
     resolution: {integrity: sha512-XJT8/ZQCL7NUut9QDLf6l24JfAEl7bnNdgxfj50cHIpEPRJLHHDDFOAq6i+GsEmeFfH7NamhBE4c4Thtb2egWg==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/web-push@3.6.3:
     resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/webgl-ext@0.0.30:
@@ -8037,16 +8055,16 @@ packages:
     requiresBuild: true
     dev: false
 
-  /@types/websocket@1.0.9:
-    resolution: {integrity: sha512-xrMBdqdKdlE+7L9Wg2PQblIkZGSgiMlEoP6UAaYKMHbbxqCJ6PV/pTZ2RcMcSSERurU2TtGbmO4lqpFOJd01ww==}
+  /@types/websocket@1.0.10:
+    resolution: {integrity: sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
-  /@types/ws@8.5.9:
-    resolution: {integrity: sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==}
+  /@types/ws@8.5.10:
+    resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /@types/yargs-parser@21.0.0:
@@ -8069,12 +8087,12 @@ packages:
     resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
     requiresBuild: true
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
     dev: true
     optional: true
 
-  /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==}
+  /@typescript-eslint/eslint-plugin@6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
@@ -8085,13 +8103,13 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
-      '@typescript-eslint/scope-manager': 6.11.0
-      '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
-      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
-      '@typescript-eslint/visitor-keys': 6.11.0
+      '@typescript-eslint/parser': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
+      '@typescript-eslint/scope-manager': 6.12.0
+      '@typescript-eslint/type-utils': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
+      '@typescript-eslint/utils': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
+      '@typescript-eslint/visitor-keys': 6.12.0
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.53.0
+      eslint: 8.54.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -8102,8 +8120,8 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==}
+  /@typescript-eslint/parser@6.12.0(eslint@8.54.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
@@ -8112,27 +8130,27 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/scope-manager': 6.11.0
-      '@typescript-eslint/types': 6.11.0
-      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
-      '@typescript-eslint/visitor-keys': 6.11.0
+      '@typescript-eslint/scope-manager': 6.12.0
+      '@typescript-eslint/types': 6.12.0
+      '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.3.2)
+      '@typescript-eslint/visitor-keys': 6.12.0
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.53.0
+      eslint: 8.54.0
       typescript: 5.3.2
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/scope-manager@6.11.0:
-    resolution: {integrity: sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==}
+  /@typescript-eslint/scope-manager@6.12.0:
+    resolution: {integrity: sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dependencies:
-      '@typescript-eslint/types': 6.11.0
-      '@typescript-eslint/visitor-keys': 6.11.0
+      '@typescript-eslint/types': 6.12.0
+      '@typescript-eslint/visitor-keys': 6.12.0
     dev: true
 
-  /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==}
+  /@typescript-eslint/type-utils@6.12.0(eslint@8.54.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
@@ -8141,23 +8159,23 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
-      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.3.2)
+      '@typescript-eslint/utils': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
       debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.53.0
+      eslint: 8.54.0
       ts-api-utils: 1.0.1(typescript@5.3.2)
       typescript: 5.3.2
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/types@6.11.0:
-    resolution: {integrity: sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==}
+  /@typescript-eslint/types@6.12.0:
+    resolution: {integrity: sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
-  /@typescript-eslint/typescript-estree@6.11.0(typescript@5.3.2):
-    resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==}
+  /@typescript-eslint/typescript-estree@6.12.0(typescript@5.3.2):
+    resolution: {integrity: sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       typescript: '*'
@@ -8165,8 +8183,8 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/types': 6.11.0
-      '@typescript-eslint/visitor-keys': 6.11.0
+      '@typescript-eslint/types': 6.12.0
+      '@typescript-eslint/visitor-keys': 6.12.0
       debug: 4.3.4(supports-color@8.1.1)
       globby: 11.1.0
       is-glob: 4.0.3
@@ -8177,30 +8195,30 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==}
+  /@typescript-eslint/utils@6.12.0(eslint@8.54.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==}
     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.53.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0)
       '@types/json-schema': 7.0.12
-      '@types/semver': 7.5.5
-      '@typescript-eslint/scope-manager': 6.11.0
-      '@typescript-eslint/types': 6.11.0
-      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
-      eslint: 8.53.0
+      '@types/semver': 7.5.6
+      '@typescript-eslint/scope-manager': 6.12.0
+      '@typescript-eslint/types': 6.12.0
+      '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.3.2)
+      eslint: 8.54.0
       semver: 7.5.4
     transitivePeerDependencies:
       - supports-color
       - typescript
     dev: true
 
-  /@typescript-eslint/visitor-keys@6.11.0:
-    resolution: {integrity: sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==}
+  /@typescript-eslint/visitor-keys@6.12.0:
+    resolution: {integrity: sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dependencies:
-      '@typescript-eslint/types': 6.11.0
+      '@typescript-eslint/types': 6.12.0
       eslint-visitor-keys: 3.4.3
     dev: true
 
@@ -8208,7 +8226,7 @@ packages:
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
     dev: true
 
-  /@vitejs/plugin-react@3.1.0(vite@4.5.0):
+  /@vitejs/plugin-react@3.1.0(vite@5.0.2):
     resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
@@ -8219,20 +8237,20 @@ 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.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@vitejs/plugin-vue@4.5.0(vite@4.5.0)(vue@3.3.8):
+  /@vitejs/plugin-vue@4.5.0(vite@5.0.2)(vue@3.3.9):
     resolution: {integrity: sha512-a2WSpP8X8HTEww/U00bU4mX1QpLINNuz/2KMNpLsdu3BzOpak3AGI1CJYBTXcc4SPhaD0eNRUp7IyQK405L5dQ==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
       vite: ^4.0.0 || ^5.0.0
       vue: ^3.2.25
     dependencies:
-      vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
-      vue: 3.3.8(typescript@5.3.2)
+      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+      vue: 3.3.9(typescript@5.3.2)
 
   /@vitest/coverage-v8@0.34.6(vitest@0.34.6):
     resolution: {integrity: sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==}
@@ -8312,7 +8330,7 @@ packages:
       path-browserify: 1.0.1
     dev: true
 
-  /@vue-macros/common@1.9.0(rollup@4.4.1)(vue@3.3.8):
+  /@vue-macros/common@1.9.0(rollup@4.6.0)(vue@3.3.9):
     resolution: {integrity: sha512-LbfRHDkceuokkLlVuQW9Wq3ZLmRs6KIDPzCjUvvL14HB4GslWdtvBB1suFfNs6VMvh9Zj30cEKF/EAP7QBCZ6Q==}
     engines: {node: '>=16.14.0'}
     peerDependencies:
@@ -8322,29 +8340,29 @@ packages:
         optional: true
     dependencies:
       '@babel/types': 7.23.3
-      '@rollup/pluginutils': 5.0.5(rollup@4.4.1)
-      '@vue/compiler-sfc': 3.3.8
-      ast-kit: 0.11.2(rollup@4.4.1)
+      '@rollup/pluginutils': 5.0.5(rollup@4.6.0)
+      '@vue/compiler-sfc': 3.3.9
+      ast-kit: 0.11.2(rollup@4.6.0)
       local-pkg: 0.5.0
       magic-string-ast: 0.3.0
-      vue: 3.3.8(typescript@5.3.2)
+      vue: 3.3.9(typescript@5.3.2)
     transitivePeerDependencies:
       - rollup
     dev: false
 
-  /@vue-macros/reactivity-transform@0.4.0(rollup@4.4.1)(vue@3.3.8):
+  /@vue-macros/reactivity-transform@0.4.0(rollup@4.6.0)(vue@3.3.9):
     resolution: {integrity: sha512-3DG+FWkIZe5xZJhIdxyieIYcDKJGC3aUab1JWtEOkS8Q21rLpu6VKUjV6TmB5LNyLSGVp+7de/87Ptd6C6RHOA==}
     engines: {node: '>=16.14.0'}
     peerDependencies:
       vue: ^2.7.0 || ^3.2.25
     dependencies:
       '@babel/parser': 7.23.3
-      '@vue-macros/common': 1.9.0(rollup@4.4.1)(vue@3.3.8)
+      '@vue-macros/common': 1.9.0(rollup@4.6.0)(vue@3.3.9)
       '@vue/compiler-core': 3.3.8
       '@vue/shared': 3.3.8
       magic-string: 0.30.5
       unplugin: 1.5.1
-      vue: 3.3.8(typescript@5.3.2)
+      vue: 3.3.9(typescript@5.3.2)
     transitivePeerDependencies:
       - rollup
     dev: false
@@ -8375,6 +8393,14 @@ packages:
       estree-walker: 2.0.2
       source-map-js: 1.0.2
 
+  /@vue/compiler-core@3.3.9:
+    resolution: {integrity: sha512-+/Lf68Vr/nFBA6ol4xOtJrW+BQWv3QWKfRwGSm70jtXwfhZNF4R/eRgyVJYoxFRhdCTk/F6g99BP0ffPgZihfQ==}
+    dependencies:
+      '@babel/parser': 7.23.3
+      '@vue/shared': 3.3.9
+      estree-walker: 2.0.2
+      source-map-js: 1.0.2
+
   /@vue/compiler-dom@3.3.6:
     resolution: {integrity: sha512-1MxXcJYMHiTPexjLAJUkNs/Tw2eDf2tY3a0rL+LfuWyiKN2s6jvSwywH3PWD8bKICjfebX3GWx2Os8jkRDq3Ng==}
     dependencies:
@@ -8389,31 +8415,31 @@ packages:
       '@vue/shared': 3.3.7
     dev: true
 
-  /@vue/compiler-dom@3.3.8:
-    resolution: {integrity: sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==}
+  /@vue/compiler-dom@3.3.9:
+    resolution: {integrity: sha512-nfWubTtLXuT4iBeDSZ5J3m218MjOy42Vp2pmKVuBKo2/BLcrFUX8nCSr/bKRFiJ32R8qbdnnnBgRn9AdU5v0Sg==}
     dependencies:
-      '@vue/compiler-core': 3.3.8
-      '@vue/shared': 3.3.8
+      '@vue/compiler-core': 3.3.9
+      '@vue/shared': 3.3.9
 
-  /@vue/compiler-sfc@3.3.8:
-    resolution: {integrity: sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==}
+  /@vue/compiler-sfc@3.3.9:
+    resolution: {integrity: sha512-wy0CNc8z4ihoDzjASCOCsQuzW0A/HP27+0MDSSICMjVIFzk/rFViezkR3dzH+miS2NDEz8ywMdbjO5ylhOLI2A==}
     dependencies:
-      '@babel/parser': 7.23.0
-      '@vue/compiler-core': 3.3.8
-      '@vue/compiler-dom': 3.3.8
-      '@vue/compiler-ssr': 3.3.8
-      '@vue/reactivity-transform': 3.3.8
-      '@vue/shared': 3.3.8
+      '@babel/parser': 7.23.3
+      '@vue/compiler-core': 3.3.9
+      '@vue/compiler-dom': 3.3.9
+      '@vue/compiler-ssr': 3.3.9
+      '@vue/reactivity-transform': 3.3.9
+      '@vue/shared': 3.3.9
       estree-walker: 2.0.2
       magic-string: 0.30.5
       postcss: 8.4.31
       source-map-js: 1.0.2
 
-  /@vue/compiler-ssr@3.3.8:
-    resolution: {integrity: sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==}
+  /@vue/compiler-ssr@3.3.9:
+    resolution: {integrity: sha512-NO5oobAw78R0G4SODY5A502MGnDNiDjf6qvhn7zD7TJGc8XDeIEw4fg6JU705jZ/YhuokBKz0A5a/FL/XZU73g==}
     dependencies:
-      '@vue/compiler-dom': 3.3.8
-      '@vue/shared': 3.3.8
+      '@vue/compiler-dom': 3.3.9
+      '@vue/shared': 3.3.9
 
   /@vue/language-core@1.8.22(typescript@5.3.2):
     resolution: {integrity: sha512-bsMoJzCrXZqGsxawtUea1cLjUT9dZnDsy5TuZ+l1fxRMzUGQUG9+Ypq4w//CqpWmrx7nIAJpw2JVF/t258miRw==}
@@ -8434,41 +8460,41 @@ packages:
       vue-template-compiler: 2.7.14
     dev: true
 
-  /@vue/reactivity-transform@3.3.8:
-    resolution: {integrity: sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==}
+  /@vue/reactivity-transform@3.3.9:
+    resolution: {integrity: sha512-HnUFm7Ry6dFa4Lp63DAxTixUp8opMtQr6RxQCpDI1vlh12rkGIeYqMvJtK+IKyEfEOa2I9oCkD1mmsPdaGpdVg==}
     dependencies:
-      '@babel/parser': 7.23.0
-      '@vue/compiler-core': 3.3.8
-      '@vue/shared': 3.3.8
+      '@babel/parser': 7.23.3
+      '@vue/compiler-core': 3.3.9
+      '@vue/shared': 3.3.9
       estree-walker: 2.0.2
       magic-string: 0.30.5
 
-  /@vue/reactivity@3.3.8:
-    resolution: {integrity: sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==}
+  /@vue/reactivity@3.3.9:
+    resolution: {integrity: sha512-VmpIqlNp+aYDg2X0xQhJqHx9YguOmz2UxuUJDckBdQCNkipJvfk9yA75woLWElCa0Jtyec3lAAt49GO0izsphw==}
     dependencies:
-      '@vue/shared': 3.3.8
+      '@vue/shared': 3.3.9
 
-  /@vue/runtime-core@3.3.8:
-    resolution: {integrity: sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==}
+  /@vue/runtime-core@3.3.9:
+    resolution: {integrity: sha512-xxaG9KvPm3GTRuM4ZyU8Tc+pMVzcu6eeoSRQJ9IE7NmCcClW6z4B3Ij6L4EDl80sxe/arTtQ6YmgiO4UZqRc+w==}
     dependencies:
-      '@vue/reactivity': 3.3.8
-      '@vue/shared': 3.3.8
+      '@vue/reactivity': 3.3.9
+      '@vue/shared': 3.3.9
 
-  /@vue/runtime-dom@3.3.8:
-    resolution: {integrity: sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==}
+  /@vue/runtime-dom@3.3.9:
+    resolution: {integrity: sha512-e7LIfcxYSWbV6BK1wQv9qJyxprC75EvSqF/kQKe6bdZEDNValzeRXEVgiX7AHI6hZ59HA4h7WT5CGvm69vzJTQ==}
     dependencies:
-      '@vue/runtime-core': 3.3.8
-      '@vue/shared': 3.3.8
+      '@vue/runtime-core': 3.3.9
+      '@vue/shared': 3.3.9
       csstype: 3.1.2
 
-  /@vue/server-renderer@3.3.8(vue@3.3.8):
-    resolution: {integrity: sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==}
+  /@vue/server-renderer@3.3.9(vue@3.3.9):
+    resolution: {integrity: sha512-w0zT/s5l3Oa3ZjtLW88eO4uV6AQFqU8X5GOgzq7SkQQu6vVr+8tfm+OI2kDBplS/W/XgCBuFXiPw6T5EdwXP0A==}
     peerDependencies:
-      vue: 3.3.8
+      vue: 3.3.9
     dependencies:
-      '@vue/compiler-ssr': 3.3.8
-      '@vue/shared': 3.3.8
-      vue: 3.3.8(typescript@5.3.2)
+      '@vue/compiler-ssr': 3.3.9
+      '@vue/shared': 3.3.9
+      vue: 3.3.9(typescript@5.3.2)
 
   /@vue/shared@3.3.6:
     resolution: {integrity: sha512-Xno5pEqg8SVhomD0kTSmfh30ZEmV/+jZtyh39q6QflrjdJCXah5lrnOLi9KB6a5k5aAHXMXjoMnxlzUkCNfWLQ==}
@@ -8481,7 +8507,10 @@ packages:
   /@vue/shared@3.3.8:
     resolution: {integrity: sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==}
 
-  /@vue/test-utils@2.4.1(vue@3.3.8):
+  /@vue/shared@3.3.9:
+    resolution: {integrity: sha512-ZE0VTIR0LmYgeyhurPTpy4KzKsuDyQbMSdM49eKkMnT5X4VfFBLysMzjIZhLEFQYjjOVVfbvUDHckwjDFiO2eA==}
+
+  /@vue/test-utils@2.4.1(vue@3.3.9):
     resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==}
     peerDependencies:
       '@vue/server-renderer': ^3.0.1
@@ -8491,7 +8520,7 @@ packages:
         optional: true
     dependencies:
       js-beautify: 1.14.9
-      vue: 3.3.8(typescript@5.3.2)
+      vue: 3.3.9(typescript@5.3.2)
       vue-component-type-helpers: 1.8.4
     dev: true
 
@@ -8537,10 +8566,6 @@ packages:
     dev: true
     optional: true
 
-  /abab@2.0.6:
-    resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
-    dev: false
-
   /abbrev@1.1.1:
     resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
 
@@ -8939,12 +8964,12 @@ packages:
     resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
     dev: true
 
-  /ast-kit@0.11.2(rollup@4.4.1):
+  /ast-kit@0.11.2(rollup@4.6.0):
     resolution: {integrity: sha512-Q0DjXK4ApbVoIf9GLyCo252tUH44iTnD/hiJ2TQaJeydYWSpKk0sI34+WMel8S9Wt5pbLgG02oJ+gkgX5DV3sQ==}
     engines: {node: '>=16.14.0'}
     dependencies:
       '@babel/parser': 7.23.3
-      '@rollup/pluginutils': 5.0.5(rollup@4.4.1)
+      '@rollup/pluginutils': 5.0.5(rollup@4.6.0)
       pathe: 1.1.1
     transitivePeerDependencies:
       - rollup
@@ -9409,8 +9434,8 @@ packages:
     dependencies:
       node-gyp-build: 4.6.0
 
-  /bullmq@4.13.3:
-    resolution: {integrity: sha512-CGCT62MJ9vB57iZpoNVhyJUTH1yO7tEdxHfcvtnHxlA16t4FxeK7dPeCnKzlx3nfy4nJ900WTts1EPSXaQvTbA==}
+  /bullmq@4.14.2:
+    resolution: {integrity: sha512-lzK4F6H61oH5S3Mg4JP4rnSxpQx00Qq7KQKt1oWjcQarka7TdN50CDsZGXg9z6kzvu26Pd3aiwTxwr4YvcEFgw==}
     dependencies:
       cron-parser: 4.8.1
       glob: 8.1.0
@@ -9758,8 +9783,8 @@ packages:
     engines: {node: '>=10'}
     requiresBuild: true
 
-  /chromatic@9.0.0:
-    resolution: {integrity: sha512-PczbWYOfqsf/SM0yIzI3ZOpFznEyf9Fc96EkxoYkhztFaA9CEOcpA9nk8zpmNgPUQWmIWNyG7z4xEK40sApFzw==}
+  /chromatic@9.1.0:
+    resolution: {integrity: sha512-9ib8k4LIfg/88kKufxz1N8vgCB7nlLhJqmx+Vx55cM/6DCB/M6oqroirVRXfdeC7qaZuhyUemPF2QHxBh7GXtQ==}
     hasBin: true
     dev: false
 
@@ -10115,7 +10140,7 @@ packages:
       readable-stream: 3.6.0
     dev: false
 
-  /create-jest@29.7.0(@types/node@20.9.1):
+  /create-jest@29.7.0(@types/node@20.10.0):
     resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -10124,7 +10149,7 @@ packages:
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.9.1)
+      jest-config: 29.7.0(@types/node@20.10.0)
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -10327,8 +10352,8 @@ packages:
       uniq: 1.0.1
     dev: false
 
-  /cypress@13.5.1:
-    resolution: {integrity: sha512-yqLViT0D/lPI8Kkm7ciF/x/DCK/H/DnogdGyiTnQgX4OVR2aM30PtK+kvklTOD1u3TuItiD9wUQAF8EYWtyZug==}
+  /cypress@13.6.0:
+    resolution: {integrity: sha512-quIsnFmtj4dBUEJYU4OH0H12bABJpSujvWexC24Ju1gTlKMJbeT6tTO0vh7WNfiBPPjoIXLN+OUqVtiKFs6SGw==}
     engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
     hasBin: true
     requiresBuild: true
@@ -10392,13 +10417,12 @@ packages:
     resolution: {integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==}
     engines: {node: '>= 12'}
 
-  /data-urls@4.0.0:
-    resolution: {integrity: sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==}
-    engines: {node: '>=14'}
+  /data-urls@5.0.0:
+    resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
+    engines: {node: '>=18'}
     dependencies:
-      abab: 2.0.6
-      whatwg-mimetype: 3.0.0
-      whatwg-url: 12.0.1
+      whatwg-mimetype: 4.0.0
+      whatwg-url: 14.0.0
     dev: false
 
   /date-fns@2.30.0:
@@ -10744,13 +10768,6 @@ packages:
   /domelementtype@2.3.0:
     resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
 
-  /domexception@4.0.0:
-    resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==}
-    engines: {node: '>=12'}
-    dependencies:
-      webidl-conversions: 7.0.0
-    dev: false
-
   /domhandler@5.0.3:
     resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
     engines: {node: '>= 4'}
@@ -11041,36 +11058,36 @@ packages:
       '@esbuild/win32-arm64': 0.18.17
       '@esbuild/win32-ia32': 0.18.17
       '@esbuild/win32-x64': 0.18.17
+    dev: true
 
-  /esbuild@0.19.5:
-    resolution: {integrity: sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==}
+  /esbuild@0.19.8:
+    resolution: {integrity: sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==}
     engines: {node: '>=12'}
     hasBin: true
     requiresBuild: true
     optionalDependencies:
-      '@esbuild/android-arm': 0.19.5
-      '@esbuild/android-arm64': 0.19.5
-      '@esbuild/android-x64': 0.19.5
-      '@esbuild/darwin-arm64': 0.19.5
-      '@esbuild/darwin-x64': 0.19.5
-      '@esbuild/freebsd-arm64': 0.19.5
-      '@esbuild/freebsd-x64': 0.19.5
-      '@esbuild/linux-arm': 0.19.5
-      '@esbuild/linux-arm64': 0.19.5
-      '@esbuild/linux-ia32': 0.19.5
-      '@esbuild/linux-loong64': 0.19.5
-      '@esbuild/linux-mips64el': 0.19.5
-      '@esbuild/linux-ppc64': 0.19.5
-      '@esbuild/linux-riscv64': 0.19.5
-      '@esbuild/linux-s390x': 0.19.5
-      '@esbuild/linux-x64': 0.19.5
-      '@esbuild/netbsd-x64': 0.19.5
-      '@esbuild/openbsd-x64': 0.19.5
-      '@esbuild/sunos-x64': 0.19.5
-      '@esbuild/win32-arm64': 0.19.5
-      '@esbuild/win32-ia32': 0.19.5
-      '@esbuild/win32-x64': 0.19.5
-    dev: false
+      '@esbuild/android-arm': 0.19.8
+      '@esbuild/android-arm64': 0.19.8
+      '@esbuild/android-x64': 0.19.8
+      '@esbuild/darwin-arm64': 0.19.8
+      '@esbuild/darwin-x64': 0.19.8
+      '@esbuild/freebsd-arm64': 0.19.8
+      '@esbuild/freebsd-x64': 0.19.8
+      '@esbuild/linux-arm': 0.19.8
+      '@esbuild/linux-arm64': 0.19.8
+      '@esbuild/linux-ia32': 0.19.8
+      '@esbuild/linux-loong64': 0.19.8
+      '@esbuild/linux-mips64el': 0.19.8
+      '@esbuild/linux-ppc64': 0.19.8
+      '@esbuild/linux-riscv64': 0.19.8
+      '@esbuild/linux-s390x': 0.19.8
+      '@esbuild/linux-x64': 0.19.8
+      '@esbuild/netbsd-x64': 0.19.8
+      '@esbuild/openbsd-x64': 0.19.8
+      '@esbuild/sunos-x64': 0.19.8
+      '@esbuild/win32-arm64': 0.19.8
+      '@esbuild/win32-ia32': 0.19.8
+      '@esbuild/win32-x64': 0.19.8
 
   /escalade@3.1.1:
     resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
@@ -11137,7 +11154,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.11.0)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0):
+  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.12.0)(eslint-import-resolver-node@0.3.9)(eslint@8.54.0):
     resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11158,15 +11175,15 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      '@typescript-eslint/parser': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
       debug: 3.2.7(supports-color@5.5.0)
-      eslint: 8.53.0
+      eslint: 8.54.0
       eslint-import-resolver-node: 0.3.9
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0):
+  /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0):
     resolution: {integrity: sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11176,16 +11193,16 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      '@typescript-eslint/parser': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
       array.prototype.flatmap: 1.3.2
       debug: 3.2.7(supports-color@5.5.0)
       doctrine: 2.1.0
-      eslint: 8.53.0
+      eslint: 8.54.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.11.0)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.12.0)(eslint-import-resolver-node@0.3.9)(eslint@8.54.0)
       hasown: 2.0.0
       is-core-module: 2.13.1
       is-glob: 4.0.3
@@ -11201,19 +11218,19 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-plugin-vue@9.18.1(eslint@8.53.0):
+  /eslint-plugin-vue@9.18.1(eslint@8.54.0):
     resolution: {integrity: sha512-7hZFlrEgg9NIzuVik2I9xSnJA5RsmOfueYgsUGUokEDLJ1LHtxO0Pl4duje1BriZ/jDWb+44tcIlC3yi0tdlZg==}
     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.53.0)
-      eslint: 8.53.0
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0)
+      eslint: 8.54.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.2(eslint@8.53.0)
+      vue-eslint-parser: 9.3.2(eslint@8.54.0)
       xml-name-validator: 4.0.0
     transitivePeerDependencies:
       - supports-color
@@ -11236,15 +11253,15 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
-  /eslint@8.53.0:
-    resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==}
+  /eslint@8.54.0:
+    resolution: {integrity: sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     hasBin: true
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0)
       '@eslint-community/regexpp': 4.6.2
       '@eslint/eslintrc': 2.1.3
-      '@eslint/js': 8.53.0
+      '@eslint/js': 8.54.0
       '@humanwhocodes/config-array': 0.11.13
       '@humanwhocodes/module-importer': 1.0.1
       '@nodelib/fs.walk': 1.2.8
@@ -12010,6 +12027,13 @@ packages:
     requiresBuild: true
     optional: true
 
+  /fsevents@2.3.3:
+    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+    requiresBuild: true
+    optional: true
+
   /function-bind@1.1.1:
     resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
 
@@ -12227,7 +12251,7 @@ packages:
       foreground-child: 3.1.1
       jackspeak: 2.3.6
       minimatch: 9.0.3
-      minipass: 5.0.0
+      minipass: 7.0.4
       path-scurry: 1.10.1
     dev: false
 
@@ -12531,11 +12555,11 @@ packages:
     engines: {node: '>=14'}
     dev: false
 
-  /html-encoding-sniffer@3.0.0:
-    resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==}
-    engines: {node: '>=12'}
+  /html-encoding-sniffer@4.0.0:
+    resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+    engines: {node: '>=18'}
     dependencies:
-      whatwg-encoding: 2.0.0
+      whatwg-encoding: 3.1.1
     dev: false
 
   /html-entities@2.3.2:
@@ -12576,17 +12600,6 @@ packages:
     engines: {node: '>=6.0.0'}
     dev: false
 
-  /http-proxy-agent@5.0.0:
-    resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
-    engines: {node: '>= 6'}
-    dependencies:
-      '@tootallnate/once': 2.0.0
-      agent-base: 6.0.2
-      debug: 4.3.4(supports-color@8.1.1)
-    transitivePeerDependencies:
-      - supports-color
-    dev: false
-
   /http-proxy-agent@7.0.0:
     resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==}
     engines: {node: '>= 14'}
@@ -13360,7 +13373,7 @@ packages:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.3.0
@@ -13381,7 +13394,7 @@ packages:
       - supports-color
     dev: true
 
-  /jest-cli@29.7.0(@types/node@20.9.1):
+  /jest-cli@29.7.0(@types/node@20.10.0):
     resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -13395,10 +13408,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.9.1)
+      create-jest: 29.7.0(@types/node@20.10.0)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.9.1)
+      jest-config: 29.7.0(@types/node@20.10.0)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.6.2
@@ -13409,7 +13422,7 @@ packages:
       - ts-node
     dev: true
 
-  /jest-config@29.7.0(@types/node@20.9.1):
+  /jest-config@29.7.0(@types/node@20.10.0):
     resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     peerDependencies:
@@ -13424,7 +13437,7 @@ packages:
       '@babel/core': 7.22.11
       '@jest/test-sequencer': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       babel-jest: 29.7.0(@babel/core@7.22.11)
       chalk: 4.1.2
       ci-info: 3.7.1
@@ -13504,7 +13517,7 @@ packages:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
     dev: true
@@ -13534,7 +13547,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.6
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -13595,7 +13608,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.9.1
+      '@types/node': 20.10.0
     dev: true
 
   /jest-mock@29.7.0:
@@ -13603,7 +13616,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       jest-util: 29.7.0
     dev: true
 
@@ -13658,7 +13671,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -13689,7 +13702,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       chalk: 4.1.2
       cjs-module-lexer: 1.2.2
       collect-v8-coverage: 1.0.1
@@ -13741,7 +13754,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       chalk: 4.1.2
       ci-info: 3.7.1
       graceful-fs: 4.2.11
@@ -13766,7 +13779,7 @@ packages:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -13785,13 +13798,13 @@ packages:
     resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
     dev: true
 
-  /jest@29.7.0(@types/node@20.9.1):
+  /jest@29.7.0(@types/node@20.10.0):
     resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -13804,7 +13817,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.9.1)
+      jest-cli: 29.7.0(@types/node@20.10.0)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -13921,38 +13934,36 @@ packages:
       - supports-color
     dev: true
 
-  /jsdom@22.1.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
-    resolution: {integrity: sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==}
-    engines: {node: '>=16'}
+  /jsdom@23.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
+    resolution: {integrity: sha512-cbL/UCtohJguhFC7c2/hgW6BeZCNvP7URQGnx9tSJRYKCdnfbfWOrtuLTMfiB2VxKsx5wPHVsh/J0aBy9lIIhQ==}
+    engines: {node: '>=18'}
     peerDependencies:
-      canvas: ^2.5.0
+      canvas: ^3.0.0
     peerDependenciesMeta:
       canvas:
         optional: true
     dependencies:
-      abab: 2.0.6
       cssstyle: 3.0.0
-      data-urls: 4.0.0
+      data-urls: 5.0.0
       decimal.js: 10.4.3
-      domexception: 4.0.0
       form-data: 4.0.0
-      html-encoding-sniffer: 3.0.0
-      http-proxy-agent: 5.0.0
-      https-proxy-agent: 5.0.1
+      html-encoding-sniffer: 4.0.0
+      http-proxy-agent: 7.0.0
+      https-proxy-agent: 7.0.2
       is-potential-custom-element-name: 1.0.1
-      nwsapi: 2.2.5
+      nwsapi: 2.2.7
       parse5: 7.1.2
       rrweb-cssom: 0.6.0
       saxes: 6.0.0
       symbol-tree: 3.2.4
-      tough-cookie: 4.1.2
-      w3c-xmlserializer: 4.0.0
+      tough-cookie: 4.1.3
+      w3c-xmlserializer: 5.0.0
       webidl-conversions: 7.0.0
-      whatwg-encoding: 2.0.0
-      whatwg-mimetype: 3.0.0
-      whatwg-url: 12.0.1
+      whatwg-encoding: 3.1.1
+      whatwg-mimetype: 4.0.0
+      whatwg-url: 14.0.0
       ws: 8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3)
-      xml-name-validator: 4.0.0
+      xml-name-validator: 5.0.0
     transitivePeerDependencies:
       - bufferutil
       - supports-color
@@ -14072,8 +14083,8 @@ packages:
       verror: 1.10.0
     dev: true
 
-  /jsrsasign@10.8.6:
-    resolution: {integrity: sha512-bQmbVtsfbgaKBTWCKiDCPlUPbdlRIK/FzSwT3BzIgZl/cU6TqXu6pZJsCI/dJVrZ9Gir5GC4woqw9shH/v7MBw==}
+  /jsrsasign@10.9.0:
+    resolution: {integrity: sha512-QWLUikj1SBJGuyGK8tjKSx3K7Y69KYJnrs/pQ1KZ6wvZIkHkWjZ1PJDpuvc1/28c1uP0KW9qn1eI1LzHQqDOwQ==}
     dev: false
 
   /jssha@3.3.1:
@@ -14369,6 +14380,7 @@ packages:
   /lru-cache@9.1.2:
     resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==}
     engines: {node: 14 || >=16.14}
+    dev: true
 
   /luxon@3.3.0:
     resolution: {integrity: sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==}
@@ -14510,8 +14522,8 @@ packages:
     resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
     engines: {node: '>= 0.6'}
 
-  /meilisearch@0.35.0:
-    resolution: {integrity: sha512-gF1I6K5/Wpe7BWfjBnG+o19y/FIpJ9HbN+byON6CB9U3uE7qc6GvwUbjKOllh7LKXQVVxH/kCu7Jn0ODCUwqbQ==}
+  /meilisearch@0.36.0:
+    resolution: {integrity: sha512-swcvEYrct0/zsGj3jlbPm1OYxbH14IURnlysKlXywNicIQ5EMkSYLYCLCwOuBKAaGcdISWdgdylH9TXVLegmOQ==}
     dependencies:
       cross-fetch: 3.1.6
     transitivePeerDependencies:
@@ -15262,8 +15274,8 @@ packages:
     dependencies:
       boolbase: 1.0.0
 
-  /nwsapi@2.2.5:
-    resolution: {integrity: sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==}
+  /nwsapi@2.2.7:
+    resolution: {integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==}
     dev: false
 
   /oauth-sign@0.9.0:
@@ -15664,8 +15676,8 @@ packages:
     resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
     engines: {node: '>=16 || 14 >=14.17'}
     dependencies:
-      lru-cache: 9.1.2
-      minipass: 5.0.0
+      lru-cache: 10.0.2
+      minipass: 7.0.4
     dev: false
 
   /path-scurry@1.9.2:
@@ -16598,7 +16610,7 @@ packages:
       mime: 2.6.0
       mime-types: 2.1.35
       progress: 2.0.3
-      proxy-from-env: 1.0.0
+      proxy-from-env: 1.1.0
       rimraf: 2.7.1
       ws: 6.2.2
     transitivePeerDependencies:
@@ -16752,8 +16764,8 @@ packages:
       setimmediate: 1.0.5
     dev: false
 
-  /re2@1.20.8:
-    resolution: {integrity: sha512-5GArE3towC0ZyinRkkaZARZxlbX3K+z2REXSVltGSW+F/ID8SLrbh1okTXEcTFBp9zsAhKcGH1Vm+zJ2IwMb7Q==}
+  /re2@1.20.9:
+    resolution: {integrity: sha512-ZYcPTFr5ha2xq3WQjBDTF9CWPSDK1z28MLh5UFRxc//7X8BNQ3A7yR7ITnP0jO346661ertdKVFqw1qoL3FMEQ==}
     requiresBuild: true
     dependencies:
       install-artifact-from-github: 1.3.5
@@ -17334,24 +17346,25 @@ packages:
     hasBin: true
     optionalDependencies:
       fsevents: 2.3.2
+    dev: true
 
-  /rollup@4.4.1:
-    resolution: {integrity: sha512-idZzrUpWSblPJX66i+GzrpjKE3vbYrlWirUHteoAbjKReZwa0cohAErOYA5efoMmNCdvG9yrJS+w9Kl6csaH4w==}
+  /rollup@4.6.0:
+    resolution: {integrity: sha512-R8i5Her4oO1LiMQ3jKf7MUglYV/mhQ5g5OKeld5CnkmPdIGo79FDDQYqPhq/PCVuTQVuxsWgIbDy9F+zdHn80w==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.4.1
-      '@rollup/rollup-android-arm64': 4.4.1
-      '@rollup/rollup-darwin-arm64': 4.4.1
-      '@rollup/rollup-darwin-x64': 4.4.1
-      '@rollup/rollup-linux-arm-gnueabihf': 4.4.1
-      '@rollup/rollup-linux-arm64-gnu': 4.4.1
-      '@rollup/rollup-linux-arm64-musl': 4.4.1
-      '@rollup/rollup-linux-x64-gnu': 4.4.1
-      '@rollup/rollup-linux-x64-musl': 4.4.1
-      '@rollup/rollup-win32-arm64-msvc': 4.4.1
-      '@rollup/rollup-win32-ia32-msvc': 4.4.1
-      '@rollup/rollup-win32-x64-msvc': 4.4.1
+      '@rollup/rollup-android-arm-eabi': 4.6.0
+      '@rollup/rollup-android-arm64': 4.6.0
+      '@rollup/rollup-darwin-arm64': 4.6.0
+      '@rollup/rollup-darwin-x64': 4.6.0
+      '@rollup/rollup-linux-arm-gnueabihf': 4.6.0
+      '@rollup/rollup-linux-arm64-gnu': 4.6.0
+      '@rollup/rollup-linux-arm64-musl': 4.6.0
+      '@rollup/rollup-linux-x64-gnu': 4.6.0
+      '@rollup/rollup-linux-x64-musl': 4.6.0
+      '@rollup/rollup-win32-arm64-msvc': 4.6.0
+      '@rollup/rollup-win32-ia32-msvc': 4.6.0
+      '@rollup/rollup-win32-x64-msvc': 4.6.0
       fsevents: 2.3.2
 
   /rrweb-cssom@0.6.0:
@@ -18313,8 +18326,8 @@ packages:
     resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==}
     dev: true
 
-  /systeminformation@5.21.17:
-    resolution: {integrity: sha512-JZYRCbIjk3WuBV59A9/rTla2rROX+aAJ9uo2Z1dI+bjieORcukClN8rlM1zE9NYKpULSbaGc+KKct/870lO0DA==}
+  /systeminformation@5.21.18:
+    resolution: {integrity: sha512-PEoWd95nI5170rvIk4fagLH0SmzwfGt18w0+ex1Ljb2bSXvDs9PQdLNexMazL5L6Pzd6wxlpoWUAjX+hNRKN7g==}
     engines: {node: '>=8.0.0'}
     os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
     hasBin: true
@@ -18581,16 +18594,6 @@ packages:
       punycode: 2.3.1
     dev: false
 
-  /tough-cookie@4.1.2:
-    resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==}
-    engines: {node: '>=6'}
-    dependencies:
-      psl: 1.9.0
-      punycode: 2.3.1
-      universalify: 0.2.0
-      url-parse: 1.5.10
-    dev: false
-
   /tough-cookie@4.1.3:
     resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
     engines: {node: '>=6'}
@@ -18599,15 +18602,14 @@ packages:
       punycode: 2.3.1
       universalify: 0.2.0
       url-parse: 1.5.10
-    dev: true
 
   /tr46@0.0.3:
     resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
     requiresBuild: true
 
-  /tr46@4.1.1:
-    resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==}
-    engines: {node: '>=14'}
+  /tr46@5.0.0:
+    resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
+    engines: {node: '>=18'}
     dependencies:
       punycode: 2.3.1
     dev: false
@@ -19173,7 +19175,7 @@ packages:
     resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
     hasBin: true
 
-  /v-code-diff@1.7.2(vue@3.3.8):
+  /v-code-diff@1.7.2(vue@3.3.9):
     resolution: {integrity: sha512-y+q8ZHf8GfphYLhcZbjAKcId/h6vZujS71Ryq5u+dI6Jg4ZLTdLrBNVSzYpHywHSSFFfBMdilm6XvVryEaH4+A==}
     requiresBuild: true
     peerDependencies:
@@ -19186,8 +19188,8 @@ packages:
       diff: 5.1.0
       diff-match-patch: 1.0.5
       highlight.js: 11.8.0
-      vue: 3.3.8(typescript@5.3.2)
-      vue-demi: 0.13.11(vue@3.3.8)
+      vue: 3.3.9(typescript@5.3.2)
+      vue-demi: 0.13.11(vue@3.3.9)
     dev: false
 
   /v8-to-istanbul@9.1.0:
@@ -19227,7 +19229,7 @@ packages:
       core-util-is: 1.0.2
       extsprintf: 1.3.0
 
-  /vite-node@0.34.6(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0):
+  /vite-node@0.34.6(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0):
     resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
@@ -19237,7 +19239,7 @@ packages:
       mlly: 1.4.0
       pathe: 1.1.1
       picocolors: 1.0.0
-      vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -19253,12 +19255,12 @@ packages:
     resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
     dev: true
 
-  /vite@4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0):
-    resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==}
-    engines: {node: ^14.18.0 || >=16.0.0}
+  /vite@5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0):
+    resolution: {integrity: sha512-6CCq1CAJCNM1ya2ZZA7+jS2KgnhbzvxakmlIjN24cF/PXhRMzpM/z8QgsVJA/Dm5fWUWnVEsmtBoMhmerPxT0g==}
+    engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
-      '@types/node': '>= 14'
+      '@types/node': ^18.0.0 || >=20.0.0
       less: '*'
       lightningcss: ^1.21.0
       sass: '*'
@@ -19281,14 +19283,14 @@ packages:
       terser:
         optional: true
     dependencies:
-      '@types/node': 20.9.1
-      esbuild: 0.18.17
+      '@types/node': 20.10.0
+      esbuild: 0.19.8
       postcss: 8.4.31
-      rollup: 3.29.4
+      rollup: 4.6.0
       sass: 1.69.5
       terser: 5.24.0
     optionalDependencies:
-      fsevents: 2.3.2
+      fsevents: 2.3.3
 
   /vitest-fetch-mock@0.2.2(vitest@0.34.6):
     resolution: {integrity: sha512-XmH6QgTSjCWrqXoPREIdbj40T7i1xnGmAsTAgfckoO75W1IEHKR8hcPCQ7SO16RsdW1t85oUm6pcQRLeBgjVYQ==}
@@ -19335,7 +19337,7 @@ packages:
     dependencies:
       '@types/chai': 4.3.5
       '@types/chai-subset': 1.3.3
-      '@types/node': 20.9.1
+      '@types/node': 20.10.0
       '@vitest/expect': 0.34.6
       '@vitest/runner': 0.34.6
       '@vitest/snapshot': 0.34.6
@@ -19355,8 +19357,8 @@ packages:
       strip-literal: 1.0.1
       tinybench: 2.5.0
       tinypool: 0.7.0
-      vite: 4.5.0(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
-      vite-node: 0.34.6(@types/node@20.9.1)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+      vite-node: 0.34.6(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
       why-is-node-running: 2.2.2
     transitivePeerDependencies:
       - less
@@ -19388,7 +19390,7 @@ packages:
     resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
     dev: true
 
-  /vue-demi@0.13.11(vue@3.3.8):
+  /vue-demi@0.13.11(vue@3.3.9):
     resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
     engines: {node: '>=12'}
     hasBin: true
@@ -19400,35 +19402,35 @@ packages:
       '@vue/composition-api':
         optional: true
     dependencies:
-      vue: 3.3.8(typescript@5.3.2)
+      vue: 3.3.9(typescript@5.3.2)
     dev: false
 
-  /vue-docgen-api@4.64.1(vue@3.3.8):
+  /vue-docgen-api@4.64.1(vue@3.3.9):
     resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==}
     dependencies:
       '@babel/parser': 7.23.0
       '@babel/types': 7.22.17
       '@vue/compiler-dom': 3.3.7
-      '@vue/compiler-sfc': 3.3.8
+      '@vue/compiler-sfc': 3.3.9
       ast-types: 0.14.2
       hash-sum: 2.0.0
       lru-cache: 8.0.4
       pug: 3.0.2
       recast: 0.22.0
       ts-map: 1.0.3
-      vue-inbrowser-compiler-independent-utils: 4.64.1(vue@3.3.8)
+      vue-inbrowser-compiler-independent-utils: 4.64.1(vue@3.3.9)
     transitivePeerDependencies:
       - vue
     dev: true
 
-  /vue-eslint-parser@9.3.2(eslint@8.53.0):
+  /vue-eslint-parser@9.3.2(eslint@8.54.0):
     resolution: {integrity: sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==}
     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.53.0
+      eslint: 8.54.0
       eslint-scope: 7.2.2
       eslint-visitor-keys: 3.4.3
       espree: 9.6.1
@@ -19439,12 +19441,12 @@ packages:
       - supports-color
     dev: true
 
-  /vue-inbrowser-compiler-independent-utils@4.64.1(vue@3.3.8):
+  /vue-inbrowser-compiler-independent-utils@4.64.1(vue@3.3.9):
     resolution: {integrity: sha512-Hn32n07XZ8j9W8+fmOXPQL+i+W2e/8i6mkH4Ju3H6nR0+cfvmWM95GhczYi5B27+Y8JlCKgAo04IUiYce4mKAw==}
     peerDependencies:
       vue: '>=2'
     dependencies:
-      vue: 3.3.8(typescript@5.3.2)
+      vue: 3.3.9(typescript@5.3.2)
     dev: true
 
   /vue-template-compiler@2.7.14:
@@ -19466,35 +19468,35 @@ packages:
       typescript: 5.3.2
     dev: true
 
-  /vue@3.3.8(typescript@5.3.2):
-    resolution: {integrity: sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==}
+  /vue@3.3.9(typescript@5.3.2):
+    resolution: {integrity: sha512-sy5sLCTR8m6tvUk1/ijri3Yqzgpdsmxgj6n6yl7GXXCXqVbmW2RCXe9atE4cEI6Iv7L89v5f35fZRRr5dChP9w==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@vue/compiler-dom': 3.3.8
-      '@vue/compiler-sfc': 3.3.8
-      '@vue/runtime-dom': 3.3.8
-      '@vue/server-renderer': 3.3.8(vue@3.3.8)
-      '@vue/shared': 3.3.8
+      '@vue/compiler-dom': 3.3.9
+      '@vue/compiler-sfc': 3.3.9
+      '@vue/runtime-dom': 3.3.9
+      '@vue/server-renderer': 3.3.9(vue@3.3.9)
+      '@vue/shared': 3.3.9
       typescript: 5.3.2
 
-  /vuedraggable@4.1.0(vue@3.3.8):
+  /vuedraggable@4.1.0(vue@3.3.9):
     resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
     peerDependencies:
       vue: ^3.0.1
     dependencies:
       sortablejs: 1.14.0
-      vue: 3.3.8(typescript@5.3.2)
+      vue: 3.3.9(typescript@5.3.2)
     dev: false
 
-  /w3c-xmlserializer@4.0.0:
-    resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==}
-    engines: {node: '>=14'}
+  /w3c-xmlserializer@5.0.0:
+    resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+    engines: {node: '>=18'}
     dependencies:
-      xml-name-validator: 4.0.0
+      xml-name-validator: 5.0.0
     dev: false
 
   /wait-on@7.2.0(debug@4.3.4):
@@ -19583,15 +19585,27 @@ packages:
     dependencies:
       iconv-lite: 0.6.3
 
+  /whatwg-encoding@3.1.1:
+    resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+    engines: {node: '>=18'}
+    dependencies:
+      iconv-lite: 0.6.3
+    dev: false
+
   /whatwg-mimetype@3.0.0:
     resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==}
     engines: {node: '>=12'}
 
-  /whatwg-url@12.0.1:
-    resolution: {integrity: sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==}
-    engines: {node: '>=14'}
+  /whatwg-mimetype@4.0.0:
+    resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+    engines: {node: '>=18'}
+    dev: false
+
+  /whatwg-url@14.0.0:
+    resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
+    engines: {node: '>=18'}
     dependencies:
-      tr46: 4.1.1
+      tr46: 5.0.0
       webidl-conversions: 7.0.0
     dev: false
 
@@ -19784,6 +19798,12 @@ packages:
   /xml-name-validator@4.0.0:
     resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
     engines: {node: '>=12'}
+    dev: true
+
+  /xml-name-validator@5.0.0:
+    resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+    engines: {node: '>=18'}
+    dev: false
 
   /xml2js@0.5.0:
     resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==}

From 2d0253bc422a165ea54d085e24cddc36d6d5e8ff Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 27 Nov 2023 21:05:37 +0900
Subject: [PATCH 059/435] 2023.12.0-beta.1

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

diff --git a/package.json b/package.json
index 7db2b0e7fc..fc328a650b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.11.1",
+	"version": "2023.12.0-beta.1",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From d58ec4e65ba2336d7dc06f767d1dcaf43d29e1e2 Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Mon, 27 Nov 2023 21:37:37 +0900
Subject: [PATCH 060/435] =?UTF-8?q?docs(changelog):=20GHSA-3f39-6537-3cgc?=
 =?UTF-8?q?=20=E3=81=AB=E3=81=A4=E3=81=84=E3=81=A6=E8=BF=BD=E8=A8=98=20(#1?=
 =?UTF-8?q?2482)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d59307caae..6cc62dedcf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -79,6 +79,7 @@
 
 ### Note
 - iOS 16.4未満を使用している場合はiOS 16.4以上にアップデートをお願いします
+- 悪意のある第三者がリモートユーザーになりすました任意のアクティビティを受け取れてしまう問題を修正しました。詳しくは[GitHub security advisory](https://github.com/misskey-dev/misskey/security/advisories/GHSA-3f39-6537-3cgc)をご覧ください。
 
 ### General
 - Feat: アイコンデコレーション機能

From 4e5b7768dcc381a2f944ba503acaf9458ea87d0e 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: Mon, 27 Nov 2023 21:41:19 +0900
Subject: [PATCH 061/435] =?UTF-8?q?fix(docs):=20"docs(changelog):=20GHSA-3?=
 =?UTF-8?q?f39-6537-3cgc=20=E3=81=AB=E3=81=A4=E3=81=84=E3=81=A6=E8=BF=BD?=
 =?UTF-8?q?=E8=A8=98=20(#12482)"=20(#12483)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Revert "docs(changelog): GHSA-3f39-6537-3cgc について追記 (#12482)"

This reverts commit d58ec4e65ba2336d7dc06f767d1dcaf43d29e1e2.

* Update CHANGELOG.md
---
 CHANGELOG.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6cc62dedcf..bdd082a1c2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -42,6 +42,9 @@
 
 ## 2023.11.1
 
+### Note
+- 悪意のある第三者がリモートユーザーになりすました任意のアクティビティを受け取れてしまう問題を修正しました。詳しくは[GitHub security advisory](https://github.com/misskey-dev/misskey/security/advisories/GHSA-3f39-6537-3cgc)をご覧ください。
+
 ### General
 - Feat: 管理者がコントロールパネルからメールアドレスの照会を行えるようになりました
 - Enhance: ローカリゼーションの更新
@@ -79,7 +82,6 @@
 
 ### Note
 - iOS 16.4未満を使用している場合はiOS 16.4以上にアップデートをお願いします
-- 悪意のある第三者がリモートユーザーになりすました任意のアクティビティを受け取れてしまう問題を修正しました。詳しくは[GitHub security advisory](https://github.com/misskey-dev/misskey/security/advisories/GHSA-3f39-6537-3cgc)をご覧ください。
 
 ### General
 - Feat: アイコンデコレーション機能

From ec04c76ee536df548b6b55e314f1b3eb1e12dde4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Tue, 28 Nov 2023 20:43:25 +0900
Subject: [PATCH 062/435] =?UTF-8?q?=E9=80=9A=E7=9F=A5=E3=82=B0=E3=83=AB?=
 =?UTF-8?q?=E3=83=BC=E3=83=94=E3=83=B3=E3=82=B0=E8=A8=AD=E5=AE=9A=E3=81=AE?=
 =?UTF-8?q?=E5=8D=B3=E6=99=82=E5=8F=8D=E6=98=A0=E5=AF=BE=E5=BF=9C=20(#1248?=
 =?UTF-8?q?5)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* wip

* ログ出しの削除

* fix CHANGELOG.md

---------

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
---
 CHANGELOG.md                                         | 1 +
 packages/frontend/src/components/MkNotifications.vue | 6 +++---
 packages/frontend/src/components/MkPagination.vue    | 2 +-
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bdd082a1c2..f026b97fa4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,7 @@
 - Fix: コードエディタが正しく表示されない問題を修正
 - Fix: プロフィールの「ファイル」にセンシティブな画像がある際のデザインを修正
 - Fix: 一度に大量の通知が入った際に通知音が音割れする問題を修正
+- Fix: 通知のグルーピング設定を変更してもリロードされるまで表示が変わらない問題を修正 #12470
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index 7b072fa492..2a0082204a 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onUnmounted, onDeactivated, onMounted, computed, shallowRef, onActivated } from 'vue';
+import { onUnmounted, onDeactivated, onMounted, computed, shallowRef, onActivated, watch } from 'vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import XNotification from '@/components/MkNotification.vue';
 import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
@@ -43,7 +43,7 @@ const props = defineProps<{
 
 const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
 
-const pagination: Paging = defaultStore.state.useGroupedNotifications ? {
+let pagination = $computed(() => defaultStore.reactiveState.useGroupedNotifications.value ? {
 	endpoint: 'i/notifications-grouped' as const,
 	limit: 20,
 	params: computed(() => ({
@@ -55,7 +55,7 @@ const pagination: Paging = defaultStore.state.useGroupedNotifications ? {
 	params: computed(() => ({
 		excludeTypes: props.excludeTypes ?? undefined,
 	})),
-};
+});
 
 function onNotification(notification) {
 	const isMuted = props.excludeTypes ? props.excludeTypes.includes(notification.type) : false;
diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue
index e7796dfcb5..2c59e6d4e8 100644
--- a/packages/frontend/src/components/MkPagination.vue
+++ b/packages/frontend/src/components/MkPagination.vue
@@ -186,7 +186,7 @@ watch([$$(backed), $$(contentEl)], () => {
 });
 
 // パラメータに何らかの変更があった際、再読込したい(チャンネル等のIDが変わったなど)
-watch(() => props.pagination.params, init, { deep: true });
+watch(() => [props.pagination.endpoint, props.pagination.params], init, { deep: true });
 
 watch(queue, (a, b) => {
 	if (a.size === 0 && b.size === 0) return;

From 3b3b908ccdf1f475c3f36df06d8da3cabe526a68 Mon Sep 17 00:00:00 2001
From: yupix <yupi0982@outlook.jp>
Date: Wed, 29 Nov 2023 08:08:06 +0900
Subject: [PATCH 063/435] =?UTF-8?q?fix:=20packedNoteSchema=E3=81=ABclipped?=
 =?UTF-8?q?Count=E3=81=8C=E6=8A=9C=E3=81=91=E3=81=A6=E3=81=84=E3=82=8B=20(?=
 =?UTF-8?q?#12499)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/models/json-schema/note.ts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts
index 392fa7e1cb..aa749943f0 100644
--- a/packages/backend/src/models/json-schema/note.ts
+++ b/packages/backend/src/models/json-schema/note.ts
@@ -186,6 +186,10 @@ export const packedNoteSchema = {
 				optional: false, nullable: false,
 			},
 		},
+		clippedCount: {
+			type: 'number',
+			optional: true, nullable: false,
+		},
 
 		myReaction: {
 			type: 'object',

From 4e882414b25fe6e9b464b41078089bf295255282 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Wed, 29 Nov 2023 10:29:00 +0900
Subject: [PATCH 064/435] =?UTF-8?q?fix:=20=E9=9F=B3=E5=A3=B0=E3=81=8C?=
 =?UTF-8?q?=E4=B8=80=E5=88=87=E9=B3=B4=E3=82=89=E3=81=AA=E3=81=8F=E3=81=AA?=
 =?UTF-8?q?=E3=82=8B=E5=8F=AF=E8=83=BD=E6=80=A7=E3=81=8C=E3=81=82=E3=82=8B?=
 =?UTF-8?q?=20(#12491)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore: 音声が一切鳴らなくなる可能性を軽減

https://github.com/misskey-dev/misskey/pull/12433#discussion_r1405774767

* chore: IIFEではなくPromise.prototype.finallyを使用するように
---
 packages/frontend/src/scripts/sound.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index a4c6967d18..2f7545ef0d 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -161,7 +161,7 @@ export function play(operationType: OperationType) {
 	if (sound.type == null || !canPlay) return;
 
 	canPlay = false;
-	playFile(sound).then(() => {
+	playFile(sound).finally(() => {
 		// ごく短時間に音が重複しないように
 		setTimeout(() => {
 			canPlay = true;

From d5deef5699aee6907e00e7a99c8b753e18332fa8 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Wed, 29 Nov 2023 10:29:24 +0900
Subject: [PATCH 065/435] =?UTF-8?q?fix(frontend):=20WebKit=E3=83=96?=
 =?UTF-8?q?=E3=83=A9=E3=82=A6=E3=82=B6=E3=83=BC=E4=B8=8A=E3=81=A7=E3=82=82?=
 =?UTF-8?q?=E3=80=8C=E3=83=87=E3=83=90=E3=82=A4=E3=82=B9=E3=81=AE=E7=94=BB?=
 =?UTF-8?q?=E9=9D=A2=E3=82=92=E5=B8=B8=E3=81=AB=E3=82=AA=E3=83=B3=E3=81=AB?=
 =?UTF-8?q?=E3=81=99=E3=82=8B=E3=80=8D=E6=A9=9F=E8=83=BD=E3=81=8C=E5=8A=B9?=
 =?UTF-8?q?=E3=81=8F=E3=82=88=E3=81=86=E3=81=AB=20(#12484)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): WebKitブラウザー上でもkeepScreenOnが効くように

* chore: add comment
---
 packages/frontend/src/boot/common.ts | 30 ++++++++++++++++------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index 594fe64230..6e216a78b4 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -202,20 +202,24 @@ export async function common(createVue: () => App<Element>) {
 		}
 	}, { immediate: true });
 
-	if (defaultStore.state.keepScreenOn) {
-		if ('wakeLock' in navigator) {
-			navigator.wakeLock.request('screen')
-			.then(() => {
-				document.addEventListener('visibilitychange', async () => {
-					if (document.visibilityState === 'visible') {
-						navigator.wakeLock.request('screen');
-					}
-				});
-			})
-			.catch(() => {
-				// If Permission fails on an AppleDevice such as Safari
-			});
+	// Keep screen on
+	const onVisibilityChange = () => document.addEventListener('visibilitychange', () => {
+		if (document.visibilityState === 'visible') {
+			navigator.wakeLock.request('screen');
 		}
+	});
+	if (defaultStore.state.keepScreenOn && 'wakeLock' in navigator) {
+		navigator.wakeLock.request('screen')
+			.then(onVisibilityChange)
+			.catch(() => {
+				// On WebKit-based browsers, user activation is required to send wake lock request
+				// https://webkit.org/blog/13862/the-user-activation-api/
+				document.addEventListener(
+					'click',
+					() => navigator.wakeLock.request('screen').then(onVisibilityChange),
+					{ once: true },
+				);
+			});
 	}
 
 	//#region Fetch user

From ea1a2dc8db34ec42e8f56ca8b4020be8bfe58972 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Wed, 29 Nov 2023 10:41:11 +0900
Subject: [PATCH 066/435] Update the Vitest configuration (#12493)

---
 packages/frontend/vite.config.ts | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts
index da976b7917..4d2bb00e33 100644
--- a/packages/frontend/vite.config.ts
+++ b/packages/frontend/vite.config.ts
@@ -150,10 +150,14 @@ export function getConfig(): UserConfig {
 		test: {
 			environment: 'happy-dom',
 			deps: {
-				inline: [
-					// XXX: misskey-dev/browser-image-resizer has no "type": "module"
-					'browser-image-resizer',
-				],
+				optimizer: {
+					web: {
+						include: [
+							// XXX: misskey-dev/browser-image-resizer has no "type": "module"
+							'browser-image-resizer',
+						],
+					},
+				},
 			},
 		},
 	};

From c41d03018c37b892ee937643b7785ac5b8e52bc9 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 30 Nov 2023 01:06:11 +0900
Subject: [PATCH 067/435] ci: use refs/pull/*/merge to get head (#12508)

---
 .github/workflows/get-api-diff.yml | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index 281e2058a6..75a458424e 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -22,16 +22,13 @@ jobs:
         api-json-name: [api-base.json, api-head.json]
         include:
           - api-json-name: api-base.json
-            repo-name: ${{ github.event.pull_request.base.repo.full_name }}
             ref: ${{ github.base_ref }}
           - api-json-name: api-head.json
-            repo-name: ${{ github.event.pull_request.head.repo.full_name }}
-            ref: ${{ github.head_ref }}
+            ref: refs/pull/${{ github.event.number }}/merge
 
     steps:
     - uses: actions/checkout@v4.1.1
       with:
-        repository: ${{ matrix.repo-name }}
         ref: ${{ matrix.ref }}
         submodules: true
     - name: Install pnpm

From 37cff405ed620a9a885475f63067446c56c58415 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: Thu, 30 Nov 2023 01:08:29 +0900
Subject: [PATCH 068/435] =?UTF-8?q?enhance(frontend):=20Share=E3=83=9A?=
 =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=81=A7=E3=81=AE=E6=8A=95=E7=A8=BF=E5=AE=8C?=
 =?UTF-8?q?=E4=BA=86=E6=99=82=E3=81=ABpostMessage=E3=82=92=E7=99=BA?=
 =?UTF-8?q?=E7=81=AB=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1250?=
 =?UTF-8?q?5)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): Shareページでの投稿完了時にpostMessageを発火

* Update Changelog

* fix

* 名前の混同をさける

* 名前をわかりやすくする

* watchを使わずパフォーマンス改善
---
 CHANGELOG.md                                  |  1 +
 packages/frontend/src/pages/share.vue         | 15 +++++++----
 packages/frontend/src/scripts/post-message.ts | 25 +++++++++++++++++++
 3 files changed, 36 insertions(+), 5 deletions(-)
 create mode 100644 packages/frontend/src/scripts/post-message.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f026b97fa4..1ed930437e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
 - Enhance: ユーザーのRawデータを表示するページが復活
 - Enhance: リアクション選択時に音を鳴らせるように
 - Enhance: サウンドにドライブのファイルを使用できるように
+- Enhance: Shareページで投稿を完了すると、親ウィンドウ(親フレーム)にpostMessageするように
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
diff --git a/packages/frontend/src/pages/share.vue b/packages/frontend/src/pages/share.vue
index d66457e823..1d77e5931d 100644
--- a/packages/frontend/src/pages/share.vue
+++ b/packages/frontend/src/pages/share.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:renote="renote"
 			:initialVisibleUsers="visibleUsers"
 			class="_panel"
-			@posted="state = 'posted'"
+			@posted="onPosted"
 		/>
 		<div v-else-if="state === 'posted'" class="_buttonsCenter">
 			<MkButton primary @click="close">{{ i18n.ts.close }}</MkButton>
@@ -32,20 +32,20 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 // SPECIFICATION: https://misskey-hub.net/docs/features/share-form.html
 
-import { } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import MkPostForm from '@/components/MkPostForm.vue';
 import * as os from '@/os.js';
-import { mainRouter } from '@/router.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { postMessageToParentWindow } from '@/scripts/post-message.js';
 import { i18n } from '@/i18n.js';
 
 const urlParams = new URLSearchParams(window.location.search);
 const localOnlyQuery = urlParams.get('localOnly');
 const visibilityQuery = urlParams.get('visibility') as typeof Misskey.noteVisibilities[number];
 
-let state = $ref('fetching' as 'fetching' | 'writing' | 'posted');
+const state = ref<'fetching' | 'writing' | 'posted'>('fetching');
 let title = $ref(urlParams.get('title'));
 const text = urlParams.get('text');
 const url = urlParams.get('url');
@@ -144,7 +144,7 @@ async function init() {
 		});
 	}
 
-	state = 'writing';
+	state.value = 'writing';
 }
 
 init();
@@ -162,6 +162,11 @@ function goToMisskey(): void {
 	location.href = '/';
 }
 
+function onPosted(): void {
+	state.value = 'posted';
+	postMessageToParentWindow('misskey:shareForm:shareCompleted');
+}
+
 const headerActions = $computed(() => []);
 
 const headerTabs = $computed(() => []);
diff --git a/packages/frontend/src/scripts/post-message.ts b/packages/frontend/src/scripts/post-message.ts
new file mode 100644
index 0000000000..80441caf15
--- /dev/null
+++ b/packages/frontend/src/scripts/post-message.ts
@@ -0,0 +1,25 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export const postMessageEventTypes = [
+	'misskey:shareForm:shareCompleted',
+] as const;
+
+export type PostMessageEventType = typeof postMessageEventTypes[number];
+
+export type MiPostMessageEvent = {
+	type: PostMessageEventType;
+	payload?: any;
+};
+
+/**
+ * 親フレームにイベントを送信
+ */
+export function postMessageToParentWindow(type: PostMessageEventType, payload?: any): void {
+	window.postMessage({
+		type,
+		payload,
+	}, '*');
+}

From 413f7bfb44e885c82ba49fc098b620831830d0c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Thu, 30 Nov 2023 08:15:13 +0900
Subject: [PATCH 069/435] =?UTF-8?q?Fix:=20navigator.share=E6=9C=AA?=
 =?UTF-8?q?=E3=82=B5=E3=83=9D=E3=83=BC=E3=83=88=E3=81=AE=E5=A0=B4=E5=90=88?=
 =?UTF-8?q?=E3=81=AF=E5=85=B1=E6=9C=89=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92?=
 =?UTF-8?q?=E9=9D=9E=E8=A1=A8=E7=A4=BA=E3=81=AB=E3=81=99=E3=82=8B=EF=BC=88?=
 =?UTF-8?q?=EF=BC=8BURL=E3=81=AE=E3=82=B3=E3=83=94=E3=83=BC=E3=83=9C?=
 =?UTF-8?q?=E3=82=BF=E3=83=B3=E3=82=92=E8=A8=AD=E7=BD=AE=EF=BC=89=20(#1250?=
 =?UTF-8?q?6)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* navigator.share未サポートの場合は共有ボタンを非表示にする

* fix CHANGELOG.md

* ライセンス表示追加

* URLのコピーボタンを設置
---
 CHANGELOG.md                                  |  2 +
 .../src/components/global/MkPageHeader.vue    |  8 +--
 packages/frontend/src/pages/channel.vue       | 53 +++++++++++++------
 packages/frontend/src/pages/clip.vue          |  9 ++++
 packages/frontend/src/pages/flash/flash.vue   | 10 +++-
 packages/frontend/src/pages/gallery/post.vue  | 10 +++-
 packages/frontend/src/pages/page.vue          | 10 +++-
 .../frontend/src/scripts/get-note-menu.ts     |  7 +--
 packages/frontend/src/scripts/navigator.ts    |  8 +++
 packages/frontend/src/types/page-header.ts    | 11 ++++
 10 files changed, 99 insertions(+), 29 deletions(-)
 create mode 100644 packages/frontend/src/scripts/navigator.ts
 create mode 100644 packages/frontend/src/types/page-header.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1ed930437e..d2e6702a92 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,11 +26,13 @@
 - Enhance: リアクション選択時に音を鳴らせるように
 - Enhance: サウンドにドライブのファイルを使用できるように
 - Enhance: Shareページで投稿を完了すると、親ウィンドウ(親フレーム)にpostMessageするように
+- Enhance: チャンネル、クリップ、ページ、Play、ギャラリーにURLのコピーボタンを設置 #11305
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
 - Fix: プロフィールの「ファイル」にセンシティブな画像がある際のデザインを修正
 - Fix: 一度に大量の通知が入った際に通知音が音割れする問題を修正
+- Fix: 共有機能をサポートしていないブラウザの場合は共有ボタンを非表示にする #11305
 - Fix: 通知のグルーピング設定を変更してもリロードされるまで表示が変わらない問題を修正 #12470
 
 ### Server
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 580816abaa..935ca33eb5 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -48,16 +48,12 @@ import { scrollToTop } from '@/scripts/scroll.js';
 import { globalEvents } from '@/events.js';
 import { injectPageMetadata } from '@/scripts/page-metadata.js';
 import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
+import { PageHeaderItem } from '@/types/page-header.js';
 
 const props = withDefaults(defineProps<{
 	tabs?: Tab[];
 	tab?: string;
-	actions?: {
-		text: string;
-		icon: string;
-		highlighted?: boolean;
-		handler: (ev: MouseEvent) => void;
-	}[];
+	actions?: PageHeaderItem[];
 	thin?: boolean;
 	displayMyAvatar?: boolean;
 }>(), {
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index 1d41fe7529..dc374e2925 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -86,6 +86,9 @@ import { defaultStore } from '@/store.js';
 import MkNote from '@/components/MkNote.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
+import { PageHeaderItem } from '@/types/page-header.js';
+import { isSupportShare } from '@/scripts/navigator.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 
 const router = useRouter();
 
@@ -167,24 +170,40 @@ async function search() {
 
 const headerActions = $computed(() => {
 	if (channel && channel.userId) {
-		const share = {
-			icon: 'ti ti-share',
-			text: i18n.ts.share,
-			handler: async (): Promise<void> => {
-				navigator.share({
-					title: channel.name,
-					text: channel.description,
-					url: `${url}/channels/${channel.id}`,
-				});
-			},
-		};
+		const headerItems: PageHeaderItem[] = [];
 
-		const canEdit = ($i && $i.id === channel.userId) || iAmModerator;
-		return canEdit ? [share, {
-			icon: 'ti ti-settings',
-			text: i18n.ts.edit,
-			handler: edit,
-		}] : [share];
+		headerItems.push({
+			icon: 'ti ti-link',
+			text: i18n.ts.copyUrl,
+			handler: async (): Promise<void> => {
+				copyToClipboard(`${url}/channels/${channel.id}`);
+				os.success();
+			},
+		});
+
+		if (isSupportShare()) {
+			headerItems.push({
+				icon: 'ti ti-share',
+				text: i18n.ts.share,
+				handler: async (): Promise<void> => {
+					navigator.share({
+						title: channel.name,
+						text: channel.description,
+						url: `${url}/channels/${channel.id}`,
+					});
+				},
+			});
+		}
+
+		if (($i && $i.id === channel.userId) || iAmModerator) {
+			headerItems.push({
+				icon: 'ti ti-settings',
+				text: i18n.ts.edit,
+				handler: edit,
+			});
+		}
+
+		return headerItems.length > 0 ? headerItems : null;
 	} else {
 		return null;
 	}
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index 4573bbb81c..b32c8a3864 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -36,6 +36,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { url } from '@/config.js';
 import MkButton from '@/components/MkButton.vue';
 import { clipsCache } from '@/cache';
+import { isSupportShare } from '@/scripts/navigator.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 
 const props = defineProps<{
 	clipId: string,
@@ -118,6 +120,13 @@ const headerActions = $computed(() => clip && isOwned ? [{
 		clipsCache.delete();
 	},
 }, ...(clip.isPublic ? [{
+	icon: 'ti ti-link',
+	text: i18n.ts.copyUrl,
+	handler: async (): Promise<void> => {
+		copyToClipboard(`${url}/clips/${clip.id}`);
+		os.success();
+	},
+}] : []), ...(clip.isPublic && isSupportShare() ? [{
 	icon: 'ti ti-share',
 	text: i18n.ts.share,
 	handler: async (): Promise<void> => {
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index ebf117ffbf..4755eb5062 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -18,7 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<MkButton v-if="flash.isLiked" v-tooltip="i18n.ts.unlike" asLike class="button" rounded primary @click="unlike()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton>
 							<MkButton v-else v-tooltip="i18n.ts.like" asLike class="button" rounded @click="like()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton>
 							<MkButton v-tooltip="i18n.ts.shareWithNote" class="button" rounded @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></MkButton>
-							<MkButton v-tooltip="i18n.ts.share" class="button" rounded @click="share"><i class="ti ti-share ti-fw"></i></MkButton>
+							<MkButton v-tooltip="i18n.ts.copyLink" class="button" rounded @click="copyLink"><i class="ti ti-link ti-fw"></i></MkButton>
+							<MkButton v-if="isSupportShare()" v-tooltip="i18n.ts.share" class="button" rounded @click="share"><i class="ti ti-share ti-fw"></i></MkButton>
 						</div>
 					</div>
 					<div v-else :class="$style.ready">
@@ -70,6 +71,8 @@ import MkFolder from '@/components/MkFolder.vue';
 import MkCode from '@/components/MkCode.vue';
 import { defaultStore } from '@/store.js';
 import { $i } from '@/account.js';
+import { isSupportShare } from '@/scripts/navigator.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 
 const props = defineProps<{
 	id: string;
@@ -89,6 +92,11 @@ function fetchFlash() {
 	});
 }
 
+function copyLink() {
+	copyToClipboard(`${url}/play/${flash.id}`);
+	os.success();
+}
+
 function share() {
 	navigator.share({
 		title: flash.title,
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 3863348eae..5b551f75b5 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -29,7 +29,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<div class="other">
 								<button v-if="$i && $i.id === post.user.id" v-tooltip="i18n.ts.edit" v-click-anime class="_button" @click="edit"><i class="ti ti-pencil ti-fw"></i></button>
 								<button v-tooltip="i18n.ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></button>
-								<button v-tooltip="i18n.ts.share" v-click-anime class="_button" @click="share"><i class="ti ti-share ti-fw"></i></button>
+								<button v-tooltip="i18n.ts.copyLink" v-click-anime class="_button" @click="copyLink"><i class="ti ti-link ti-fw"></i></button>
+								<button v-if="isSupportShare()" v-tooltip="i18n.ts.share" v-click-anime class="_button" @click="share"><i class="ti ti-share ti-fw"></i></button>
 							</div>
 						</div>
 						<div class="user">
@@ -74,6 +75,8 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { defaultStore } from '@/store.js';
 import { $i } from '@/account.js';
+import { isSupportShare } from '@/scripts/navigator.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 
 const router = useRouter();
 
@@ -102,6 +105,11 @@ function fetchPost() {
 	});
 }
 
+function copyLink() {
+	copyToClipboard(`${url}/gallery/${post.id}`);
+	os.success();
+}
+
 function share() {
 	navigator.share({
 		title: post.title,
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 98cbaab2bb..2bc053ccfe 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -34,7 +34,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</div>
 						<div class="other">
 							<button v-tooltip="i18n.ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></button>
-							<button v-tooltip="i18n.ts.share" v-click-anime class="_button" @click="share"><i class="ti ti-share ti-fw"></i></button>
+							<button v-tooltip="i18n.ts.copyLink" v-click-anime class="_button" @click="copyLink"><i class="ti ti-link ti-fw"></i></button>
+							<button v-if="isSupportShare()" v-tooltip="i18n.ts.share" v-click-anime class="_button" @click="share"><i class="ti ti-share ti-fw"></i></button>
 						</div>
 					</div>
 					<div class="user">
@@ -90,6 +91,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { pageViewInterruptors, defaultStore } from '@/store.js';
 import { deepClone } from '@/scripts/clone.js';
 import { $i } from '@/account.js';
+import { isSupportShare } from '@/scripts/navigator.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 
 const props = defineProps<{
 	pageName: string;
@@ -136,6 +139,11 @@ function share() {
 	});
 }
 
+function copyLink() {
+	copyToClipboard(`${url}/@${page.user.username}/pages/${page.name}`);
+	os.success();
+}
+
 function shareWithNote() {
 	os.post({
 		initialText: `${page.title || page.name} ${url}/@${page.user.username}/pages/${page.name}`,
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index d0753872ff..763f6ff513 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -18,6 +18,7 @@ import { getUserMenu } from '@/scripts/get-user-menu.js';
 import { clipsCache } from '@/cache.js';
 import { MenuItem } from '@/types/menu.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
+import { isSupportShare } from '@/scripts/navigator.js';
 
 export async function getNoteClipMenu(props: {
 	note: Misskey.entities.Note;
@@ -280,11 +281,11 @@ export function getNoteMenu(props: {
 					window.open(appearNote.url ?? appearNote.uri, '_blank');
 				},
 			} : undefined,
-			{
+			...(isSupportShare() ? [{
 				icon: 'ti ti-share',
 				text: i18n.ts.share,
 				action: share,
-			},
+			}] : []),
 			$i && $i.policies.canUseTranslator && instance.translatorAvailable ? {
 				icon: 'ti ti-language-hiragana',
 				text: i18n.ts.translate,
@@ -484,7 +485,7 @@ export function getRenoteMenu(props: {
 		}]);
 	}
 
-	if (!appearNote.channel || appearNote.channel?.allowRenoteToExternal) {
+	if (!appearNote.channel || appearNote.channel.allowRenoteToExternal) {
 		normalRenoteItems.push(...[{
 			text: i18n.ts.renote,
 			icon: 'ti ti-repeat',
diff --git a/packages/frontend/src/scripts/navigator.ts b/packages/frontend/src/scripts/navigator.ts
new file mode 100644
index 0000000000..b13186a10e
--- /dev/null
+++ b/packages/frontend/src/scripts/navigator.ts
@@ -0,0 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export function isSupportShare(): boolean {
+	return 'share' in navigator;
+}
diff --git a/packages/frontend/src/types/page-header.ts b/packages/frontend/src/types/page-header.ts
new file mode 100644
index 0000000000..295b97a7fd
--- /dev/null
+++ b/packages/frontend/src/types/page-header.ts
@@ -0,0 +1,11 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export type PageHeaderItem = {
+    text: string;
+    icon: string;
+    highlighted?: boolean;
+    handler: (ev: MouseEvent) => void;
+};

From 98e1af28b8b6a8b9a08c19b2abf926ae11e31dd9 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Thu, 30 Nov 2023 13:49:31 +0900
Subject: [PATCH 070/435] =?UTF-8?q?enhance(frontend):=20=E3=83=8E=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E3=83=97=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC=E3=81=AB?=
 =?UTF-8?q?CW=E3=81=8C=E5=8F=8D=E6=98=A0=E3=81=95=E3=82=8C=E3=82=8B?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=20(#12509)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): ノートプレビューにCWが反映されるように

* Update CHANGELOG.md

* refactor: 不要な条件を除去

* Revert "refactor: 不要な条件を除去"

This reverts commit e4eff689bd1f2df78411a2f01ee05434e2d298a8.

* fix: やっぱり不要な条件だった
---
 CHANGELOG.md                                  |  1 +
 .../frontend/src/components/MkCwButton.vue    | 23 ++++++++++++---
 packages/frontend/src/components/MkNote.vue   |  2 +-
 .../src/components/MkNoteDetailed.vue         |  2 +-
 .../frontend/src/components/MkNotePreview.vue | 28 +++++++++++++++++--
 .../frontend/src/components/MkNoteSimple.vue  |  2 +-
 .../frontend/src/components/MkNoteSub.vue     |  2 +-
 .../frontend/src/components/MkPostForm.vue    |  2 +-
 8 files changed, 51 insertions(+), 11 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d2e6702a92..0a41c2cef4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -27,6 +27,7 @@
 - Enhance: サウンドにドライブのファイルを使用できるように
 - Enhance: Shareページで投稿を完了すると、親ウィンドウ(親フレーム)にpostMessageするように
 - Enhance: チャンネル、クリップ、ページ、Play、ギャラリーにURLのコピーボタンを設置 #11305
+- Enhance: ノートプレビューに「内容を隠す」が反映されるように
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue
index 0cdaf7c9bd..70b7bc8295 100644
--- a/packages/frontend/src/components/MkCwButton.vue
+++ b/packages/frontend/src/components/MkCwButton.vue
@@ -16,7 +16,22 @@ import MkButton from '@/components/MkButton.vue';
 
 const props = defineProps<{
 	modelValue: boolean;
-	note: Misskey.entities.Note;
+	text: string | null;
+	files: Misskey.entities.DriveFile[];
+	poll?: {
+		expiresAt: string | null;
+		multiple: boolean;
+		choices: {
+			isVoted: boolean;
+			text: string;
+			votes: number;
+		}[];
+	} | {
+		choices: string[];
+		multiple: boolean;
+		expiresAt: string | null;
+		expiredAfter: string | null;
+	};
 }>();
 
 const emit = defineEmits<{
@@ -25,9 +40,9 @@ const emit = defineEmits<{
 
 const label = computed(() => {
 	return concat([
-		props.note.text ? [i18n.t('_cw.chars', { count: props.note.text.length })] : [],
-		props.note.files && props.note.files.length !== 0 ? [i18n.t('_cw.files', { count: props.note.files.length })] : [],
-		props.note.poll != null ? [i18n.ts.poll] : [],
+		props.text ? [i18n.t('_cw.chars', { count: props.text.length })] : [],
+		props.files.length !== 0 ? [i18n.t('_cw.files', { count: props.files.length })] : [],
+		props.poll != null ? [i18n.ts.poll] : [],
 	] as string[][]).join(' / ');
 });
 
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 04fe8d52fb..596895efb9 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div style="container-type: inline-size;">
 				<p v-if="appearNote.cw != null" :class="$style.cw">
 					<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
-					<MkCwButton v-model="showContent" :note="appearNote" style="margin: 4px 0;"/>
+					<MkCwButton v-model="showContent" :text="appearNote.text" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;"/>
 				</p>
 				<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]">
 					<div :class="$style.text">
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index d8089ac36f..31e97b6aaa 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -68,7 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div :class="$style.noteContent">
 			<p v-if="appearNote.cw != null" :class="$style.cw">
 				<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
-				<MkCwButton v-model="showContent" :note="appearNote"/>
+				<MkCwButton v-model="showContent" :text="appearNote.text" :files="appearNote.files" :poll="appearNote.poll"/>
 			</p>
 			<div v-show="appearNote.cw == null || showContent">
 				<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
diff --git a/packages/frontend/src/components/MkNotePreview.vue b/packages/frontend/src/components/MkNotePreview.vue
index 9b7a39b537..d664d88231 100644
--- a/packages/frontend/src/components/MkNotePreview.vue
+++ b/packages/frontend/src/components/MkNotePreview.vue
@@ -11,7 +11,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<MkUserName :user="user" :nowrap="true"/>
 		</div>
 		<div>
-			<div>
+			<p v-if="useCw" :class="$style.cw">
+				<Mfm v-if="cw != ''" :text="cw" :author="user" :nyaize="'respect'" :i="user" style="margin-right: 8px;"/>
+				<MkCwButton v-model="showContent" :text="text.trim()" :files="files" :poll="poll" style="margin: 4px 0;"/>
+			</p>
+			<div v-show="!useCw || showContent">
 				<Mfm :text="text.trim()" :author="user" :nyaize="'respect'" :i="user"/>
 			</div>
 		</div>
@@ -20,11 +24,23 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
+import MkCwButton from '@/components/MkCwButton.vue';
+
+const showContent = ref(false);
 
 const props = defineProps<{
 	text: string;
+	files: Misskey.entities.DriveFile[];
+	poll?: {
+		choices: string[];
+		multiple: boolean;
+		expiresAt: string | null;
+		expiredAfter: string | null;
+	};
+	useCw: boolean;
+	cw: string | null;
 	user: Misskey.entities.User;
 }>();
 </script>
@@ -53,6 +69,14 @@ const props = defineProps<{
 	min-width: 0;
 }
 
+.cw {
+	cursor: default;
+	display: block;
+	margin: 0;
+	padding: 0;
+	overflow-wrap: break-word;
+}
+
 .header {
 	margin-bottom: 2px;
 	font-weight: bold;
diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue
index a40dcaf003..f3ab6b2723 100644
--- a/packages/frontend/src/components/MkNoteSimple.vue
+++ b/packages/frontend/src/components/MkNoteSimple.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div>
 			<p v-if="note.cw != null" :class="$style.cw">
 				<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/>
-				<MkCwButton v-model="showContent" :note="note"/>
+				<MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/>
 			</p>
 			<div v-show="note.cw == null || showContent">
 				<MkSubNoteContent :class="$style.text" :note="note"/>
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 422e9094cc..1e901a1fd6 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div>
 				<p v-if="note.cw != null" :class="$style.cw">
 					<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'"/>
-					<MkCwButton v-model="showContent" :note="note"/>
+					<MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/>
 				</p>
 				<div v-show="note.cw == null || showContent">
 					<MkSubNoteContent :class="$style.text" :note="note"/>
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index d163ea2487..07c7213202 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -73,7 +73,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags">
 	<XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName" @replaceFile="replaceFile"/>
 	<MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/>
-	<MkNotePreview v-if="showPreview" :class="$style.preview" :text="text" :user="postAccount ?? $i"/>
+	<MkNotePreview v-if="showPreview" :class="$style.preview" :text="text" :files="files" :poll="poll ?? undefined" :useCw="useCw" :cw="cw" :user="postAccount ?? $i"/>
 	<div v-if="showingOptions" style="padding: 8px 16px;">
 	</div>
 	<footer :class="$style.footer">

From 28cb0fc70b67abf5fe0b1a765df0d97b0b4f4902 Mon Sep 17 00:00:00 2001
From: GrapeApple0 <84321396+GrapeApple0@users.noreply.github.com>
Date: Thu, 30 Nov 2023 14:35:56 +0900
Subject: [PATCH 071/435] =?UTF-8?q?enhance:=20=E8=A8=AD=E5=AE=9A=E3=81=97?=
 =?UTF-8?q?=E3=81=9F=E3=82=BF=E3=82=B0=E3=82=92=E3=83=88=E3=83=AC=E3=83=B3?=
 =?UTF-8?q?=E3=83=89=E3=81=AB=E8=A1=A8=E7=A4=BA=E3=81=95=E3=81=9B=E3=81=AA?=
 =?UTF-8?q?=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=E9=A0=85?=
 =?UTF-8?q?=E7=9B=AE=E3=82=92=E7=AE=A1=E7=90=86=E7=94=BB=E9=9D=A2=E3=81=A7?=
 =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#12512)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: hiddenTagsを管理画面で設定できるように

* Update locales/ja-JP.yml

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

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 locales/index.d.ts                               | 2 ++
 locales/ja-JP.yml                                | 2 ++
 packages/frontend/src/pages/admin/moderation.vue | 8 ++++++++
 3 files changed, 12 insertions(+)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 64ee30410e..d72e7d29f5 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1030,6 +1030,8 @@ export interface Locale {
     "sensitiveWords": string;
     "sensitiveWordsDescription": string;
     "sensitiveWordsDescription2": string;
+    "hiddenTags": string;
+    "hiddenTagsDescription": string;
     "notesSearchNotAvailable": string;
     "license": string;
     "unfavoriteConfirm": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index f4daefa978..0f4164652c 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1027,6 +1027,8 @@ resetPasswordConfirm: "パスワードリセットしますか?"
 sensitiveWords: "センシティブワード"
 sensitiveWordsDescription: "設定したワードが含まれるノートの公開範囲をホームにします。改行で区切って複数設定できます。"
 sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。"
+hiddenTags: "非表示ハッシュタグ"
+hiddenTagsDescription: "設定したタグをトレンドに表示させないようにします。改行で区切って複数設定できます。"
 notesSearchNotAvailable: "ノート検索は利用できません。"
 license: "ライセンス"
 unfavoriteConfirm: "お気に入り解除しますか?"
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index 59ee041386..47f46fe6cf 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -39,6 +39,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<template #label>{{ i18n.ts.sensitiveWords }}</template>
 						<template #caption>{{ i18n.ts.sensitiveWordsDescription }}<br>{{ i18n.ts.sensitiveWordsDescription2 }}</template>
 					</MkTextarea>
+
+					<MkTextarea v-model="hiddenTags">
+						<template #label>{{ i18n.ts.hiddenTags }}</template>
+						<template #caption>{{ i18n.ts.hiddenTagsDescription }}</template>
+					</MkTextarea>
 				</div>
 			</FormSuspense>
 		</MkSpacer>
@@ -72,6 +77,7 @@ import FormLink from '@/components/form/link.vue';
 let enableRegistration: boolean = $ref(false);
 let emailRequiredForSignup: boolean = $ref(false);
 let sensitiveWords: string = $ref('');
+let hiddenTags: string = $ref('');
 let preservedUsernames: string = $ref('');
 let tosUrl: string | null = $ref(null);
 let privacyPolicyUrl: string | null = $ref(null);
@@ -81,6 +87,7 @@ async function init() {
 	enableRegistration = !meta.disableRegistration;
 	emailRequiredForSignup = meta.emailRequiredForSignup;
 	sensitiveWords = meta.sensitiveWords.join('\n');
+	hiddenTags = meta.hiddenTags.join('\n');
 	preservedUsernames = meta.preservedUsernames.join('\n');
 	tosUrl = meta.tosUrl;
 	privacyPolicyUrl = meta.privacyPolicyUrl;
@@ -93,6 +100,7 @@ function save() {
 		tosUrl,
 		privacyPolicyUrl,
 		sensitiveWords: sensitiveWords.split('\n'),
+		hiddenTags: hiddenTags.split('\n'),
 		preservedUsernames: preservedUsernames.split('\n'),
 	}).then(() => {
 		fetchInstance();

From 47a10f6a6df1bfbb90af519e1b34996a328d28ca Mon Sep 17 00:00:00 2001
From: Kisaragi <48310258+KisaragiEffective@users.noreply.github.com>
Date: Thu, 30 Nov 2023 14:46:16 +0900
Subject: [PATCH 072/435] refactor(frontend): give local variable to explicit
 type annotation to avoid TS7043 (#12495)

* refactor: give local variable to explicit type annotation to avoid TS7043

* chore: fix lint error
---
 .../src/components/global/MkMisskeyFlavoredMarkdown.ts        | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index c5f247bce9..d4c3ef60aa 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -103,7 +103,7 @@ export default function(props: MfmProps) {
 
 			case 'fn': {
 				// TODO: CSSを文字列で組み立てていくと token.props.args.~~~ 経由でCSSインジェクションできるのでよしなにやる
-				let style;
+				let style: string | undefined;
 				switch (token.props.name) {
 					case 'tada': {
 						const speed = validTime(token.props.args.speed) ?? '1s';
@@ -268,7 +268,7 @@ export default function(props: MfmProps) {
 						]);
 					}
 				}
-				if (style == null) {
+				if (style === undefined) {
 					return h('span', {}, ['$[', token.props.name, ' ', ...genEl(token.children, scale), ']']);
 				} else {
 					return h('span', {

From 4f6e0985423950d1ad1d279cb2c019cf8814828d Mon Sep 17 00:00:00 2001
From: Cocoa Hoto <cocoa@hoto.us>
Date: Thu, 30 Nov 2023 14:47:08 +0900
Subject: [PATCH 073/435] fix(docker): cannot build docker image on some
 environments (#12494)

---
 Dockerfile | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index 028a3976d2..38aa5bc7b3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -67,8 +67,8 @@ RUN apt-get update \
 	&& corepack enable \
 	&& groupadd -g "${GID}" misskey \
 	&& useradd -l -u "${UID}" -g "${GID}" -m -d /misskey misskey \
-	&& find / -type d -path /proc -prune -o -type f -perm /u+s -ignore_readdir_race -exec chmod u-s {} \; \
-	&& find / -type d -path /proc -prune -o -type f -perm /g+s -ignore_readdir_race -exec chmod g-s {} \; \
+	&& find / -type d -path /sys -prune -o -type d -path /proc -prune -o -type f -perm /u+s -ignore_readdir_race -exec chmod u-s {} \; \
+	&& find / -type d -path /sys -prune -o -type d -path /proc -prune -o -type f -perm /g+s -ignore_readdir_race -exec chmod g-s {} \; \
 	&& apt-get clean \
 	&& rm -rf /var/lib/apt/lists
 

From 22d6fa1fdf1dd4b61673d10cac6ca866dd5f26d8 Mon Sep 17 00:00:00 2001
From: yukineko <27853966+hideki0403@users.noreply.github.com>
Date: Thu, 30 Nov 2023 14:48:02 +0900
Subject: [PATCH 074/435] =?UTF-8?q?enhance(dev):=20=E9=96=8B=E7=99=BA?=
 =?UTF-8?q?=E3=83=A2=E3=83=BC=E3=83=89=E6=99=82=E3=81=ABlocale=E3=81=A8?=
 =?UTF-8?q?=E5=9E=8B=E5=AE=9A=E7=BE=A9=E3=81=8C=E8=87=AA=E5=8B=95=E7=9A=84?=
 =?UTF-8?q?=E3=81=AB=E5=86=8D=E7=94=9F=E6=88=90=E3=81=95=E3=82=8C=E3=82=8B?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=20(#12481)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: localeを任意のタイミングでリビルドできるように

* enhance: localeも監視し、必要であればlocaleをリビルドするように

* feat: devモードの時のみナビゲーションバーからキャッシュクリアができるように

* refactor: キャッシュクリア部分を共通化

* fix: localesのファイル変更イベントが取れないのを修正

* fix: replaceAllでコケるのを修正

* change: 開発モードに関係なくナビゲーションバーからキャッシュクリアできるように

* refactor: 必要のないリビルドをしないように

* update: CHANGELOG.md

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                  |  1 +
 locales/generateDTS.js                        | 12 ++++
 locales/index.d.ts                            |  1 +
 locales/index.js                              | 58 ++++++++++---------
 packages/frontend/src/navbar.ts               |  8 +++
 .../frontend/src/pages/settings/index.vue     | 12 +---
 packages/frontend/src/scripts/clear-cache.ts  | 14 +++++
 scripts/build-assets.mjs                      | 18 +++---
 8 files changed, 80 insertions(+), 44 deletions(-)
 create mode 100644 packages/frontend/src/scripts/clear-cache.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a41c2cef4..db26ffc8ee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
 - Enhance: ユーザーのRawデータを表示するページが復活
 - Enhance: リアクション選択時に音を鳴らせるように
 - Enhance: サウンドにドライブのファイルを使用できるように
+- Enhance: ナビゲーションバーに項目「キャッシュを削除」を追加
 - Enhance: Shareページで投稿を完了すると、親ウィンドウ(親フレーム)にpostMessageするように
 - Enhance: チャンネル、クリップ、ページ、Play、ギャラリーにURLのコピーボタンを設置 #11305
 - Enhance: ノートプレビューに「内容を隠す」が反映されるように
diff --git a/locales/generateDTS.js b/locales/generateDTS.js
index 7af773f3b1..d3afdd6e15 100644
--- a/locales/generateDTS.js
+++ b/locales/generateDTS.js
@@ -56,6 +56,18 @@ export default function generateDTS() {
 				ts.NodeFlags.Const | ts.NodeFlags.Ambient | ts.NodeFlags.ContextFlags,
 			),
 		),
+		ts.factory.createFunctionDeclaration(
+			[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
+			undefined,
+			ts.factory.createIdentifier('build'),
+			undefined,
+			[],
+			ts.factory.createTypeReferenceNode(
+				ts.factory.createIdentifier('Locale'),
+				undefined,
+			),
+			undefined,
+		),
 		ts.factory.createExportDefault(ts.factory.createIdentifier('locales')),
 	];
 	const printed = ts.createPrinter({
diff --git a/locales/index.d.ts b/locales/index.d.ts
index d72e7d29f5..6036c6fa66 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2505,4 +2505,5 @@ export interface Locale {
 declare const locales: {
     [lang: string]: Locale;
 };
+export function build(): Locale;
 export default locales;
diff --git a/locales/index.js b/locales/index.js
index 67a406d98d..650e552337 100644
--- a/locales/index.js
+++ b/locales/index.js
@@ -51,33 +51,37 @@ const primaries = {
 // 何故か文字列にバックスペース文字が混入することがあり、YAMLが壊れるので取り除く
 const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), '');
 
-const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(new URL(`${c}.yml`, import.meta.url), 'utf-8'))) || {}, a), {});
+export function build() {
+	const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(new URL(`${c}.yml`, import.meta.url), 'utf-8'))) || {}, a), {});
 
-// 空文字列が入ることがあり、フォールバックが動作しなくなるのでプロパティごと消す
-const removeEmpty = (obj) => {
-	for (const [k, v] of Object.entries(obj)) {
-		if (v === '') {
-			delete obj[k];
-		} else if (typeof v === 'object') {
-			removeEmpty(v);
+	// 空文字列が入ることがあり、フォールバックが動作しなくなるのでプロパティごと消す
+	const removeEmpty = (obj) => {
+		for (const [k, v] of Object.entries(obj)) {
+			if (v === '') {
+				delete obj[k];
+			} else if (typeof v === 'object') {
+				removeEmpty(v);
+			}
 		}
-	}
-	return obj;
-};
-removeEmpty(locales);
+		return obj;
+	};
+	removeEmpty(locales);
 
-export default Object.entries(locales)
-	.reduce((a, [k ,v]) => (a[k] = (() => {
-		const [lang] = k.split('-');
-		switch (k) {
-			case 'ja-JP': return v;
-			case 'ja-KS':
-			case 'en-US': return merge(locales['ja-JP'], v);
-			default: return merge(
-				locales['ja-JP'],
-				locales['en-US'],
-				locales[`${lang}-${primaries[lang]}`] ?? {},
-				v
-			);
-		}
-	})(), a), {});
+	return Object.entries(locales)
+		.reduce((a, [k, v]) => (a[k] = (() => {
+			const [lang] = k.split('-');
+			switch (k) {
+				case 'ja-JP': return v;
+				case 'ja-KS':
+				case 'en-US': return merge(locales['ja-JP'], v);
+				default: return merge(
+					locales['ja-JP'],
+					locales['en-US'],
+					locales[`${lang}-${primaries[lang]}`] ?? {},
+					v
+				);
+			}
+		})(), a), {});
+}
+
+export default build();
diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts
index f0ed773f82..78a0945ddb 100644
--- a/packages/frontend/src/navbar.ts
+++ b/packages/frontend/src/navbar.ts
@@ -12,6 +12,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { ui } from '@/config.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
+import { clearCache } from './scripts/clear-cache.js';
 
 export const navbarItemDef = reactive({
 	notifications: {
@@ -171,4 +172,11 @@ export const navbarItemDef = reactive({
 		show: computed(() => $i != null),
 		to: `/@${$i?.username}`,
 	},
+	cacheClear: {
+		title: i18n.ts.cacheClear,
+		icon: 'ti ti-trash',
+		action: (ev) => {
+			clearCache();
+		},
+	},
 });
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index 361a6c8c78..5a1a9aedb3 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -33,13 +33,11 @@ import { i18n } from '@/i18n.js';
 import MkInfo from '@/components/MkInfo.vue';
 import MkSuperMenu from '@/components/MkSuperMenu.vue';
 import { signout, $i } from '@/account.js';
-import { unisonReload } from '@/scripts/unison-reload.js';
+import { clearCache } from '@/scripts/clear-cache.js';
 import { instance } from '@/instance.js';
 import { useRouter } from '@/router.js';
 import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
 import * as os from '@/os.js';
-import { miLocalStorage } from '@/local-storage.js';
-import { fetchCustomEmojis } from '@/custom-emojis.js';
 
 const indexInfo = {
 	title: i18n.ts.settings,
@@ -182,13 +180,7 @@ const menuDef = computed(() => [{
 		icon: 'ti ti-trash',
 		text: i18n.ts.clearCache,
 		action: async () => {
-			os.waiting();
-			miLocalStorage.removeItem('locale');
-			miLocalStorage.removeItem('theme');
-			miLocalStorage.removeItem('emojis');
-			miLocalStorage.removeItem('lastEmojisFetchedAt');
-			await fetchCustomEmojis(true);
-			unisonReload();
+			await clearCache();
 		},
 	}, {
 		type: 'button',
diff --git a/packages/frontend/src/scripts/clear-cache.ts b/packages/frontend/src/scripts/clear-cache.ts
new file mode 100644
index 0000000000..5f27254b8a
--- /dev/null
+++ b/packages/frontend/src/scripts/clear-cache.ts
@@ -0,0 +1,14 @@
+import { unisonReload } from '@/scripts/unison-reload.js';
+import * as os from '@/os.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { fetchCustomEmojis } from '@/custom-emojis.js';
+
+export async function clearCache() {
+	os.waiting();
+	miLocalStorage.removeItem('locale');
+	miLocalStorage.removeItem('theme');
+	miLocalStorage.removeItem('emojis');
+	miLocalStorage.removeItem('lastEmojisFetchedAt');
+	await fetchCustomEmojis(true);
+	unisonReload();
+}
diff --git a/scripts/build-assets.mjs b/scripts/build-assets.mjs
index 1ffcec8aa3..f8f09ec2fb 100644
--- a/scripts/build-assets.mjs
+++ b/scripts/build-assets.mjs
@@ -9,10 +9,12 @@ import cssnano from 'cssnano';
 import postcss from 'postcss';
 import * as terser from 'terser';
 
-import locales from '../locales/index.js';
+import { build as buildLocales } from '../locales/index.js';
 import generateDTS from '../locales/generateDTS.js';
 import meta from '../package.json' assert { type: "json" };
 
+let locales = buildLocales();
+
 async function copyFrontendFonts() {
   await fs.cp('./packages/frontend/node_modules/three/examples/fonts', './built/_frontend_dist_/fonts', { dereference: true, recursive: true });
 }
@@ -89,10 +91,12 @@ async function build() {
 await build();
 
 if (process.argv.includes("--watch")) {
-  const watcher = fs.watch('./packages', { recursive: true });
-  for await (const event of watcher) {
-    if (/^[a-z]+\/src/.test(event.filename)) {
-      await build();
-    }
-  }
+	const watcher = fs.watch('./locales');
+	for await (const event of watcher) {
+		const filename = event.filename?.replaceAll('\\', '/');
+		if (/^[a-z]+-[A-Z]+\.yml/.test(filename)) {
+			locales = buildLocales();
+			await copyFrontendLocales()
+		}
+	}
 }

From b05d71fabff55ac5653994a1188c6db88048e8ee 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: Thu, 30 Nov 2023 14:49:26 +0900
Subject: [PATCH 075/435] =?UTF-8?q?feat(frontend):=20=E4=BB=8A=E6=97=A5?=
 =?UTF-8?q?=E8=AA=95=E7=94=9F=E6=97=A5=E3=81=AE=E3=83=95=E3=82=A9=E3=83=AD?=
 =?UTF-8?q?=E3=83=BC=E4=B8=AD=E3=81=AE=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC?=
 =?UTF-8?q?=E3=82=92=E4=B8=80=E8=A6=A7=E8=A1=A8=E7=A4=BA=E3=81=A7=E3=81=8D?=
 =?UTF-8?q?=E3=82=8B=E3=82=A6=E3=82=A3=E3=82=B8=E3=82=A7=E3=83=83=E3=83=88?=
 =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=20(#12450)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (add) 今日誕生日のフォロイー一覧表示

* Update Changelog

* Update Changelog

* 実装漏れ

* create index

* (fix) index
---
 CHANGELOG.md                                  |   1 +
 locales/index.d.ts                            |   1 +
 locales/ja-JP.yml                             |   1 +
 .../migration/1700902349231-add-bday-index.js |  16 +++
 packages/backend/src/models/UserProfile.ts    |   1 +
 .../server/api/endpoints/users/following.ts   |  23 ++++
 .../src/widgets/WidgetBirthdayFollowings.vue  | 127 ++++++++++++++++++
 packages/frontend/src/widgets/index.ts        |   2 +
 8 files changed, 172 insertions(+)
 create mode 100644 packages/backend/migration/1700902349231-add-bday-index.js
 create mode 100644 packages/frontend/src/widgets/WidgetBirthdayFollowings.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index db26ffc8ee..e40c5f0fd3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@
 - Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
 
 ### Client
+- Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加
 - Enhance: 絵文字のオートコンプリート機能強化 #12364
 - Enhance: ユーザーのRawデータを表示するページが復活
 - Enhance: リアクション選択時に音を鳴らせるように
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 6036c6fa66..d462816494 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2110,6 +2110,7 @@ export interface Locale {
             "chooseList": string;
         };
         "clicker": string;
+        "birthdayFollowings": string;
     };
     "_cw": {
         "hide": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0f4164652c..aa3cdf0750 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2014,6 +2014,7 @@ _widgets:
   _userList:
     chooseList: "リストを選択"
   clicker: "クリッカー"
+  birthdayFollowings: "今日誕生日のユーザー"
 
 _cw:
   hide: "隠す"
diff --git a/packages/backend/migration/1700902349231-add-bday-index.js b/packages/backend/migration/1700902349231-add-bday-index.js
new file mode 100644
index 0000000000..251526fc26
--- /dev/null
+++ b/packages/backend/migration/1700902349231-add-bday-index.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class AddBdayIndex1700902349231 {
+    name = 'AddBdayIndex1700902349231'
+
+    async up(queryRunner) {
+      await queryRunner.query(`CREATE INDEX "IDX_de22cd2b445eee31ae51cdbe99" ON "user_profile" (SUBSTR("birthday", 6, 5))`);
+    }
+
+    async down(queryRunner) {
+			await queryRunner.query(`DROP INDEX "public"."IDX_de22cd2b445eee31ae51cdbe99"`);
+    }
+}
diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts
index 8a43b60039..6659a01412 100644
--- a/packages/backend/src/models/UserProfile.ts
+++ b/packages/backend/src/models/UserProfile.ts
@@ -29,6 +29,7 @@ export class MiUserProfile {
 	})
 	public location: string | null;
 
+	@Index()
 	@Column('char', {
 		length: 10, nullable: true,
 		comment: 'The birthday (YYYY-MM-DD) of the User.',
diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts
index 03487275a3..ead7ba8c40 100644
--- a/packages/backend/src/server/api/endpoints/users/following.ts
+++ b/packages/backend/src/server/api/endpoints/users/following.ts
@@ -42,6 +42,12 @@ export const meta = {
 			code: 'FORBIDDEN',
 			id: 'f6cdb0df-c19f-ec5c-7dbb-0ba84a1f92ba',
 		},
+
+		birthdayInvalid: {
+			message: 'Birthday date format is invalid.',
+			code: 'BIRTHDAY_DATE_FORMAT_INVALID',
+			id: 'a2b007b9-4782-4eba-abd3-93b05ed4130d',
+		},
 	},
 } as const;
 
@@ -59,6 +65,8 @@ export const paramDef = {
 			nullable: true,
 			description: 'The local host is represented with `null`.',
 		},
+
+		birthday: { type: 'string', nullable: true },
 	},
 	anyOf: [
 		{ required: ['userId'] },
@@ -117,6 +125,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				.andWhere('following.followerId = :userId', { userId: user.id })
 				.innerJoinAndSelect('following.followee', 'followee');
 
+			if (ps.birthday) {
+				try {
+					const d = new Date(ps.birthday);
+					d.setHours(0, 0, 0, 0);
+					const birthday = `${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`;
+					const birthdayUserQuery = this.userProfilesRepository.createQueryBuilder('user_profile');
+					birthdayUserQuery.select('user_profile.userId')
+						.where(`SUBSTR(user_profile.birthday, 6, 5) = '${birthday}'`);
+
+					query.andWhere(`following.followeeId IN (${ birthdayUserQuery.getQuery() })`);
+				} catch (err) {
+					throw new ApiError(meta.errors.birthdayInvalid);
+				}
+			}
+
 			const followings = await query
 				.limit(ps.limit)
 				.getMany();
diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
new file mode 100644
index 0000000000..7c4455516d
--- /dev/null
+++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
@@ -0,0 +1,127 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkContainer :showHeader="widgetProps.showHeader" class="mkw-bdayfollowings">
+	<template #icon><i class="ti ti-cake"></i></template>
+	<template #header>{{ i18n.ts._widgets.birthdayFollowings }}</template>
+
+	<div :class="$style.bdayFRoot">
+		<MkLoading v-if="fetching"/>
+		<div v-else-if="users.length > 0" :class="$style.bdayFGrid">
+			<MkAvatar v-for="user in users" :key="user.id" :user="user.followee" link preview></MkAvatar>
+		</div>
+		<div v-else :class="$style.bdayFFallback">
+			<img :src="infoImageUrl" class="_ghost" :class="$style.bdayFFallbackImage"/>
+			<div>{{ i18n.ts.nothing }}</div>
+		</div>
+	</div>
+</MkContainer>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+import * as Misskey from 'misskey-js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { GetFormResultType } from '@/scripts/form.js';
+import MkContainer from '@/components/MkContainer.vue';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import { i18n } from '@/i18n.js';
+import { infoImageUrl } from '@/instance.js';
+import { $i } from '@/account.js';
+
+const name = i18n.ts._widgets.birthdayFollowings;
+
+const widgetPropsDef = {
+	showHeader: {
+		type: 'boolean' as const,
+		default: true,
+	},
+};
+
+type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
+
+const props = defineProps<WidgetComponentProps<WidgetProps>>();
+const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
+
+const { widgetProps, configure } = useWidgetPropsManager(name,
+	widgetPropsDef,
+	props,
+	emit,
+);
+
+const users = ref<Misskey.entities.FollowingFolloweePopulated[]>([]);
+const fetching = ref(true);
+let lastFetchedAt = '1970-01-01';
+
+const fetch = () => {
+	if (!$i) {
+		users.value = [];
+		fetching.value = false;
+		return;
+	}
+
+	const lfAtD = new Date(lastFetchedAt);
+	lfAtD.setHours(0, 0, 0, 0);
+	const now = new Date();
+	now.setHours(0, 0, 0, 0);
+
+	if (now > lfAtD) {
+		os.api('users/following', {
+			limit: 18,
+			birthday: now.toISOString(),
+			userId: $i.id,
+		}).then(res => {
+			users.value = res;
+			fetching.value = false;
+		});
+
+		lastFetchedAt = now.toISOString();
+	}
+};
+
+useInterval(fetch, 1000 * 60, {
+	immediate: true,
+	afterMounted: true,
+});
+
+defineExpose<WidgetComponentExpose>({
+	name,
+	configure,
+	id: props.widget ? props.widget.id : null,
+});
+</script>
+
+<style lang="scss" module>
+.bdayFRoot {
+	overflow: hidden;
+	min-height: calc(calc(calc(50px * 3) - 8px) + calc(var(--margin) * 2));
+}
+.bdayFGrid {
+	display: grid;
+	grid-template-columns: repeat(6, 42px);
+	grid-template-rows: repeat(3, 42px);
+	place-content: center;
+	gap: 8px;
+	margin: var(--margin) auto;
+}
+
+.bdayFFallback {
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+}
+
+.bdayFFallbackImage {
+	height: 96px;
+	width: auto;
+	max-width: 90%;
+	margin-bottom: 8px;
+	border-radius: var(--radius);
+}
+</style>
diff --git a/packages/frontend/src/widgets/index.ts b/packages/frontend/src/widgets/index.ts
index 405c49ab06..36925e1bd8 100644
--- a/packages/frontend/src/widgets/index.ts
+++ b/packages/frontend/src/widgets/index.ts
@@ -33,6 +33,7 @@ export default function(app: App) {
 	app.component('WidgetAichan', defineAsyncComponent(() => import('./WidgetAichan.vue')));
 	app.component('WidgetUserList', defineAsyncComponent(() => import('./WidgetUserList.vue')));
 	app.component('WidgetClicker', defineAsyncComponent(() => import('./WidgetClicker.vue')));
+	app.component('WidgetBirthdayFollowings', defineAsyncComponent(() => import('./WidgetBirthdayFollowings.vue')));
 }
 
 export const widgets = [
@@ -63,4 +64,5 @@ export const widgets = [
 	'aichan',
 	'userList',
 	'clicker',
+	'birthdayFollowings',
 ];

From e500fe2586cdf9a60ac75b45f33be99dfd4122bf Mon Sep 17 00:00:00 2001
From: Srgr0 <66754887+Srgr0@users.noreply.github.com>
Date: Thu, 30 Nov 2023 14:59:42 +0900
Subject: [PATCH 076/435] =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E8=A9=B3?=
 =?UTF-8?q?=E7=B4=B0=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AB=E8=A8=98=E8=BC=89?=
 =?UTF-8?q?=E3=81=99=E3=82=8B=E6=83=85=E5=A0=B1=E3=82=92=E8=BF=BD=E5=8A=A0?=
 =?UTF-8?q?=20(#12417)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Update emojis.emoji.vue

* Update CHANGELOG.md

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                 | 1 +
 packages/frontend/src/pages/emojis.emoji.vue | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e40c5f0fd3..d32b46f63d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -32,6 +32,7 @@
 - Enhance: ノートプレビューに「内容を隠す」が反映されるように
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
+- Enhance: 絵文字の詳細ページに記載される情報を追加
 - Fix: コードエディタが正しく表示されない問題を修正
 - Fix: プロフィールの「ファイル」にセンシティブな画像がある際のデザインを修正
 - Fix: 一度に大量の通知が入った際に通知音が音割れする問題を修正
diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue
index 9aaa7890a9..9ba9047ca3 100644
--- a/packages/frontend/src/pages/emojis.emoji.vue
+++ b/packages/frontend/src/pages/emojis.emoji.vue
@@ -46,7 +46,7 @@ function menu(ev) {
 			os.apiGet('emoji', { name: props.emoji.name }).then(res => {
 				os.alert({
 					type: 'info',
-					text: `License: ${res.license}`,
+					text: `Name: ${res.name}\nAliases: ${res.aliases.join(' ')}\nCategory: ${res.category}\nisSensitive: ${res.isSensitive}\nlocalOnly: ${res.localOnly}\nLicense: ${res.license}\nURL: ${res.url}`,
 				});
 			});
 		},

From ca424df80e3072ee5f3971f0c3e97e7d3f34b57c Mon Sep 17 00:00:00 2001
From: yupix <yupi0982@outlook.jp>
Date: Thu, 30 Nov 2023 15:56:25 +0900
Subject: [PATCH 077/435] =?UTF-8?q?fix:=20invite=E7=B3=BB=E3=81=AE?=
 =?UTF-8?q?=E6=88=BB=E3=82=8A=E5=80=A4=E3=81=8C=E9=96=93=E9=81=95=E3=81=A3?=
 =?UTF-8?q?=E3=81=A6=E3=81=84=E3=82=8B=20close=20#12517=20(#12518)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/server/api/endpoints/admin/invite/create.ts       | 8 +-------
 .../backend/src/server/api/endpoints/admin/invite/list.ts | 1 +
 .../backend/src/server/api/endpoints/invite/create.ts     | 8 +-------
 packages/backend/src/server/api/endpoints/invite/list.ts  | 2 +-
 4 files changed, 4 insertions(+), 15 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/admin/invite/create.ts b/packages/backend/src/server/api/endpoints/admin/invite/create.ts
index 4a22fd4824..c6ee45735e 100644
--- a/packages/backend/src/server/api/endpoints/admin/invite/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/invite/create.ts
@@ -33,13 +33,7 @@ export const meta = {
 		items: {
 			type: 'object',
 			optional: false, nullable: false,
-			properties: {
-				code: {
-					type: 'string',
-					optional: false, nullable: false,
-					example: 'GR6S02ERUA5VR',
-				},
-			},
+			ref: 'InviteCode',
 		},
 	},
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/invite/list.ts b/packages/backend/src/server/api/endpoints/admin/invite/list.ts
index f25d3fcb33..ff57940d48 100644
--- a/packages/backend/src/server/api/endpoints/admin/invite/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/invite/list.ts
@@ -21,6 +21,7 @@ export const meta = {
 		items: {
 			type: 'object',
 			optional: false, nullable: false,
+			ref: 'InviteCode',
 		},
 	},
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/invite/create.ts b/packages/backend/src/server/api/endpoints/invite/create.ts
index 94836283fa..d82fa50e4f 100644
--- a/packages/backend/src/server/api/endpoints/invite/create.ts
+++ b/packages/backend/src/server/api/endpoints/invite/create.ts
@@ -31,13 +31,7 @@ export const meta = {
 	res: {
 		type: 'object',
 		optional: false, nullable: false,
-		properties: {
-			code: {
-				type: 'string',
-				optional: false, nullable: false,
-				example: 'GR6S02ERUA5VR',
-			},
-		},
+		ref: 'InviteCode',
 	},
 } as const;
 
diff --git a/packages/backend/src/server/api/endpoints/invite/list.ts b/packages/backend/src/server/api/endpoints/invite/list.ts
index 06139b6806..2107516ce4 100644
--- a/packages/backend/src/server/api/endpoints/invite/list.ts
+++ b/packages/backend/src/server/api/endpoints/invite/list.ts
@@ -9,7 +9,6 @@ import type { RegistrationTicketsRepository } from '@/models/_.js';
 import { InviteCodeEntityService } from '@/core/entities/InviteCodeEntityService.js';
 import { QueryService } from '@/core/QueryService.js';
 import { DI } from '@/di-symbols.js';
-import { ApiError } from '../../error.js';
 
 export const meta = {
 	tags: ['meta'],
@@ -23,6 +22,7 @@ export const meta = {
 		items: {
 			type: 'object',
 			optional: false, nullable: false,
+			ref: 'InviteCode',
 		},
 	},
 } as const;

From 749befdf63cfe12ac328546d21d26c7cb9445a20 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Fri, 1 Dec 2023 00:29:26 +0100
Subject: [PATCH 078/435] chore: up dev version

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

diff --git a/package.json b/package.json
index 1207e18028..df95e29ad5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "sharkey",
-	"version": "2023.11.2.beta1",
+	"version": "2023.12.0.beta1",
 	"codename": "shonk",
 	"repository": {
 		"type": "git",

From 5cd4c36cad1fa38080f2b596b936d6b44db45cc7 Mon Sep 17 00:00:00 2001
From: nullnyat <nullnyat@nca10.moe>
Date: Fri, 1 Dec 2023 11:19:33 +0900
Subject: [PATCH 079/435] rename docker-compose.yml.example to
 docker-compose_example.yml (#12530)

* rename docker-compose.yml.example to docker-compose_example.yml

* fix: dockle.yml
---
 .github/workflows/dockle.yml                             | 2 +-
 docker-compose.yml.example => docker-compose_example.yml | 0
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename docker-compose.yml.example => docker-compose_example.yml (100%)

diff --git a/.github/workflows/dockle.yml b/.github/workflows/dockle.yml
index edb18b04da..eee7a78fed 100644
--- a/.github/workflows/dockle.yml
+++ b/.github/workflows/dockle.yml
@@ -20,7 +20,7 @@ jobs:
           sudo dpkg -i dockle.deb
       - run: |
           cp .config/docker_example.env .config/docker.env
-          cp ./docker-compose.yml.example ./docker-compose.yml
+          cp ./docker-compose_example.yml ./docker-compose.yml
       - run: |
           docker compose up -d web
           docker tag "$(docker compose images web | awk 'OFS=":" {print $4}' | tail -n +2)" misskey-web:latest
diff --git a/docker-compose.yml.example b/docker-compose_example.yml
similarity index 100%
rename from docker-compose.yml.example
rename to docker-compose_example.yml

From ad5a6c1e41806da46232e2b604b55d79d905959a Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Fri, 1 Dec 2023 14:51:48 +0100
Subject: [PATCH 080/435] fix: reactions escaping view in note view

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

diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 4487582562..30198d2a6a 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -744,7 +744,8 @@ function animatedMFM() {
 		z-index: 1;
 		margin-top: 0.4em;
 		width: max-content;
-		min-width: max-content;
+		min-width: min-content;
+		max-width: fit-content;
 }
 
 .replyTo {

From c927d6824c3003078410e5cf38d394544accb711 Mon Sep 17 00:00:00 2001
From: Qwreey <qwreey75@gmail.com>
Date: Sat, 2 Dec 2023 09:28:00 +0900
Subject: [PATCH 081/435] Fix: missing receiver warn is not disappear (#12538)

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

diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 07c7213202..9b04f5165c 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -366,8 +366,8 @@ function checkMissingMention() {
 				return;
 			}
 		}
-		hasNotSpecifiedMentions = false;
 	}
+	hasNotSpecifiedMentions = false;
 }
 
 function addMissingMention() {

From 1a4bff769825f8b7319c0f86660d78d049023229 Mon Sep 17 00:00:00 2001
From: Insert5StarName <anime@shourai.de>
Date: Sat, 2 Dec 2023 01:39:29 +0100
Subject: [PATCH 082/435] upd: rework threading/post ui 1/2

Co-authored-by: Gaspard Wierzbinski <contact@cpluspatch.com>
Co-authored-by: Marie <marie@kaifa.ch>
---
 .../src/components/MkInstanceTicker.vue       |  49 +++---
 packages/frontend/src/components/MkNote.vue   |  49 +++---
 .../src/components/MkNoteDetailed.vue         |  69 ++++++--
 .../frontend/src/components/MkNoteHeader.vue  | 153 +++++++++++++++++-
 .../frontend/src/components/MkNoteSimple.vue  |   2 +-
 .../frontend/src/components/MkNoteSub.vue     |  80 +++++++--
 .../frontend/src/pages/settings/general.vue   |   2 +
 packages/frontend/src/store.ts                |   4 +
 8 files changed, 327 insertions(+), 81 deletions(-)

diff --git a/packages/frontend/src/components/MkInstanceTicker.vue b/packages/frontend/src/components/MkInstanceTicker.vue
index f0650e48f1..4e2856388e 100644
--- a/packages/frontend/src/components/MkInstanceTicker.vue
+++ b/packages/frontend/src/components/MkInstanceTicker.vue
@@ -35,51 +35,48 @@ const faviconUrl = $computed(() => props.instance ? getProxiedImageUrlNullable(p
 const themeColor = instance.themeColor ?? '#777777';
 
 const bg = {
-	background: `linear-gradient(90deg, ${themeColor}, ${themeColor}00)`,
+	//background: `linear-gradient(90deg, ${themeColor}, ${themeColor}00)`,
+	background: `${themeColor}`,
 };
 </script>
 
 <style lang="scss" module>
-$height: 2ex;
-
 .root {
 	display: flex;
 	align-items: center;
-	height: $height;
-	border-radius: var(--radius-xs) 0 0 var(--radius-xs);
+	height: 1.5ex;
+	border-radius: var(--radius-xl);
+	margin-top: 5px;
+	padding: 4px;
 	overflow: clip;
 	color: #fff;
-	text-shadow: /* .866 ≈ sin(60deg) */
-		1px 0 1px #000,
-		.866px .5px 1px #000,
-		.5px .866px 1px #000,
-		0 1px 1px #000,
-		-.5px .866px 1px #000,
-		-.866px .5px 1px #000,
-		-1px 0 1px #000,
-		-.866px -.5px 1px #000,
-		-.5px -.866px 1px #000,
-		0 -1px 1px #000,
-		.5px -.866px 1px #000,
-		.866px -.5px 1px #000;
-	mask-image: linear-gradient(90deg,
-		rgb(0,0,0),
-		rgb(0,0,0) calc(100% - 16px),
-		rgba(0,0,0,0) 100%
-	);
+	text-shadow: -1px -1px 0 var(--bg),1px -1px 0 var(--bg),-1px 1px 0 var(--bg),1px 1px 0 var(--bg)
 }
 
 .icon {
-	height: $height;
+	height: 2ex;
 	flex-shrink: 0;
 }
 
 .name {
 	margin-left: 4px;
 	line-height: 1;
-	font-size: 0.9em;
+	font-size: 0.8em;
 	font-weight: bold;
 	white-space: nowrap;
-	overflow: visible;
+	overflow: hidden;
+	overflow-wrap: anywhere;
+	max-width: 300px;
+	text-overflow: ellipsis;
+
+	&::-webkit-scrollbar {
+		display: none;
+	}
+}
+
+@container (max-width: 400px) {
+	.name {
+		max-width: 50px;
+	}
 }
 </style>
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 4da8f16df1..869164273e 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -47,11 +47,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :nyaize="'respect'" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/>
 	</div>
 	<article v-else :class="$style.article" @contextmenu.stop="onContextmenu">
-		<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
-		<MkAvatar :class="$style.avatar" :user="appearNote.user" :link="!mock" :preview="!mock"/>
-		<div :class="[$style.main, { [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined">
-			<MkNoteHeader :note="appearNote" :mini="true" v-on:click.stop/>
-			<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
+		<div style="display: flex; padding-bottom: 10px;">
+			<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
+			<MkAvatar :class="[$style.avatar, { [$style.avatarReplyTo]: appearNote.reply }]" :user="appearNote.user" :link="!mock" :preview="!mock"/>
+			<div :class="$style.main">
+				<MkNoteHeader :note="appearNote" :mini="true"/>
+			</div>
+		</div>
+		<div :class="[{ [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined">
 			<div style="container-type: inline-size;">
 				<p v-if="appearNote.cw != null" :class="$style.cw">
 					<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
@@ -60,7 +63,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]" >
 					<div :class="$style.text">
 						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
-						<MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA>
 						<Mfm
 							v-if="appearNote.text"
 							:parsedNodes="parsed"
@@ -176,7 +178,6 @@ import MkCwButton from '@/components/MkCwButton.vue';
 import MkPoll from '@/components/MkPoll.vue';
 import MkUsersTooltip from '@/components/MkUsersTooltip.vue';
 import MkUrlPreview from '@/components/MkUrlPreview.vue';
-import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
 import MkButton from '@/components/MkButton.vue';
 import { pleaseLogin } from '@/scripts/please-login.js';
 import { focusPrev, focusNext } from '@/scripts/focus.js';
@@ -830,7 +831,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	position: relative;
 	display: flex;
 	align-items: center;
-	padding: 16px 32px 8px 32px;
+	padding: 24px 38px 16px;
 	line-height: 28px;
 	white-space: pre;
 	color: var(--renote);
@@ -882,7 +883,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	align-items: center;
 	line-height: 28px;
 	white-space: pre;
-	padding: 0 32px 18px;
+	padding: 8px 38px 24px;
 }
 
 .collapsedRenoteTargetAvatar {
@@ -909,7 +910,6 @@ function emitUpdReaction(emoji: string, delta: number) {
 
 .article {
 	position: relative;
-	display: flex;
 	padding: 28px 32px;
 }
 
@@ -926,12 +926,19 @@ function emitUpdReaction(emoji: string, delta: number) {
 .avatar {
 	flex-shrink: 0;
 	display: block !important;
+	position: sticky !important;
 	margin: 0 14px 0 0;
 	width: 58px;
 	height: 58px;
 	position: sticky !important;
 	top: calc(22px + var(--stickyTop, 0px));
 	left: 0;
+	transition: top 0.5s;
+
+	&.avatarReplyTo {
+		position: relative !important;
+		top: 0 !important;
+	}
 }
 
 .main {
@@ -994,7 +1001,6 @@ function emitUpdReaction(emoji: string, delta: number) {
 
 .text {
 	overflow-wrap: break-word;
-	overflow: hidden;
 }
 
 .replyIcon {
@@ -1027,7 +1033,8 @@ function emitUpdReaction(emoji: string, delta: number) {
 
 .quoteNote {
 	padding: 16px;
-	border: dashed 1px var(--renote);
+	// Made border solid, stylistic choice
+	border: solid 1px var(--renote);
 	border-radius: var(--radius-sm);
 	overflow: clip;
 }
@@ -1067,7 +1074,11 @@ function emitUpdReaction(emoji: string, delta: number) {
 	}
 
 	.renote {
-		padding: 12px 26px 0 26px;
+		padding: 24px 28px 16px;
+	}
+
+	.collapsedRenoteTarget {
+		padding: 8px 28px 24px;
 	}
 
 	.article {
@@ -1085,12 +1096,8 @@ function emitUpdReaction(emoji: string, delta: number) {
 		font-size: 0.9em;
 	}
 
-	.renote {
-		padding: 10px 22px 0 22px;
-	}
-
 	.article {
-		padding: 20px 22px;
+		padding: 23px 25px;
 	}
 
 	.footer {
@@ -1100,7 +1107,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 
 @container (max-width: 480px) {
 	.renote {
-		padding: 8px 16px 0 16px;
+		padding: 20px 24px 8px;
 	}
 
 	.tip {
@@ -1108,12 +1115,12 @@ function emitUpdReaction(emoji: string, delta: number) {
 	}
 
 	.collapsedRenoteTarget {
-		padding: 0 16px 9px;
+		padding: 8px 24px 20px;
 		margin-top: 4px;
 	}
 
 	.article {
-		padding: 14px 16px;
+		padding: 22px 24px;
 	}
 }
 
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 30198d2a6a..95cbe38b1b 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -11,13 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 	v-hotkey="keymap"
 	:class="$style.root"
 >
-	<div v-if="appearNote.reply && appearNote.reply.replyId">
-		<div v-if="!conversationLoaded" style="padding: 16px">
-			<MkButton style="margin: 0 auto;" primary rounded @click="loadConversation">{{ i18n.ts.loadConversation }}</MkButton>
-		</div>
-		<MkNoteSub v-for="note in conversation" :key="note.id" :class="$style.replyToMore" :note="note" :expandAllCws="props.expandAllCws"/>
+	<div v-if="appearNote.reply && appearNote.reply.replyId && !conversationLoaded" style="padding: 16px">
+		<MkButton style="margin: 0 auto;" primary rounded @click="loadConversation">{{ i18n.ts.loadConversation }}</MkButton>
 	</div>
-	<MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo" :expandAllCws="props.expandAllCws"/>
 	<div v-if="isRenote" :class="$style.renote">
 		<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
 		<i class="ph-rocket-launch ph-bold ph-lg" style="margin-right: 4px;"></i>
@@ -43,15 +39,29 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
 		</div>
 	</div>
+	<template v-if="appearNote.reply && appearNote.reply.replyId">
+		<MkNoteSub v-for="note in conversation" :key="note.id" :class="$style.replyToMore" :note="note" :expandAllCws="props.expandAllCws"/>
+	</template>
+	<MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo" :expandAllCws="props.expandAllCws"/>
 	<article :class="$style.note" @contextmenu.stop="onContextmenu">
 		<header :class="$style.noteHeader">
 			<MkAvatar :class="$style.noteHeaderAvatar" :user="appearNote.user" indicator link preview/>
-			<div :class="$style.noteHeaderBody">
-				<div>
-					<MkA v-user-preview="appearNote.user.id" :class="$style.noteHeaderName" :to="userPage(appearNote.user)">
-						<MkUserName :nowrap="false" :user="appearNote.user"/>
-					</MkA>
-					<span v-if="appearNote.user.isBot" :class="$style.isBot">bot</span>
+			<div style="display: flex; align-items: center; white-space: nowrap; overflow: hidden;">
+				<div :class="$style.noteHeaderBody">
+					<div>
+						<MkA v-user-preview="appearNote.user.id" :class="$style.noteHeaderName" :to="userPage(appearNote.user)">
+							<MkUserName :nowrap="false" :user="appearNote.user"/>
+						</MkA>
+						<span v-if="appearNote.user.isBot" :class="$style.isBot">bot</span>
+						<span v-if="appearNote.user.badgeRoles" :class="$style.badgeRoles">
+							<img v-for="role in appearNote.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
+						</span>
+					</div>
+					<div :class="$style.noteHeaderUsername"><MkAcct :user="appearNote.user"/></div>
+				</div>
+			</div>
+			<div style="display: flex; align-items: flex-end; margin-left: auto;">
+				<div :class="$style.noteHeaderBody">
 					<div :class="$style.noteHeaderInfo">
 						<span v-if="appearNote.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[appearNote.visibility]">
 							<i v-if="appearNote.visibility === 'home'" class="ph-house ph-bold ph-lg"></i>
@@ -61,9 +71,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<span v-if="appearNote.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
 						<span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
 					</div>
+					<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
 				</div>
-				<div :class="$style.noteHeaderUsername"><MkAcct :user="appearNote.user"/></div>
-				<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
 			</div>
 		</header>
 		<div :class="$style.noteContent">
@@ -73,7 +82,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</p>
 			<div v-show="appearNote.cw == null || showContent">
 				<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
-				<MkA v-if="appearNote.replyId" :class="$style.noteReplyTarget" :to="`/notes/${appearNote.replyId}`"><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA>
 				<Mfm
 					v-if="appearNote.text"
 					:parsedNodes="parsed"
@@ -464,7 +472,7 @@ function renote() {
 		if (appearNote.channel?.isSensitive) {
 			visibility = smallerVisibility(visibility, 'home');
 		}
-		
+
 		os.api('notes/create', {
 			localOnly,
 			visibility,
@@ -851,12 +859,19 @@ function animatedMFM() {
 
 .noteHeaderInfo {
 	float: right;
+	text-align: right;
 }
 
 .noteHeaderUsername {
 	margin-bottom: 2px;
 	line-height: 1.3;
 	word-wrap: anywhere;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+
+	&::-webkit-scrollbar {
+		display: none;
+	}
 }
 
 .playMFMButton {
@@ -1037,9 +1052,31 @@ function animatedMFM() {
 	}
 }
 
+.avatar {
+	flex-shrink: 0 !important;
+	display: block !important;
+	margin: 0 10px 0 0 !important;
+	width: 40px !important;
+	height: 40px !important;
+	border-radius: var(--radius-sm) !important;
+}
+
 .muted {
 	padding: 8px;
 	text-align: center;
 	opacity: 0.7;
 }
+
+.badgeRoles {
+	margin: 0 .5em 0 0;
+}
+
+.badgeRole {
+	height: 1.3em;
+	vertical-align: -20%;
+
+	& + .badgeRole {
+		margin-left: 0.2em;
+	}
+}
 </style>
diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue
index 6121db3f8f..9984c3774d 100644
--- a/packages/frontend/src/components/MkNoteHeader.vue
+++ b/packages/frontend/src/components/MkNoteHeader.vue
@@ -4,19 +4,56 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<header :class="$style.root">
+<header v-if="!classic" :class="$style.root">
+	<div :class="$style.section">
+		<div style="display: flex;">
+			<div v-if="mock" :class="$style.name">
+				<MkUserName :user="note.user"/>
+			</div>
+			<MkA v-else v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)">
+				<MkUserName :user="note.user"/>
+			</MkA>
+			<div v-if="note.user.isBot" :class="$style.isBot">bot</div>
+			<div v-if="note.user.badgeRoles" :class="$style.badgeRoles">
+				<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
+			</div>
+		</div>
+		<div :class="$style.username"><MkAcct :user="note.user"/></div>
+	</div>
+	<div :class="$style.section">
+		<div :class="$style.info">
+			<div v-if="mock">
+				<MkTime :time="note.createdAt" colored/>
+			</div>
+			<MkA v-else :class="$style.time" :to="notePage(note)">
+				<MkTime :time="note.createdAt" colored/>
+			</MkA>
+			<span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]">
+				<i v-if="note.visibility === 'home'" class="ph-house ph-bold ph-lg"></i>
+				<i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
+				<i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
+			</span>
+			<span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em; cursor: pointer;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
+			<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
+			<span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span>
+		</div>
+		<div :class="$style.info"><MkInstanceTicker v-if="showTicker" style="cursor: pointer;" :instance="note.user.instance" @click.stop="showOnRemote()"/></div>
+	</div>
+</header>
+<header v-else :class="$style.classicRoot">
 	<div v-if="mock" :class="$style.name">
 		<MkUserName :user="note.user"/>
 	</div>
-	<MkA v-else v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)">
+	<MkA v-else v-user-preview="note.user.id" :class="$style.classicName" :to="userPage(note.user)">
 		<MkUserName :user="note.user"/>
 	</MkA>
 	<div v-if="note.user.isBot" :class="$style.isBot">bot</div>
-	<div :class="$style.username"><MkAcct :user="note.user"/></div>
+	<div :class="$style.classicUsername"><MkAcct :user="note.user"/></div>
 	<div v-if="note.user.badgeRoles" :class="$style.badgeRoles">
 		<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
 	</div>
-	<div :class="$style.info">
+	<MkInstanceTicker v-if="showTicker && !isMobile && defaultStore.state.showTickerOnReplies" style="cursor: pointer; max-height: 5px; top: 3px; position: relative; margin-top: 0px !important;" :instance="note.user.instance" @click.stop="showOnRemote()"/>
+	<div :class="$style.classicInfo">
 		<div v-if="mock">
 			<MkTime :time="note.createdAt" colored/>
 		</div>
@@ -36,19 +73,29 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { inject, shallowRef } from 'vue';
+import { inject, shallowRef, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { i18n } from '@/i18n.js';
 import { notePage } from '@/filters/note.js';
 import { userPage } from '@/filters/user.js';
 import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js';
+import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
 import { popupMenu } from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { useRouter } from '@/router.js';
+import { deviceKind } from '@/scripts/device-kind.js';
 
 const props = defineProps<{
 	note: Misskey.entities.Note;
+	classic?: boolean;
 }>();
 
 const menuVersionsButton = shallowRef<HTMLElement>();
+const router = useRouter();
+const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && props.note.user.instance);
+
+const MOBILE_THRESHOLD = 500;
+const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
 
 async function menuVersions(viaKeyboard = false): Promise<void> {
 	const { menu, cleanup } = await getNoteVersionsMenu({ note: props.note, menuVersionsButton });
@@ -57,18 +104,67 @@ async function menuVersions(viaKeyboard = false): Promise<void> {
 	}).then(focus).finally(cleanup);
 }
 
+function showOnRemote() {
+	if (props.note.url ?? props.note.uri === undefined) router.push(notePage(props.note));
+	else window.open(props.note.url ?? props.note.uri);
+}
+
 const mock = inject<boolean>('mock', false);
 </script>
 
 <style lang="scss" module>
 .root {
+	display: flex;
+	cursor: auto; /* not clickToOpen-able */
+}
+
+.classicRoot {
 	display: flex;
 	align-items: baseline;
 	white-space: nowrap;
 	cursor: auto; /* not clickToOpen-able */
 }
 
+.section {
+		align-items: flex-start;
+		white-space: nowrap;
+		flex-direction: column;
+		overflow: hidden;
+
+		&:last-child {
+			display: flex;
+			align-items: flex-end;
+			margin-left: auto;
+			padding-left: 10px;
+			overflow: clip;
+		}
+}
+
 .name {
+	flex-shrink: 1;
+	display: block;
+	// note, these margin top values were done by hand may need futher checking if it actualy aligns pixel perfect
+	margin: 3px .5em 0 0;
+	padding: 0;
+	overflow: scroll;
+	overflow-wrap: anywhere;
+	font-size: 1em;
+	font-weight: bold;
+	text-decoration: none;
+	text-overflow: ellipsis;
+	max-width: 300px;
+
+		&::-webkit-scrollbar {
+			display: none;
+		}
+
+		&:hover {
+			color: var(--nameHover);
+			text-decoration: none;
+		}
+}
+
+.classicName {
 	flex-shrink: 1;
 	display: block;
 	margin: 0 .5em 0 0;
@@ -95,6 +191,20 @@ const mock = inject<boolean>('mock', false);
 }
 
 .username {
+	flex-shrink: 9999999;
+	// note these top margins were made to align with the instance ticker
+	margin: 4px .5em 0 0;
+	overflow: hidden;
+	text-overflow: ellipsis;
+	font-size: .95em;
+	max-width: 300px;
+
+	&::-webkit-scrollbar {
+		display: none;
+	}
+}
+
+.classicUsername {
 	flex-shrink: 9999999;
 	margin: 0 .5em 0 0;
 	overflow: hidden;
@@ -102,11 +212,34 @@ const mock = inject<boolean>('mock', false);
 }
 
 .info {
+	&:first-child {
+		margin-top: 4px;
+		flex-shrink: 0;
+		margin-left: auto;
+		font-size: 0.9em;
+	}
+
+	&:not(:first-child) {
+		flex-shrink: 0;
+		margin-left: auto;
+		font-size: 0.9em;
+	}
+}
+
+.classicInfo {
 	flex-shrink: 0;
 	margin-left: auto;
 	font-size: 0.9em;
 }
 
+.time {
+	text-decoration: none;
+
+	&:hover {
+		text-decoration: none;
+	}
+}
+
 .badgeRoles {
 	margin: 0 .5em 0 0;
 }
@@ -119,4 +252,14 @@ const mock = inject<boolean>('mock', false);
 		margin-left: 0.2em;
 	}
 }
+
+.danger {
+		color: var(--accent);
+	}
+
+	@container (max-width: 500px) {
+		.name, .username {
+			max-width: 200px;
+		}
+	}
 </style>
diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue
index b1d4ed3f7e..8ebd24b322 100644
--- a/packages/frontend/src/components/MkNoteSimple.vue
+++ b/packages/frontend/src/components/MkNoteSimple.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <div :class="$style.root">
 	<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
 	<div :class="$style.main">
-		<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
+		<MkNoteHeader :class="$style.header" :classic="true" :note="note" :mini="true"/>
 		<div>
 			<p v-if="note.cw != null" :class="$style.cw">
 				<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/>
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 3e33c7aa69..822b4de83f 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -5,11 +5,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div v-if="!muted" ref="el" :class="[$style.root, { [$style.children]: depth > 1 }]">
+	<div v-if="!hideLine" :class="$style.line"></div>
 	<div :class="$style.main">
 		<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
 		<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
 		<div :class="$style.body">
-			<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
+			<MkNoteHeader :class="$style.header" :note="note" :classic="true" :mini="true"/>
 			<div :class="$style.content">
 				<p v-if="note.cw != null" :class="$style.cw">
 					<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'"/>
@@ -106,6 +107,7 @@ import { getNoteMenu } from '@/scripts/get-note-menu.js';
 import { useNoteCapture } from '@/scripts/use-note-capture.js';
 
 const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
+const hideLine = computed(() => { return props.detail ? true : false; });
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
@@ -361,7 +363,7 @@ if (props.detail) {
 
 <style lang="scss" module>
 .root {
-	padding: 16px 32px;
+	padding: 28px 32px;
 	font-size: 0.9em;
 	position: relative;
 
@@ -371,12 +373,20 @@ if (props.detail) {
 	}
 }
 
+.line {
+	position: absolute;
+	height: 100%;
+	left: 60px;
+	// using solid instead of dotted, stylelistic choice
+	border-left: 2.5px solid rgb(174, 174, 174);
+}
+
 .footer {
-		position: relative;
-		z-index: 1;
-		margin-top: 0.4em;
-		width: max-content;
-		min-width: max-content;
+	position: relative;
+	z-index: 1;
+	margin-top: 0.4em;
+	width: max-content;
+	min-width: max-content;
 }
 
 .main {
@@ -396,9 +406,9 @@ if (props.detail) {
 .avatar {
 	flex-shrink: 0;
 	display: block;
-	margin: 0 8px 0 0;
-	width: 38px;
-	height: 38px;
+	margin: 0 14px 0 0;
+	width: 58px;
+	height: 58px;
 	border-radius: var(--radius-sm);
 }
 
@@ -411,6 +421,11 @@ if (props.detail) {
 	overflow: hidden;
 }
 
+.text {
+	margin: 0;
+	padding: 0;
+}
+
 .header {
 	margin-bottom: 2px;
 }
@@ -430,6 +445,36 @@ if (props.detail) {
 	}
 }
 
+.reply, .more {
+	border-left: solid 0.5px var(--divider);
+	margin-top: 10px;
+}
+
+.more {
+	padding: 10px 0 0 16px;
+}
+
+@container (max-width: 580px) {
+	.root {
+		padding: 28px 26px 0;
+	}
+
+	.line {
+		left: 50.5px;
+	}
+
+	.avatar {
+		width: 50px;
+		height: 50px;
+	}
+}
+
+@container (max-width: 500px) {
+	.root {
+		padding: 23px 25px;
+	}
+}
+
 @container (max-width: 400px) {
 	.noteFooterButton {
 		&:not(:last-child) {
@@ -469,9 +514,9 @@ if (props.detail) {
 	padding: 10px 0 0 16px;
 }
 
-@container (max-width: 450px) {
+@container (max-width: 480px) {
 	.root {
-		padding: 14px 16px;
+		padding: 22px 24px;
 
 		&.children {
 			padding: 10px 0 0 8px;
@@ -479,6 +524,17 @@ if (props.detail) {
 	}
 }
 
+@container (max-width: 450px) {
+	.line {
+		left: 46px;
+	}
+
+	.avatar {
+		width: 46px;
+		height: 46px;
+	}
+}
+
 .muted {
 	text-align: center;
 	padding: 8px !important;
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index bbfc8da483..c55728dc77 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -52,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
 				<MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
 				<MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch>
+				<MkSwitch v-model="showTickerOnReplies">Show instance ticker on replies</MkSwitch>
 				<MkRadios v-model="reactionsDisplaySize">
 					<template #label>{{ i18n.ts.reactionsDisplaySize }}</template>
 					<option value="small">{{ i18n.ts.small }}</option>
@@ -271,6 +272,7 @@ const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificati
 const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn'));
 const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline'));
 const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications'));
+const showTickerOnReplies = computed(defaultStore.makeGetterSetter('showTickerOnReplies'));
 
 watch(lang, () => {
 	miLocalStorage.setItem('lang', lang.value as string);
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 7844ef0fb5..9443e8b210 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -250,6 +250,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: false,
 	},
+	showTickerOnReplies: {
+		where: 'device',
+		default: false,
+	},
 	enableInfiniteScroll: {
 		where: 'device',
 		default: true,

From a5f0b5ec74940b0c53242dfc64c322139c91e362 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 2 Dec 2023 11:37:31 +0900
Subject: [PATCH 083/435] fix #12528 (#12536)

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

diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index b6383487c9..bc97f971ef 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -44,7 +44,7 @@ export const getBuiltinThemes = () => Promise.all(
 		'd-cherry',
 		'd-ice',
 		'd-u0',
-	].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
+	].map(name => import(`${__dirname}/../themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
 );
 
 export const getBuiltinThemesRef = () => {

From 1c7533947174a599d5c49fefbc33615b133d39cb Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 2 Dec 2023 03:55:44 +0100
Subject: [PATCH 084/435] chore: add documentation section to `README`

---
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/README.md b/README.md
index 8b9da7e712..5dde58ccc1 100644
--- a/README.md
+++ b/README.md
@@ -49,3 +49,7 @@ With Sharkey, you can enable sign-ups, subject to manual moderator approval and
 </div>
 
 <div style="clear: both;"></div>
+
+## Documentation
+
+Sharkey Documentation can be found at [Sharkey Documentation](https://docs.joinsharkey.org/docs/install/fresh/)

From 43c9ab20725d50d337474b3ef3f3854adda9c971 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sat, 2 Dec 2023 12:04:11 +0900
Subject: [PATCH 085/435] =?UTF-8?q?fix(frontend):=20=E9=95=B7=E3=81=84?=
 =?UTF-8?q?=E5=90=8D=E5=89=8D=E3=81=AE=E3=83=81=E3=83=A3=E3=83=B3=E3=83=8D?=
 =?UTF-8?q?=E3=83=AB=E3=81=AB=E3=81=8A=E3=81=91=E3=82=8B=E6=8A=95=E7=A8=BF?=
 =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=BC=E3=83=A0=E3=81=AE=E8=A1=A8=E7=A4=BA?=
 =?UTF-8?q?=E3=81=8C=E5=B4=A9=E3=82=8C=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#12524)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): 長い名前のチャンネルにおける投稿フォームの表示が崩れる問題を修正

* Update CHANGELOG.md
---
 CHANGELOG.md                                    | 1 +
 packages/frontend/src/components/MkPostForm.vue | 5 +++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d32b46f63d..a512047819 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,7 @@
 - Fix: 一度に大量の通知が入った際に通知音が音割れする問題を修正
 - Fix: 共有機能をサポートしていないブラウザの場合は共有ボタンを非表示にする #11305
 - Fix: 通知のグルーピング設定を変更してもリロードされるまで表示が変わらない問題を修正 #12470
+- Fix: 長い名前のチャンネルにおける投稿フォームの表示が崩れる問題を修正
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 9b04f5165c..3244f743ac 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -1059,8 +1059,9 @@ defineExpose({
 
 .visibility {
 	overflow: clip;
-    text-overflow: ellipsis;
-    white-space: nowrap;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	max-width: 210px;
 
 	&:enabled {
 		> .headerRightButtonText {

From da0ecb650e55c46c4878d9f0200109b4bd7cc4c8 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sat, 2 Dec 2023 12:04:30 +0900
Subject: [PATCH 086/435] =?UTF-8?q?chore:=20=E3=83=95=E3=82=A9=E3=83=AD?=
 =?UTF-8?q?=E3=83=BC=E3=81=97=E3=81=9F=E3=81=A8=E3=81=8D=E3=81=ABHTL?=
 =?UTF-8?q?=E3=82=92=E3=83=91=E3=83=BC=E3=82=B8=E3=81=97=E3=81=AA=E3=81=8F?=
 =?UTF-8?q?=E3=81=99=E3=82=8B=20(#12522)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/core/UserFollowingService.ts | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts
index 3062999c08..d600ffb9d9 100644
--- a/packages/backend/src/core/UserFollowingService.ts
+++ b/packages/backend/src/core/UserFollowingService.ts
@@ -304,8 +304,6 @@ export class UserFollowingService implements OnModuleInit {
 					});
 				}
 			});
-
-			this.fanoutTimelineService.purge(`homeTimeline:${follower.id}`);
 		}
 
 		// Publish followed event
@@ -373,8 +371,6 @@ export class UserFollowingService implements OnModuleInit {
 					});
 				}
 			});
-
-			this.fanoutTimelineService.purge(`homeTimeline:${follower.id}`);
 		}
 
 		if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {

From b37e8ffa69b20fa05ae6a999f872fb5cd4772291 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, 2 Dec 2023 12:05:03 +0900
Subject: [PATCH 087/435] =?UTF-8?q?(fix)=20=E7=BF=BB=E8=A8=B3=E3=81=AE?=
 =?UTF-8?q?=E3=83=80=E3=83=96=E3=82=8A=E3=82=92=E8=A7=A3=E6=B6=88=20(#1251?=
 =?UTF-8?q?9)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 locales/index.d.ts                               | 1 -
 locales/ja-JP.yml                                | 1 -
 packages/frontend/src/components/MkLaunchPad.vue | 2 ++
 packages/frontend/src/navbar.ts                  | 2 +-
 4 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index d462816494..402c2413f8 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -440,7 +440,6 @@ export interface Locale {
     "notFound": string;
     "notFoundDescription": string;
     "uploadFolder": string;
-    "cacheClear": string;
     "markAsReadAllNotifications": string;
     "markAsReadAllUnreadNotes": string;
     "markAsReadAllTalkMessages": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index aa3cdf0750..2b2f05aa88 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -437,7 +437,6 @@ share: "共有"
 notFound: "見つかりません"
 notFoundDescription: "指定されたURLに該当するページはありませんでした。"
 uploadFolder: "既定アップロード先"
-cacheClear: "キャッシュを削除"
 markAsReadAllNotifications: "すべての通知を既読にする"
 markAsReadAllUnreadNotes: "すべての投稿を既読にする"
 markAsReadAllTalkMessages: "すべてのチャットを既読にする"
diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue
index 87f15a110f..b16c05f575 100644
--- a/packages/frontend/src/components/MkLaunchPad.vue
+++ b/packages/frontend/src/components/MkLaunchPad.vue
@@ -101,6 +101,8 @@ function close() {
 			vertical-align: bottom;
 			height: 100px;
 			border-radius: 10px;
+			padding: 10px;
+			box-sizing: border-box;
 
 			&:hover {
 				color: var(--accent);
diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts
index 78a0945ddb..95fd6bf29c 100644
--- a/packages/frontend/src/navbar.ts
+++ b/packages/frontend/src/navbar.ts
@@ -173,7 +173,7 @@ export const navbarItemDef = reactive({
 		to: `/@${$i?.username}`,
 	},
 	cacheClear: {
-		title: i18n.ts.cacheClear,
+		title: i18n.ts.clearCache,
 		icon: 'ti ti-trash',
 		action: (ev) => {
 			clearCache();

From b6b838416d40c96a58dfcf271287ccea0400f9a1 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sat, 2 Dec 2023 12:05:53 +0900
Subject: [PATCH 088/435] chore: remove unimplemented excludeNsfw (#12520)

---
 .../backend/src/server/api/endpoints/notes/local-timeline.ts    | 1 -
 packages/backend/src/server/api/endpoints/users/notes.ts        | 1 -
 .../lib/rollup-plugin-unwind-css-module-class-name.test.ts      | 2 --
 packages/frontend/src/pages/user/index.files.vue                | 1 -
 4 files changed, 5 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index 7d8dec7b8f..8867070055 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -48,7 +48,6 @@ export const paramDef = {
 		withFiles: { type: 'boolean', default: false },
 		withRenotes: { type: 'boolean', default: true },
 		withReplies: { type: 'boolean', default: false },
-		excludeNsfw: { type: 'boolean', default: false },
 		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
 		sinceId: { type: 'string', format: 'misskey:id' },
 		untilId: { type: 'string', format: 'misskey:id' },
diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index a775b58f03..76033ddb06 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -53,7 +53,6 @@ export const paramDef = {
 		sinceDate: { type: 'integer' },
 		untilDate: { type: 'integer' },
 		withFiles: { type: 'boolean', default: false },
-		excludeNsfw: { type: 'boolean', default: false },
 	},
 	required: ['userId'],
 } as const;
diff --git a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts
index a7b8cbb037..759f270393 100644
--- a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts
+++ b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts
@@ -89,7 +89,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
       api("users/notes", {
         userId: props.user.id,
         fileType: image,
-        excludeNsfw: defaultStore.state.nsfw !== "ignore",
         limit: 10
       }).then((notes) => {
         for (const note of notes) {
@@ -198,7 +197,6 @@ const _sfc_main = defineComponent({
       api("users/notes", {
         userId: props.user.id,
         fileType: image,
-        excludeNsfw: defaultStore.state.nsfw !== "ignore",
         limit: 10
       }).then(notes => {
         for (const note of notes) {
diff --git a/packages/frontend/src/pages/user/index.files.vue b/packages/frontend/src/pages/user/index.files.vue
index f9328b8d6b..72f95ec1a5 100644
--- a/packages/frontend/src/pages/user/index.files.vue
+++ b/packages/frontend/src/pages/user/index.files.vue
@@ -64,7 +64,6 @@ onMounted(() => {
 	os.api('users/notes', {
 		userId: props.user.id,
 		withFiles: true,
-		excludeNsfw: defaultStore.state.nsfw !== 'ignore',
 		limit: 15,
 	}).then(notes => {
 		for (const note of notes) {

From c190b720d3d4f3aa93a0556f63be4b09e241108c Mon Sep 17 00:00:00 2001
From: meron <meronmks.8914@gmail.com>
Date: Sat, 2 Dec 2023 15:26:46 +0900
Subject: [PATCH 089/435] =?UTF-8?q?feat(frontend):=20=E7=B5=B5=E6=96=87?=
 =?UTF-8?q?=E5=AD=97=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC=E3=81=AE=E3=82=AB?=
 =?UTF-8?q?=E3=83=86=E3=82=B4=E3=83=AA=E3=82=92=E5=A4=9A=E9=9A=8E=E5=B1=A4?=
 =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=AB=E3=83=80=E3=81=A7=E5=88=86=E9=A1=9E?=
 =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1213?=
 =?UTF-8?q?2)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Update CHANGELOG.md

* Feat:emoji picker folder select

* Fix: lint error

* Fix: lint error 2

* Fix: lint error 3

* カスタム絵文字のカテゴリ表示部分が長かったので短くした

* エフェクトが壊れて出ないのを修正

* padding 18px -> 9px

* Update CHANGELOG.md

* Revert: en-US.yml

* chg: Folder -> folder

* chg: isChildrenExits -> isChildren

* chg: isChildren -> categoryFolderFlag

* カテゴリ末尾が / の場合ピッカーから消失するので「その他」と扱い対応

* 特定のパターンのカテゴリ名でピッカーに出てこないのを修正

「i18n.ts.other」や「/」始まりの場合壊れる

* chg: categoryFolderFlag -> hasChildSection

* code format

* Del: ti-fw

* fix

* 絵文字とフォルダの表示順序入れ替え

* ネストした場合にパネルでどこまでがどのフォルダのものかをわかりやすくした

* fix lint

* カテゴリの名前が長いと表示がおかしくなる問題を修正

Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
Co-authored-by: atsuchan <83960488+atsu1125@users.noreply.github.com>
Co-authored-by: Masaya Suzuki <15100604+massongit@users.noreply.github.com>
Co-authored-by: Kagami Sascha Rosylight <saschanaz@outlook.com>
Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com>
Co-authored-by: xianon <xianon@hotmail.co.jp>
Co-authored-by: kabo2468 <28654659+kabo2468@users.noreply.github.com>
Co-authored-by: YS <47836716+yszkst@users.noreply.github.com>
Co-authored-by: Khsmty <me@khsmty.com>
Co-authored-by: Soni L <EnderMoneyMod@gmail.com>
Co-authored-by: mei23 <m@m544.net>
Co-authored-by: daima3629 <52790780+daima3629@users.noreply.github.com>
Co-authored-by: Windymelt <1113940+windymelt@users.noreply.github.com>
Co-authored-by: Ebise Lutica <7106976+EbiseLutica@users.noreply.github.com>
Co-authored-by: nenohi <kimutipartylove@gmail.com>
Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
Co-authored-by: rinsuki <428rinsuki+git@gmail.com>
Co-authored-by: FineArchs <133759614+FineArchs@users.noreply.github.com>
Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com>
---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            |  1 +
 locales/ja-JP.yml                             |  1 +
 .../src/components/MkEmojiPicker.section.vue  | 47 ++++++++++++++--
 .../frontend/src/components/MkEmojiPicker.vue | 53 ++++++++++++++++---
 packages/frontend/src/scripts/emojilist.ts    |  6 +++
 6 files changed, 98 insertions(+), 11 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a512047819..4f7389c31c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -177,6 +177,7 @@
 ### Client
 - Enhance: TLの返信表示オプションを記憶するように
 - Enhance: 投稿されてから時間が経過しているノートであることを視覚的に分かりやすく
+- Feat: 絵文字ピッカーのカテゴリに「/」を入れることでフォルダ分け表示できるように
 
 ### Server
 - Enhance: タイムライン取得時のパフォーマンスを向上
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 402c2413f8..a8f54e2e18 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -314,6 +314,7 @@ export interface Locale {
     "createFolder": string;
     "renameFolder": string;
     "deleteFolder": string;
+    "folder": string;
     "addFile": string;
     "emptyDrive": string;
     "emptyFolder": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 2b2f05aa88..04fb1f947d 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -311,6 +311,7 @@ folderName: "フォルダー名"
 createFolder: "フォルダーを作成"
 renameFolder: "フォルダー名を変更"
 deleteFolder: "フォルダーを削除"
+folder: "フォルダー"
 addFile: "ファイルを追加"
 emptyDrive: "ドライブは空です"
 emptyFolder: "フォルダーは空です"
diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index 08297ea5ba..35de9bbb5e 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -5,9 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと -->
-<section>
+<!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) -->
+<section v-if="!hasChildSection" v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);">
 	<header class="_acrylic" @click="shown = !shown">
-		<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> ({{ emojis.length }})
+		<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-icons"></i>:{{ emojis.length }})
 	</header>
 	<div v-if="shown" class="body">
 		<button
@@ -23,15 +24,52 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</button>
 	</div>
 </section>
+<!-- フォルダの中にはカスタム絵文字やフォルダがある -->
+<section v-else v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);">
+  <header class="_acrylic" @click="shown = !shown">
+    <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-folder ti-fw"></i>:{{ customEmojiTree.length }} <i class="ti ti-icons ti-fw"></i>:{{ emojis.length }})
+  </header>
+  <div v-if="shown" style="padding-left: 9px;">
+      <MkEmojiPickerSection
+          v-for="child in customEmojiTree"
+          :key="`custom:${child.value}`"
+          :initialShown="initialShown"
+          :emojis="computed(() => customEmojis.filter(e => e.category === child.category).map(e => `:${e.name}:`))"
+          :hasChildSection="child.children.length !== 0"
+          :customEmojiTree="child.children"
+          @chosen="nestedChosen"
+      >
+          {{ child.value || i18n.ts.other }}
+      </MkEmojiPickerSection>
+  </div>
+  <div v-if="shown" class="body">
+    <button
+        v-for="emoji in emojis"
+        :key="emoji"
+        :data-emoji="emoji"
+        class="_button item"
+        @pointerenter="computeButtonTitle"
+        @click="emit('chosen', emoji, $event)"
+    >
+      <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/>
+      <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
+    </button>
+  </div>
+</section>
 </template>
 
 <script lang="ts" setup>
 import { ref, computed, Ref } from 'vue';
-import { getEmojiName } from '@/scripts/emojilist.js';
+import { CustomEmojiFolderTree, getEmojiName } from '@/scripts/emojilist.js';
+import { i18n } from '../i18n.js';
+import { customEmojis } from '@/custom-emojis.js';
+import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue';
 
 const props = defineProps<{
 	emojis: string[] | Ref<string[]>;
 	initialShown?: boolean;
+	hasChildSection?: boolean;
+	customEmojiTree?: CustomEmojiFolderTree[];
 }>();
 
 const emit = defineEmits<{
@@ -49,4 +87,7 @@ function computeButtonTitle(ev: MouseEvent): void {
 	elm.title = getEmojiName(emoji) ?? emoji;
 }
 
+function nestedChosen(emoji: any, ev?: MouseEvent) {
+	emit('chosen', emoji, ev);
+}
 </script>
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 5b420c499e..603f676d5d 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -73,18 +73,20 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div v-once class="group">
 			<header class="_acrylic">{{ i18n.ts.customEmojis }}</header>
 			<XSection
-				v-for="category in customEmojiCategories"
-				:key="`custom:${category}`"
+				v-for="child in customEmojiFolderRoot.children"
+				:key="`custom:${child.value}`"
 				:initialShown="false"
-				:emojis="computed(() => customEmojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))"
+				:emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value).filter(filterAvailable).map(e => `:${e.name}:`))"
+        :hasChildSection="child.children.length !== 0"
+        :customEmojiTree="child.children"
 				@chosen="chosen"
 			>
-				{{ category || i18n.ts.other }}
+				{{ child.value || i18n.ts.other }}
 			</XSection>
 		</div>
 		<div v-once class="group">
 			<header class="_acrylic">{{ i18n.ts.emoji }}</header>
-			<XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" @chosen="chosen">{{ category }}</XSection>
+			<XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" :hasChildSection="false" @chosen="chosen">{{ category }}</XSection>
 		</div>
 	</div>
 	<div class="tabs">
@@ -100,7 +102,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, shallowRef, computed, watch, onMounted } from 'vue';
 import * as Misskey from 'misskey-js';
 import XSection from '@/components/MkEmojiPicker.section.vue';
-import { emojilist, emojiCharByCategory, UnicodeEmojiDef, unicodeEmojiCategories as categories, getEmojiName } from '@/scripts/emojilist.js';
+import {
+  emojilist,
+  emojiCharByCategory,
+  UnicodeEmojiDef,
+  unicodeEmojiCategories as categories,
+  getEmojiName,
+  CustomEmojiFolderTree
+} from '@/scripts/emojilist.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import * as os from '@/os.js';
 import { isTouchUsing } from '@/scripts/touch.js';
@@ -144,6 +153,35 @@ const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]);
 const searchResultUnicode = ref<UnicodeEmojiDef[]>([]);
 const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index');
 
+const customEmojiFolderRoot: CustomEmojiFolderTree = { value: "", category: "", children: [] };
+
+function parseAndMergeCategories(input: string, root: CustomEmojiFolderTree): CustomEmojiFolderTree {
+	const parts = input.split('/').map(p => p.trim());
+	let currentNode: CustomEmojiFolderTree = root;
+
+	for (const part of parts) {
+		let existingNode = currentNode.children.find((node) => node.value === part);
+
+		if (!existingNode) {
+			const newNode: CustomEmojiFolderTree = { value: part, category: input, children: [] };
+			currentNode.children.push(newNode);
+			existingNode = newNode;
+		}
+
+		currentNode = existingNode;
+	}
+
+	return currentNode;
+}
+
+customEmojiCategories.value.forEach(ec => {
+  if (ec !== null) {
+    parseAndMergeCategories(ec, customEmojiFolderRoot);
+  }
+});
+
+parseAndMergeCategories('', customEmojiFolderRoot);
+
 watch(q, () => {
 	if (emojisEl.value) emojisEl.value.scrollTop = 0;
 
@@ -572,8 +610,7 @@ defineExpose({
 				position: sticky;
 				top: 0;
 				left: 0;
-				height: 32px;
-				line-height: 32px;
+				line-height: 28px;
 				z-index: 1;
 				padding: 0 8px;
 				font-size: 12px;
diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts
index 4159da84c8..8885bf4b7f 100644
--- a/packages/frontend/src/scripts/emojilist.ts
+++ b/packages/frontend/src/scripts/emojilist.ts
@@ -43,3 +43,9 @@ export function getEmojiName(char: string): string | null {
 		return emojilist[idx].name;
 	}
 }
+
+export interface CustomEmojiFolderTree {
+	value: string;
+	category: string;
+	children: CustomEmojiFolderTree[];
+}

From 8968bfd309e505f7e33796d9d2084783bcfae377 Mon Sep 17 00:00:00 2001
From: Camilla Ett <camilla.ett@gmail.com>
Date: Sat, 2 Dec 2023 17:07:57 +0900
Subject: [PATCH 090/435] =?UTF-8?q?fix(backend):=20=E3=82=AB=E3=82=B9?=
 =?UTF-8?q?=E3=82=BF=E3=83=A0=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE=E3=82=A4?=
 =?UTF-8?q?=E3=83=B3=E3=83=9D=E3=83=BC=E3=83=88=E6=99=82=E3=81=AE=E5=8B=95?=
 =?UTF-8?q?=E4=BD=9C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12360)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 .../server/api/endpoints/admin/emoji/copy.ts  | 42 +++++++++----------
 .../src/pages/custom-emojis-manager.vue       |  4 +-
 2 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
index a65e4e7624..5b41dfb514 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
@@ -6,11 +6,10 @@
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import type { EmojisRepository } from '@/models/_.js';
-import { IdService } from '@/core/IdService.js';
 import type { MiDriveFile } from '@/models/DriveFile.js';
 import { DI } from '@/di-symbols.js';
 import { DriveService } from '@/core/DriveService.js';
-import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
 import { ApiError } from '../../../error.js';
 
@@ -26,6 +25,11 @@ export const meta = {
 			code: 'NO_SUCH_EMOJI',
 			id: 'e2785b66-dca3-4087-9cac-b93c541cc425',
 		},
+		duplicateName: {
+			message: 'Duplicate name.',
+			code: 'DUPLICATE_NAME',
+			id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975',
+		},
 	},
 
 	res: {
@@ -56,15 +60,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 	constructor(
 		@Inject(DI.emojisRepository)
 		private emojisRepository: EmojisRepository,
-
 		private emojiEntityService: EmojiEntityService,
-		private idService: IdService,
-		private globalEventService: GlobalEventService,
+		private customEmojiService: CustomEmojiService,
 		private driveService: DriveService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const emoji = await this.emojisRepository.findOneBy({ id: ps.emojiId });
-
 			if (emoji == null) {
 				throw new ApiError(meta.errors.noSuchEmoji);
 			}
@@ -75,28 +76,27 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				// Create file
 				driveFile = await this.driveService.uploadFromUrl({ url: emoji.originalUrl, user: null, force: true });
 			} catch (e) {
+				// TODO: need to return Drive Error
 				throw new ApiError();
 			}
 
-			const copied = await this.emojisRepository.insert({
-				id: this.idService.gen(),
-				updatedAt: new Date(),
+			// Duplication Check
+			const isDuplicate = await this.customEmojiService.checkDuplicate(emoji.name);
+			if (isDuplicate) throw new ApiError(meta.errors.duplicateName);
+
+			const addedEmoji = await this.customEmojiService.add({
+				driveFile,
 				name: emoji.name,
+				category: emoji.category,
+				aliases: emoji.aliases,
 				host: null,
-				aliases: [],
-				originalUrl: driveFile.url,
-				publicUrl: driveFile.webpublicUrl ?? driveFile.url,
-				type: driveFile.webpublicType ?? driveFile.type,
 				license: emoji.license,
-			}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
+				isSensitive: emoji.isSensitive,
+				localOnly: emoji.localOnly,
+				roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction,
+			}, me);
 
-			this.globalEventService.publishBroadcastStream('emojiAdded', {
-				emoji: await this.emojiEntityService.packDetailed(copied.id),
-			});
-
-			return {
-				id: copied.id,
-			};
+			return this.emojiEntityService.packDetailed(addedEmoji);
 		});
 	}
 }
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 7450cf97c9..316dbaa3aa 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -155,7 +155,7 @@ const edit = (emoji) => {
 	}, 'closed');
 };
 
-const im = (emoji) => {
+const importEmoji = (emoji) => {
 	os.apiWithDialog('admin/emoji/copy', {
 		emojiId: emoji.id,
 	});
@@ -168,7 +168,7 @@ const remoteMenu = (emoji, ev: MouseEvent) => {
 	}, {
 		text: i18n.ts.import,
 		icon: 'ti ti-plus',
-		action: () => { im(emoji); },
+		action: () => { importEmoji(emoji); },
 	}], ev.currentTarget ?? ev.target);
 };
 

From cf3d45e7c89fb1d2103c04315bee27a546bef34f Mon Sep 17 00:00:00 2001
From: paihu <13479783+paihu@users.noreply.github.com>
Date: Sat, 2 Dec 2023 17:09:22 +0900
Subject: [PATCH 091/435] fix(frontend): MFM ruby nyaize (#12362)

---
 CHANGELOG.md                                           |  1 +
 .../src/components/global/MkMisskeyFlavoredMarkdown.ts | 10 ++++++++--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f7389c31c..2d96a02325 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@
 
 ### Client
 - Fix: ページ一覧ページの表示がモバイル環境において崩れているのを修正
+- Fix: MFMでルビの中のテキストがnyaizeされない問題を修正
 
 ### Server
 -
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index d4c3ef60aa..fe599dcead 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -242,11 +242,17 @@ export default function(props: MfmProps) {
 					case 'ruby': {
 						if (token.children.length === 1) {
 							const child = token.children[0];
-							const text = child.type === 'text' ? child.props.text : '';
+							let text = child.type === 'text' ? child.props.text : '';
+							if (!disableNyaize && shouldNyaize) {
+								text = doNyaize(text);
+							}
 							return h('ruby', {}, [text.split(' ')[0], h('rt', text.split(' ')[1])]);
 						} else {
 							const rt = token.children.at(-1)!;
-							const text = rt.type === 'text' ? rt.props.text : '';
+							let text = rt.type === 'text' ? rt.props.text : '';
+							if (!disableNyaize && shouldNyaize) {
+								text = doNyaize(text);
+							}
 							return h('ruby', {}, [...genEl(token.children.slice(0, token.children.length - 1), scale), h('rt', text.trim())]);
 						}
 					}

From a631b976c99a4b3977079a4bafc8a7b7de0bf269 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sat, 2 Dec 2023 18:25:07 +0900
Subject: [PATCH 092/435] Refine fanout timeline (#12507)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore(endpoints/hybrid-timeline): don't pack inside getFromDb

* chore(endpoints/hybrid-timeline): Redisから取得する部分のうちSTLに依存しなそうなところを別のServiceに切り出し

* chore(endpoints/local-timeline): FanoutTimelineEndpointServiceで再実装

* chore(endpoints/channels/timeline): FanoutTimelineEndpointServiceで再実装

* chore(endpoints/timeline): FanoutTimelineEndpointServiceで再実装

* chore(endpoints/user-list-timeline): FanoutTimelineEndpointServiceで再実装

* chore(endpoints/users/notes): FanoutTimelineEndpointServiceで再実装

* chore: add useDbFallback to FanoutTimelineEndpointService.timeline and always true for channel / user note list

* style: fix lint error

* chore: split logic to multiple functions

* chore: implement redis fallback

* chore: 成功率を上げる

* fix: db fallback not working

* feat: allowPartial

* chore(frontend): set allowPartial

* chore(backend): remove fallbackIfEmpty

HTL will never be purged so it's no longer required

* fix: missing allowPartial in channel timeline

* fix: type of timelineConfig in hybrid-timeline

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 packages/backend/src/core/CoreModule.ts       |   6 +
 .../src/core/FanoutTimelineEndpointService.ts | 123 +++++++++++
 .../server/api/endpoints/channels/timeline.ts | 112 +++++-----
 .../api/endpoints/notes/hybrid-timeline.ts    | 110 ++++-----
 .../api/endpoints/notes/local-timeline.ts     | 103 ++++-----
 .../server/api/endpoints/notes/timeline.ts    |  91 ++++----
 .../api/endpoints/notes/user-list-timeline.ts |  78 +++----
 .../src/server/api/endpoints/users/notes.ts   | 208 +++++++++---------
 .../frontend/src/components/MkPagination.vue  |   1 +
 9 files changed, 438 insertions(+), 394 deletions(-)
 create mode 100644 packages/backend/src/core/FanoutTimelineEndpointService.ts

diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index bf6f0ef879..bc6d24b951 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -4,6 +4,7 @@
  */
 
 import { Module } from '@nestjs/common';
+import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
 import { AccountMoveService } from './AccountMoveService.js';
 import { AccountUpdateService } from './AccountUpdateService.js';
 import { AiService } from './AiService.js';
@@ -195,6 +196,7 @@ const $SearchService: Provider = { provide: 'SearchService', useExisting: Search
 const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipService };
 const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService };
 const $FanoutTimelineService: Provider = { provide: 'FanoutTimelineService', useExisting: FanoutTimelineService };
+const $FanoutTimelineEndpointService: Provider = { provide: 'FanoutTimelineEndpointService', useExisting: FanoutTimelineEndpointService };
 const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService };
 const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService };
 
@@ -331,6 +333,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		ClipService,
 		FeaturedService,
 		FanoutTimelineService,
+		FanoutTimelineEndpointService,
 		ChannelFollowingService,
 		RegistryApiService,
 		ChartLoggerService,
@@ -460,6 +463,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$ClipService,
 		$FeaturedService,
 		$FanoutTimelineService,
+		$FanoutTimelineEndpointService,
 		$ChannelFollowingService,
 		$RegistryApiService,
 		$ChartLoggerService,
@@ -590,6 +594,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		ClipService,
 		FeaturedService,
 		FanoutTimelineService,
+		FanoutTimelineEndpointService,
 		ChannelFollowingService,
 		RegistryApiService,
 		FederationChart,
@@ -718,6 +723,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
 		$ClipService,
 		$FeaturedService,
 		$FanoutTimelineService,
+		$FanoutTimelineEndpointService,
 		$ChannelFollowingService,
 		$RegistryApiService,
 		$FederationChart,
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
new file mode 100644
index 0000000000..157fcbe877
--- /dev/null
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -0,0 +1,123 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { DI } from '@/di-symbols.js';
+import { bindThis } from '@/decorators.js';
+import type { MiUser } from '@/models/User.js';
+import type { MiNote } from '@/models/Note.js';
+import { Packed } from '@/misc/json-schema.js';
+import type { NotesRepository } from '@/models/_.js';
+import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
+import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
+
+@Injectable()
+export class FanoutTimelineEndpointService {
+	constructor(
+		@Inject(DI.notesRepository)
+		private notesRepository: NotesRepository,
+
+		private noteEntityService: NoteEntityService,
+		private fanoutTimelineService: FanoutTimelineService,
+	) {
+	}
+
+	@bindThis
+	async timeline(ps: {
+		untilId: string | null,
+		sinceId: string | null,
+		limit: number,
+		allowPartial: boolean,
+		me?: { id: MiUser['id'] } | undefined | null,
+		useDbFallback: boolean,
+		redisTimelines: string[],
+		noteFilter: (note: MiNote) => boolean,
+		dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise<MiNote[]>,
+	}): Promise<Packed<'Note'>[]> {
+		return await this.noteEntityService.packMany(await this.getMiNotes(ps), ps.me);
+	}
+
+	@bindThis
+	private async getMiNotes(ps: {
+		untilId: string | null,
+		sinceId: string | null,
+		limit: number,
+		allowPartial: boolean,
+		me?: { id: MiUser['id'] } | undefined | null,
+		useDbFallback: boolean,
+		redisTimelines: string[],
+		noteFilter: (note: MiNote) => boolean,
+		dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise<MiNote[]>,
+	}): Promise<MiNote[]> {
+		let noteIds: string[];
+		let shouldFallbackToDb = false;
+
+		// 呼び出し元と以下の処理をシンプルにするためにdbFallbackを置き換える
+		if (!ps.useDbFallback) ps.dbFallback = () => Promise.resolve([]);
+
+		const redisResult = await this.fanoutTimelineService.getMulti(ps.redisTimelines, ps.untilId, ps.sinceId);
+
+		const redisResultIds = Array.from(new Set(redisResult.flat(1)));
+
+		redisResultIds.sort((a, b) => a > b ? -1 : 1);
+		noteIds = redisResultIds.slice(0, ps.limit);
+
+		shouldFallbackToDb = shouldFallbackToDb || (noteIds.length === 0);
+
+		if (!shouldFallbackToDb) {
+			const redisTimeline: MiNote[] = [];
+			let readFromRedis = 0;
+			let lastSuccessfulRate = 1; // rateをキャッシュする?
+			let trialCount = 1;
+
+			while ((redisResultIds.length - readFromRedis) !== 0) {
+				const remainingToRead = ps.limit - redisTimeline.length;
+
+				// DBからの取り直しを減らす初回と同じ割合以上で成功すると仮定するが、クエリの長さを考えて三倍まで
+				const countToGet = remainingToRead * Math.ceil(Math.min(1.1 / lastSuccessfulRate, 3));
+				noteIds = redisResultIds.slice(readFromRedis, readFromRedis + countToGet);
+
+				readFromRedis += noteIds.length;
+
+				const gotFromDb = await this.getAndFilterFromDb(noteIds, ps.noteFilter);
+				redisTimeline.push(...gotFromDb);
+				lastSuccessfulRate = gotFromDb.length / noteIds.length;
+
+				console.log(`fanoutTimelineTrial#${trialCount++}: req: ${ps.limit}, tried: ${noteIds.length}, got: ${gotFromDb.length}, rate: ${lastSuccessfulRate}, total: ${redisTimeline.length}, fromRedis: ${redisResultIds.length}`);
+
+				if (ps.allowPartial ? redisTimeline.length !== 0 : redisTimeline.length >= ps.limit) {
+					// 十分Redisからとれた
+					return redisTimeline.slice(0, ps.limit);
+				}
+			}
+
+			// まだ足りない分はDBにフォールバック
+			const remainingToRead = ps.limit - redisTimeline.length;
+			const gotFromDb = await ps.dbFallback(noteIds[noteIds.length - 1], ps.sinceId, remainingToRead);
+			redisTimeline.push(...gotFromDb);
+			console.log(`fanoutTimelineTrial#db: req: ${ps.limit}, tried: ${remainingToRead}, got: ${gotFromDb.length}, since: ${noteIds[noteIds.length - 1]}, until: ${ps.untilId}, total: ${redisTimeline.length}`);
+			return redisTimeline;
+		}
+
+		return await ps.dbFallback(ps.untilId, ps.sinceId, ps.limit);
+	}
+
+	private async getAndFilterFromDb(noteIds: string[], noteFilter: (note: MiNote) => boolean): Promise<MiNote[]> {
+		const query = this.notesRepository.createQueryBuilder('note')
+			.where('note.id IN (:...noteIds)', { noteIds: noteIds })
+			.innerJoinAndSelect('note.user', 'user')
+			.leftJoinAndSelect('note.reply', 'reply')
+			.leftJoinAndSelect('note.renote', 'renote')
+			.leftJoinAndSelect('reply.user', 'replyUser')
+			.leftJoinAndSelect('renote.user', 'renoteUser')
+			.leftJoinAndSelect('note.channel', 'channel');
+
+		const notes = (await query.getMany()).filter(noteFilter);
+
+		notes.sort((a, b) => a.id > b.id ? -1 : 1);
+
+		return notes;
+	}
+}
diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts
index f9207199d6..9ef494d6d8 100644
--- a/packages/backend/src/server/api/endpoints/channels/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts
@@ -12,10 +12,11 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import ActiveUsersChart from '@/core/chart/charts/active-users.js';
 import { DI } from '@/di-symbols.js';
 import { IdService } from '@/core/IdService.js';
-import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
 import { CacheService } from '@/core/CacheService.js';
 import { MetaService } from '@/core/MetaService.js';
+import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
+import { MiLocalUser } from '@/models/User.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -51,6 +52,7 @@ export const paramDef = {
 		untilId: { type: 'string', format: 'misskey:id' },
 		sinceDate: { type: 'integer' },
 		untilDate: { type: 'integer' },
+		allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
 	},
 	required: ['channelId'],
 } as const;
@@ -58,9 +60,6 @@ export const paramDef = {
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 	constructor(
-		@Inject(DI.redisForTimelines)
-		private redisForTimelines: Redis.Redis,
-
 		@Inject(DI.notesRepository)
 		private notesRepository: NotesRepository,
 
@@ -70,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private idService: IdService,
 		private noteEntityService: NoteEntityService,
 		private queryService: QueryService,
-		private fanoutTimelineService: FanoutTimelineService,
+		private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
 		private cacheService: CacheService,
 		private activeUsersChart: ActiveUsersChart,
 		private metaService: MetaService,
@@ -78,7 +77,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		super(meta, paramDef, async (ps, me) => {
 			const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
 			const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null);
-			const isRangeSpecified = untilId != null && sinceId != null;
 
 			const serverSettings = await this.metaService.fetch();
 
@@ -92,64 +90,58 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			if (me) this.activeUsersChart.read(me);
 
-			if (serverSettings.enableFanoutTimeline && (isRangeSpecified || sinceId == null)) {
-				const [
-					userIdsWhoMeMuting,
-				] = me ? await Promise.all([
-					this.cacheService.userMutingsCache.fetch(me.id),
-				]) : [new Set<string>()];
-
-				let noteIds = await this.fanoutTimelineService.get(`channelTimeline:${channel.id}`, untilId, sinceId);
-				noteIds = noteIds.slice(0, ps.limit);
-
-				if (noteIds.length > 0) {
-					const query = this.notesRepository.createQueryBuilder('note')
-						.where('note.id IN (:...noteIds)', { noteIds: noteIds })
-						.innerJoinAndSelect('note.user', 'user')
-						.leftJoinAndSelect('note.reply', 'reply')
-						.leftJoinAndSelect('note.renote', 'renote')
-						.leftJoinAndSelect('reply.user', 'replyUser')
-						.leftJoinAndSelect('renote.user', 'renoteUser')
-						.leftJoinAndSelect('note.channel', 'channel');
-
-					let timeline = await query.getMany();
-
-					timeline = timeline.filter(note => {
-						if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
-
-						return true;
-					});
-
-					// TODO: フィルタで件数が減った場合の埋め合わせ処理
-
-					timeline.sort((a, b) => a.id > b.id ? -1 : 1);
-
-					if (timeline.length > 0) {
-						return await this.noteEntityService.packMany(timeline, me);
-					}
-				}
+			if (!serverSettings.enableFanoutTimeline) {
+				return await this.noteEntityService.packMany(await this.getFromDb({ untilId, sinceId, limit: ps.limit, channelId: channel.id }, me), me);
 			}
 
-			//#region fallback to database
-			const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
-				.andWhere('note.channelId = :channelId', { channelId: channel.id })
-				.innerJoinAndSelect('note.user', 'user')
-				.leftJoinAndSelect('note.reply', 'reply')
-				.leftJoinAndSelect('note.renote', 'renote')
-				.leftJoinAndSelect('reply.user', 'replyUser')
-				.leftJoinAndSelect('renote.user', 'renoteUser')
-				.leftJoinAndSelect('note.channel', 'channel');
+			const [
+				userIdsWhoMeMuting,
+			] = me ? await Promise.all([
+				this.cacheService.userMutingsCache.fetch(me.id),
+			]) : [new Set<string>()];
 
-			if (me) {
-				this.queryService.generateMutedUserQuery(query, me);
-				this.queryService.generateBlockedUserQuery(query, me);
-			}
-			//#endregion
+			return await this.fanoutTimelineEndpointService.timeline({
+				untilId,
+				sinceId,
+				limit: ps.limit,
+				allowPartial: ps.allowPartial,
+				me,
+				useDbFallback: true,
+				redisTimelines: [`channelTimeline:${channel.id}`],
+				noteFilter: note => {
+					if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
 
-			const timeline = await query.limit(ps.limit).getMany();
-
-			return await this.noteEntityService.packMany(timeline, me);
-			//#endregion
+					return true;
+				},
+				dbFallback: async (untilId, sinceId, limit) => {
+					return await this.getFromDb({ untilId, sinceId, limit, channelId: channel.id }, me);
+				},
+			});
 		});
 	}
+
+	private async getFromDb(ps: {
+		untilId: string | null,
+		sinceId: string | null,
+		limit: number,
+		channelId: string
+	}, me: MiLocalUser | null) {
+		//#region fallback to database
+		const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
+			.andWhere('note.channelId = :channelId', { channelId: ps.channelId })
+			.innerJoinAndSelect('note.user', 'user')
+			.leftJoinAndSelect('note.reply', 'reply')
+			.leftJoinAndSelect('note.renote', 'renote')
+			.leftJoinAndSelect('reply.user', 'replyUser')
+			.leftJoinAndSelect('renote.user', 'renoteUser')
+			.leftJoinAndSelect('note.channel', 'channel');
+
+		if (me) {
+			this.queryService.generateMutedUserQuery(query, me);
+			this.queryService.generateBlockedUserQuery(query, me);
+		}
+		//#endregion
+
+		return await query.limit(ps.limit).getMany();
+	}
 }
diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index 372199844d..820692626c 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -5,7 +5,7 @@
 
 import { Brackets } from 'typeorm';
 import { Inject, Injectable } from '@nestjs/common';
-import type { NotesRepository, FollowingsRepository, MiNote, ChannelFollowingsRepository } from '@/models/_.js';
+import type { NotesRepository, ChannelFollowingsRepository } from '@/models/_.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import ActiveUsersChart from '@/core/chart/charts/active-users.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
@@ -19,6 +19,7 @@ import { QueryService } from '@/core/QueryService.js';
 import { UserFollowingService } from '@/core/UserFollowingService.js';
 import { MetaService } from '@/core/MetaService.js';
 import { MiLocalUser } from '@/models/User.js';
+import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -53,6 +54,7 @@ export const paramDef = {
 		untilId: { type: 'string', format: 'misskey:id' },
 		sinceDate: { type: 'integer' },
 		untilDate: { type: 'integer' },
+		allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
 		includeMyRenotes: { type: 'boolean', default: true },
 		includeRenotedMyNotes: { type: 'boolean', default: true },
 		includeLocalRenotes: { type: 'boolean', default: true },
@@ -77,10 +79,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private activeUsersChart: ActiveUsersChart,
 		private idService: IdService,
 		private cacheService: CacheService,
-		private fanoutTimelineService: FanoutTimelineService,
 		private queryService: QueryService,
 		private userFollowingService: UserFollowingService,
 		private metaService: MetaService,
+		private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
@@ -94,7 +96,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const serverSettings = await this.metaService.fetch();
 
 			if (!serverSettings.enableFanoutTimeline) {
-				return await this.getFromDb({
+				const timeline = await this.getFromDb({
 					untilId,
 					sinceId,
 					limit: ps.limit,
@@ -104,6 +106,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					withFiles: ps.withFiles,
 					withReplies: ps.withReplies,
 				}, me);
+
+				process.nextTick(() => {
+					this.activeUsersChart.read(me);
+				});
+
+				return await this.noteEntityService.packMany(timeline, me);
 			}
 
 			const [
@@ -116,51 +124,34 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				this.cacheService.userBlockedCache.fetch(me.id),
 			]);
 
-			let noteIds: string[];
-			let shouldFallbackToDb = false;
+			let timelineConfig: string[];
 
 			if (ps.withFiles) {
-				const [htlNoteIds, ltlNoteIds] = await this.fanoutTimelineService.getMulti([
+				timelineConfig = [
 					`homeTimelineWithFiles:${me.id}`,
 					'localTimelineWithFiles',
-				], untilId, sinceId);
-				noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds]));
+				];
 			} else if (ps.withReplies) {
-				const [htlNoteIds, ltlNoteIds, ltlReplyNoteIds] = await this.fanoutTimelineService.getMulti([
+				timelineConfig = [
 					`homeTimeline:${me.id}`,
 					'localTimeline',
 					'localTimelineWithReplies',
-				], untilId, sinceId);
-				noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds, ...ltlReplyNoteIds]));
+				];
 			} else {
-				const [htlNoteIds, ltlNoteIds] = await this.fanoutTimelineService.getMulti([
+				timelineConfig = [
 					`homeTimeline:${me.id}`,
 					'localTimeline',
-				], untilId, sinceId);
-				noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds]));
-				shouldFallbackToDb = htlNoteIds.length === 0;
+				];
 			}
 
-			noteIds.sort((a, b) => a > b ? -1 : 1);
-			noteIds = noteIds.slice(0, ps.limit);
-
-			shouldFallbackToDb = shouldFallbackToDb || (noteIds.length === 0);
-
-			let redisTimeline: MiNote[] = [];
-
-			if (!shouldFallbackToDb) {
-				const query = this.notesRepository.createQueryBuilder('note')
-					.where('note.id IN (:...noteIds)', { noteIds: noteIds })
-					.innerJoinAndSelect('note.user', 'user')
-					.leftJoinAndSelect('note.reply', 'reply')
-					.leftJoinAndSelect('note.renote', 'renote')
-					.leftJoinAndSelect('reply.user', 'replyUser')
-					.leftJoinAndSelect('renote.user', 'renoteUser')
-					.leftJoinAndSelect('note.channel', 'channel');
-
-				redisTimeline = await query.getMany();
-
-				redisTimeline = redisTimeline.filter(note => {
+			const redisTimeline = await this.fanoutTimelineEndpointService.timeline({
+				untilId,
+				sinceId,
+				limit: ps.limit,
+				allowPartial: ps.allowPartial,
+				redisTimelines: timelineConfig,
+				useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
+				noteFilter: (note) => {
 					if (note.userId === me.id) {
 						return true;
 					}
@@ -174,33 +165,24 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					}
 
 					return true;
-				});
+				},
+				dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
+					untilId,
+					sinceId,
+					limit,
+					includeMyRenotes: ps.includeMyRenotes,
+					includeRenotedMyNotes: ps.includeRenotedMyNotes,
+					includeLocalRenotes: ps.includeLocalRenotes,
+					withFiles: ps.withFiles,
+					withReplies: ps.withReplies,
+				}, me),
+			});
 
-				redisTimeline.sort((a, b) => a.id > b.id ? -1 : 1);
-			}
+			process.nextTick(() => {
+				this.activeUsersChart.read(me);
+			});
 
-			if (redisTimeline.length > 0) {
-				process.nextTick(() => {
-					this.activeUsersChart.read(me);
-				});
-
-				return await this.noteEntityService.packMany(redisTimeline, me);
-			} else {
-				if (serverSettings.enableFanoutTimelineDbFallback) { // fallback to db
-					return await this.getFromDb({
-						untilId,
-						sinceId,
-						limit: ps.limit,
-						includeMyRenotes: ps.includeMyRenotes,
-						includeRenotedMyNotes: ps.includeRenotedMyNotes,
-						includeLocalRenotes: ps.includeLocalRenotes,
-						withFiles: ps.withFiles,
-						withReplies: ps.withReplies,
-					}, me);
-				} else {
-					return [];
-				}
-			}
+			return redisTimeline;
 		});
 	}
 
@@ -301,12 +283,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		}
 		//#endregion
 
-		const timeline = await query.limit(ps.limit).getMany();
-
-		process.nextTick(() => {
-			this.activeUsersChart.read(me);
-		});
-
-		return await this.noteEntityService.packMany(timeline, me);
+		return await query.limit(ps.limit).getMany();
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index 8867070055..97b05016ec 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -14,10 +14,10 @@ import { RoleService } from '@/core/RoleService.js';
 import { IdService } from '@/core/IdService.js';
 import { CacheService } from '@/core/CacheService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
-import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { QueryService } from '@/core/QueryService.js';
 import { MetaService } from '@/core/MetaService.js';
 import { MiLocalUser } from '@/models/User.js';
+import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -51,6 +51,7 @@ export const paramDef = {
 		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
 		sinceId: { type: 'string', format: 'misskey:id' },
 		untilId: { type: 'string', format: 'misskey:id' },
+		allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
 		sinceDate: { type: 'integer' },
 		untilDate: { type: 'integer' },
 	},
@@ -68,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private activeUsersChart: ActiveUsersChart,
 		private idService: IdService,
 		private cacheService: CacheService,
-		private fanoutTimelineService: FanoutTimelineService,
+		private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
 		private queryService: QueryService,
 		private metaService: MetaService,
 	) {
@@ -84,13 +85,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const serverSettings = await this.metaService.fetch();
 
 			if (!serverSettings.enableFanoutTimeline) {
-				return await this.getFromDb({
+				const timeline = await this.getFromDb({
 					untilId,
 					sinceId,
 					limit: ps.limit,
 					withFiles: ps.withFiles,
 					withReplies: ps.withReplies,
 				}, me);
+
+				process.nextTick(() => {
+					if (me) {
+						this.activeUsersChart.read(me);
+					}
+				});
+
+				return await this.noteEntityService.packMany(timeline, me);
 			}
 
 			const [
@@ -103,36 +112,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				this.cacheService.userBlockedCache.fetch(me.id),
 			]) : [new Set<string>(), new Set<string>(), new Set<string>()];
 
-			let noteIds: string[];
-
-			if (ps.withFiles) {
-				noteIds = await this.fanoutTimelineService.get('localTimelineWithFiles', untilId, sinceId);
-			} else {
-				const [nonReplyNoteIds, replyNoteIds] = await this.fanoutTimelineService.getMulti([
-					'localTimeline',
-					'localTimelineWithReplies',
-				], untilId, sinceId);
-				noteIds = Array.from(new Set([...nonReplyNoteIds, ...replyNoteIds]));
-				noteIds.sort((a, b) => a > b ? -1 : 1);
-			}
-
-			noteIds = noteIds.slice(0, ps.limit);
-
-			let redisTimeline: MiNote[] = [];
-
-			if (noteIds.length > 0) {
-				const query = this.notesRepository.createQueryBuilder('note')
-					.where('note.id IN (:...noteIds)', { noteIds: noteIds })
-					.innerJoinAndSelect('note.user', 'user')
-					.leftJoinAndSelect('note.reply', 'reply')
-					.leftJoinAndSelect('note.renote', 'renote')
-					.leftJoinAndSelect('reply.user', 'replyUser')
-					.leftJoinAndSelect('renote.user', 'renoteUser')
-					.leftJoinAndSelect('note.channel', 'channel');
-
-				redisTimeline = await query.getMany();
-
-				redisTimeline = redisTimeline.filter(note => {
+			const timeline = await this.fanoutTimelineEndpointService.timeline({
+				untilId,
+				sinceId,
+				limit: ps.limit,
+				allowPartial: ps.allowPartial,
+				me,
+				useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
+				redisTimelines: ps.withFiles ? ['localTimelineWithFiles'] : ['localTimeline', 'localTimelineWithReplies'],
+				noteFilter: note => {
 					if (me && (note.userId === me.id)) {
 						return true;
 					}
@@ -147,32 +135,23 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					}
 
 					return true;
-				});
+				},
+				dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
+					untilId,
+					sinceId,
+					limit,
+					withFiles: ps.withFiles,
+					withReplies: ps.withReplies,
+				}, me),
+			});
 
-				redisTimeline.sort((a, b) => a.id > b.id ? -1 : 1);
-			}
-
-			if (redisTimeline.length > 0) {
-				process.nextTick(() => {
-					if (me) {
-						this.activeUsersChart.read(me);
-					}
-				});
-
-				return await this.noteEntityService.packMany(redisTimeline, me);
-			} else {
-				if (serverSettings.enableFanoutTimelineDbFallback) { // fallback to db
-					return await this.getFromDb({
-						untilId,
-						sinceId,
-						limit: ps.limit,
-						withFiles: ps.withFiles,
-						withReplies: ps.withReplies,
-					}, me);
-				} else {
-					return [];
+			process.nextTick(() => {
+				if (me) {
+					this.activeUsersChart.read(me);
 				}
-			}
+			});
+
+			return timeline;
 		});
 	}
 
@@ -213,14 +192,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}));
 		}
 
-		const timeline = await query.limit(ps.limit).getMany();
-
-		process.nextTick(() => {
-			if (me) {
-				this.activeUsersChart.read(me);
-			}
-		});
-
-		return await this.noteEntityService.packMany(timeline, me);
+		return await query.limit(ps.limit).getMany();
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index 470abe0b14..74d0a6e0c0 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -5,7 +5,7 @@
 
 import { Brackets } from 'typeorm';
 import { Inject, Injectable } from '@nestjs/common';
-import type { MiNote, NotesRepository, ChannelFollowingsRepository } from '@/models/_.js';
+import type { NotesRepository, ChannelFollowingsRepository } from '@/models/_.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { QueryService } from '@/core/QueryService.js';
 import ActiveUsersChart from '@/core/chart/charts/active-users.js';
@@ -14,10 +14,10 @@ import { DI } from '@/di-symbols.js';
 import { IdService } from '@/core/IdService.js';
 import { CacheService } from '@/core/CacheService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
-import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { UserFollowingService } from '@/core/UserFollowingService.js';
 import { MiLocalUser } from '@/models/User.js';
 import { MetaService } from '@/core/MetaService.js';
+import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
 
 export const meta = {
 	tags: ['notes'],
@@ -43,6 +43,7 @@ export const paramDef = {
 		untilId: { type: 'string', format: 'misskey:id' },
 		sinceDate: { type: 'integer' },
 		untilDate: { type: 'integer' },
+		allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
 		includeMyRenotes: { type: 'boolean', default: true },
 		includeRenotedMyNotes: { type: 'boolean', default: true },
 		includeLocalRenotes: { type: 'boolean', default: true },
@@ -65,7 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private activeUsersChart: ActiveUsersChart,
 		private idService: IdService,
 		private cacheService: CacheService,
-		private fanoutTimelineService: FanoutTimelineService,
+		private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
 		private userFollowingService: UserFollowingService,
 		private queryService: QueryService,
 		private metaService: MetaService,
@@ -77,7 +78,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const serverSettings = await this.metaService.fetch();
 
 			if (!serverSettings.enableFanoutTimeline) {
-				return await this.getFromDb({
+				const timeline = await this.getFromDb({
 					untilId,
 					sinceId,
 					limit: ps.limit,
@@ -87,6 +88,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					withFiles: ps.withFiles,
 					withRenotes: ps.withRenotes,
 				}, me);
+
+				process.nextTick(() => {
+					this.activeUsersChart.read(me);
+				});
+
+				return await this.noteEntityService.packMany(timeline, me);
 			}
 
 			const [
@@ -101,24 +108,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				this.cacheService.userBlockedCache.fetch(me.id),
 			]);
 
-			let noteIds = await this.fanoutTimelineService.get(ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, untilId, sinceId);
-			noteIds = noteIds.slice(0, ps.limit);
-
-			let redisTimeline: MiNote[] = [];
-
-			if (noteIds.length > 0) {
-				const query = this.notesRepository.createQueryBuilder('note')
-					.where('note.id IN (:...noteIds)', { noteIds: noteIds })
-					.innerJoinAndSelect('note.user', 'user')
-					.leftJoinAndSelect('note.reply', 'reply')
-					.leftJoinAndSelect('note.renote', 'renote')
-					.leftJoinAndSelect('reply.user', 'replyUser')
-					.leftJoinAndSelect('renote.user', 'renoteUser')
-					.leftJoinAndSelect('note.channel', 'channel');
-
-				redisTimeline = await query.getMany();
-
-				redisTimeline = redisTimeline.filter(note => {
+			const timeline = this.fanoutTimelineEndpointService.timeline({
+				untilId,
+				sinceId,
+				limit: ps.limit,
+				allowPartial: ps.allowPartial,
+				me,
+				useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
+				redisTimelines: ps.withFiles ? [`homeTimelineWithFiles:${me.id}`] : [`homeTimeline:${me.id}`],
+				noteFilter: note => {
 					if (note.userId === me.id) {
 						return true;
 					}
@@ -135,33 +133,24 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					}
 
 					return true;
-				});
+				},
+				dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
+					untilId,
+					sinceId,
+					limit,
+					includeMyRenotes: ps.includeMyRenotes,
+					includeRenotedMyNotes: ps.includeRenotedMyNotes,
+					includeLocalRenotes: ps.includeLocalRenotes,
+					withFiles: ps.withFiles,
+					withRenotes: ps.withRenotes,
+				}, me),
+			});
 
-				redisTimeline.sort((a, b) => a.id > b.id ? -1 : 1);
-			}
+			process.nextTick(() => {
+				this.activeUsersChart.read(me);
+			});
 
-			if (redisTimeline.length > 0) {
-				process.nextTick(() => {
-					this.activeUsersChart.read(me);
-				});
-
-				return await this.noteEntityService.packMany(redisTimeline, me);
-			} else {
-				if (serverSettings.enableFanoutTimelineDbFallback) { // fallback to db
-					return await this.getFromDb({
-						untilId,
-						sinceId,
-						limit: ps.limit,
-						includeMyRenotes: ps.includeMyRenotes,
-						includeRenotedMyNotes: ps.includeRenotedMyNotes,
-						includeLocalRenotes: ps.includeLocalRenotes,
-						withFiles: ps.withFiles,
-						withRenotes: ps.withRenotes,
-					}, me);
-				} else {
-					return [];
-				}
-			}
+			return timeline;
 		});
 	}
 
@@ -269,12 +258,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		}
 		//#endregion
 
-		const timeline = await query.limit(ps.limit).getMany();
-
-		process.nextTick(() => {
-			this.activeUsersChart.read(me);
-		});
-
-		return await this.noteEntityService.packMany(timeline, me);
+		return await query.limit(ps.limit).getMany();
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
index 1ac1d37f48..f39cac5c3e 100644
--- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -17,6 +17,7 @@ import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { QueryService } from '@/core/QueryService.js';
 import { MiLocalUser } from '@/models/User.js';
 import { MetaService } from '@/core/MetaService.js';
+import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -52,6 +53,7 @@ export const paramDef = {
 		untilId: { type: 'string', format: 'misskey:id' },
 		sinceDate: { type: 'integer' },
 		untilDate: { type: 'integer' },
+		allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
 		includeMyRenotes: { type: 'boolean', default: true },
 		includeRenotedMyNotes: { type: 'boolean', default: true },
 		includeLocalRenotes: { type: 'boolean', default: true },
@@ -82,6 +84,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private cacheService: CacheService,
 		private idService: IdService,
 		private fanoutTimelineService: FanoutTimelineService,
+		private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
 		private queryService: QueryService,
 		private metaService: MetaService,
 	) {
@@ -101,7 +104,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			const serverSettings = await this.metaService.fetch();
 
 			if (!serverSettings.enableFanoutTimeline) {
-				return await this.getFromDb(list, {
+				const timeline = await this.getFromDb(list, {
 					untilId,
 					sinceId,
 					limit: ps.limit,
@@ -111,6 +114,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					withFiles: ps.withFiles,
 					withRenotes: ps.withRenotes,
 				}, me);
+
+				this.activeUsersChart.read(me);
+
+				await this.noteEntityService.packMany(timeline, me);
 			}
 
 			const [
@@ -123,24 +130,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				this.cacheService.userBlockedCache.fetch(me.id),
 			]);
 
-			let noteIds = await this.fanoutTimelineService.get(ps.withFiles ? `userListTimelineWithFiles:${list.id}` : `userListTimeline:${list.id}`, untilId, sinceId);
-			noteIds = noteIds.slice(0, ps.limit);
-
-			let redisTimeline: MiNote[] = [];
-
-			if (noteIds.length > 0) {
-				const query = this.notesRepository.createQueryBuilder('note')
-					.where('note.id IN (:...noteIds)', { noteIds: noteIds })
-					.innerJoinAndSelect('note.user', 'user')
-					.leftJoinAndSelect('note.reply', 'reply')
-					.leftJoinAndSelect('note.renote', 'renote')
-					.leftJoinAndSelect('reply.user', 'replyUser')
-					.leftJoinAndSelect('renote.user', 'renoteUser')
-					.leftJoinAndSelect('note.channel', 'channel');
-
-				redisTimeline = await query.getMany();
-
-				redisTimeline = redisTimeline.filter(note => {
+			const timeline = await this.fanoutTimelineEndpointService.timeline({
+				untilId,
+				sinceId,
+				limit: ps.limit,
+				allowPartial: ps.allowPartial,
+				me,
+				useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
+				redisTimelines: ps.withFiles ? [`userListTimelineWithFiles:${list.id}`] : [`userListTimeline:${list.id}`],
+				noteFilter: note => {
 					if (note.userId === me.id) {
 						return true;
 					}
@@ -154,30 +152,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					}
 
 					return true;
-				});
+				},
+				dbFallback: async (untilId, sinceId, limit) => await this.getFromDb(list, {
+					untilId,
+					sinceId,
+					limit,
+					includeMyRenotes: ps.includeMyRenotes,
+					includeRenotedMyNotes: ps.includeRenotedMyNotes,
+					includeLocalRenotes: ps.includeLocalRenotes,
+					withFiles: ps.withFiles,
+					withRenotes: ps.withRenotes,
+				}, me),
+			});
 
-				redisTimeline.sort((a, b) => a.id > b.id ? -1 : 1);
-			}
+			this.activeUsersChart.read(me);
 
-			if (redisTimeline.length > 0) {
-				this.activeUsersChart.read(me);
-				return await this.noteEntityService.packMany(redisTimeline, me);
-			} else {
-				if (serverSettings.enableFanoutTimelineDbFallback) { // fallback to db
-					return await this.getFromDb(list, {
-						untilId,
-						sinceId,
-						limit: ps.limit,
-						includeMyRenotes: ps.includeMyRenotes,
-						includeRenotedMyNotes: ps.includeRenotedMyNotes,
-						includeLocalRenotes: ps.includeLocalRenotes,
-						withFiles: ps.withFiles,
-						withRenotes: ps.withRenotes,
-					}, me);
-				} else {
-					return [];
-				}
-			}
+			return timeline;
 		});
 	}
 
@@ -271,10 +261,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		}
 		//#endregion
 
-		const timeline = await query.limit(ps.limit).getMany();
-
-		this.activeUsersChart.read(me);
-
-		return await this.noteEntityService.packMany(timeline, me);
+		return await query.limit(ps.limit).getMany();
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index 76033ddb06..56983f7bc4 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -5,8 +5,7 @@
 
 import { Brackets } from 'typeorm';
 import { Inject, Injectable } from '@nestjs/common';
-import * as Redis from 'ioredis';
-import type { MiNote, NotesRepository } from '@/models/_.js';
+import type { NotesRepository } from '@/models/_.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { DI } from '@/di-symbols.js';
@@ -14,9 +13,9 @@ import { CacheService } from '@/core/CacheService.js';
 import { IdService } from '@/core/IdService.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
 import { QueryService } from '@/core/QueryService.js';
-import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { MetaService } from '@/core/MetaService.js';
-import { ApiError } from '../../error.js';
+import { MiLocalUser } from '@/models/User.js';
+import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
 
 export const meta = {
 	tags: ['users', 'notes'],
@@ -52,6 +51,7 @@ export const paramDef = {
 		untilId: { type: 'string', format: 'misskey:id' },
 		sinceDate: { type: 'integer' },
 		untilDate: { type: 'integer' },
+		allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default
 		withFiles: { type: 'boolean', default: false },
 	},
 	required: ['userId'],
@@ -60,9 +60,6 @@ export const paramDef = {
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 	constructor(
-		@Inject(DI.redisForTimelines)
-		private redisForTimelines: Redis.Redis,
-
 		@Inject(DI.notesRepository)
 		private notesRepository: NotesRepository,
 
@@ -70,121 +67,130 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private queryService: QueryService,
 		private cacheService: CacheService,
 		private idService: IdService,
-		private fanoutTimelineService: FanoutTimelineService,
+		private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
 		private metaService: MetaService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null);
 			const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null);
-			const isRangeSpecified = untilId != null && sinceId != null;
 			const isSelf = me && (me.id === ps.userId);
 
 			const serverSettings = await this.metaService.fetch();
 
-			if (serverSettings.enableFanoutTimeline && (isRangeSpecified || sinceId == null)) {
-				const [
-					userIdsWhoMeMuting,
-				] = me ? await Promise.all([
-					this.cacheService.userMutingsCache.fetch(me.id),
-				]) : [new Set<string>()];
+			if (!serverSettings.enableFanoutTimeline) {
+				const timeline = await this.getFromDb({
+					untilId,
+					sinceId,
+					limit: ps.limit,
+					userId: ps.userId,
+					withChannelNotes: ps.withChannelNotes,
+					withFiles: ps.withFiles,
+					withRenotes: ps.withRenotes,
+				}, me);
 
-				const [noteIdsRes, repliesNoteIdsRes, channelNoteIdsRes] = await Promise.all([
-					this.fanoutTimelineService.get(ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`, untilId, sinceId),
-					ps.withReplies ? this.fanoutTimelineService.get(`userTimelineWithReplies:${ps.userId}`, untilId, sinceId) : Promise.resolve([]),
-					ps.withChannelNotes ? this.fanoutTimelineService.get(`userTimelineWithChannel:${ps.userId}`, untilId, sinceId) : Promise.resolve([]),
-				]);
+				return await this.noteEntityService.packMany(timeline, me);
+			}
 
-				let noteIds = Array.from(new Set([
-					...noteIdsRes,
-					...repliesNoteIdsRes,
-					...channelNoteIdsRes,
-				]));
-				noteIds.sort((a, b) => a > b ? -1 : 1);
-				noteIds = noteIds.slice(0, ps.limit);
+			const [
+				userIdsWhoMeMuting,
+			] = me ? await Promise.all([
+				this.cacheService.userMutingsCache.fetch(me.id),
+			]) : [new Set<string>()];
 
-				if (noteIds.length > 0) {
-					const isFollowing = me && Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId);
+			const redisTimelines = [ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`];
 
-					const query = this.notesRepository.createQueryBuilder('note')
-						.where('note.id IN (:...noteIds)', { noteIds: noteIds })
-						.innerJoinAndSelect('note.user', 'user')
-						.leftJoinAndSelect('note.reply', 'reply')
-						.leftJoinAndSelect('note.renote', 'renote')
-						.leftJoinAndSelect('reply.user', 'replyUser')
-						.leftJoinAndSelect('renote.user', 'renoteUser')
-						.leftJoinAndSelect('note.channel', 'channel');
+			if (ps.withReplies) redisTimelines.push(`userTimelineWithReplies:${ps.userId}`);
+			if (ps.withChannelNotes) redisTimelines.push(`userTimelineWithChannel:${ps.userId}`);
 
-					let timeline = await query.getMany();
+			const isFollowing = me && Object.hasOwn(await this.cacheService.userFollowingsCache.fetch(me.id), ps.userId);
 
-					timeline = timeline.filter(note => {
-						if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false;
+			const timeline = await this.fanoutTimelineEndpointService.timeline({
+				untilId,
+				sinceId,
+				limit: ps.limit,
+				allowPartial: ps.allowPartial,
+				me,
+				redisTimelines,
+				useDbFallback: true,
+				noteFilter: note => {
+					if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false;
 
-						if (note.renoteId) {
-							if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
-								if (ps.withRenotes === false) return false;
-							}
+					if (note.renoteId) {
+						if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
+							if (ps.withRenotes === false) return false;
 						}
-
-						if (note.channel?.isSensitive && !isSelf) return false;
-						if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false;
-						if (note.visibility === 'followers' && !isFollowing && !isSelf) return false;
-
-						return true;
-					});
-
-					// TODO: フィルタで件数が減った場合の埋め合わせ処理
-
-					timeline.sort((a, b) => a.id > b.id ? -1 : 1);
-
-					if (timeline.length > 0) {
-						return await this.noteEntityService.packMany(timeline, me);
 					}
-				}
-			}
 
-			//#region fallback to database
-			const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
-				.andWhere('note.userId = :userId', { userId: ps.userId })
-				.innerJoinAndSelect('note.user', 'user')
-				.leftJoinAndSelect('note.reply', 'reply')
-				.leftJoinAndSelect('note.renote', 'renote')
-				.leftJoinAndSelect('note.channel', 'channel')
-				.leftJoinAndSelect('reply.user', 'replyUser')
-				.leftJoinAndSelect('renote.user', 'renoteUser');
+					if (note.channel?.isSensitive && !isSelf) return false;
+					if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false;
+					if (note.visibility === 'followers' && !isFollowing && !isSelf) return false;
 
-			if (ps.withChannelNotes) {
-				if (!isSelf) query.andWhere(new Brackets(qb => {
-					qb.orWhere('note.channelId IS NULL');
-					qb.orWhere('channel.isSensitive = false');
-				}));
-			} else {
-				query.andWhere('note.channelId IS NULL');
-			}
+					return true;
+				},
+				dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
+					untilId,
+					sinceId,
+					limit,
+					userId: ps.userId,
+					withChannelNotes: ps.withChannelNotes,
+					withFiles: ps.withFiles,
+					withRenotes: ps.withRenotes,
+				}, me),
+			});
 
-			this.queryService.generateVisibilityQuery(query, me);
-			if (me) {
-				this.queryService.generateMutedUserQuery(query, me, { id: ps.userId });
-				this.queryService.generateBlockedUserQuery(query, me);
-			}
-
-			if (ps.withFiles) {
-				query.andWhere('note.fileIds != \'{}\'');
-			}
-
-			if (ps.withRenotes === false) {
-				query.andWhere(new Brackets(qb => {
-					qb.orWhere('note.userId != :userId', { userId: ps.userId });
-					qb.orWhere('note.renoteId IS NULL');
-					qb.orWhere('note.text IS NOT NULL');
-					qb.orWhere('note.fileIds != \'{}\'');
-					qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
-				}));
-			}
-
-			const timeline = await query.limit(ps.limit).getMany();
-
-			return await this.noteEntityService.packMany(timeline, me);
-			//#endregion
+			return timeline;
 		});
 	}
+
+	private async getFromDb(ps: {
+		untilId: string | null,
+		sinceId: string | null,
+		limit: number,
+		userId: string,
+		withChannelNotes: boolean,
+		withFiles: boolean,
+		withRenotes: boolean,
+	}, me: MiLocalUser | null) {
+		const isSelf = me && (me.id === ps.userId);
+
+		const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
+			.andWhere('note.userId = :userId', { userId: ps.userId })
+			.innerJoinAndSelect('note.user', 'user')
+			.leftJoinAndSelect('note.reply', 'reply')
+			.leftJoinAndSelect('note.renote', 'renote')
+			.leftJoinAndSelect('note.channel', 'channel')
+			.leftJoinAndSelect('reply.user', 'replyUser')
+			.leftJoinAndSelect('renote.user', 'renoteUser');
+
+		if (ps.withChannelNotes) {
+			if (!isSelf) query.andWhere(new Brackets(qb => {
+				qb.orWhere('note.channelId IS NULL');
+				qb.orWhere('channel.isSensitive = false');
+			}));
+		} else {
+			query.andWhere('note.channelId IS NULL');
+		}
+
+		this.queryService.generateVisibilityQuery(query, me);
+		if (me) {
+			this.queryService.generateMutedUserQuery(query, me, { id: ps.userId });
+			this.queryService.generateBlockedUserQuery(query, me);
+		}
+
+		if (ps.withFiles) {
+			query.andWhere('note.fileIds != \'{}\'');
+		}
+
+		if (ps.withRenotes === false) {
+			query.andWhere(new Brackets(qb => {
+				qb.orWhere('note.userId != :userId', { userId: ps.userId });
+				qb.orWhere('note.renoteId IS NULL');
+				qb.orWhere('note.text IS NOT NULL');
+				qb.orWhere('note.fileIds != \'{}\'');
+				qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)');
+			}));
+		}
+
+		return await query.limit(ps.limit).getMany();
+	}
 }
diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue
index 2c59e6d4e8..57348cde53 100644
--- a/packages/frontend/src/components/MkPagination.vue
+++ b/packages/frontend/src/components/MkPagination.vue
@@ -206,6 +206,7 @@ async function init(): Promise<void> {
 	await os.api(props.pagination.endpoint, {
 		...params,
 		limit: props.pagination.limit ?? 10,
+		allowPartial: true,
 	}).then(res => {
 		for (let i = 0; i < res.length; i++) {
 			const item = res[i];

From 238e8ce93967c19d48e7b07c55d1faef4e64cb56 Mon Sep 17 00:00:00 2001
From: MeiMei <30769358+mei23@users.noreply.github.com>
Date: Sat, 2 Dec 2023 19:32:30 +0900
Subject: [PATCH 093/435] fix: Filter featured collection (#12541)

---
 packages/backend/src/core/NotePiningService.ts          | 4 ++--
 packages/backend/src/server/ActivityPubServerService.ts | 5 +++--
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/packages/backend/src/core/NotePiningService.ts b/packages/backend/src/core/NotePiningService.ts
index 52abb4c2a1..74e53c5c46 100644
--- a/packages/backend/src/core/NotePiningService.ts
+++ b/packages/backend/src/core/NotePiningService.ts
@@ -77,7 +77,7 @@ export class NotePiningService {
 		} as MiUserNotePining);
 
 		// Deliver to remote followers
-		if (this.userEntityService.isLocalUser(user)) {
+		if (this.userEntityService.isLocalUser(user) && !note.localOnly && ['public', 'home'].includes(note.visibility)) {
 			this.deliverPinnedChange(user.id, note.id, true);
 		}
 	}
@@ -105,7 +105,7 @@ export class NotePiningService {
 		});
 
 		// Deliver to remote followers
-		if (this.userEntityService.isLocalUser(user)) {
+		if (this.userEntityService.isLocalUser(user) && !note.localOnly && ['public', 'home'].includes(note.visibility)) {
 			this.deliverPinnedChange(user.id, noteId, false);
 		}
 	}
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 2c9e2cf24f..78d2daa403 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -370,8 +370,9 @@ export class ActivityPubServerService {
 			order: { id: 'DESC' },
 		});
 
-		const pinnedNotes = await Promise.all(pinings.map(pining =>
-			this.notesRepository.findOneByOrFail({ id: pining.noteId })));
+		const pinnedNotes = (await Promise.all(pinings.map(pining =>
+			this.notesRepository.findOneByOrFail({ id: pining.noteId }))))
+			.filter(note => !note.localOnly && ['public', 'home'].includes(note.visibility));
 
 		const renderedNotes = await Promise.all(pinnedNotes.map(note => this.apRendererService.renderNote(note)));
 

From 92029ac325c8cf74d2227beffb763ec3e6c01aea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 2 Dec 2023 20:11:31 +0900
Subject: [PATCH 094/435] fix: #12544 (#12545)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* meを渡し忘れている

* fix CHANGELOG.md

* Revert "fix CHANGELOG.md"

This reverts commit aaee4e9b8a6abf510f393bc02282f6ac016d2124.
---
 .../backend/src/server/api/endpoints/notes/hybrid-timeline.ts    | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index 820692626c..deb9e014c4 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -149,6 +149,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				sinceId,
 				limit: ps.limit,
 				allowPartial: ps.allowPartial,
+				me,
 				redisTimelines: timelineConfig,
 				useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
 				noteFilter: (note) => {

From 336416261a0a9f46467f90e93854d81ca01292d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 2 Dec 2023 21:00:05 +0900
Subject: [PATCH 095/435] =?UTF-8?q?=E3=83=90=E3=83=83=E3=82=AF=E3=82=A8?=
 =?UTF-8?q?=E3=83=B3=E3=83=89=E3=81=8C=E7=94=9F=E6=88=90=E3=81=99=E3=82=8B?=
 =?UTF-8?q?api.json=E3=81=8B=E3=82=89misskey-js=E3=81=AE=E5=9E=8B=E3=82=92?=
 =?UTF-8?q?=E4=BD=9C=E6=88=90=E3=81=99=E3=82=8B=20(#12434)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* ひとまず生成できるところまで

* ファイル構成整理

* 生成コマンド整理

* misskey-jsへの組み込み

* fix generator.ts

* wip

* fix generator.ts

* fix package.json

* 生成ロジックの調整

* 型レベルでのswitch-case機構をmisskey-jsからfrontendに持ち込めるようにした

* 型チェック用のtsconfig.jsonを作成

* 他のエンドポイントを呼ぶ関数にも適用

* 未使用エンティティなどを削除

* misskey-js側で手動定義されていた型を自動生成された型に移行(ただしapi.jsonがvalidでなくなってしまったので後で修正する)

* messagingは廃止されている(テストのビルドエラー解消)

* validなapi.jsonを出力できるように修正

* 修正漏れ対応

* Ajvに怒られて起動できなかったところを修正

* fix ci(途中)

* パラメータenumをやめる

* add command

* add api.json

* 都度自動生成をやめる

* 一気通貫スクリプト修正

* fix ci

* 生成ロジック修正

* フロントの型チェックは結局やらなかったので戻しておく

* fix pnpm-lock.yaml

* add README.md

---------

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 package.json                                  |     4 +-
 .../src/server/api/endpoints/admin/meta.ts    |    76 +
 .../api/endpoints/federation/instances.ts     |    29 +-
 .../backend/src/server/api/endpoints/meta.ts  |    27 +
 .../frontend/src/components/MkEmojiPicker.vue |     8 +-
 .../src/components/MkFeaturedPhotos.vue       |     2 +-
 .../src/components/MkInstanceCardMini.vue     |     6 +-
 .../frontend/src/components/MkInviteCode.vue  |     2 +-
 .../src/components/MkVisitorDashboard.vue     |     7 +-
 packages/frontend/src/custom-emojis.ts        |     6 +-
 packages/frontend/src/filters/user.ts         |     1 -
 packages/frontend/src/instance.ts             |     2 +-
 packages/frontend/src/pages/_error_.vue       |     2 +-
 packages/frontend/src/pages/about.emojis.vue  |     2 +-
 packages/frontend/src/pages/auth.form.vue     |     2 +-
 packages/frontend/src/pages/auth.vue          |     2 +-
 packages/frontend/src/pages/instance-info.vue |    15 +-
 packages/frontend/src/pages/invite.vue        |     2 +-
 .../frontend/src/pages/welcome.entrance.a.vue |    12 +-
 packages/frontend/src/scripts/api.ts          |    19 +-
 .../src/ui/_common_/statusbar-federation.vue  |     2 +-
 packages/misskey-js/etc/misskey-js.api.md     |  4501 ++-
 packages/misskey-js/generator/.eslintrc.cjs   |     9 +
 packages/misskey-js/generator/.gitignore      |     1 +
 packages/misskey-js/generator/README.md       |    19 +
 packages/misskey-js/generator/package.json    |    24 +
 .../misskey-js/generator/src/generator.ts     |   284 +
 packages/misskey-js/generator/tsconfig.json   |    20 +
 packages/misskey-js/package.json              |     6 +-
 packages/misskey-js/src/api.ts                |    62 +-
 packages/misskey-js/src/api.types.ts          |   703 +-
 packages/misskey-js/src/autogen/endpoint.ts   |   802 +
 packages/misskey-js/src/autogen/entities.ts   |   478 +
 packages/misskey-js/src/autogen/models.ts     |    39 +
 packages/misskey-js/src/autogen/types.ts      | 22560 ++++++++++++++++
 packages/misskey-js/src/entities.ts           |   610 +-
 packages/misskey-js/src/streaming.types.ts    |    25 +-
 packages/misskey-js/test-d/api.ts             |    10 +-
 packages/misskey-js/test-d/streaming.ts       |    14 -
 packages/misskey-js/tsconfig.json             |     2 +-
 pnpm-lock.yaml                                |   619 +-
 pnpm-workspace.yaml                           |     1 +
 42 files changed, 27053 insertions(+), 3964 deletions(-)
 create mode 100644 packages/misskey-js/generator/.eslintrc.cjs
 create mode 100644 packages/misskey-js/generator/.gitignore
 create mode 100644 packages/misskey-js/generator/README.md
 create mode 100644 packages/misskey-js/generator/package.json
 create mode 100644 packages/misskey-js/generator/src/generator.ts
 create mode 100644 packages/misskey-js/generator/tsconfig.json
 create mode 100644 packages/misskey-js/src/autogen/endpoint.ts
 create mode 100644 packages/misskey-js/src/autogen/entities.ts
 create mode 100644 packages/misskey-js/src/autogen/models.ts
 create mode 100644 packages/misskey-js/src/autogen/types.ts

diff --git a/package.json b/package.json
index fc328a650b..9fa094e049 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
 		"build-assets": "node ./scripts/build-assets.mjs",
 		"build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
 		"build-storybook": "pnpm --filter frontend build-storybook",
+		"build-misskey-js-with-types": "pnpm --filter backend build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build",
 		"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
 		"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
 		"init": "pnpm migrate",
@@ -57,7 +58,8 @@
 		"cross-env": "7.0.3",
 		"cypress": "13.6.0",
 		"eslint": "8.54.0",
-		"start-server-and-test": "2.0.3"
+		"start-server-and-test": "2.0.3",
+		"ncp": "2.0.0"
 	},
 	"optionalDependencies": {
 		"@tensorflow/tfjs-core": "4.4.0"
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 1dddb166ae..8774bcbb67 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -327,6 +327,82 @@ export const meta = {
 				type: 'number',
 				optional: false, nullable: false,
 			},
+			backgroundImageUrl: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			deeplAuthKey: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			deeplIsPro: {
+				type: 'boolean',
+				optional: false, nullable: false,
+			},
+			defaultDarkTheme: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			defaultLightTheme: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			description: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			disableRegistration: {
+				type: 'boolean',
+				optional: false, nullable: false,
+			},
+			impressumUrl: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			maintainerEmail: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			maintainerName: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			name: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			objectStorageS3ForcePathStyle: {
+				type: 'boolean',
+				optional: false, nullable: false,
+			},
+			privacyPolicyUrl: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			repositoryUrl: {
+				type: 'string',
+				optional: false, nullable: false,
+			},
+			summalyProxy: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			themeColor: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			tosUrl: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			uri: {
+				type: 'string',
+				optional: false, nullable: false,
+			},
+			version: {
+				type: 'string',
+				optional: false, nullable: false,
+			},
 		},
 	},
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts
index c8beefa9c7..e5a90715f5 100644
--- a/packages/backend/src/server/api/endpoints/federation/instances.ts
+++ b/packages/backend/src/server/api/endpoints/federation/instances.ts
@@ -36,13 +36,32 @@ export const paramDef = {
 		blocked: { type: 'boolean', nullable: true },
 		notResponding: { type: 'boolean', nullable: true },
 		suspended: { type: 'boolean', nullable: true },
-		silenced: { type: "boolean", nullable: true },
+		silenced: { type: 'boolean', nullable: true },
 		federating: { type: 'boolean', nullable: true },
 		subscribing: { type: 'boolean', nullable: true },
 		publishing: { type: 'boolean', nullable: true },
 		limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 },
 		offset: { type: 'integer', default: 0 },
-		sort: { type: 'string' },
+		sort: {
+			type: 'string',
+			nullable: true,
+			enum: [
+				'+pubSub',
+				'-pubSub',
+				'+notes',
+				'-notes',
+				'+users',
+				'-users',
+				'+following',
+				'-following',
+				'+followers',
+				'-followers',
+				'+firstRetrievedAt',
+				'-firstRetrievedAt',
+				'+latestRequestReceivedAt',
+				'-latestRequestReceivedAt',
+			],
+		},
 	},
 	required: [],
 } as const;
@@ -103,18 +122,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				}
 			}
 
-			if (typeof ps.silenced === "boolean") {
+			if (typeof ps.silenced === 'boolean') {
 				const meta = await this.metaService.fetch(true);
 
 				if (ps.silenced) {
 					if (meta.silencedHosts.length === 0) {
 						return [];
 					}
-					query.andWhere("instance.host IN (:...silences)", {
+					query.andWhere('instance.host IN (:...silences)', {
 						silences: meta.silencedHosts,
 					});
 				} else if (meta.silencedHosts.length > 0) {
-					query.andWhere("instance.host NOT IN (:...silences)", {
+					query.andWhere('instance.host NOT IN (:...silences)', {
 						silences: meta.silencedHosts,
 					});
 				}
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index 2727e4f093..9dd4553152 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -250,6 +250,33 @@ export const meta = {
 					},
 				},
 			},
+			backgroundImageUrl: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			impressumUrl: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			logoImageUrl: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			privacyPolicyUrl: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
+			serverRules: {
+				type: 'array',
+				optional: false, nullable: false,
+				items: {
+					type: 'string',
+				},
+			},
+			themeColor: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
 		},
 	},
 } as const;
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 603f676d5d..ecff2b5ace 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -149,7 +149,7 @@ const size = computed(() => props.asReactionPicker ? reactionPickerSize.value :
 const width = computed(() => props.asReactionPicker ? reactionPickerWidth.value : 3);
 const height = computed(() => props.asReactionPicker ? reactionPickerHeight.value : 2);
 const q = ref<string>('');
-const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]);
+const searchResultCustom = ref<Misskey.entities.EmojiSimple[]>([]);
 const searchResultUnicode = ref<UnicodeEmojiDef[]>([]);
 const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index');
 
@@ -196,7 +196,7 @@ watch(q, () => {
 	const searchCustom = () => {
 		const max = 100;
 		const emojis = customEmojis.value;
-		const matches = new Set<Misskey.entities.CustomEmoji>();
+		const matches = new Set<Misskey.entities.EmojiSimple>();
 
 		const exactMatch = emojis.find(emoji => emoji.name === newQ);
 		if (exactMatch) matches.add(exactMatch);
@@ -326,7 +326,7 @@ watch(q, () => {
 	searchResultUnicode.value = Array.from(searchUnicode());
 });
 
-function filterAvailable(emoji: Misskey.entities.CustomEmoji): boolean {
+function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean {
 	return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id)));
 }
 
@@ -343,7 +343,7 @@ function reset() {
 	q.value = '';
 }
 
-function getKey(emoji: string | Misskey.entities.CustomEmoji | UnicodeEmojiDef): string {
+function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef): string {
 	return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`;
 }
 
diff --git a/packages/frontend/src/components/MkFeaturedPhotos.vue b/packages/frontend/src/components/MkFeaturedPhotos.vue
index cef1943d5c..6d1bad7433 100644
--- a/packages/frontend/src/components/MkFeaturedPhotos.vue
+++ b/packages/frontend/src/components/MkFeaturedPhotos.vue
@@ -12,7 +12,7 @@ import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
 
-const meta = ref<Misskey.entities.DetailedInstanceMetadata>();
+const meta = ref<Misskey.entities.MetaResponse>();
 
 os.api('meta', { detail: true }).then(gotMeta => {
 	meta.value = gotMeta;
diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue
index e384b7a0bc..9710f779d5 100644
--- a/packages/frontend/src/components/MkInstanceCardMini.vue
+++ b/packages/frontend/src/components/MkInstanceCardMini.vue
@@ -21,15 +21,15 @@ import * as os from '@/os.js';
 import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
 
 const props = defineProps<{
-	instance: Misskey.entities.Instance;
+	instance: Misskey.entities.FederationInstance;
 }>();
 
 let chartValues = $ref<number[] | null>(null);
 
 os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
 	// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
-	res.requests.received.splice(0, 1);
-	chartValues = res.requests.received;
+	res['requests.received'].splice(0, 1);
+	chartValues = res['requests.received'];
 });
 
 function getInstanceIcon(instance): string {
diff --git a/packages/frontend/src/components/MkInviteCode.vue b/packages/frontend/src/components/MkInviteCode.vue
index ff3794ad18..84d797484d 100644
--- a/packages/frontend/src/components/MkInviteCode.vue
+++ b/packages/frontend/src/components/MkInviteCode.vue
@@ -67,7 +67,7 @@ import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
 
 const props = defineProps<{
-	invite: Misskey.entities.Invite;
+	invite: Misskey.entities.InviteCode;
 	moderator?: boolean;
 }>();
 
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 40493a5d06..3eb5c19660 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -67,15 +67,14 @@ import number from '@/filters/number.js';
 import MkNumber from '@/components/MkNumber.vue';
 import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
 
-let meta = $ref<Misskey.entities.Instance>();
-let stats = $ref(null);
+let meta = $ref<Misskey.entities.MetaResponse | null>(null);
+let stats = $ref<Misskey.entities.StatsResponse | null>(null);
 
 os.api('meta', { detail: true }).then(_meta => {
 	meta = _meta;
 });
 
-os.api('stats', {
-}).then((res) => {
+os.api('stats', {}).then((res) => {
 	stats = res;
 });
 
diff --git a/packages/frontend/src/custom-emojis.ts b/packages/frontend/src/custom-emojis.ts
index 8ecd1bd2eb..6a48159f13 100644
--- a/packages/frontend/src/custom-emojis.ts
+++ b/packages/frontend/src/custom-emojis.ts
@@ -10,7 +10,7 @@ import { useStream } from '@/stream.js';
 import { get, set } from '@/scripts/idb-proxy.js';
 
 const storageCache = await get('emojis');
-export const customEmojis = shallowRef<Misskey.entities.CustomEmoji[]>(Array.isArray(storageCache) ? storageCache : []);
+export const customEmojis = shallowRef<Misskey.entities.EmojiSimple[]>(Array.isArray(storageCache) ? storageCache : []);
 export const customEmojiCategories = computed<[ ...string[], null ]>(() => {
 	const categories = new Set<string>();
 	for (const emoji of customEmojis.value) {
@@ -21,7 +21,7 @@ export const customEmojiCategories = computed<[ ...string[], null ]>(() => {
 	return markRaw([...Array.from(categories), null]);
 });
 
-export const customEmojisMap = new Map<string, Misskey.entities.CustomEmoji>();
+export const customEmojisMap = new Map<string, Misskey.entities.EmojiSimple>();
 watch(customEmojis, emojis => {
 	customEmojisMap.clear();
 	for (const emoji of emojis) {
@@ -38,7 +38,7 @@ stream.on('emojiAdded', emojiData => {
 });
 
 stream.on('emojiUpdated', emojiData => {
-	customEmojis.value = customEmojis.value.map(item => emojiData.emojis.find(search => search.name === item.name) as Misskey.entities.CustomEmoji ?? item);
+	customEmojis.value = customEmojis.value.map(item => emojiData.emojis.find(search => search.name === item.name) as Misskey.entities.EmojiSimple ?? item);
 	set('emojis', customEmojis.value);
 });
 
diff --git a/packages/frontend/src/filters/user.ts b/packages/frontend/src/filters/user.ts
index a7a13bec6e..8d20603725 100644
--- a/packages/frontend/src/filters/user.ts
+++ b/packages/frontend/src/filters/user.ts
@@ -3,7 +3,6 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as Misskey from 'misskey-js';
 import * as Misskey from 'misskey-js';
 import { url } from '@/config.js';
 
diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts
index cbfd95951c..b09264dabb 100644
--- a/packages/frontend/src/instance.ts
+++ b/packages/frontend/src/instance.ts
@@ -15,7 +15,7 @@ const cached = miLocalStorage.getItem('instance');
 
 // TODO: instanceをリアクティブにするかは再考の余地あり
 
-export const instance: Misskey.entities.InstanceMetadata = reactive(cached ? JSON.parse(cached) : {
+export const instance: Misskey.entities.MetaResponse = reactive(cached ? JSON.parse(cached) : {
 	// TODO: set default values
 });
 
diff --git a/packages/frontend/src/pages/_error_.vue b/packages/frontend/src/pages/_error_.vue
index 7a3e2f444b..4821687ac3 100644
--- a/packages/frontend/src/pages/_error_.vue
+++ b/packages/frontend/src/pages/_error_.vue
@@ -44,7 +44,7 @@ const props = withDefaults(defineProps<{
 
 let loaded = $ref(false);
 let serverIsDead = $ref(false);
-let meta = $ref<Misskey.entities.LiteInstanceMetadata | null>(null);
+let meta = $ref<Misskey.entities.MetaResponse | null>(null);
 
 os.api('meta', {
 	detail: false,
diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue
index c9bb6f897e..4ae460f763 100644
--- a/packages/frontend/src/pages/about.emojis.vue
+++ b/packages/frontend/src/pages/about.emojis.vue
@@ -48,7 +48,7 @@ import { $i } from '@/account.js';
 
 const customEmojiTags = getCustomEmojiTags();
 let q = $ref('');
-let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null);
+let searchEmojis = $ref<Misskey.entities.EmojiSimple[]>(null);
 let selectedTags = $ref(new Set());
 
 function search() {
diff --git a/packages/frontend/src/pages/auth.form.vue b/packages/frontend/src/pages/auth.form.vue
index 3f6f58df55..9d39a1e603 100644
--- a/packages/frontend/src/pages/auth.form.vue
+++ b/packages/frontend/src/pages/auth.form.vue
@@ -27,7 +27,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
-	session: Misskey.entities.AuthSession;
+	session: Misskey.entities.AuthSessionShowResponse;
 }>();
 
 const emit = defineEmits<{
diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue
index 124323a483..bcd54a6ed5 100644
--- a/packages/frontend/src/pages/auth.vue
+++ b/packages/frontend/src/pages/auth.vue
@@ -56,7 +56,7 @@ const props = defineProps<{
 }>();
 
 let state = $ref<'waiting' | 'accepted' | 'fetch-session-error' | 'denied' | null>(null);
-let session = $ref<Misskey.entities.AuthSession | null>(null);
+let session = $ref<Misskey.entities.AuthSessionShowResponse | null>(null);
 
 function accepted() {
 	state = 'accepted';
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 1ed25c9a47..8706228fd1 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -144,8 +144,8 @@ const props = defineProps<{
 
 let tab = $ref('overview');
 let chartSrc = $ref('instance-requests');
-let meta = $ref<Misskey.entities.AdminInstanceMetadata | null>(null);
-let instance = $ref<Misskey.entities.Instance | null>(null);
+let meta = $ref<Misskey.entities.AdminMetaResponse | null>(null);
+let instance = $ref<Misskey.entities.FederationInstance | null>(null);
 let suspended = $ref(false);
 let isBlocked = $ref(false);
 let isSilenced = $ref(false);
@@ -169,10 +169,10 @@ async function fetch(): Promise<void> {
 	instance = await os.api('federation/show-instance', {
 		host: props.host,
 	});
-	suspended = instance.isSuspended;
-	isBlocked = instance.isBlocked;
-	isSilenced = instance.isSilenced;
-	faviconUrl = getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.iconUrl, 'preview');
+	suspended = instance?.isSuspended ?? false;
+	isBlocked = instance?.isBlocked ?? false;
+	isSilenced = instance?.isSilenced ?? false;
+	faviconUrl = getProxiedImageUrlNullable(instance?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance?.iconUrl, 'preview');
 }
 
 async function toggleBlock(): Promise<void> {
@@ -188,8 +188,9 @@ async function toggleSilenced(): Promise<void> {
 	if (!meta) throw new Error('No meta?');
 	if (!instance) throw new Error('No instance?');
 	const { host } = instance;
+	const silencedHosts = meta.silencedHosts ?? [];
 	await os.api('admin/update-meta', {
-		silencedHosts: isSilenced ? meta.silencedHosts.concat([host]) : meta.silencedHosts.filter(x => x !== host),
+		silencedHosts: isSilenced ? silencedHosts.concat([host]) : silencedHosts.filter(x => x !== host),
 	});
 }
 
diff --git a/packages/frontend/src/pages/invite.vue b/packages/frontend/src/pages/invite.vue
index b44b580e86..25ce38e0ef 100644
--- a/packages/frontend/src/pages/invite.vue
+++ b/packages/frontend/src/pages/invite.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<MkPagination ref="pagingComponent" :pagination="pagination">
 				<template #default="{ items }">
 					<div class="_gaps_s">
-						<MkInviteCode v-for="item in (items as Misskey.entities.Invite[])" :key="item.id" :invite="item" :onDeleted="deleted"/>
+						<MkInviteCode v-for="item in (items as Misskey.entities.InviteCode[])" :key="item.id" :invite="item" :onDeleted="deleted"/>
 					</div>
 				</template>
 			</MkPagination>
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index e1f2a0cbda..89d0eb9a83 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -48,11 +48,15 @@ import MkNumber from '@/components/MkNumber.vue';
 import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
 import { getProxiedImageUrl } from '@/scripts/media-proxy.js';
 
-let meta = $ref<Misskey.entities.Instance>();
-let instances = $ref<any[]>();
+let meta = $ref<Misskey.entities.MetaResponse>();
+let instances = $ref<Misskey.entities.FederationInstance[]>();
 
-function getInstanceIcon(instance): string {
-  return getProxiedImageUrl(instance.iconUrl, 'preview');
+function getInstanceIcon(instance: Misskey.entities.FederationInstance): string {
+	if (!instance.iconUrl) {
+		return '';
+	}
+
+	return getProxiedImageUrl(instance.iconUrl, 'preview');
 }
 
 os.api('meta', { detail: true }).then(_meta => {
diff --git a/packages/frontend/src/scripts/api.ts b/packages/frontend/src/scripts/api.ts
index 080977e5e4..0f54f779a6 100644
--- a/packages/frontend/src/scripts/api.ts
+++ b/packages/frontend/src/scripts/api.ts
@@ -10,7 +10,12 @@ import { $i } from '@/account.js';
 export const pendingApiRequestsCount = ref(0);
 
 // Implements Misskey.api.ApiClient.request
-export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal): Promise<Misskey.Endpoints[E]['res']> {
+export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
+	endpoint: E,
+	data: P = {} as any,
+	token?: string | null | undefined,
+	signal?: AbortSignal,
+): Promise<Misskey.api.SwitchCaseResponseType<E, P>> {
 	if (endpoint.includes('://')) throw new Error('invalid endpoint');
 	pendingApiRequestsCount.value++;
 
@@ -51,7 +56,12 @@ export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoin
 	return promise;
 }
 
-export function apiExternal<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(hostUrl: string, endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal): Promise<Misskey.Endpoints[E]['res']> {
+export function apiExternal<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
+	hostUrl: string,
+	endpoint: E, data: P = {} as any,
+	token?: string | null | undefined,
+	signal?: AbortSignal,
+): Promise<Misskey.api.SwitchCaseResponseType<E, P>> {
 	if (!/^https?:\/\//.test(hostUrl)) throw new Error('invalid host name');
 	if (endpoint.includes('://')) throw new Error('invalid endpoint');
 	pendingApiRequestsCount.value++;
@@ -95,7 +105,10 @@ export function apiExternal<E extends keyof Misskey.Endpoints, P extends Misskey
 }
 
 // Implements Misskey.api.ApiClient.request
-export function apiGet <E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(endpoint: E, data: P = {} as any): Promise<Misskey.Endpoints[E]['res']> {
+export function apiGet<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
+	endpoint: E,
+	data: P = {} as any,
+): Promise<Misskey.api.SwitchCaseResponseType<E, P>> {
 	pendingApiRequestsCount.value++;
 
 	const onFinally = () => {
diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue
index ff980e0d88..a4ea916d23 100644
--- a/packages/frontend/src/ui/_common_/statusbar-federation.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue
@@ -47,7 +47,7 @@ const props = defineProps<{
 	refreshIntervalSec?: number;
 }>();
 
-const instances = ref<Misskey.entities.Instance[]>([]);
+const instances = ref<Misskey.entities.FederationInstance[]>([]);
 const fetching = ref(true);
 let key = $ref(0);
 
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index dc93c4be3b..4e6e2adc02 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -21,57 +21,326 @@ declare namespace acct {
 }
 export { acct }
 
-// Warning: (ae-forgotten-export) The symbol "TODO_2" needs to be exported by the entry point index.d.ts
+// Warning: (ae-forgotten-export) The symbol "operations" needs to be exported by the entry point index.d.ts
 //
 // @public (undocumented)
-type Ad = TODO_2;
+type AdminAbuseUserReportsRequest = operations['admin/abuse-user-reports']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type AdminInstanceMetadata = DetailedInstanceMetadata & {
-    blockedHosts: string[];
-    silencedHosts: string[];
-    app192IconUrl: string | null;
-    app512IconUrl: string | null;
-    manifestJsonOverride: string;
-};
+type AdminAbuseUserReportsResponse = operations['admin/abuse-user-reports']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type Announcement = {
-    id: ID;
-    createdAt: DateString;
-    updatedAt: DateString | null;
-    text: string;
-    title: string;
-    imageUrl: string | null;
-    display: 'normal' | 'banner' | 'dialog';
-    icon: 'info' | 'warning' | 'error' | 'success';
-    needConfirmationToRead: boolean;
-    forYou: boolean;
-    isRead?: boolean;
-};
+type AdminAccountsCreateRequest = operations['admin/accounts/create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type Antenna = {
-    id: ID;
-    createdAt: DateString;
-    name: string;
-    keywords: string[][];
-    excludeKeywords: string[][];
-    src: 'home' | 'all' | 'users' | 'list' | 'group';
-    userListId: ID | null;
-    userGroupId: ID | null;
-    users: string[];
-    caseSensitive: boolean;
-    localOnly: boolean;
-    notify: boolean;
-    withReplies: boolean;
-    withFile: boolean;
-    hasUnreadNote: boolean;
-};
+type AdminAccountsCreateResponse = operations['admin/accounts/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAccountsDeleteRequest = operations['admin/accounts/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAccountsFindByEmailRequest = operations['admin/accounts/find-by-email']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAdCreateRequest = operations['admin/ad/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAdDeleteRequest = operations['admin/ad/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAdListRequest = operations['admin/ad/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAdUpdateRequest = operations['admin/ad/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAnnouncementsCreateRequest = operations['admin/announcements/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAnnouncementsCreateResponse = operations['admin/announcements/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAnnouncementsDeleteRequest = operations['admin/announcements/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAnnouncementsListRequest = operations['admin/announcements/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAnnouncementsListResponse = operations['admin/announcements/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAnnouncementsUpdateRequest = operations['admin/announcements/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAvatarDecorationsCreateRequest = operations['admin/avatar-decorations/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAvatarDecorationsDeleteRequest = operations['admin/avatar-decorations/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAvatarDecorationsListRequest = operations['admin/avatar-decorations/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAvatarDecorationsListResponse = operations['admin/avatar-decorations/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminAvatarDecorationsUpdateRequest = operations['admin/avatar-decorations/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminDeleteAccountRequest = operations['admin/delete-account']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminDeleteAccountResponse = operations['admin/delete-account']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminDeleteAllFilesOfAUserRequest = operations['admin/delete-all-files-of-a-user']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminDriveFilesRequest = operations['admin/drive/files']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminDriveFilesResponse = operations['admin/drive/files']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminDriveShowFileRequest = operations['admin/drive/show-file']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminDriveShowFileResponse = operations['admin/drive/show-file']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiCopyResponse = operations['admin/emoji/copy']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiDeleteRequest = operations['admin/emoji/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiListRemoteRequest = operations['admin/emoji/list-remote']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiListRemoteResponse = operations['admin/emoji/list-remote']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiListRequest = operations['admin/emoji/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiListResponse = operations['admin/emoji/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiRemoveAliasesBulkRequest = operations['admin/emoji/remove-aliases-bulk']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiSetAliasesBulkRequest = operations['admin/emoji/set-aliases-bulk']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiSetCategoryBulkRequest = operations['admin/emoji/set-category-bulk']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiSetLicenseBulkRequest = operations['admin/emoji/set-license-bulk']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminEmojiUpdateRequest = operations['admin/emoji/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminFederationDeleteAllFilesRequest = operations['admin/federation/delete-all-files']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminFederationRefreshRemoteInstanceMetadataRequest = operations['admin/federation/refresh-remote-instance-metadata']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminFederationRemoveAllFollowingRequest = operations['admin/federation/remove-all-following']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminFederationUpdateInstanceRequest = operations['admin/federation/update-instance']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminGetTableStatsResponse = operations['admin/get-table-stats']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminInviteCreateRequest = operations['admin/invite/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminInviteCreateResponse = operations['admin/invite/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminInviteListRequest = operations['admin/invite/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminInviteListResponse = operations['admin/invite/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminMetaResponse = operations['admin/meta']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminPromoCreateRequest = operations['admin/promo/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminQueueDeliverDelayedResponse = operations['admin/queue/deliver-delayed']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminQueueInboxDelayedResponse = operations['admin/queue/inbox-delayed']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminQueuePromoteRequest = operations['admin/queue/promote']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminQueueStatsResponse = operations['admin/queue/stats']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRelaysAddRequest = operations['admin/relays/add']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRelaysAddResponse = operations['admin/relays/add']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRelaysListResponse = operations['admin/relays/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRelaysRemoveRequest = operations['admin/relays/remove']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminResetPasswordRequest = operations['admin/reset-password']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminResetPasswordResponse = operations['admin/reset-password']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminResolveAbuseUserReportRequest = operations['admin/resolve-abuse-user-report']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRolesAssignRequest = operations['admin/roles/assign']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRolesCreateRequest = operations['admin/roles/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRolesDeleteRequest = operations['admin/roles/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRolesShowRequest = operations['admin/roles/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRolesUnassignRequest = operations['admin/roles/unassign']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRolesUpdateDefaultPoliciesRequest = operations['admin/roles/update-default-policies']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRolesUpdateRequest = operations['admin/roles/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminRolesUsersRequest = operations['admin/roles/users']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminSendEmailRequest = operations['admin/send-email']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminServerInfoResponse = operations['admin/server-info']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminShowModerationLogsRequest = operations['admin/show-moderation-logs']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminShowModerationLogsResponse = operations['admin/show-moderation-logs']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminShowUserRequest = operations['admin/show-user']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminShowUserResponse = operations['admin/show-user']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminShowUsersRequest = operations['admin/show-users']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminShowUsersResponse = operations['admin/show-users']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AdminSuspendUserRequest = operations['admin/suspend-user']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminUnsetUserAvatarRequest = operations['admin/unset-user-avatar']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminUnsetUserBannerRequest = operations['admin/unset-user-banner']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminUnsuspendUserRequest = operations['admin/unsuspend-user']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminUpdateMetaRequest = operations['admin/update-meta']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AdminUpdateUserNoteRequest = operations['admin/update-user-note']['requestBody']['content']['application/json'];
+
+// Warning: (ae-forgotten-export) The symbol "components" needs to be exported by the entry point index.d.ts
+//
+// @public (undocumented)
+type Announcement = components['schemas']['Announcement'];
+
+// @public (undocumented)
+type AnnouncementsRequest = operations['announcements']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AnnouncementsResponse = operations['announcements']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type Antenna = components['schemas']['Antenna'];
+
+// @public (undocumented)
+type AntennasCreateRequest = operations['antennas/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AntennasCreateResponse = operations['antennas/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AntennasDeleteRequest = operations['antennas/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AntennasListResponse = operations['antennas/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AntennasNotesRequest = operations['antennas/notes']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AntennasNotesResponse = operations['antennas/notes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AntennasShowRequest = operations['antennas/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AntennasShowResponse = operations['antennas/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AntennasUpdateRequest = operations['antennas/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AntennasUpdateResponse = operations['antennas/update']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ApGetRequest = operations['ap/get']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ApGetResponse = operations['ap/get']['responses']['200']['content']['application/json'];
 
 declare namespace api {
     export {
         isAPIError,
+        SwitchCaseResponseType,
         APIError,
         FetchLike,
         APIClient
@@ -92,16 +361,8 @@ class APIClient {
     fetch: FetchLike;
     // (undocumented)
     origin: string;
-    // Warning: (ae-forgotten-export) The symbol "IsCaseMatched" needs to be exported by the entry point index.d.ts
-    // Warning: (ae-forgotten-export) The symbol "GetCaseResult" needs to be exported by the entry point index.d.ts
-    //
     // (undocumented)
-    request<E extends keyof Endpoints, P extends Endpoints[E]['req']>(endpoint: E, params?: P, credential?: string | null | undefined): Promise<Endpoints[E]['res'] extends {
-        $switch: {
-            $cases: [any, any][];
-            $default: any;
-        };
-    } ? IsCaseMatched<E, P, 0> extends true ? GetCaseResult<E, P, 0> : IsCaseMatched<E, P, 1> extends true ? GetCaseResult<E, P, 1> : IsCaseMatched<E, P, 2> extends true ? GetCaseResult<E, P, 2> : IsCaseMatched<E, P, 3> extends true ? GetCaseResult<E, P, 3> : IsCaseMatched<E, P, 4> extends true ? GetCaseResult<E, P, 4> : IsCaseMatched<E, P, 5> extends true ? GetCaseResult<E, P, 5> : IsCaseMatched<E, P, 6> extends true ? GetCaseResult<E, P, 6> : IsCaseMatched<E, P, 7> extends true ? GetCaseResult<E, P, 7> : IsCaseMatched<E, P, 8> extends true ? GetCaseResult<E, P, 8> : IsCaseMatched<E, P, 9> extends true ? GetCaseResult<E, P, 9> : Endpoints[E]['res']['$switch']['$default'] : Endpoints[E]['res']>;
+    request<E extends keyof Endpoints, P extends Endpoints[E]['req']>(endpoint: E, params?: P, credential?: string | null): Promise<SwitchCaseResponseType<E, P>>;
 }
 
 // @public (undocumented)
@@ -114,41 +375,67 @@ type APIError = {
 };
 
 // @public (undocumented)
-type App = TODO_2;
+type App = components['schemas']['App'];
 
 // @public (undocumented)
-type AuthSession = {
-    id: ID;
-    app: App;
-    token: string;
-};
+type AppCreateRequest = operations['app/create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type Blocking = {
-    id: ID;
-    createdAt: DateString;
-    blockeeId: User['id'];
-    blockee: UserDetailed;
-};
+type AppCreateResponse = operations['app/create']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type Channel = {
-    id: ID;
-    lastNotedAt: Date | null;
-    userId: User['id'] | null;
-    user: User | null;
-    name: string;
-    description: string | null;
-    bannerId: DriveFile['id'] | null;
-    banner: DriveFile | null;
-    pinnedNoteIds: string[];
-    color: string;
-    isArchived: boolean;
-    notesCount: number;
-    usersCount: number;
-    isSensitive: boolean;
-    allowRenoteToExternal: boolean;
-};
+type AppShowRequest = operations['app/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AppShowResponse = operations['app/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ApShowRequest = operations['ap/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ApShowResponse = operations['ap/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AuthSessionGenerateRequest = operations['auth/session/generate']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AuthSessionGenerateResponse = operations['auth/session/generate']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AuthSessionShowRequest = operations['auth/session/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AuthSessionShowResponse = operations['auth/session/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type AuthSessionUserkeyRequest = operations['auth/session/userkey']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AuthSessionUserkeyResponse = operations['auth/session/userkey']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type Blocking = components['schemas']['Blocking'];
+
+// @public (undocumented)
+type BlockingCreateRequest = operations['blocking/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type BlockingCreateResponse = operations['blocking/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type BlockingDeleteRequest = operations['blocking/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type BlockingDeleteResponse = operations['blocking/delete']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type BlockingListRequest = operations['blocking/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type BlockingListResponse = operations['blocking/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type Channel = components['schemas']['Channel'];
 
 // Warning: (ae-forgotten-export) The symbol "AnyOf" needs to be exported by the entry point index.d.ts
 //
@@ -197,9 +484,6 @@ export type Channels = {
             readAllUnreadMentions: () => void;
             unreadSpecifiedNote: (payload: Note['id']) => void;
             readAllUnreadSpecifiedNotes: () => void;
-            readAllMessagingMessages: () => void;
-            messagingMessage: (payload: MessagingMessage) => void;
-            unreadMessagingMessage: (payload: MessagingMessage) => void;
             readAllAntennas: () => void;
             unreadAntenna: (payload: Antenna) => void;
             readAllAnnouncements: () => void;
@@ -245,23 +529,6 @@ export type Channels = {
         };
         receives: null;
     };
-    messaging: {
-        params: {
-            otherparty?: User['id'] | null;
-            group?: UserGroup['id'] | null;
-        };
-        events: {
-            message: (payload: MessagingMessage) => void;
-            deleted: (payload: MessagingMessage['id']) => void;
-            read: (payload: MessagingMessage['id'][]) => void;
-            typers: (payload: User[]) => void;
-        };
-        receives: {
-            read: {
-                id: MessagingMessage['id'];
-            };
-        };
-    };
     serverStats: {
         params: null;
         events: {
@@ -289,1961 +556,333 @@ export type Channels = {
 };
 
 // @public (undocumented)
-type Clip = TODO_2;
+type ChannelsCreateRequest = operations['channels/create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type CustomEmoji = {
-    id: string;
-    name: string;
-    url: string;
-    category: string;
-    aliases: string[];
-};
+type ChannelsCreateResponse = operations['channels/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsFavoriteRequest = operations['channels/favorite']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsFeaturedResponse = operations['channels/featured']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsFollowedRequest = operations['channels/followed']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsFollowedResponse = operations['channels/followed']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsFollowRequest = operations['channels/follow']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsMyFavoritesResponse = operations['channels/my-favorites']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsOwnedRequest = operations['channels/owned']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsOwnedResponse = operations['channels/owned']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsSearchRequest = operations['channels/search']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsSearchResponse = operations['channels/search']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsShowRequest = operations['channels/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsShowResponse = operations['channels/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsTimelineRequest = operations['channels/timeline']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsTimelineResponse = operations['channels/timeline']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsUnfavoriteRequest = operations['channels/unfavorite']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsUnfollowRequest = operations['channels/unfollow']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsUpdateRequest = operations['channels/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChannelsUpdateResponse = operations['channels/update']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsActiveUsersRequest = operations['charts/active-users']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsActiveUsersResponse = operations['charts/active-users']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsApRequestRequest = operations['charts/ap-request']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsApRequestResponse = operations['charts/ap-request']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsDriveRequest = operations['charts/drive']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsDriveResponse = operations['charts/drive']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsFederationRequest = operations['charts/federation']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsFederationResponse = operations['charts/federation']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsInstanceRequest = operations['charts/instance']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsInstanceResponse = operations['charts/instance']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsNotesRequest = operations['charts/notes']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsNotesResponse = operations['charts/notes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUserDriveRequest = operations['charts/user/drive']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUserDriveResponse = operations['charts/user/drive']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUserFollowingRequest = operations['charts/user/following']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUserFollowingResponse = operations['charts/user/following']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUserNotesRequest = operations['charts/user/notes']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUserNotesResponse = operations['charts/user/notes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUserPvRequest = operations['charts/user/pv']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUserPvResponse = operations['charts/user/pv']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUserReactionsRequest = operations['charts/user/reactions']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUserReactionsResponse = operations['charts/user/reactions']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUsersRequest = operations['charts/users']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ChartsUsersResponse = operations['charts/users']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type Clip = components['schemas']['Clip'];
+
+// @public (undocumented)
+type ClipsAddNoteRequest = operations['clips/add-note']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsCreateRequest = operations['clips/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsCreateResponse = operations['clips/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsDeleteRequest = operations['clips/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsFavoriteRequest = operations['clips/favorite']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsListResponse = operations['clips/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsMyFavoritesResponse = operations['clips/my-favorites']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsNotesRequest = operations['clips/notes']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsNotesResponse = operations['clips/notes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsRemoveNoteRequest = operations['clips/remove-note']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsShowRequest = operations['clips/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsShowResponse = operations['clips/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsUnfavoriteRequest = operations['clips/unfavorite']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsUpdateRequest = operations['clips/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ClipsUpdateResponse = operations['clips/update']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type DateString = string;
 
 // @public (undocumented)
-type DetailedInstanceMetadata = LiteInstanceMetadata & {
-    pinnedPages: string[];
-    pinnedClipId: string | null;
-    cacheRemoteFiles: boolean;
-    cacheRemoteSensitiveFiles: boolean;
-    requireSetup: boolean;
-    proxyAccountName: string | null;
-    features: Record<string, any>;
-};
+type DriveFile = components['schemas']['DriveFile'];
 
 // @public (undocumented)
-type DriveFile = {
-    id: ID;
-    createdAt: DateString;
-    isSensitive: boolean;
-    name: string;
-    thumbnailUrl: string;
-    url: string;
-    type: string;
-    size: number;
-    md5: string;
-    blurhash: string;
-    comment: string | null;
-    properties: Record<string, any>;
-};
+type DriveFilesAttachedNotesRequest = operations['drive/files/attached-notes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type DriveFolder = TODO_2;
+type DriveFilesAttachedNotesResponse = operations['drive/files/attached-notes']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-export type Endpoints = {
-    'admin/abuse-user-reports': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/delete-all-files-of-a-user': {
-        req: {
-            userId: User['id'];
-        };
-        res: null;
-    };
-    'admin/unset-user-avatar': {
-        req: {
-            userId: User['id'];
-        };
-        res: null;
-    };
-    'admin/unset-user-banner': {
-        req: {
-            userId: User['id'];
-        };
-        res: null;
-    };
-    'admin/delete-logs': {
-        req: NoParams;
-        res: null;
-    };
-    'admin/get-index-stats': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/get-table-stats': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/invite': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/logs': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/meta': {
-        req: NoParams;
-        res: AdminInstanceMetadata;
-    };
-    'admin/reset-password': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/resolve-abuse-user-report': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/resync-chart': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/send-email': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/server-info': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/show-moderation-logs': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/show-user': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/show-users': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/silence-user': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/suspend-user': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/unsilence-user': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/unsuspend-user': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/update-meta': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/vacuum': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/accounts/create': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/ad/create': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/ad/delete': {
-        req: {
-            id: Ad['id'];
-        };
-        res: null;
-    };
-    'admin/ad/list': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/ad/update': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/announcements/create': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/announcements/delete': {
-        req: {
-            id: Announcement['id'];
-        };
-        res: null;
-    };
-    'admin/announcements/list': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/announcements/update': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/drive/clean-remote-files': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/drive/cleanup': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/drive/files': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/drive/show-file': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/emoji/add': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/emoji/copy': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/emoji/list-remote': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/emoji/list': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/emoji/remove': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/emoji/update': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/federation/delete-all-files': {
-        req: {
-            host: string;
-        };
-        res: null;
-    };
-    'admin/federation/refresh-remote-instance-metadata': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/federation/remove-all-following': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/federation/update-instance': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/invite/create': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/invite/list': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/moderators/add': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/moderators/remove': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/promo/create': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/queue/clear': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/queue/deliver-delayed': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/queue/inbox-delayed': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/queue/jobs': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/queue/stats': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/relays/add': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/relays/list': {
-        req: TODO;
-        res: TODO;
-    };
-    'admin/relays/remove': {
-        req: TODO;
-        res: TODO;
-    };
-    'announcements': {
-        req: {
-            limit?: number;
-            withUnreads?: boolean;
-            sinceId?: Announcement['id'];
-            untilId?: Announcement['id'];
-        };
-        res: Announcement[];
-    };
-    'antennas/create': {
-        req: TODO;
-        res: Antenna;
-    };
-    'antennas/delete': {
-        req: {
-            antennaId: Antenna['id'];
-        };
-        res: null;
-    };
-    'antennas/list': {
-        req: NoParams;
-        res: Antenna[];
-    };
-    'antennas/notes': {
-        req: {
-            antennaId: Antenna['id'];
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-        };
-        res: Note[];
-    };
-    'antennas/show': {
-        req: {
-            antennaId: Antenna['id'];
-        };
-        res: Antenna;
-    };
-    'antennas/update': {
-        req: TODO;
-        res: Antenna;
-    };
-    'ap/get': {
-        req: {
-            uri: string;
-        };
-        res: Record<string, any>;
-    };
-    'ap/show': {
-        req: {
-            uri: string;
-        };
-        res: {
-            type: 'Note';
-            object: Note;
-        } | {
-            type: 'User';
-            object: UserDetailed;
-        };
-    };
-    'app/create': {
-        req: TODO;
-        res: App;
-    };
-    'app/show': {
-        req: {
-            appId: App['id'];
-        };
-        res: App;
-    };
-    'auth/accept': {
-        req: {
-            token: string;
-        };
-        res: null;
-    };
-    'auth/session/generate': {
-        req: {
-            appSecret: string;
-        };
-        res: {
-            token: string;
-            url: string;
-        };
-    };
-    'auth/session/show': {
-        req: {
-            token: string;
-        };
-        res: AuthSession;
-    };
-    'auth/session/userkey': {
-        req: {
-            appSecret: string;
-            token: string;
-        };
-        res: {
-            accessToken: string;
-            user: User;
-        };
-    };
-    'blocking/create': {
-        req: {
-            userId: User['id'];
-        };
-        res: UserDetailed;
-    };
-    'blocking/delete': {
-        req: {
-            userId: User['id'];
-        };
-        res: UserDetailed;
-    };
-    'blocking/list': {
-        req: {
-            limit?: number;
-            sinceId?: Blocking['id'];
-            untilId?: Blocking['id'];
-        };
-        res: Blocking[];
-    };
-    'channels/create': {
-        req: TODO;
-        res: TODO;
-    };
-    'channels/featured': {
-        req: TODO;
-        res: TODO;
-    };
-    'channels/follow': {
-        req: TODO;
-        res: TODO;
-    };
-    'channels/followed': {
-        req: TODO;
-        res: TODO;
-    };
-    'channels/owned': {
-        req: TODO;
-        res: TODO;
-    };
-    'channels/pin-note': {
-        req: TODO;
-        res: TODO;
-    };
-    'channels/show': {
-        req: TODO;
-        res: TODO;
-    };
-    'channels/timeline': {
-        req: TODO;
-        res: TODO;
-    };
-    'channels/unfollow': {
-        req: TODO;
-        res: TODO;
-    };
-    'channels/update': {
-        req: TODO;
-        res: TODO;
-    };
-    'charts/active-users': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-        };
-        res: {
-            local: {
-                users: number[];
-            };
-            remote: {
-                users: number[];
-            };
-        };
-    };
-    'charts/drive': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-        };
-        res: {
-            local: {
-                decCount: number[];
-                decSize: number[];
-                incCount: number[];
-                incSize: number[];
-                totalCount: number[];
-                totalSize: number[];
-            };
-            remote: {
-                decCount: number[];
-                decSize: number[];
-                incCount: number[];
-                incSize: number[];
-                totalCount: number[];
-                totalSize: number[];
-            };
-        };
-    };
-    'charts/federation': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-        };
-        res: {
-            instance: {
-                dec: number[];
-                inc: number[];
-                total: number[];
-            };
-        };
-    };
-    'charts/hashtag': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-        };
-        res: TODO;
-    };
-    'charts/instance': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-            host: string;
-        };
-        res: {
-            drive: {
-                decFiles: number[];
-                decUsage: number[];
-                incFiles: number[];
-                incUsage: number[];
-                totalFiles: number[];
-                totalUsage: number[];
-            };
-            followers: {
-                dec: number[];
-                inc: number[];
-                total: number[];
-            };
-            following: {
-                dec: number[];
-                inc: number[];
-                total: number[];
-            };
-            notes: {
-                dec: number[];
-                inc: number[];
-                total: number[];
-                diffs: {
-                    normal: number[];
-                    renote: number[];
-                    reply: number[];
-                };
-            };
-            requests: {
-                failed: number[];
-                received: number[];
-                succeeded: number[];
-            };
-            users: {
-                dec: number[];
-                inc: number[];
-                total: number[];
-            };
-        };
-    };
-    'charts/network': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-        };
-        res: TODO;
-    };
-    'charts/notes': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-        };
-        res: {
-            local: {
-                dec: number[];
-                inc: number[];
-                total: number[];
-                diffs: {
-                    normal: number[];
-                    renote: number[];
-                    reply: number[];
-                };
-            };
-            remote: {
-                dec: number[];
-                inc: number[];
-                total: number[];
-                diffs: {
-                    normal: number[];
-                    renote: number[];
-                    reply: number[];
-                };
-            };
-        };
-    };
-    'charts/user/drive': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-            userId: User['id'];
-        };
-        res: {
-            decCount: number[];
-            decSize: number[];
-            incCount: number[];
-            incSize: number[];
-            totalCount: number[];
-            totalSize: number[];
-        };
-    };
-    'charts/user/following': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-            userId: User['id'];
-        };
-        res: TODO;
-    };
-    'charts/user/notes': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-            userId: User['id'];
-        };
-        res: {
-            dec: number[];
-            inc: number[];
-            total: number[];
-            diffs: {
-                normal: number[];
-                renote: number[];
-                reply: number[];
-            };
-        };
-    };
-    'charts/user/reactions': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-            userId: User['id'];
-        };
-        res: TODO;
-    };
-    'charts/users': {
-        req: {
-            span: 'day' | 'hour';
-            limit?: number;
-            offset?: number | null;
-        };
-        res: {
-            local: {
-                dec: number[];
-                inc: number[];
-                total: number[];
-            };
-            remote: {
-                dec: number[];
-                inc: number[];
-                total: number[];
-            };
-        };
-    };
-    'clips/add-note': {
-        req: TODO;
-        res: TODO;
-    };
-    'clips/create': {
-        req: TODO;
-        res: TODO;
-    };
-    'clips/delete': {
-        req: {
-            clipId: Clip['id'];
-        };
-        res: null;
-    };
-    'clips/list': {
-        req: TODO;
-        res: TODO;
-    };
-    'clips/notes': {
-        req: TODO;
-        res: TODO;
-    };
-    'clips/show': {
-        req: TODO;
-        res: TODO;
-    };
-    'clips/update': {
-        req: TODO;
-        res: TODO;
-    };
-    'drive': {
-        req: NoParams;
-        res: {
-            capacity: number;
-            usage: number;
-        };
-    };
-    'drive/files': {
-        req: {
-            folderId?: DriveFolder['id'] | null;
-            type?: DriveFile['type'] | null;
-            limit?: number;
-            sinceId?: DriveFile['id'];
-            untilId?: DriveFile['id'];
-        };
-        res: DriveFile[];
-    };
-    'drive/files/attached-notes': {
-        req: TODO;
-        res: TODO;
-    };
-    'drive/files/check-existence': {
-        req: TODO;
-        res: TODO;
-    };
-    'drive/files/create': {
-        req: {
-            folderId?: string;
-            name?: string;
-            comment?: string;
-            isSentisive?: boolean;
-            force?: boolean;
-        };
-        res: DriveFile;
-    };
-    'drive/files/delete': {
-        req: {
-            fileId: DriveFile['id'];
-        };
-        res: null;
-    };
-    'drive/files/find-by-hash': {
-        req: TODO;
-        res: TODO;
-    };
-    'drive/files/find': {
-        req: {
-            name: string;
-            folderId?: DriveFolder['id'] | null;
-        };
-        res: DriveFile[];
-    };
-    'drive/files/show': {
-        req: {
-            fileId?: DriveFile['id'];
-            url?: string;
-        };
-        res: DriveFile;
-    };
-    'drive/files/update': {
-        req: {
-            fileId: DriveFile['id'];
-            folderId?: DriveFolder['id'] | null;
-            name?: string;
-            isSensitive?: boolean;
-            comment?: string | null;
-        };
-        res: DriveFile;
-    };
-    'drive/files/upload-from-url': {
-        req: {
-            url: string;
-            folderId?: DriveFolder['id'] | null;
-            isSensitive?: boolean;
-            comment?: string | null;
-            marker?: string | null;
-            force?: boolean;
-        };
-        res: null;
-    };
-    'drive/folders': {
-        req: {
-            folderId?: DriveFolder['id'] | null;
-            limit?: number;
-            sinceId?: DriveFile['id'];
-            untilId?: DriveFile['id'];
-        };
-        res: DriveFolder[];
-    };
-    'drive/folders/create': {
-        req: {
-            name?: string;
-            parentId?: DriveFolder['id'] | null;
-        };
-        res: DriveFolder;
-    };
-    'drive/folders/delete': {
-        req: {
-            folderId: DriveFolder['id'];
-        };
-        res: null;
-    };
-    'drive/folders/find': {
-        req: {
-            name: string;
-            parentId?: DriveFolder['id'] | null;
-        };
-        res: DriveFolder[];
-    };
-    'drive/folders/show': {
-        req: {
-            folderId: DriveFolder['id'];
-        };
-        res: DriveFolder;
-    };
-    'drive/folders/update': {
-        req: {
-            folderId: DriveFolder['id'];
-            name?: string;
-            parentId?: DriveFolder['id'] | null;
-        };
-        res: DriveFolder;
-    };
-    'drive/stream': {
-        req: {
-            type?: DriveFile['type'] | null;
-            limit?: number;
-            sinceId?: DriveFile['id'];
-            untilId?: DriveFile['id'];
-        };
-        res: DriveFile[];
-    };
-    'endpoint': {
-        req: {
-            endpoint: string;
-        };
-        res: {
-            params: {
-                name: string;
-                type: string;
-            }[];
-        };
-    };
-    'endpoints': {
-        req: NoParams;
-        res: string[];
-    };
-    'federation/dns': {
-        req: {
-            host: string;
-        };
-        res: {
-            a: string[];
-            aaaa: string[];
-            cname: string[];
-            txt: string[];
-        };
-    };
-    'federation/followers': {
-        req: {
-            host: string;
-            limit?: number;
-            sinceId?: Following['id'];
-            untilId?: Following['id'];
-        };
-        res: FollowingFolloweePopulated[];
-    };
-    'federation/following': {
-        req: {
-            host: string;
-            limit?: number;
-            sinceId?: Following['id'];
-            untilId?: Following['id'];
-        };
-        res: FollowingFolloweePopulated[];
-    };
-    'federation/instances': {
-        req: {
-            host?: string | null;
-            blocked?: boolean | null;
-            notResponding?: boolean | null;
-            suspended?: boolean | null;
-            federating?: boolean | null;
-            subscribing?: boolean | null;
-            publishing?: boolean | null;
-            limit?: number;
-            offset?: number;
-            sort?: '+pubSub' | '-pubSub' | '+notes' | '-notes' | '+users' | '-users' | '+following' | '-following' | '+followers' | '-followers' | '+caughtAt' | '-caughtAt' | '+lastCommunicatedAt' | '-lastCommunicatedAt' | '+driveUsage' | '-driveUsage' | '+driveFiles' | '-driveFiles';
-        };
-        res: Instance[];
-    };
-    'federation/show-instance': {
-        req: {
-            host: string;
-        };
-        res: Instance;
-    };
-    'federation/update-remote-user': {
-        req: {
-            userId: User['id'];
-        };
-        res: null;
-    };
-    'federation/users': {
-        req: {
-            host: string;
-            limit?: number;
-            sinceId?: User['id'];
-            untilId?: User['id'];
-        };
-        res: UserDetailed[];
-    };
-    'following/create': {
-        req: {
-            userId: User['id'];
-            withReplies?: boolean;
-        };
-        res: User;
-    };
-    'following/delete': {
-        req: {
-            userId: User['id'];
-        };
-        res: User;
-    };
-    'following/requests/accept': {
-        req: {
-            userId: User['id'];
-        };
-        res: null;
-    };
-    'following/requests/cancel': {
-        req: {
-            userId: User['id'];
-        };
-        res: User;
-    };
-    'following/requests/list': {
-        req: NoParams;
-        res: FollowRequest[];
-    };
-    'following/requests/reject': {
-        req: {
-            userId: User['id'];
-        };
-        res: null;
-    };
-    'gallery/featured': {
-        req: null;
-        res: GalleryPost[];
-    };
-    'gallery/popular': {
-        req: null;
-        res: GalleryPost[];
-    };
-    'gallery/posts': {
-        req: {
-            limit?: number;
-            sinceId?: GalleryPost['id'];
-            untilId?: GalleryPost['id'];
-        };
-        res: GalleryPost[];
-    };
-    'gallery/posts/create': {
-        req: {
-            title: GalleryPost['title'];
-            description?: GalleryPost['description'];
-            fileIds: GalleryPost['fileIds'];
-            isSensitive?: GalleryPost['isSensitive'];
-        };
-        res: GalleryPost;
-    };
-    'gallery/posts/delete': {
-        req: {
-            postId: GalleryPost['id'];
-        };
-        res: null;
-    };
-    'gallery/posts/like': {
-        req: {
-            postId: GalleryPost['id'];
-        };
-        res: null;
-    };
-    'gallery/posts/show': {
-        req: {
-            postId: GalleryPost['id'];
-        };
-        res: GalleryPost;
-    };
-    'gallery/posts/unlike': {
-        req: {
-            postId: GalleryPost['id'];
-        };
-        res: null;
-    };
-    'gallery/posts/update': {
-        req: {
-            postId: GalleryPost['id'];
-            title: GalleryPost['title'];
-            description?: GalleryPost['description'];
-            fileIds: GalleryPost['fileIds'];
-            isSensitive?: GalleryPost['isSensitive'];
-        };
-        res: GalleryPost;
-    };
-    'games/reversi/games': {
-        req: TODO;
-        res: TODO;
-    };
-    'games/reversi/games/show': {
-        req: TODO;
-        res: TODO;
-    };
-    'games/reversi/games/surrender': {
-        req: TODO;
-        res: TODO;
-    };
-    'games/reversi/invitations': {
-        req: TODO;
-        res: TODO;
-    };
-    'games/reversi/match': {
-        req: TODO;
-        res: TODO;
-    };
-    'games/reversi/match/cancel': {
-        req: TODO;
-        res: TODO;
-    };
-    'get-online-users-count': {
-        req: NoParams;
-        res: {
-            count: number;
-        };
-    };
-    'hashtags/list': {
-        req: TODO;
-        res: TODO;
-    };
-    'hashtags/search': {
-        req: TODO;
-        res: TODO;
-    };
-    'hashtags/show': {
-        req: TODO;
-        res: TODO;
-    };
-    'hashtags/trend': {
-        req: TODO;
-        res: TODO;
-    };
-    'hashtags/users': {
-        req: TODO;
-        res: TODO;
-    };
-    'i': {
-        req: NoParams;
-        res: User;
-    };
-    'i/apps': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/authorized-apps': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/change-password': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/delete-account': {
-        req: {
-            password: string;
-        };
-        res: null;
-    };
-    'i/export-blocking': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/export-following': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/export-mute': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/export-notes': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/export-user-lists': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/favorites': {
-        req: {
-            limit?: number;
-            sinceId?: NoteFavorite['id'];
-            untilId?: NoteFavorite['id'];
-        };
-        res: NoteFavorite[];
-    };
-    'i/gallery/likes': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/gallery/posts': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/import-following': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/import-user-lists': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/move': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/notifications': {
-        req: {
-            limit?: number;
-            sinceId?: Notification_2['id'];
-            untilId?: Notification_2['id'];
-            following?: boolean;
-            markAsRead?: boolean;
-            includeTypes?: Notification_2['type'][];
-            excludeTypes?: Notification_2['type'][];
-        };
-        res: Notification_2[];
-    };
-    'i/page-likes': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/pages': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/pin': {
-        req: {
-            noteId: Note['id'];
-        };
-        res: MeDetailed;
-    };
-    'i/read-all-messaging-messages': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/read-all-unread-notes': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/read-announcement': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/regenerate-token': {
-        req: {
-            password: string;
-        };
-        res: null;
-    };
-    'i/registry/get-all': {
-        req: {
-            scope?: string[];
-        };
-        res: Record<string, any>;
-    };
-    'i/registry/get-detail': {
-        req: {
-            key: string;
-            scope?: string[];
-        };
-        res: {
-            updatedAt: DateString;
-            value: any;
-        };
-    };
-    'i/registry/get': {
-        req: {
-            key: string;
-            scope?: string[];
-        };
-        res: any;
-    };
-    'i/registry/keys-with-type': {
-        req: {
-            scope?: string[];
-        };
-        res: Record<string, 'null' | 'array' | 'number' | 'string' | 'boolean' | 'object'>;
-    };
-    'i/registry/keys': {
-        req: {
-            scope?: string[];
-        };
-        res: string[];
-    };
-    'i/registry/remove': {
-        req: {
-            key: string;
-            scope?: string[];
-        };
-        res: null;
-    };
-    'i/registry/set': {
-        req: {
-            key: string;
-            value: any;
-            scope?: string[];
-        };
-        res: null;
-    };
-    'i/revoke-token': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/signin-history': {
-        req: {
-            limit?: number;
-            sinceId?: Signin['id'];
-            untilId?: Signin['id'];
-        };
-        res: Signin[];
-    };
-    'i/unpin': {
-        req: {
-            noteId: Note['id'];
-        };
-        res: MeDetailed;
-    };
-    'i/update-email': {
-        req: {
-            password: string;
-            email?: string | null;
-        };
-        res: MeDetailed;
-    };
-    'i/update': {
-        req: {
-            name?: string | null;
-            description?: string | null;
-            lang?: string | null;
-            location?: string | null;
-            birthday?: string | null;
-            avatarId?: DriveFile['id'] | null;
-            bannerId?: DriveFile['id'] | null;
-            fields?: {
-                name: string;
-                value: string;
-            }[];
-            isLocked?: boolean;
-            isExplorable?: boolean;
-            hideOnlineStatus?: boolean;
-            carefulBot?: boolean;
-            autoAcceptFollowed?: boolean;
-            noCrawle?: boolean;
-            isBot?: boolean;
-            isCat?: boolean;
-            injectFeaturedNote?: boolean;
-            receiveAnnouncementEmail?: boolean;
-            alwaysMarkNsfw?: boolean;
-            mutedWords?: (string[] | string)[];
-            hardMutedWords?: (string[] | string)[];
-            notificationRecieveConfig?: any;
-            emailNotificationTypes?: string[];
-            alsoKnownAs?: string[];
-        };
-        res: MeDetailed;
-    };
-    'i/user-group-invites': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/2fa/done': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/2fa/key-done': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/2fa/password-less': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/2fa/register-key': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/2fa/register': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/2fa/remove-key': {
-        req: TODO;
-        res: TODO;
-    };
-    'i/2fa/unregister': {
-        req: TODO;
-        res: TODO;
-    };
-    'invite/create': {
-        req: NoParams;
-        res: Invite;
-    };
-    'invite/delete': {
-        req: {
-            inviteId: Invite['id'];
-        };
-        res: null;
-    };
-    'invite/list': {
-        req: {
-            limit?: number;
-            sinceId?: Invite['id'];
-            untilId?: Invite['id'];
-        };
-        res: Invite[];
-    };
-    'invite/limit': {
-        req: NoParams;
-        res: InviteLimit;
-    };
-    'messaging/history': {
-        req: {
-            limit?: number;
-            group?: boolean;
-        };
-        res: MessagingMessage[];
-    };
-    'messaging/messages': {
-        req: {
-            userId?: User['id'];
-            groupId?: UserGroup['id'];
-            limit?: number;
-            sinceId?: MessagingMessage['id'];
-            untilId?: MessagingMessage['id'];
-            markAsRead?: boolean;
-        };
-        res: MessagingMessage[];
-    };
-    'messaging/messages/create': {
-        req: {
-            userId?: User['id'];
-            groupId?: UserGroup['id'];
-            text?: string;
-            fileId?: DriveFile['id'];
-        };
-        res: MessagingMessage;
-    };
-    'messaging/messages/delete': {
-        req: {
-            messageId: MessagingMessage['id'];
-        };
-        res: null;
-    };
-    'messaging/messages/read': {
-        req: {
-            messageId: MessagingMessage['id'];
-        };
-        res: null;
-    };
-    'meta': {
-        req: {
-            detail?: boolean;
-        };
-        res: {
-            $switch: {
-                $cases: [
-                [
-                    {
-                    detail: true;
-                },
-                DetailedInstanceMetadata
-                ],
-                [
-                    {
-                    detail: false;
-                },
-                LiteInstanceMetadata
-                ],
-                [
-                    {
-                    detail: boolean;
-                },
-                LiteInstanceMetadata | DetailedInstanceMetadata
-                ]
-                ];
-                $default: LiteInstanceMetadata;
-            };
-        };
-    };
-    'miauth/gen-token': {
-        req: TODO;
-        res: TODO;
-    };
-    'mute/create': {
-        req: TODO;
-        res: TODO;
-    };
-    'mute/delete': {
-        req: {
-            userId: User['id'];
-        };
-        res: null;
-    };
-    'mute/list': {
-        req: TODO;
-        res: TODO;
-    };
-    'my/apps': {
-        req: TODO;
-        res: TODO;
-    };
-    'notes': {
-        req: {
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-        };
-        res: Note[];
-    };
-    'notes/children': {
-        req: {
-            noteId: Note['id'];
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-        };
-        res: Note[];
-    };
-    'notes/clips': {
-        req: TODO;
-        res: TODO;
-    };
-    'notes/conversation': {
-        req: TODO;
-        res: TODO;
-    };
-    'notes/create': {
-        req: {
-            visibility?: 'public' | 'home' | 'followers' | 'specified';
-            visibleUserIds?: User['id'][];
-            text?: null | string;
-            cw?: null | string;
-            viaMobile?: boolean;
-            localOnly?: boolean;
-            fileIds?: DriveFile['id'][];
-            replyId?: null | Note['id'];
-            renoteId?: null | Note['id'];
-            channelId?: null | Channel['id'];
-            poll?: null | {
-                choices: string[];
-                multiple?: boolean;
-                expiresAt?: null | number;
-                expiredAfter?: null | number;
-            };
-        };
-        res: {
-            createdNote: Note;
-        };
-    };
-    'notes/delete': {
-        req: {
-            noteId: Note['id'];
-        };
-        res: null;
-    };
-    'notes/favorites/create': {
-        req: {
-            noteId: Note['id'];
-        };
-        res: null;
-    };
-    'notes/favorites/delete': {
-        req: {
-            noteId: Note['id'];
-        };
-        res: null;
-    };
-    'notes/featured': {
-        req: TODO;
-        res: Note[];
-    };
-    'notes/global-timeline': {
-        req: {
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-            sinceDate?: number;
-            untilDate?: number;
-        };
-        res: Note[];
-    };
-    'notes/hybrid-timeline': {
-        req: {
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-            sinceDate?: number;
-            untilDate?: number;
-        };
-        res: Note[];
-    };
-    'notes/local-timeline': {
-        req: {
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-            sinceDate?: number;
-            untilDate?: number;
-        };
-        res: Note[];
-    };
-    'notes/mentions': {
-        req: {
-            following?: boolean;
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-        };
-        res: Note[];
-    };
-    'notes/polls/recommendation': {
-        req: TODO;
-        res: TODO;
-    };
-    'notes/polls/vote': {
-        req: {
-            noteId: Note['id'];
-            choice: number;
-        };
-        res: null;
-    };
-    'notes/reactions': {
-        req: {
-            noteId: Note['id'];
-            type?: string | null;
-            limit?: number;
-        };
-        res: NoteReaction[];
-    };
-    'notes/reactions/create': {
-        req: {
-            noteId: Note['id'];
-            reaction: string;
-        };
-        res: null;
-    };
-    'notes/reactions/delete': {
-        req: {
-            noteId: Note['id'];
-        };
-        res: null;
-    };
-    'notes/renotes': {
-        req: {
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-            noteId: Note['id'];
-        };
-        res: Note[];
-    };
-    'notes/replies': {
-        req: {
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-            noteId: Note['id'];
-        };
-        res: Note[];
-    };
-    'notes/search-by-tag': {
-        req: TODO;
-        res: TODO;
-    };
-    'notes/search': {
-        req: TODO;
-        res: TODO;
-    };
-    'notes/show': {
-        req: {
-            noteId: Note['id'];
-        };
-        res: Note;
-    };
-    'notes/state': {
-        req: TODO;
-        res: TODO;
-    };
-    'notes/timeline': {
-        req: {
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-            sinceDate?: number;
-            untilDate?: number;
-        };
-        res: Note[];
-    };
-    'notes/unrenote': {
-        req: {
-            noteId: Note['id'];
-        };
-        res: null;
-    };
-    'notes/user-list-timeline': {
-        req: {
-            listId: UserList['id'];
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-            sinceDate?: number;
-            untilDate?: number;
-        };
-        res: Note[];
-    };
-    'notes/watching/create': {
-        req: TODO;
-        res: TODO;
-    };
-    'notes/watching/delete': {
-        req: {
-            noteId: Note['id'];
-        };
-        res: null;
-    };
-    'notifications/create': {
-        req: {
-            body: string;
-            header?: string | null;
-            icon?: string | null;
-        };
-        res: null;
-    };
-    'notifications/test-notification': {
-        req: NoParams;
-        res: null;
-    };
-    'notifications/mark-all-as-read': {
-        req: NoParams;
-        res: null;
-    };
-    'page-push': {
-        req: {
-            pageId: Page['id'];
-            event: string;
-            var?: any;
-        };
-        res: null;
-    };
-    'pages/create': {
-        req: TODO;
-        res: Page;
-    };
-    'pages/delete': {
-        req: {
-            pageId: Page['id'];
-        };
-        res: null;
-    };
-    'pages/featured': {
-        req: NoParams;
-        res: Page[];
-    };
-    'pages/like': {
-        req: {
-            pageId: Page['id'];
-        };
-        res: null;
-    };
-    'pages/show': {
-        req: {
-            pageId?: Page['id'];
-            name?: string;
-            username?: string;
-        };
-        res: Page;
-    };
-    'pages/unlike': {
-        req: {
-            pageId: Page['id'];
-        };
-        res: null;
-    };
-    'pages/update': {
-        req: TODO;
-        res: null;
-    };
-    'ping': {
-        req: NoParams;
-        res: {
-            pong: number;
-        };
-    };
-    'pinned-users': {
-        req: TODO;
-        res: TODO;
-    };
-    'promo/read': {
-        req: TODO;
-        res: TODO;
-    };
-    'request-reset-password': {
-        req: {
-            username: string;
-            email: string;
-        };
-        res: null;
-    };
-    'reset-password': {
-        req: {
-            token: string;
-            password: string;
-        };
-        res: null;
-    };
-    'room/show': {
-        req: TODO;
-        res: TODO;
-    };
-    'room/update': {
-        req: TODO;
-        res: TODO;
-    };
-    'signup': {
-        req: {
-            username: string;
-            password: string;
-            host?: string;
-            invitationCode?: string;
-            emailAddress?: string;
-            'hcaptcha-response'?: string;
-            'g-recaptcha-response'?: string;
-            'turnstile-response'?: string;
-        };
-        res: MeSignup | null;
-    };
-    'stats': {
-        req: NoParams;
-        res: Stats;
-    };
-    'server-info': {
-        req: NoParams;
-        res: ServerInfo;
-    };
-    'sw/register': {
-        req: TODO;
-        res: TODO;
-    };
-    'username/available': {
-        req: {
-            username: string;
-        };
-        res: {
-            available: boolean;
-        };
-    };
-    'users': {
-        req: {
-            limit?: number;
-            offset?: number;
-            sort?: UserSorting;
-            origin?: OriginType;
-        };
-        res: User[];
-    };
-    'users/clips': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/followers': {
-        req: {
-            userId?: User['id'];
-            username?: User['username'];
-            host?: User['host'] | null;
-            limit?: number;
-            sinceId?: Following['id'];
-            untilId?: Following['id'];
-        };
-        res: FollowingFollowerPopulated[];
-    };
-    'users/following': {
-        req: {
-            userId?: User['id'];
-            username?: User['username'];
-            host?: User['host'] | null;
-            limit?: number;
-            sinceId?: Following['id'];
-            untilId?: Following['id'];
-        };
-        res: FollowingFolloweePopulated[];
-    };
-    'users/gallery/posts': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/get-frequently-replied-users': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/groups/create': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/groups/delete': {
-        req: {
-            groupId: UserGroup['id'];
-        };
-        res: null;
-    };
-    'users/groups/invitations/accept': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/groups/invitations/reject': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/groups/invite': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/groups/joined': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/groups/owned': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/groups/pull': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/groups/show': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/groups/transfer': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/groups/update': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/lists/create': {
-        req: {
-            name: string;
-        };
-        res: UserList;
-    };
-    'users/lists/delete': {
-        req: {
-            listId: UserList['id'];
-        };
-        res: null;
-    };
-    'users/lists/list': {
-        req: NoParams;
-        res: UserList[];
-    };
-    'users/lists/pull': {
-        req: {
-            listId: UserList['id'];
-            userId: User['id'];
-        };
-        res: null;
-    };
-    'users/lists/push': {
-        req: {
-            listId: UserList['id'];
-            userId: User['id'];
-        };
-        res: null;
-    };
-    'users/lists/show': {
-        req: {
-            listId: UserList['id'];
-        };
-        res: UserList;
-    };
-    'users/lists/update': {
-        req: {
-            listId: UserList['id'];
-            name: string;
-        };
-        res: UserList;
-    };
-    'users/notes': {
-        req: {
-            userId: User['id'];
-            limit?: number;
-            sinceId?: Note['id'];
-            untilId?: Note['id'];
-            sinceDate?: number;
-            untilDate?: number;
-        };
-        res: Note[];
-    };
-    'users/pages': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/flashs': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/recommendation': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/relation': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/report-abuse': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/search-by-username-and-host': {
-        req: TODO;
-        res: TODO;
-    };
-    'users/search': {
-        req: TODO;
-        res: TODO;
-    };
+type DriveFilesCheckExistenceRequest = operations['drive/files/check-existence']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesCheckExistenceResponse = operations['drive/files/check-existence']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesCreateRequest = operations['drive/files/create']['requestBody']['content']['multipart/form-data'];
+
+// @public (undocumented)
+type DriveFilesCreateResponse = operations['drive/files/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesDeleteRequest = operations['drive/files/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesFindByHashRequest = operations['drive/files/find-by-hash']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesFindByHashResponse = operations['drive/files/find-by-hash']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesFindRequest = operations['drive/files/find']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesFindResponse = operations['drive/files/find']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesRequest = operations['drive/files']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesResponse = operations['drive/files']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesShowRequest = operations['drive/files/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesShowResponse = operations['drive/files/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesUpdateRequest = operations['drive/files/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesUpdateResponse = operations['drive/files/update']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFilesUploadFromUrlRequest = operations['drive/files/upload-from-url']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFolder = components['schemas']['DriveFolder'];
+
+// @public (undocumented)
+type DriveFoldersCreateRequest = operations['drive/folders/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFoldersCreateResponse = operations['drive/folders/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFoldersDeleteRequest = operations['drive/folders/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFoldersFindRequest = operations['drive/folders/find']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFoldersFindResponse = operations['drive/folders/find']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFoldersRequest = operations['drive/folders']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFoldersResponse = operations['drive/folders']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFoldersShowRequest = operations['drive/folders/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFoldersShowResponse = operations['drive/folders/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFoldersUpdateRequest = operations['drive/folders/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveFoldersUpdateResponse = operations['drive/folders/update']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveResponse = operations['drive']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type DriveStreamRequest = operations['drive/stream']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type DriveStreamResponse = operations['drive/stream']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type EmailAddressAvailableRequest = operations['email-address/available']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type EmailAddressAvailableResponse = operations['email-address/available']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type EmojiDetailed = components['schemas']['EmojiDetailed'];
+
+// @public (undocumented)
+type EmojiRequest = operations['emoji']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type EmojiResponse = operations['emoji']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type EmojiSimple = components['schemas']['EmojiSimple'];
+
+// @public (undocumented)
+type EmojisResponse = operations['emojis']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type EmptyRequest = Record<string, unknown> | undefined;
+
+// @public (undocumented)
+type EmptyResponse = Record<string, unknown> | undefined;
+
+// @public (undocumented)
+type EndpointRequest = operations['endpoint']['requestBody']['content']['application/json'];
+
+// Warning: (ae-forgotten-export) The symbol "Overwrite" needs to be exported by the entry point index.d.ts
+// Warning: (ae-forgotten-export) The symbol "Endpoints_2" needs to be exported by the entry point index.d.ts
+//
+// @public (undocumented)
+export type Endpoints = Overwrite<Endpoints_2, {
     'users/show': {
-        req: ShowUserReq | {
-            userIds: User['id'][];
-        };
+        req: UsersShowRequest;
         res: {
             $switch: {
                 $cases: [
                 [
                     {
-                    userIds: User['id'][];
+                    userIds?: string[];
                 },
                 UserDetailed[]
                 ]
@@ -2252,76 +891,569 @@ export type Endpoints = {
             };
         };
     };
-    'fetch-rss': {
-        req: {
-            url: string;
-        };
-        res: TODO;
-    };
-    'fetch-external-resources': {
-        req: {
-            url: string;
-            hash: string;
-        };
-        res: {
-            type: string;
-            data: string;
-        };
-    };
-};
+}>;
+
+// @public (undocumented)
+type EndpointsResponse = operations['endpoints']['responses']['200']['content']['application/json'];
 
 declare namespace entities {
     export {
         ID,
         DateString,
-        User,
+        PageEvent,
+        ModerationLog,
+        EmptyRequest,
+        EmptyResponse,
+        AdminMetaResponse,
+        AdminAbuseUserReportsRequest,
+        AdminAbuseUserReportsResponse,
+        AdminAccountsCreateRequest,
+        AdminAccountsCreateResponse,
+        AdminAccountsDeleteRequest,
+        AdminAccountsFindByEmailRequest,
+        AdminAdCreateRequest,
+        AdminAdDeleteRequest,
+        AdminAdListRequest,
+        AdminAdUpdateRequest,
+        AdminAnnouncementsCreateRequest,
+        AdminAnnouncementsCreateResponse,
+        AdminAnnouncementsDeleteRequest,
+        AdminAnnouncementsListRequest,
+        AdminAnnouncementsListResponse,
+        AdminAnnouncementsUpdateRequest,
+        AdminAvatarDecorationsCreateRequest,
+        AdminAvatarDecorationsDeleteRequest,
+        AdminAvatarDecorationsListRequest,
+        AdminAvatarDecorationsListResponse,
+        AdminAvatarDecorationsUpdateRequest,
+        AdminDeleteAllFilesOfAUserRequest,
+        AdminUnsetUserAvatarRequest,
+        AdminUnsetUserBannerRequest,
+        AdminDriveFilesRequest,
+        AdminDriveFilesResponse,
+        AdminDriveShowFileRequest,
+        AdminDriveShowFileResponse,
+        AdminEmojiAddAliasesBulkRequest,
+        AdminEmojiAddRequest,
+        AdminEmojiCopyRequest,
+        AdminEmojiCopyResponse,
+        AdminEmojiDeleteBulkRequest,
+        AdminEmojiDeleteRequest,
+        AdminEmojiListRemoteRequest,
+        AdminEmojiListRemoteResponse,
+        AdminEmojiListRequest,
+        AdminEmojiListResponse,
+        AdminEmojiRemoveAliasesBulkRequest,
+        AdminEmojiSetAliasesBulkRequest,
+        AdminEmojiSetCategoryBulkRequest,
+        AdminEmojiSetLicenseBulkRequest,
+        AdminEmojiUpdateRequest,
+        AdminFederationDeleteAllFilesRequest,
+        AdminFederationRefreshRemoteInstanceMetadataRequest,
+        AdminFederationRemoveAllFollowingRequest,
+        AdminFederationUpdateInstanceRequest,
+        AdminGetTableStatsResponse,
+        AdminGetUserIpsRequest,
+        AdminInviteCreateRequest,
+        AdminInviteCreateResponse,
+        AdminInviteListRequest,
+        AdminInviteListResponse,
+        AdminPromoCreateRequest,
+        AdminQueueDeliverDelayedResponse,
+        AdminQueueInboxDelayedResponse,
+        AdminQueuePromoteRequest,
+        AdminQueueStatsResponse,
+        AdminRelaysAddRequest,
+        AdminRelaysAddResponse,
+        AdminRelaysListResponse,
+        AdminRelaysRemoveRequest,
+        AdminResetPasswordRequest,
+        AdminResetPasswordResponse,
+        AdminResolveAbuseUserReportRequest,
+        AdminSendEmailRequest,
+        AdminServerInfoResponse,
+        AdminShowModerationLogsRequest,
+        AdminShowModerationLogsResponse,
+        AdminShowUserRequest,
+        AdminShowUserResponse,
+        AdminShowUsersRequest,
+        AdminShowUsersResponse,
+        AdminSuspendUserRequest,
+        AdminUnsuspendUserRequest,
+        AdminUpdateMetaRequest,
+        AdminDeleteAccountRequest,
+        AdminDeleteAccountResponse,
+        AdminUpdateUserNoteRequest,
+        AdminRolesCreateRequest,
+        AdminRolesDeleteRequest,
+        AdminRolesShowRequest,
+        AdminRolesUpdateRequest,
+        AdminRolesAssignRequest,
+        AdminRolesUnassignRequest,
+        AdminRolesUpdateDefaultPoliciesRequest,
+        AdminRolesUsersRequest,
+        AnnouncementsRequest,
+        AnnouncementsResponse,
+        AntennasCreateRequest,
+        AntennasCreateResponse,
+        AntennasDeleteRequest,
+        AntennasListResponse,
+        AntennasNotesRequest,
+        AntennasNotesResponse,
+        AntennasShowRequest,
+        AntennasShowResponse,
+        AntennasUpdateRequest,
+        AntennasUpdateResponse,
+        ApGetRequest,
+        ApGetResponse,
+        ApShowRequest,
+        ApShowResponse,
+        AppCreateRequest,
+        AppCreateResponse,
+        AppShowRequest,
+        AppShowResponse,
+        AuthSessionGenerateRequest,
+        AuthSessionGenerateResponse,
+        AuthSessionShowRequest,
+        AuthSessionShowResponse,
+        AuthSessionUserkeyRequest,
+        AuthSessionUserkeyResponse,
+        BlockingCreateRequest,
+        BlockingCreateResponse,
+        BlockingDeleteRequest,
+        BlockingDeleteResponse,
+        BlockingListRequest,
+        BlockingListResponse,
+        ChannelsCreateRequest,
+        ChannelsCreateResponse,
+        ChannelsFeaturedResponse,
+        ChannelsFollowRequest,
+        ChannelsFollowedRequest,
+        ChannelsFollowedResponse,
+        ChannelsOwnedRequest,
+        ChannelsOwnedResponse,
+        ChannelsShowRequest,
+        ChannelsShowResponse,
+        ChannelsTimelineRequest,
+        ChannelsTimelineResponse,
+        ChannelsUnfollowRequest,
+        ChannelsUpdateRequest,
+        ChannelsUpdateResponse,
+        ChannelsFavoriteRequest,
+        ChannelsUnfavoriteRequest,
+        ChannelsMyFavoritesResponse,
+        ChannelsSearchRequest,
+        ChannelsSearchResponse,
+        ChartsActiveUsersRequest,
+        ChartsActiveUsersResponse,
+        ChartsApRequestRequest,
+        ChartsApRequestResponse,
+        ChartsDriveRequest,
+        ChartsDriveResponse,
+        ChartsFederationRequest,
+        ChartsFederationResponse,
+        ChartsInstanceRequest,
+        ChartsInstanceResponse,
+        ChartsNotesRequest,
+        ChartsNotesResponse,
+        ChartsUserDriveRequest,
+        ChartsUserDriveResponse,
+        ChartsUserFollowingRequest,
+        ChartsUserFollowingResponse,
+        ChartsUserNotesRequest,
+        ChartsUserNotesResponse,
+        ChartsUserPvRequest,
+        ChartsUserPvResponse,
+        ChartsUserReactionsRequest,
+        ChartsUserReactionsResponse,
+        ChartsUsersRequest,
+        ChartsUsersResponse,
+        ClipsAddNoteRequest,
+        ClipsRemoveNoteRequest,
+        ClipsCreateRequest,
+        ClipsCreateResponse,
+        ClipsDeleteRequest,
+        ClipsListResponse,
+        ClipsNotesRequest,
+        ClipsNotesResponse,
+        ClipsShowRequest,
+        ClipsShowResponse,
+        ClipsUpdateRequest,
+        ClipsUpdateResponse,
+        ClipsFavoriteRequest,
+        ClipsUnfavoriteRequest,
+        ClipsMyFavoritesResponse,
+        DriveResponse,
+        DriveFilesRequest,
+        DriveFilesResponse,
+        DriveFilesAttachedNotesRequest,
+        DriveFilesAttachedNotesResponse,
+        DriveFilesCheckExistenceRequest,
+        DriveFilesCheckExistenceResponse,
+        DriveFilesCreateRequest,
+        DriveFilesCreateResponse,
+        DriveFilesDeleteRequest,
+        DriveFilesFindByHashRequest,
+        DriveFilesFindByHashResponse,
+        DriveFilesFindRequest,
+        DriveFilesFindResponse,
+        DriveFilesShowRequest,
+        DriveFilesShowResponse,
+        DriveFilesUpdateRequest,
+        DriveFilesUpdateResponse,
+        DriveFilesUploadFromUrlRequest,
+        DriveFoldersRequest,
+        DriveFoldersResponse,
+        DriveFoldersCreateRequest,
+        DriveFoldersCreateResponse,
+        DriveFoldersDeleteRequest,
+        DriveFoldersFindRequest,
+        DriveFoldersFindResponse,
+        DriveFoldersShowRequest,
+        DriveFoldersShowResponse,
+        DriveFoldersUpdateRequest,
+        DriveFoldersUpdateResponse,
+        DriveStreamRequest,
+        DriveStreamResponse,
+        EmailAddressAvailableRequest,
+        EmailAddressAvailableResponse,
+        EndpointRequest,
+        EndpointsResponse,
+        FederationFollowersRequest,
+        FederationFollowersResponse,
+        FederationFollowingRequest,
+        FederationFollowingResponse,
+        FederationInstancesRequest,
+        FederationInstancesResponse,
+        FederationShowInstanceRequest,
+        FederationShowInstanceResponse,
+        FederationUpdateRemoteUserRequest,
+        FederationUsersRequest,
+        FederationUsersResponse,
+        FederationStatsRequest,
+        FollowingCreateRequest,
+        FollowingCreateResponse,
+        FollowingDeleteRequest,
+        FollowingDeleteResponse,
+        FollowingUpdateRequest,
+        FollowingUpdateResponse,
+        FollowingUpdateAllRequest,
+        FollowingInvalidateRequest,
+        FollowingInvalidateResponse,
+        FollowingRequestsAcceptRequest,
+        FollowingRequestsCancelRequest,
+        FollowingRequestsCancelResponse,
+        FollowingRequestsListRequest,
+        FollowingRequestsListResponse,
+        FollowingRequestsRejectRequest,
+        GalleryFeaturedRequest,
+        GalleryFeaturedResponse,
+        GalleryPopularResponse,
+        GalleryPostsRequest,
+        GalleryPostsResponse,
+        GalleryPostsCreateRequest,
+        GalleryPostsCreateResponse,
+        GalleryPostsDeleteRequest,
+        GalleryPostsLikeRequest,
+        GalleryPostsShowRequest,
+        GalleryPostsShowResponse,
+        GalleryPostsUnlikeRequest,
+        GalleryPostsUpdateRequest,
+        GalleryPostsUpdateResponse,
+        GetAvatarDecorationsResponse,
+        HashtagsListRequest,
+        HashtagsListResponse,
+        HashtagsSearchRequest,
+        HashtagsSearchResponse,
+        HashtagsShowRequest,
+        HashtagsShowResponse,
+        HashtagsTrendResponse,
+        HashtagsUsersRequest,
+        HashtagsUsersResponse,
+        IResponse,
+        IClaimAchievementRequest,
+        IFavoritesRequest,
+        IFavoritesResponse,
+        IGalleryLikesRequest,
+        IGalleryLikesResponse,
+        IGalleryPostsRequest,
+        IGalleryPostsResponse,
+        INotificationsRequest,
+        INotificationsResponse,
+        INotificationsGroupedRequest,
+        INotificationsGroupedResponse,
+        IPageLikesRequest,
+        IPageLikesResponse,
+        IPagesRequest,
+        IPagesResponse,
+        IPinRequest,
+        IPinResponse,
+        IReadAnnouncementRequest,
+        IRegistryGetAllRequest,
+        IRegistryGetDetailRequest,
+        IRegistryGetRequest,
+        IRegistryKeysWithTypeRequest,
+        IRegistryKeysRequest,
+        IRegistryRemoveRequest,
+        IRegistrySetRequest,
+        IUnpinRequest,
+        IUnpinResponse,
+        IUpdateRequest,
+        IUpdateResponse,
+        IWebhooksCreateRequest,
+        IWebhooksShowRequest,
+        IWebhooksUpdateRequest,
+        IWebhooksDeleteRequest,
+        InviteCreateResponse,
+        InviteDeleteRequest,
+        InviteListRequest,
+        InviteListResponse,
+        InviteLimitResponse,
+        MetaRequest,
+        MetaResponse,
+        EmojisResponse,
+        EmojiRequest,
+        EmojiResponse,
+        MuteCreateRequest,
+        MuteDeleteRequest,
+        MuteListRequest,
+        MuteListResponse,
+        RenoteMuteCreateRequest,
+        RenoteMuteDeleteRequest,
+        RenoteMuteListRequest,
+        RenoteMuteListResponse,
+        MyAppsRequest,
+        MyAppsResponse,
+        NotesRequest,
+        NotesResponse,
+        NotesChildrenRequest,
+        NotesChildrenResponse,
+        NotesClipsRequest,
+        NotesClipsResponse,
+        NotesConversationRequest,
+        NotesConversationResponse,
+        NotesCreateRequest,
+        NotesCreateResponse,
+        NotesDeleteRequest,
+        NotesFavoritesCreateRequest,
+        NotesFavoritesDeleteRequest,
+        NotesFeaturedRequest,
+        NotesFeaturedResponse,
+        NotesGlobalTimelineRequest,
+        NotesGlobalTimelineResponse,
+        NotesHybridTimelineRequest,
+        NotesHybridTimelineResponse,
+        NotesLocalTimelineRequest,
+        NotesLocalTimelineResponse,
+        NotesMentionsRequest,
+        NotesMentionsResponse,
+        NotesPollsRecommendationRequest,
+        NotesPollsRecommendationResponse,
+        NotesPollsVoteRequest,
+        NotesReactionsRequest,
+        NotesReactionsResponse,
+        NotesReactionsCreateRequest,
+        NotesReactionsDeleteRequest,
+        NotesRenotesRequest,
+        NotesRenotesResponse,
+        NotesRepliesRequest,
+        NotesRepliesResponse,
+        NotesSearchByTagRequest,
+        NotesSearchByTagResponse,
+        NotesSearchRequest,
+        NotesSearchResponse,
+        NotesShowRequest,
+        NotesShowResponse,
+        NotesStateRequest,
+        NotesStateResponse,
+        NotesThreadMutingCreateRequest,
+        NotesThreadMutingDeleteRequest,
+        NotesTimelineRequest,
+        NotesTimelineResponse,
+        NotesTranslateRequest,
+        NotesTranslateResponse,
+        NotesUnrenoteRequest,
+        NotesUserListTimelineRequest,
+        NotesUserListTimelineResponse,
+        NotificationsCreateRequest,
+        PagesCreateRequest,
+        PagesCreateResponse,
+        PagesDeleteRequest,
+        PagesFeaturedResponse,
+        PagesLikeRequest,
+        PagesShowRequest,
+        PagesShowResponse,
+        PagesUnlikeRequest,
+        PagesUpdateRequest,
+        FlashCreateRequest,
+        FlashDeleteRequest,
+        FlashFeaturedResponse,
+        FlashLikeRequest,
+        FlashShowRequest,
+        FlashShowResponse,
+        FlashUnlikeRequest,
+        FlashUpdateRequest,
+        FlashMyRequest,
+        FlashMyResponse,
+        FlashMyLikesRequest,
+        FlashMyLikesResponse,
+        PingResponse,
+        PinnedUsersResponse,
+        PromoReadRequest,
+        RolesShowRequest,
+        RolesUsersRequest,
+        RolesNotesRequest,
+        RolesNotesResponse,
+        RequestResetPasswordRequest,
+        ResetPasswordRequest,
+        StatsResponse,
+        SwShowRegistrationRequest,
+        SwShowRegistrationResponse,
+        SwUpdateRegistrationRequest,
+        SwUpdateRegistrationResponse,
+        SwRegisterRequest,
+        SwRegisterResponse,
+        SwUnregisterRequest,
+        TestRequest,
+        UsernameAvailableRequest,
+        UsernameAvailableResponse,
+        UsersRequest,
+        UsersResponse,
+        UsersClipsRequest,
+        UsersClipsResponse,
+        UsersFollowersRequest,
+        UsersFollowersResponse,
+        UsersFollowingRequest,
+        UsersFollowingResponse,
+        UsersGalleryPostsRequest,
+        UsersGalleryPostsResponse,
+        UsersGetFrequentlyRepliedUsersRequest,
+        UsersGetFrequentlyRepliedUsersResponse,
+        UsersFeaturedNotesRequest,
+        UsersFeaturedNotesResponse,
+        UsersListsCreateRequest,
+        UsersListsCreateResponse,
+        UsersListsDeleteRequest,
+        UsersListsListRequest,
+        UsersListsListResponse,
+        UsersListsPullRequest,
+        UsersListsPushRequest,
+        UsersListsShowRequest,
+        UsersListsShowResponse,
+        UsersListsFavoriteRequest,
+        UsersListsUnfavoriteRequest,
+        UsersListsUpdateRequest,
+        UsersListsUpdateResponse,
+        UsersListsCreateFromPublicRequest,
+        UsersListsCreateFromPublicResponse,
+        UsersListsUpdateMembershipRequest,
+        UsersListsGetMembershipsRequest,
+        UsersNotesRequest,
+        UsersNotesResponse,
+        UsersPagesRequest,
+        UsersPagesResponse,
+        UsersFlashsRequest,
+        UsersFlashsResponse,
+        UsersReactionsRequest,
+        UsersReactionsResponse,
+        UsersRecommendationRequest,
+        UsersRecommendationResponse,
+        UsersRelationRequest,
+        UsersRelationResponse,
+        UsersReportAbuseRequest,
+        UsersSearchByUsernameAndHostRequest,
+        UsersSearchByUsernameAndHostResponse,
+        UsersSearchRequest,
+        UsersSearchResponse,
+        UsersShowRequest,
+        UsersShowResponse,
+        UsersAchievementsRequest,
+        UsersUpdateMemoRequest,
+        FetchRssRequest,
+        FetchExternalResourcesRequest,
+        RetentionResponse,
+        Error_2 as Error,
         UserLite,
-        UserDetailed,
-        UserGroup,
-        UserList,
+        UserDetailedNotMeOnly,
+        MeDetailedOnly,
+        UserDetailedNotMe,
         MeDetailed,
-        MeDetailedWithSecret,
-        MeSignup,
-        DriveFile,
-        DriveFolder,
-        GalleryPost,
+        UserDetailed,
+        User,
+        UserList,
+        Announcement,
+        App,
         Note,
         NoteReaction,
-        Notification_2 as Notification,
-        MessagingMessage,
-        CustomEmoji,
-        LiteInstanceMetadata,
-        DetailedInstanceMetadata,
-        InstanceMetadata,
-        AdminInstanceMetadata,
-        ServerInfo,
-        Stats,
-        Page,
-        PageEvent,
-        Announcement,
-        Antenna,
-        App,
-        AuthSession,
-        Ad,
-        Clip,
         NoteFavorite,
-        FollowRequest,
-        Channel,
+        Notification_2 as Notification,
+        DriveFile,
+        DriveFolder,
         Following,
-        FollowingFolloweePopulated,
-        FollowingFollowerPopulated,
+        Muting,
+        RenoteMuting,
         Blocking,
-        Instance,
-        Signin,
-        Invite,
-        InviteLimit,
-        UserSorting,
-        OriginType,
-        ModerationLog
+        Hashtag,
+        InviteCode,
+        Page,
+        Channel,
+        QueueCount,
+        Antenna,
+        Clip,
+        FederationInstance,
+        GalleryPost,
+        EmojiSimple,
+        EmojiDetailed,
+        Flash
     }
 }
 export { entities }
 
+// @public (undocumented)
+type Error_2 = components['schemas']['Error'];
+
+// @public (undocumented)
+type FederationFollowersRequest = operations['federation/followers']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FederationFollowersResponse = operations['federation/followers']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FederationFollowingRequest = operations['federation/following']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FederationFollowingResponse = operations['federation/following']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FederationInstance = components['schemas']['FederationInstance'];
+
+// @public (undocumented)
+type FederationInstancesRequest = operations['federation/instances']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FederationInstancesResponse = operations['federation/instances']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FederationShowInstanceRequest = operations['federation/show-instance']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FederationShowInstanceResponse = operations['federation/show-instance']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FederationStatsRequest = operations['federation/stats']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FederationUpdateRemoteUserRequest = operations['federation/update-remote-user']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FederationUsersRequest = operations['federation/users']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FederationUsersResponse = operations['federation/users']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FetchExternalResourcesRequest = operations['fetch-external-resources']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 type FetchLike = (input: string, init?: {
     method?: string;
@@ -2336,245 +1468,314 @@ type FetchLike = (input: string, init?: {
     json(): Promise<any>;
 }>;
 
+// @public (undocumented)
+type FetchRssRequest = operations['fetch-rss']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 export const ffVisibility: readonly ["public", "followers", "private"];
 
 // @public (undocumented)
-type Following = {
-    id: ID;
-    createdAt: DateString;
-    followerId: User['id'];
-    followeeId: User['id'];
-};
+type Flash = components['schemas']['Flash'];
 
 // @public (undocumented)
-type FollowingFolloweePopulated = Following & {
-    followee: UserDetailed;
-};
+type FlashCreateRequest = operations['flash/create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FollowingFollowerPopulated = Following & {
-    follower: UserDetailed;
-};
+type FlashDeleteRequest = operations['flash/delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type FollowRequest = {
-    id: ID;
-    follower: User;
-    followee: User;
-};
+type FlashFeaturedResponse = operations['flash/featured']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type GalleryPost = {
-    id: ID;
-    createdAt: DateString;
-    updatedAt: DateString;
-    userId: User['id'];
-    user: User;
-    title: string;
-    description: string | null;
-    fileIds: DriveFile['id'][];
-    files: DriveFile[];
-    isSensitive: boolean;
-    likedCount: number;
-    isLiked?: boolean;
-};
+type FlashLikeRequest = operations['flash/like']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FlashMyLikesRequest = operations['flash/my-likes']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FlashMyLikesResponse = operations['flash/my-likes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FlashMyRequest = operations['flash/my']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FlashMyResponse = operations['flash/my']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FlashShowRequest = operations['flash/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FlashShowResponse = operations['flash/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FlashUnlikeRequest = operations['flash/unlike']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FlashUpdateRequest = operations['flash/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type Following = components['schemas']['Following'];
+
+// @public (undocumented)
+type FollowingCreateRequest = operations['following/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingCreateResponse = operations['following/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingDeleteRequest = operations['following/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingDeleteResponse = operations['following/delete']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingInvalidateRequest = operations['following/invalidate']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingInvalidateResponse = operations['following/invalidate']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingRequestsAcceptRequest = operations['following/requests/accept']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingRequestsCancelRequest = operations['following/requests/cancel']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingRequestsCancelResponse = operations['following/requests/cancel']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingRequestsListRequest = operations['following/requests/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingRequestsListResponse = operations['following/requests/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingRequestsRejectRequest = operations['following/requests/reject']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingUpdateAllRequest = operations['following/update-all']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingUpdateRequest = operations['following/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type FollowingUpdateResponse = operations['following/update']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryFeaturedRequest = operations['gallery/featured']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryFeaturedResponse = operations['gallery/featured']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPopularResponse = operations['gallery/popular']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPost = components['schemas']['GalleryPost'];
+
+// @public (undocumented)
+type GalleryPostsCreateRequest = operations['gallery/posts/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPostsCreateResponse = operations['gallery/posts/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPostsDeleteRequest = operations['gallery/posts/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPostsLikeRequest = operations['gallery/posts/like']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPostsRequest = operations['gallery/posts']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPostsResponse = operations['gallery/posts']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPostsShowRequest = operations['gallery/posts/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPostsShowResponse = operations['gallery/posts/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPostsUnlikeRequest = operations['gallery/posts/unlike']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPostsUpdateRequest = operations['gallery/posts/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type GalleryPostsUpdateResponse = operations['gallery/posts/update']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type GetAvatarDecorationsResponse = operations['get-avatar-decorations']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type Hashtag = components['schemas']['Hashtag'];
+
+// @public (undocumented)
+type HashtagsListRequest = operations['hashtags/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type HashtagsListResponse = operations['hashtags/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type HashtagsSearchRequest = operations['hashtags/search']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type HashtagsSearchResponse = operations['hashtags/search']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type HashtagsShowRequest = operations['hashtags/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type HashtagsShowResponse = operations['hashtags/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type HashtagsTrendResponse = operations['hashtags/trend']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type HashtagsUsersRequest = operations['hashtags/users']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type HashtagsUsersResponse = operations['hashtags/users']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type IClaimAchievementRequest = operations['i/claim-achievement']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type ID = string;
 
 // @public (undocumented)
-type Instance = {
-    id: ID;
-    firstRetrievedAt: DateString;
-    host: string;
-    usersCount: number;
-    notesCount: number;
-    followingCount: number;
-    followersCount: number;
-    driveUsage: number;
-    driveFiles: number;
-    latestRequestSentAt: DateString | null;
-    latestStatus: number | null;
-    latestRequestReceivedAt: DateString | null;
-    lastCommunicatedAt: DateString;
-    isNotResponding: boolean;
-    isSuspended: boolean;
-    isSilenced: boolean;
-    isBlocked: boolean;
-    softwareName: string | null;
-    softwareVersion: string | null;
-    openRegistrations: boolean | null;
-    name: string | null;
-    description: string | null;
-    maintainerName: string | null;
-    maintainerEmail: string | null;
-    iconUrl: string | null;
-    faviconUrl: string | null;
-    themeColor: string | null;
-    infoUpdatedAt: DateString | null;
-};
+type IFavoritesRequest = operations['i/favorites']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type InstanceMetadata = LiteInstanceMetadata | DetailedInstanceMetadata;
+type IFavoritesResponse = operations['i/favorites']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type Invite = {
-    id: ID;
-    code: string;
-    expiresAt: DateString | null;
-    createdAt: DateString;
-    createdBy: UserLite | null;
-    usedBy: UserLite | null;
-    usedAt: DateString | null;
-    used: boolean;
-};
+type IGalleryLikesRequest = operations['i/gallery/likes']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type InviteLimit = {
-    remaining: number;
-};
+type IGalleryLikesResponse = operations['i/gallery/likes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type IGalleryPostsRequest = operations['i/gallery/posts']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IGalleryPostsResponse = operations['i/gallery/posts']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type INotificationsGroupedRequest = operations['i/notifications-grouped']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type INotificationsGroupedResponse = operations['i/notifications-grouped']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type INotificationsRequest = operations['i/notifications']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type INotificationsResponse = operations['i/notifications']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type InviteCode = components['schemas']['InviteCode'];
+
+// @public (undocumented)
+type InviteCreateResponse = operations['invite/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type InviteDeleteRequest = operations['invite/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type InviteLimitResponse = operations['invite/limit']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type InviteListRequest = operations['invite/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type InviteListResponse = operations['invite/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type IPageLikesRequest = operations['i/page-likes']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IPageLikesResponse = operations['i/page-likes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type IPagesRequest = operations['i/pages']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IPagesResponse = operations['i/pages']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type IPinRequest = operations['i/pin']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IPinResponse = operations['i/pin']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type IReadAnnouncementRequest = operations['i/read-announcement']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IRegistryGetDetailRequest = operations['i/registry/get-detail']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IRegistryRemoveRequest = operations['i/registry/remove']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IResponse = operations['i']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 function isAPIError(reason: any): reason is APIError;
 
 // @public (undocumented)
-type LiteInstanceMetadata = {
-    maintainerName: string | null;
-    maintainerEmail: string | null;
-    version: string;
-    name: string | null;
-    shortName: string | null;
-    uri: string;
-    description: string | null;
-    langs: string[];
-    tosUrl: string | null;
-    repositoryUrl: string;
-    feedbackUrl: string;
-    impressumUrl: string | null;
-    privacyPolicyUrl: string | null;
-    disableRegistration: boolean;
-    disableLocalTimeline: boolean;
-    disableGlobalTimeline: boolean;
-    driveCapacityPerLocalUserMb: number;
-    driveCapacityPerRemoteUserMb: number;
-    emailRequiredForSignup: boolean;
-    enableHcaptcha: boolean;
-    hcaptchaSiteKey: string | null;
-    enableRecaptcha: boolean;
-    recaptchaSiteKey: string | null;
-    enableTurnstile: boolean;
-    turnstileSiteKey: string | null;
-    swPublickey: string | null;
-    themeColor: string | null;
-    mascotImageUrl: string | null;
-    bannerUrl: string | null;
-    serverErrorImageUrl: string | null;
-    infoImageUrl: string | null;
-    notFoundImageUrl: string | null;
-    iconUrl: string | null;
-    backgroundImageUrl: string | null;
-    logoImageUrl: string | null;
-    maxNoteTextLength: number;
-    enableEmail: boolean;
-    enableTwitterIntegration: boolean;
-    enableGithubIntegration: boolean;
-    enableDiscordIntegration: boolean;
-    enableServiceWorker: boolean;
-    emojis: CustomEmoji[];
-    defaultDarkTheme: string | null;
-    defaultLightTheme: string | null;
-    ads: {
-        id: ID;
-        ratio: number;
-        place: string;
-        url: string;
-        imageUrl: string;
-    }[];
-    notesPerOneAd: number;
-    translatorAvailable: boolean;
-    serverRules: string[];
-};
+type IUnpinRequest = operations['i/unpin']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type MeDetailed = UserDetailed & {
-    avatarId: DriveFile['id'];
-    bannerId: DriveFile['id'];
-    autoAcceptFollowed: boolean;
-    alwaysMarkNsfw: boolean;
-    carefulBot: boolean;
-    emailNotificationTypes: string[];
-    hasPendingReceivedFollowRequest: boolean;
-    hasUnreadAnnouncement: boolean;
-    hasUnreadAntenna: boolean;
-    hasUnreadMentions: boolean;
-    hasUnreadMessagingMessage: boolean;
-    hasUnreadNotification: boolean;
-    hasUnreadSpecifiedNotes: boolean;
-    unreadNotificationsCount: number;
-    hideOnlineStatus: boolean;
-    injectFeaturedNote: boolean;
-    integrations: Record<string, any>;
-    isDeleted: boolean;
-    isExplorable: boolean;
-    mutedWords: (string[] | string)[];
-    hardMutedWords: (string[] | string)[];
-    notificationRecieveConfig: {
-        [notificationType in typeof notificationTypes_2[number]]?: {
-            type: 'all';
-        } | {
-            type: 'never';
-        } | {
-            type: 'following';
-        } | {
-            type: 'follower';
-        } | {
-            type: 'mutualFollow';
-        } | {
-            type: 'list';
-            userListId: string;
-        };
-    };
-    noCrawle: boolean;
-    receiveAnnouncementEmail: boolean;
-    usePasswordLessLogin: boolean;
-    unreadAnnouncements: Announcement[];
-    twoFactorBackupCodesStock: 'full' | 'partial' | 'none';
-    [other: string]: any;
-};
+type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type MeDetailedWithSecret = MeDetailed & {
-    email: string;
-    emailVerified: boolean;
-    securityKeysList: {
-        id: string;
-        name: string;
-        lastUsed: string;
-    }[];
-};
+type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type MeSignup = MeDetailedWithSecret & {
-    token: string;
-};
+type IUpdateResponse = operations['i/update']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type MessagingMessage = {
-    id: ID;
-    createdAt: DateString;
-    file: DriveFile | null;
-    fileId: DriveFile['id'] | null;
-    isRead: boolean;
-    reads: User['id'][];
-    text: string | null;
-    user: User;
-    userId: User['id'];
-    recipient?: User | null;
-    recipientId: User['id'] | null;
-    group?: UserGroup | null;
-    groupId: UserGroup['id'] | null;
-};
+type IWebhooksCreateRequest = operations['i/webhooks/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IWebhooksDeleteRequest = operations['i/webhooks/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IWebhooksShowRequest = operations['i/webhooks/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type MeDetailed = components['schemas']['MeDetailed'];
+
+// @public (undocumented)
+type MeDetailedOnly = components['schemas']['MeDetailedOnly'];
+
+// @public (undocumented)
+type MetaRequest = operations['meta']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type MetaResponse = operations['meta']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type ModerationLog = {
@@ -2698,168 +1899,206 @@ 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", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"];
 
+// @public (undocumented)
+type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type MuteDeleteRequest = operations['mute/delete']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"];
 
 // @public (undocumented)
-type Note = {
-    id: ID;
-    createdAt: DateString;
-    text: string | null;
-    cw: string | null;
-    user: User;
-    userId: User['id'];
-    reply?: Note;
-    replyId: Note['id'];
-    renote?: Note;
-    renoteId: Note['id'];
-    files: DriveFile[];
-    fileIds: DriveFile['id'][];
-    visibility: 'public' | 'home' | 'followers' | 'specified';
-    visibleUserIds?: User['id'][];
-    channel?: Channel;
-    channelId?: Channel['id'];
-    localOnly?: boolean;
-    myReaction?: string;
-    reactions: Record<string, number>;
-    renoteCount: number;
-    repliesCount: number;
-    clippedCount?: number;
-    poll?: {
-        expiresAt: DateString | null;
-        multiple: boolean;
-        choices: {
-            isVoted: boolean;
-            text: string;
-            votes: number;
-        }[];
-    };
-    emojis: {
-        name: string;
-        url: string;
-    }[];
-    uri?: string;
-    url?: string;
-    isHidden?: boolean;
-};
+type MuteListRequest = operations['mute/list']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-type NoteFavorite = {
-    id: ID;
-    createdAt: DateString;
-    noteId: Note['id'];
-    note: Note;
-};
+type MuteListResponse = operations['mute/list']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type NoteReaction = {
-    id: ID;
-    createdAt: DateString;
-    user: UserLite;
-    type: string;
-};
+type Muting = components['schemas']['Muting'];
+
+// @public (undocumented)
+type MyAppsRequest = operations['my/apps']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type MyAppsResponse = operations['my/apps']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type Note = components['schemas']['Note'];
+
+// @public (undocumented)
+type NoteFavorite = components['schemas']['NoteFavorite'];
+
+// @public (undocumented)
+type NoteReaction = components['schemas']['NoteReaction'];
+
+// @public (undocumented)
+type NotesChildrenRequest = operations['notes/children']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesChildrenResponse = operations['notes/children']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesClipsRequest = operations['notes/clips']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesClipsResponse = operations['notes/clips']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesConversationRequest = operations['notes/conversation']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesConversationResponse = operations['notes/conversation']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesCreateRequest = operations['notes/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesCreateResponse = operations['notes/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesDeleteRequest = operations['notes/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesFavoritesCreateRequest = operations['notes/favorites/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesFavoritesDeleteRequest = operations['notes/favorites/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesFeaturedRequest = operations['notes/featured']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesFeaturedResponse = operations['notes/featured']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesGlobalTimelineRequest = operations['notes/global-timeline']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesGlobalTimelineResponse = operations['notes/global-timeline']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesHybridTimelineRequest = operations['notes/hybrid-timeline']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesHybridTimelineResponse = operations['notes/hybrid-timeline']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesLocalTimelineRequest = operations['notes/local-timeline']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesLocalTimelineResponse = operations['notes/local-timeline']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesMentionsRequest = operations['notes/mentions']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesMentionsResponse = operations['notes/mentions']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesPollsRecommendationRequest = operations['notes/polls/recommendation']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesPollsRecommendationResponse = operations['notes/polls/recommendation']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesPollsVoteRequest = operations['notes/polls/vote']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesReactionsCreateRequest = operations['notes/reactions/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesReactionsDeleteRequest = operations['notes/reactions/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesReactionsRequest = operations['notes/reactions']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesReactionsResponse = operations['notes/reactions']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesRenotesRequest = operations['notes/renotes']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesRenotesResponse = operations['notes/renotes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesRepliesRequest = operations['notes/replies']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesRepliesResponse = operations['notes/replies']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesRequest = operations['notes']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesResponse = operations['notes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesSearchByTagRequest = operations['notes/search-by-tag']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesSearchByTagResponse = operations['notes/search-by-tag']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesSearchRequest = operations['notes/search']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesSearchResponse = operations['notes/search']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesShowRequest = operations['notes/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesShowResponse = operations['notes/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesStateRequest = operations['notes/state']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesStateResponse = operations['notes/state']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesThreadMutingCreateRequest = operations['notes/thread-muting/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesThreadMutingDeleteRequest = operations['notes/thread-muting/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesTimelineRequest = operations['notes/timeline']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesTimelineResponse = operations['notes/timeline']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesTranslateRequest = operations['notes/translate']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesTranslateResponse = operations['notes/translate']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type NotesUnrenoteRequest = operations['notes/unrenote']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesUserListTimelineRequest = operations['notes/user-list-timeline']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type NotesUserListTimelineResponse = operations['notes/user-list-timeline']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 export const noteVisibilities: readonly ["public", "home", "followers", "specified"];
 
 // @public (undocumented)
-type Notification_2 = {
-    id: ID;
-    createdAt: DateString;
-    isRead: boolean;
-} & ({
-    type: 'reaction';
-    reaction: string;
-    user: User;
-    userId: User['id'];
-    note: Note;
-} | {
-    type: 'reply';
-    user: User;
-    userId: User['id'];
-    note: Note;
-} | {
-    type: 'renote';
-    user: User;
-    userId: User['id'];
-    note: Note;
-} | {
-    type: 'quote';
-    user: User;
-    userId: User['id'];
-    note: Note;
-} | {
-    type: 'mention';
-    user: User;
-    userId: User['id'];
-    note: Note;
-} | {
-    type: 'note';
-    user: User;
-    userId: User['id'];
-    note: Note;
-} | {
-    type: 'pollEnded';
-    user: User;
-    userId: User['id'];
-    note: Note;
-} | {
-    type: 'follow';
-    user: User;
-    userId: User['id'];
-} | {
-    type: 'followRequestAccepted';
-    user: User;
-    userId: User['id'];
-} | {
-    type: 'receiveFollowRequest';
-    user: User;
-    userId: User['id'];
-} | {
-    type: 'groupInvited';
-    invitation: UserGroup;
-    user: User;
-    userId: User['id'];
-} | {
-    type: 'achievementEarned';
-    achievement: string;
-} | {
-    type: 'app';
-    header?: string | null;
-    body: string;
-    icon?: string | null;
-} | {
-    type: 'test';
-});
+type Notification_2 = components['schemas']['Notification'];
+
+// @public (undocumented)
+type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "achievementEarned"];
 
 // @public (undocumented)
-type OriginType = 'combined' | 'local' | 'remote';
-
-// @public (undocumented)
-type Page = {
-    id: ID;
-    createdAt: DateString;
-    updatedAt: DateString;
-    userId: User['id'];
-    user: User;
-    content: Record<string, any>[];
-    variables: Record<string, any>[];
-    title: string;
-    name: string;
-    summary: string | null;
-    hideTitleWhenPinned: boolean;
-    alignCenter: boolean;
-    font: string;
-    script: string;
-    eyeCatchingImageId: DriveFile['id'] | null;
-    eyeCatchingImage: DriveFile | null;
-    attachedFiles: any;
-    likedCount: number;
-    isLiked?: boolean;
-};
+type Page = components['schemas']['Page'];
 
 // @public (undocumented)
 type PageEvent = {
@@ -2870,6 +2109,33 @@ type PageEvent = {
     user: User;
 };
 
+// @public (undocumented)
+type PagesCreateRequest = operations['pages/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type PagesCreateResponse = operations['pages/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type PagesDeleteRequest = operations['pages/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type PagesFeaturedResponse = operations['pages/featured']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type PagesLikeRequest = operations['pages/like']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type PagesShowRequest = operations['pages/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type PagesShowResponse = operations['pages/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type PagesUnlikeRequest = operations['pages/unlike']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type PagesUpdateRequest = operations['pages/update']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 function parse(acct: string): Acct;
 
@@ -2877,40 +2143,55 @@ function parse(acct: string): Acct;
 export const permissions: string[];
 
 // @public (undocumented)
-type ServerInfo = {
-    machine: string;
-    cpu: {
-        model: string;
-        cores: number;
-    };
-    mem: {
-        total: number;
-    };
-    fs: {
-        total: number;
-        used: number;
-    };
-};
+type PingResponse = operations['ping']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type Signin = {
-    id: ID;
-    createdAt: DateString;
-    ip: string;
-    headers: Record<string, any>;
-    success: boolean;
-};
+type PinnedUsersResponse = operations['pinned-users']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
-type Stats = {
-    notesCount: number;
-    originalNotesCount: number;
-    usersCount: number;
-    originalUsersCount: number;
-    instances: number;
-    driveUsageLocal: number;
-    driveUsageRemote: number;
-};
+type PromoReadRequest = operations['promo/read']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type QueueCount = components['schemas']['QueueCount'];
+
+// @public (undocumented)
+type RenoteMuteCreateRequest = operations['renote-mute/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type RenoteMuteDeleteRequest = operations['renote-mute/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type RenoteMuteListRequest = operations['renote-mute/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type RenoteMuteListResponse = operations['renote-mute/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type RenoteMuting = components['schemas']['RenoteMuting'];
+
+// @public (undocumented)
+type RequestResetPasswordRequest = operations['request-reset-password']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ResetPasswordRequest = operations['reset-password']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type RetentionResponse = operations['retention']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type RolesNotesRequest = operations['roles/notes']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type RolesNotesResponse = operations['roles/notes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type RolesShowRequest = operations['roles/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type RolesUsersRequest = operations['roles/users']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type StatsResponse = operations['stats']['responses']['200']['content']['application/json'];
 
 // Warning: (ae-forgotten-export) The symbol "StreamEvents" needs to be exported by the entry point index.d.ts
 //
@@ -2951,114 +2232,224 @@ export class Stream extends EventEmitter<StreamEvents> {
     useChannel<C extends keyof Channels>(channel: C, params?: Channels[C]['params'], name?: string): ChannelConnection<Channels[C]>;
 }
 
+// Warning: (ae-forgotten-export) The symbol "SwitchCase" needs to be exported by the entry point index.d.ts
+// Warning: (ae-forgotten-export) The symbol "IsCaseMatched" needs to be exported by the entry point index.d.ts
+// Warning: (ae-forgotten-export) The symbol "GetCaseResult" needs to be exported by the entry point index.d.ts
+//
+// @public (undocumented)
+type SwitchCaseResponseType<E extends keyof Endpoints, P extends Endpoints[E]['req']> = Endpoints[E]['res'] extends SwitchCase ? IsCaseMatched<E, P, 0> extends true ? GetCaseResult<E, P, 0> : IsCaseMatched<E, P, 1> extends true ? GetCaseResult<E, P, 1> : IsCaseMatched<E, P, 2> extends true ? GetCaseResult<E, P, 2> : IsCaseMatched<E, P, 3> extends true ? GetCaseResult<E, P, 3> : IsCaseMatched<E, P, 4> extends true ? GetCaseResult<E, P, 4> : IsCaseMatched<E, P, 5> extends true ? GetCaseResult<E, P, 5> : IsCaseMatched<E, P, 6> extends true ? GetCaseResult<E, P, 6> : IsCaseMatched<E, P, 7> extends true ? GetCaseResult<E, P, 7> : IsCaseMatched<E, P, 8> extends true ? GetCaseResult<E, P, 8> : IsCaseMatched<E, P, 9> extends true ? GetCaseResult<E, P, 9> : Endpoints[E]['res']['$switch']['$default'] : Endpoints[E]['res'];
+
+// @public (undocumented)
+type SwRegisterRequest = operations['sw/register']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type SwRegisterResponse = operations['sw/register']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type SwShowRegistrationRequest = operations['sw/show-registration']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type SwShowRegistrationResponse = operations['sw/show-registration']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type SwUnregisterRequest = operations['sw/unregister']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type SwUpdateRegistrationRequest = operations['sw/update-registration']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type SwUpdateRegistrationResponse = operations['sw/update-registration']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type TestRequest = operations['test']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 function toString_2(acct: Acct): string;
 
 // @public (undocumented)
-type User = UserLite | UserDetailed;
+type User = components['schemas']['User'];
 
 // @public (undocumented)
-type UserDetailed = UserLite & {
-    alsoKnownAs: string[];
-    bannerBlurhash: string | null;
-    bannerColor: string | null;
-    bannerUrl: string | null;
-    birthday: string | null;
-    createdAt: DateString;
-    description: string | null;
-    ffVisibility: 'public' | 'followers' | 'private';
-    fields: {
-        name: string;
-        value: string;
-    }[];
-    verifiedLinks: string[];
-    followersCount: number;
-    followingCount: number;
-    hasPendingFollowRequestFromYou: boolean;
-    hasPendingFollowRequestToYou: boolean;
-    isAdmin: boolean;
-    isBlocked: boolean;
-    isBlocking: boolean;
-    isBot: boolean;
-    isCat: boolean;
-    isFollowed: boolean;
-    isFollowing: boolean;
-    isLocked: boolean;
-    isModerator: boolean;
-    isMuted: boolean;
-    isSilenced: boolean;
-    isSuspended: boolean;
-    lang: string | null;
-    lastFetchedAt?: DateString;
-    location: string | null;
-    movedTo: string;
-    notesCount: number;
-    pinnedNoteIds: ID[];
-    pinnedNotes: Note[];
-    pinnedPage: Page | null;
-    pinnedPageId: string | null;
-    publicReactions: boolean;
-    securityKeys: boolean;
-    twoFactorEnabled: boolean;
-    updatedAt: DateString | null;
-    uri: string | null;
-    url: string | null;
-    notify: 'normal' | 'none';
-};
+type UserDetailed = components['schemas']['UserDetailed'];
 
 // @public (undocumented)
-type UserGroup = TODO_2;
+type UserDetailedNotMe = components['schemas']['UserDetailedNotMe'];
 
 // @public (undocumented)
-type UserList = {
-    id: ID;
-    createdAt: DateString;
-    name: string;
-    userIds: User['id'][];
-};
+type UserDetailedNotMeOnly = components['schemas']['UserDetailedNotMeOnly'];
 
 // @public (undocumented)
-type UserLite = {
-    id: ID;
-    username: string;
-    host: string | null;
-    name: string | null;
-    onlineStatus: 'online' | 'active' | 'offline' | 'unknown';
-    avatarUrl: string;
-    avatarBlurhash: string;
-    avatarDecorations: {
-        id: ID;
-        url: string;
-        angle?: number;
-        flipH?: boolean;
-    }[];
-    emojis: {
-        name: string;
-        url: string;
-    }[];
-    instance?: {
-        name: Instance['name'];
-        softwareName: Instance['softwareName'];
-        softwareVersion: Instance['softwareVersion'];
-        iconUrl: Instance['iconUrl'];
-        faviconUrl: Instance['faviconUrl'];
-        themeColor: Instance['themeColor'];
-    };
-    isCat?: boolean;
-    isBot?: boolean;
-};
+type UserList = components['schemas']['UserList'];
 
 // @public (undocumented)
-type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+updatedAt' | '-updatedAt';
+type UserLite = components['schemas']['UserLite'];
+
+// @public (undocumented)
+type UsernameAvailableRequest = operations['username/available']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsernameAvailableResponse = operations['username/available']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersAchievementsRequest = operations['users/achievements']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersClipsRequest = operations['users/clips']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersClipsResponse = operations['users/clips']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersFeaturedNotesRequest = operations['users/featured-notes']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersFeaturedNotesResponse = operations['users/featured-notes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersFlashsRequest = operations['users/flashs']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersFlashsResponse = operations['users/flashs']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersFollowersRequest = operations['users/followers']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersFollowersResponse = operations['users/followers']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersFollowingRequest = operations['users/following']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersFollowingResponse = operations['users/following']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersGalleryPostsRequest = operations['users/gallery/posts']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersGalleryPostsResponse = operations['users/gallery/posts']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersGetFrequentlyRepliedUsersRequest = operations['users/get-frequently-replied-users']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersGetFrequentlyRepliedUsersResponse = operations['users/get-frequently-replied-users']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsCreateFromPublicRequest = operations['users/lists/create-from-public']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsCreateFromPublicResponse = operations['users/lists/create-from-public']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsCreateRequest = operations['users/lists/create']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsCreateResponse = operations['users/lists/create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsDeleteRequest = operations['users/lists/delete']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsFavoriteRequest = operations['users/lists/favorite']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsGetMembershipsRequest = operations['users/lists/get-memberships']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsListRequest = operations['users/lists/list']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsListResponse = operations['users/lists/list']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsPullRequest = operations['users/lists/pull']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsPushRequest = operations['users/lists/push']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsShowRequest = operations['users/lists/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsShowResponse = operations['users/lists/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsUnfavoriteRequest = operations['users/lists/unfavorite']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsUpdateMembershipRequest = operations['users/lists/update-membership']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsUpdateRequest = operations['users/lists/update']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersListsUpdateResponse = operations['users/lists/update']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersNotesRequest = operations['users/notes']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersNotesResponse = operations['users/notes']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersPagesRequest = operations['users/pages']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersPagesResponse = operations['users/pages']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersReactionsRequest = operations['users/reactions']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersReactionsResponse = operations['users/reactions']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersRecommendationRequest = operations['users/recommendation']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersRecommendationResponse = operations['users/recommendation']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersRelationRequest = operations['users/relation']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersRelationResponse = operations['users/relation']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersReportAbuseRequest = operations['users/report-abuse']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersRequest = operations['users']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersResponse = operations['users']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersSearchByUsernameAndHostRequest = operations['users/search-by-username-and-host']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersSearchByUsernameAndHostResponse = operations['users/search-by-username-and-host']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersSearchRequest = operations['users/search']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersSearchResponse = operations['users/search']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersShowRequest = operations['users/show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type UsersShowResponse = operations['users/show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type UsersUpdateMemoRequest = operations['users/update-memo']['requestBody']['content']['application/json'];
 
 // Warnings were encountered during analysis:
 //
-// 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:20:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
-// src/api.types.ts:635:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
-// src/entities.ts:117:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts
-// src/entities.ts:628: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
+// src/entities.ts:24:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
+// src/streaming.types.ts:31: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/generator/.eslintrc.cjs b/packages/misskey-js/generator/.eslintrc.cjs
new file mode 100644
index 0000000000..6a8b31da9c
--- /dev/null
+++ b/packages/misskey-js/generator/.eslintrc.cjs
@@ -0,0 +1,9 @@
+module.exports = {
+	parserOptions: {
+		tsconfigRootDir: __dirname,
+		project: ['./tsconfig.json'],
+	},
+	extends: [
+		'../../shared/.eslintrc.js',
+	],
+};
diff --git a/packages/misskey-js/generator/.gitignore b/packages/misskey-js/generator/.gitignore
new file mode 100644
index 0000000000..1a11577de7
--- /dev/null
+++ b/packages/misskey-js/generator/.gitignore
@@ -0,0 +1 @@
+api.json
diff --git a/packages/misskey-js/generator/README.md b/packages/misskey-js/generator/README.md
new file mode 100644
index 0000000000..767ccfa185
--- /dev/null
+++ b/packages/misskey-js/generator/README.md
@@ -0,0 +1,19 @@
+## misskey-js向け型生成モジュール
+
+バックエンドが吐き出すOpenAPI準拠のapi.jsonからmisskey-jsで使用される型エイリアスを生成するためのモジュールです。
+このモジュールはmisskey-jsそのものにバンドルされることは想定しておらず、生成物をmisskey-jsのsrc配下にコピーして使用することを想定しています。
+
+## 使い方
+
+まず、Misskeyのバックエンドからapi.jsonを取得する必要があります。任意のMisskeyインスタンスの/api-docからダウンロードしても良いですし、
+backendモジュール配下で`pnpm generate-api-json`を実行しても良いでしょう。
+
+api.jsonを入手したら、このファイルがあるディレクトリに置いてください。
+
+その後、以下コマンドを実行します。
+
+```shell
+pnpm generate
+```
+
+上記を実行することで、`./built`ディレクトリ配下にtsファイルが生成されます。
diff --git a/packages/misskey-js/generator/package.json b/packages/misskey-js/generator/package.json
new file mode 100644
index 0000000000..e12520f043
--- /dev/null
+++ b/packages/misskey-js/generator/package.json
@@ -0,0 +1,24 @@
+{
+	"name": "misskey-js-type-generator",
+	"version": "0.0.0",
+	"description": "Misskey TypeGenerator",
+	"type": "module",
+	"scripts": {
+		"generate": "tsx src/generator.ts && eslint ./built/**/* --ext .ts --fix"
+	},
+	"devDependencies": {
+		"@apidevtools/swagger-parser": "10.1.0",
+		"@types/node": "20.9.1",
+		"@typescript-eslint/eslint-plugin": "6.11.0",
+		"@typescript-eslint/parser": "6.11.0",
+		"eslint": "8.53.0",
+		"typescript": "5.3.2",
+		"tsx": "4.4.0",
+		"ts-case-convert": "2.0.2",
+		"openapi-types": "12.1.3",
+		"openapi-typescript": "6.7.1"
+	},
+	"files": [
+		"built"
+	]
+}
diff --git a/packages/misskey-js/generator/src/generator.ts b/packages/misskey-js/generator/src/generator.ts
new file mode 100644
index 0000000000..7ed3ae120c
--- /dev/null
+++ b/packages/misskey-js/generator/src/generator.ts
@@ -0,0 +1,284 @@
+import { mkdir, writeFile } from 'fs/promises';
+import { OpenAPIV3 } from 'openapi-types';
+import { toPascal } from 'ts-case-convert';
+import SwaggerParser from '@apidevtools/swagger-parser';
+import openapiTS from 'openapi-typescript';
+
+function generateVersionHeaderComment(openApiDocs: OpenAPIV3.Document): string {
+	const contents = {
+		version: openApiDocs.info.version,
+		generatedAt: new Date().toISOString(),
+	};
+
+	const lines: string[] = [];
+	lines.push('/*');
+	for (const [key, value] of Object.entries(contents)) {
+		lines.push(` * ${key}: ${value}`);
+	}
+	lines.push(' */');
+
+	return lines.join('\n');
+}
+
+async function generateBaseTypes(
+	openApiDocs: OpenAPIV3.Document,
+	openApiJsonPath: string,
+	typeFileName: string,
+) {
+	const disabledLints = [
+		'@typescript-eslint/naming-convention',
+		'@typescript-eslint/no-explicit-any',
+	];
+
+	const lines: string[] = [];
+	for (const lint of disabledLints) {
+		lines.push(`/* eslint ${lint}: 0 */`);
+	}
+	lines.push('');
+
+	lines.push(generateVersionHeaderComment(openApiDocs));
+	lines.push('');
+
+	const generatedTypes = await openapiTS(openApiJsonPath, { exportType: true });
+	lines.push(generatedTypes);
+	lines.push('');
+
+	await writeFile(typeFileName, lines.join('\n'));
+}
+
+async function generateSchemaEntities(
+	openApiDocs: OpenAPIV3.Document,
+	typeFileName: string,
+	outputPath: string,
+) {
+	if (!openApiDocs.components?.schemas) {
+		return;
+	}
+
+	const schemas = openApiDocs.components.schemas;
+	const schemaNames = Object.keys(schemas);
+	const typeAliasLines: string[] = [];
+
+	typeAliasLines.push(generateVersionHeaderComment(openApiDocs));
+	typeAliasLines.push('');
+	typeAliasLines.push(`import { components } from '${toImportPath(typeFileName)}';`);
+	typeAliasLines.push(
+		...schemaNames.map(it => `export type ${it} = components['schemas']['${it}'];`),
+	);
+	typeAliasLines.push('');
+
+	await writeFile(outputPath, typeAliasLines.join('\n'));
+}
+
+async function generateEndpoints(
+	openApiDocs: OpenAPIV3.Document,
+	typeFileName: string,
+	entitiesOutputPath: string,
+	endpointOutputPath: string,
+) {
+	const endpoints: Endpoint[] = [];
+
+	// misskey-jsはPOST固定で送っているので、こちらも決め打ちする。別メソッドに対応することがあればこちらも直す必要あり
+	const paths = openApiDocs.paths;
+	const postPathItems = Object.keys(paths)
+		.map(it => paths[it]?.post)
+		.filter(filterUndefined);
+
+	for (const operation of postPathItems) {
+		// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+		const operationId = operation.operationId!;
+		const endpoint = new Endpoint(operationId);
+		endpoints.push(endpoint);
+
+		if (isRequestBodyObject(operation.requestBody)) {
+			const reqContent = operation.requestBody.content;
+			const supportMediaTypes = Object.keys(reqContent);
+			if (supportMediaTypes.length > 0) {
+				// いまのところ複数のメディアタイプをとるエンドポイントは無いので決め打ちする
+				endpoint.request = new OperationTypeAlias(
+					operationId,
+					supportMediaTypes[0],
+					OperationsAliasType.REQUEST,
+				);
+			}
+		}
+
+		if (isResponseObject(operation.responses['200']) && operation.responses['200'].content) {
+			const resContent = operation.responses['200'].content;
+			const supportMediaTypes = Object.keys(resContent);
+			if (supportMediaTypes.length > 0) {
+				// いまのところ複数のメディアタイプを返すエンドポイントは無いので決め打ちする
+				endpoint.response = new OperationTypeAlias(
+					operationId,
+					supportMediaTypes[0],
+					OperationsAliasType.RESPONSE,
+				);
+			}
+		}
+	}
+
+	const entitiesOutputLine: string[] = [];
+
+	entitiesOutputLine.push(generateVersionHeaderComment(openApiDocs));
+	entitiesOutputLine.push('');
+
+	entitiesOutputLine.push(`import { operations } from '${toImportPath(typeFileName)}';`);
+	entitiesOutputLine.push('');
+
+	entitiesOutputLine.push(new EmptyTypeAlias(OperationsAliasType.REQUEST).toLine());
+	entitiesOutputLine.push(new EmptyTypeAlias(OperationsAliasType.RESPONSE).toLine());
+	entitiesOutputLine.push('');
+
+	const entities = endpoints
+		.flatMap(it => [it.request, it.response].filter(i => i))
+		.filter(filterUndefined);
+	entitiesOutputLine.push(...entities.map(it => it.toLine()));
+	entitiesOutputLine.push('');
+
+	await writeFile(entitiesOutputPath, entitiesOutputLine.join('\n'));
+
+	const endpointOutputLine: string[] = [];
+
+	endpointOutputLine.push(generateVersionHeaderComment(openApiDocs));
+	endpointOutputLine.push('');
+
+	endpointOutputLine.push('import type {');
+	endpointOutputLine.push(
+		...[emptyRequest, emptyResponse, ...entities].map(it => '\t' + it.generateName() + ','),
+	);
+	endpointOutputLine.push(`} from '${toImportPath(entitiesOutputPath)}';`);
+	endpointOutputLine.push('');
+
+	endpointOutputLine.push('export type Endpoints = {');
+	endpointOutputLine.push(
+		...endpoints.map(it => '\t' + it.toLine()),
+	);
+	endpointOutputLine.push('}');
+	endpointOutputLine.push('');
+
+	await writeFile(endpointOutputPath, endpointOutputLine.join('\n'));
+}
+
+function isRequestBodyObject(value: unknown): value is OpenAPIV3.RequestBodyObject {
+	if (!value) {
+		return false;
+	}
+
+	const { content } = value as Record<keyof OpenAPIV3.RequestBodyObject, unknown>;
+	return content !== undefined;
+}
+
+function isResponseObject(value: unknown): value is OpenAPIV3.ResponseObject {
+	if (!value) {
+		return false;
+	}
+
+	const { description } = value as Record<keyof OpenAPIV3.ResponseObject, unknown>;
+	return description !== undefined;
+}
+
+function filterUndefined<T>(item: T): item is Exclude<T, undefined> {
+	return item !== undefined;
+}
+
+function toImportPath(fileName: string, fromPath = '/built/autogen', toPath = ''): string {
+	return fileName.replace(fromPath, toPath).replace('.ts', '.js');
+}
+
+enum OperationsAliasType {
+	REQUEST = 'Request',
+	RESPONSE = 'Response'
+}
+
+interface IOperationTypeAlias {
+	readonly type: OperationsAliasType
+
+	generateName(): string
+
+	toLine(): string
+}
+
+class OperationTypeAlias implements IOperationTypeAlias {
+	public readonly operationId: string;
+	public readonly mediaType: string;
+	public readonly type: OperationsAliasType;
+
+	constructor(
+		operationId: string,
+		mediaType: string,
+		type: OperationsAliasType,
+	) {
+		this.operationId = operationId;
+		this.mediaType = mediaType;
+		this.type = type;
+	}
+
+	generateName(): string {
+		const nameBase = this.operationId.replace(/\//g, '-');
+		return toPascal(nameBase + this.type);
+	}
+
+	toLine(): string {
+		const name = this.generateName();
+		return (this.type === OperationsAliasType.REQUEST)
+			? `export type ${name} = operations['${this.operationId}']['requestBody']['content']['${this.mediaType}'];`
+			: `export type ${name} = operations['${this.operationId}']['responses']['200']['content']['${this.mediaType}'];`;
+	}
+}
+
+class EmptyTypeAlias implements IOperationTypeAlias {
+	readonly type: OperationsAliasType;
+
+	constructor(type: OperationsAliasType) {
+		this.type = type;
+	}
+
+	generateName(): string {
+		return 'Empty' + this.type;
+	}
+
+	toLine(): string {
+		const name = this.generateName();
+		return `export type ${name} = Record<string, unknown> | undefined;`;
+	}
+}
+
+const emptyRequest = new EmptyTypeAlias(OperationsAliasType.REQUEST);
+const emptyResponse = new EmptyTypeAlias(OperationsAliasType.RESPONSE);
+
+class Endpoint {
+	public readonly operationId: string;
+	public request?: IOperationTypeAlias;
+	public response?: IOperationTypeAlias;
+
+	constructor(operationId: string) {
+		this.operationId = operationId;
+	}
+
+	toLine(): string {
+		const reqName = this.request?.generateName() ?? emptyRequest.generateName();
+		const resName = this.response?.generateName() ?? emptyResponse.generateName();
+
+		return `'${this.operationId}': { req: ${reqName}; res: ${resName} };`;
+	}
+}
+
+async function main() {
+	const generatePath = './built/autogen';
+	await mkdir(generatePath, { recursive: true });
+
+	const openApiJsonPath = './api.json';
+	const openApiDocs = await SwaggerParser.validate(openApiJsonPath) as OpenAPIV3.Document;
+
+	const typeFileName = './built/autogen/types.ts';
+	await generateBaseTypes(openApiDocs, openApiJsonPath, typeFileName);
+
+	const modelFileName = `${generatePath}/models.ts`;
+	await generateSchemaEntities(openApiDocs, typeFileName, modelFileName);
+
+	const entitiesFileName = `${generatePath}/entities.ts`;
+	const endpointFileName = `${generatePath}/endpoint.ts`;
+	await generateEndpoints(openApiDocs, typeFileName, entitiesFileName, endpointFileName);
+}
+
+main();
diff --git a/packages/misskey-js/generator/tsconfig.json b/packages/misskey-js/generator/tsconfig.json
new file mode 100644
index 0000000000..c814df612e
--- /dev/null
+++ b/packages/misskey-js/generator/tsconfig.json
@@ -0,0 +1,20 @@
+{
+	"$schema": "https://json.schemastore.org/tsconfig",
+	"compilerOptions": {
+		"target": "ESNext",
+		"module": "ESNext",
+		"moduleResolution": "nodenext",
+		"strict": true,
+		"strictFunctionTypes": true,
+		"strictNullChecks": true,
+		"esModuleInterop": true,
+		"lib": [
+			"esnext",
+		]
+	},
+	"include": [
+		"src/**/*.ts",
+		"built/**/*.ts"
+	],
+	"exclude": []
+}
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 865d25146a..1d21923132 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -13,7 +13,8 @@
 		"typecheck": "tsc --noEmit",
 		"lint": "pnpm typecheck && pnpm eslint",
 		"jest": "jest --coverage --detectOpenHandles",
-		"test": "pnpm jest && pnpm tsd"
+		"test": "pnpm jest && pnpm tsd",
+		"update-autogen-code": "pnpm --filter misskey-js-type-generator generate && ncp generator/built/autogen src/autogen"
 	},
 	"repository": {
 		"type": "git",
@@ -32,7 +33,8 @@
 		"jest-websocket-mock": "2.5.0",
 		"mock-socket": "9.3.1",
 		"tsd": "0.29.0",
-		"typescript": "5.3.2"
+		"typescript": "5.3.2",
+		"ncp": "2.0.0"
 	},
 	"files": [
 		"built"
diff --git a/packages/misskey-js/src/api.ts b/packages/misskey-js/src/api.ts
index 9415e692e3..c2fa4f1790 100644
--- a/packages/misskey-js/src/api.ts
+++ b/packages/misskey-js/src/api.ts
@@ -1,4 +1,9 @@
-import type { Endpoints } from './api.types.js';
+import { SwitchCaseResponseType } from './api.types';
+import type { Endpoints } from './api.types';
+
+export {
+	SwitchCaseResponseType,
+} from './api.types';
 
 const MK_API_ERROR = Symbol();
 
@@ -15,25 +20,15 @@ export function isAPIError(reason: any): reason is APIError {
 }
 
 export type FetchLike = (input: string, init?: {
-		method?: string;
-		body?: string;
-		credentials?: RequestCredentials;
-		cache?: RequestCache;
-		headers: {[key in string]: string}
-	}) => Promise<{
-		status: number;
-		json(): Promise<any>;
-	}>;
-
-type IsNeverType<T> = [T] extends [never] ? true : false;
-
-type StrictExtract<Union, Cond> = Cond extends Union ? Union : never;
-
-type IsCaseMatched<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
-	IsNeverType<StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>> extends false ? true : false;
-
-type GetCaseResult<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
-	StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>[1];
+	method?: string;
+	body?: string;
+	credentials?: RequestCredentials;
+	cache?: RequestCache;
+	headers: { [key in string]: string }
+}) => Promise<{
+	status: number;
+	json(): Promise<any>;
+}>;
 
 export class APIClient {
 	public origin: string;
@@ -53,22 +48,11 @@ export class APIClient {
 	}
 
 	public request<E extends keyof Endpoints, P extends Endpoints[E]['req']>(
-		endpoint: E, params: P = {} as P, credential?: string | null | undefined,
-	): Promise<Endpoints[E]['res'] extends { $switch: { $cases: [any, any][]; $default: any; }; }
-		?
-			IsCaseMatched<E, P, 0> extends true ? GetCaseResult<E, P, 0> :
-			IsCaseMatched<E, P, 1> extends true ? GetCaseResult<E, P, 1> :
-			IsCaseMatched<E, P, 2> extends true ? GetCaseResult<E, P, 2> :
-			IsCaseMatched<E, P, 3> extends true ? GetCaseResult<E, P, 3> :
-			IsCaseMatched<E, P, 4> extends true ? GetCaseResult<E, P, 4> :
-			IsCaseMatched<E, P, 5> extends true ? GetCaseResult<E, P, 5> :
-			IsCaseMatched<E, P, 6> extends true ? GetCaseResult<E, P, 6> :
-			IsCaseMatched<E, P, 7> extends true ? GetCaseResult<E, P, 7> :
-			IsCaseMatched<E, P, 8> extends true ? GetCaseResult<E, P, 8> :
-			IsCaseMatched<E, P, 9> extends true ? GetCaseResult<E, P, 9> :
-			Endpoints[E]['res']['$switch']['$default']
-		: Endpoints[E]['res']> {
-		const promise = new Promise((resolve, reject) => {
+		endpoint: E,
+		params: P = {} as P,
+		credential?: string | null,
+	): Promise<SwitchCaseResponseType<E, P>> {
+		return new Promise((resolve, reject) => {
 			this.fetch(`${this.origin}/api/${endpoint}`, {
 				method: 'POST',
 				body: JSON.stringify({
@@ -83,10 +67,8 @@ export class APIClient {
 			}).then(async (res) => {
 				const body = res.status === 204 ? null : await res.json();
 
-				if (res.status === 200) {
+				if (res.status === 200 || res.status === 204) {
 					resolve(body);
-				} else if (res.status === 204) {
-					resolve(null);
 				} else {
 					reject({
 						[MK_API_ERROR]: true,
@@ -95,7 +77,5 @@ export class APIClient {
 				}
 			}).catch(reject);
 		});
-
-		return promise as any;
 	}
 }
diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts
index ba333231e5..d97646b7cc 100644
--- a/packages/misskey-js/src/api.types.ts
+++ b/packages/misskey-js/src/api.types.ts
@@ -1,651 +1,60 @@
-import type {
-	Ad, Announcement, Antenna, App, AuthSession, Blocking, Channel, Clip, DateString, DetailedInstanceMetadata, DriveFile, DriveFolder, Following, FollowingFolloweePopulated, FollowingFollowerPopulated, FollowRequest, GalleryPost, Instance,
-	LiteInstanceMetadata,
-	MeDetailed,
-	Note, NoteFavorite, OriginType, Page, ServerInfo, Stats, User, UserDetailed, MeSignup, UserGroup, UserList, UserSorting, Notification, NoteReaction, Signin, MessagingMessage, Invite, InviteLimit, AdminInstanceMetadata,
-} from './entities.js';
+import { Endpoints as Gen } from './autogen/endpoint';
+import { UserDetailed } from './autogen/models';
+import { UsersShowRequest } from './autogen/entities';
 
-type TODO = Record<string, any> | null;
+type Overwrite<T, U extends { [Key in keyof T]?: unknown }> = Omit<
+	T,
+	keyof U
+> & U;
 
-type NoParams = Record<string, never>;
-
-type ShowUserReq = { username: string; host?: string; } | { userId: User['id']; };
-
-export type Endpoints = {
-	// admin
-	'admin/abuse-user-reports': { req: TODO; res: TODO; };
-	'admin/delete-all-files-of-a-user': { req: { userId: User['id']; }; res: null; };
-	'admin/unset-user-avatar': { req: { userId: User['id']; }; res: null; };
-	'admin/unset-user-banner': { req: { userId: User['id']; }; res: null; };
-	'admin/delete-logs': { req: NoParams; res: null; };
-	'admin/get-index-stats': { req: TODO; res: TODO; };
-	'admin/get-table-stats': { req: TODO; res: TODO; };
-	'admin/invite': { req: TODO; res: TODO; };
-	'admin/logs': { req: TODO; res: TODO; };
-	'admin/meta': { req: NoParams; res: AdminInstanceMetadata; };
-	'admin/reset-password': { req: TODO; res: TODO; };
-	'admin/resolve-abuse-user-report': { req: TODO; res: TODO; };
-	'admin/resync-chart': { req: TODO; res: TODO; };
-	'admin/send-email': { req: TODO; res: TODO; };
-	'admin/server-info': { req: TODO; res: TODO; };
-	'admin/show-moderation-logs': { req: TODO; res: TODO; };
-	'admin/show-user': { req: TODO; res: TODO; };
-	'admin/show-users': { req: TODO; res: TODO; };
-	'admin/silence-user': { req: TODO; res: TODO; };
-	'admin/suspend-user': { req: TODO; res: TODO; };
-	'admin/unsilence-user': { req: TODO; res: TODO; };
-	'admin/unsuspend-user': { req: TODO; res: TODO; };
-	'admin/update-meta': { req: TODO; res: TODO; };
-	'admin/vacuum': { req: TODO; res: TODO; };
-	'admin/accounts/create': { req: TODO; res: TODO; };
-	'admin/ad/create': { req: TODO; res: TODO; };
-	'admin/ad/delete': { req: { id: Ad['id']; }; res: null; };
-	'admin/ad/list': { req: TODO; res: TODO; };
-	'admin/ad/update': { req: TODO; res: TODO; };
-	'admin/announcements/create': { req: TODO; res: TODO; };
-	'admin/announcements/delete': { req: { id: Announcement['id'] }; res: null; };
-	'admin/announcements/list': { req: TODO; res: TODO; };
-	'admin/announcements/update': { req: TODO; res: TODO; };
-	'admin/drive/clean-remote-files': { req: TODO; res: TODO; };
-	'admin/drive/cleanup': { req: TODO; res: TODO; };
-	'admin/drive/files': { req: TODO; res: TODO; };
-	'admin/drive/show-file': { req: TODO; res: TODO; };
-	'admin/emoji/add': { req: TODO; res: TODO; };
-	'admin/emoji/copy': { req: TODO; res: TODO; };
-	'admin/emoji/list-remote': { req: TODO; res: TODO; };
-	'admin/emoji/list': { req: TODO; res: TODO; };
-	'admin/emoji/remove': { req: TODO; res: TODO; };
-	'admin/emoji/update': { req: TODO; res: TODO; };
-	'admin/federation/delete-all-files': { req: { host: string; }; res: null; };
-	'admin/federation/refresh-remote-instance-metadata': { req: TODO; res: TODO; };
-	'admin/federation/remove-all-following': { req: TODO; res: TODO; };
-	'admin/federation/update-instance': { req: TODO; res: TODO; };
-	'admin/invite/create': { req: TODO; res: TODO; };
-	'admin/invite/list': { req: TODO; res: TODO; };
-	'admin/moderators/add': { req: TODO; res: TODO; };
-	'admin/moderators/remove': { req: TODO; res: TODO; };
-	'admin/promo/create': { req: TODO; res: TODO; };
-	'admin/queue/clear': { req: TODO; res: TODO; };
-	'admin/queue/deliver-delayed': { req: TODO; res: TODO; };
-	'admin/queue/inbox-delayed': { req: TODO; res: TODO; };
-	'admin/queue/jobs': { req: TODO; res: TODO; };
-	'admin/queue/stats': { req: TODO; res: TODO; };
-	'admin/relays/add': { req: TODO; res: TODO; };
-	'admin/relays/list': { req: TODO; res: TODO; };
-	'admin/relays/remove': { req: TODO; res: TODO; };
-
-	// announcements
-	'announcements': { req: { limit?: number; withUnreads?: boolean; sinceId?: Announcement['id']; untilId?: Announcement['id']; }; res: Announcement[]; };
-
-	// antennas
-	'antennas/create': { req: TODO; res: Antenna; };
-	'antennas/delete': { req: { antennaId: Antenna['id']; }; res: null; };
-	'antennas/list': { req: NoParams; res: Antenna[]; };
-	'antennas/notes': { req: { antennaId: Antenna['id']; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; }; res: Note[]; };
-	'antennas/show': { req: { antennaId: Antenna['id']; }; res: Antenna; };
-	'antennas/update': { req: TODO; res: Antenna; };
-
-	// ap
-	'ap/get': { req: { uri: string; }; res: Record<string, any>; };
-	'ap/show': { req: { uri: string; }; res: {
-		type: 'Note';
-		object: Note;
-	} | {
-		type: 'User';
-		object: UserDetailed;
-	}; };
-
-	// app
-	'app/create': { req: TODO; res: App; };
-	'app/show': { req: { appId: App['id']; }; res: App; };
-
-	// auth
-	'auth/accept': { req: { token: string; }; res: null; };
-	'auth/session/generate': { req: { appSecret: string; }; res: { token: string; url: string; }; };
-	'auth/session/show': { req: { token: string; }; res: AuthSession; };
-	'auth/session/userkey': { req: { appSecret: string; token: string; }; res: { accessToken: string; user: User }; };
-
-	// blocking
-	'blocking/create': { req: { userId: User['id'] }; res: UserDetailed; };
-	'blocking/delete': { req: { userId: User['id'] }; res: UserDetailed; };
-	'blocking/list': { req: { limit?: number; sinceId?: Blocking['id']; untilId?: Blocking['id']; }; res: Blocking[]; };
-
-	// channels
-	'channels/create': { req: TODO; res: TODO; };
-	'channels/featured': { req: TODO; res: TODO; };
-	'channels/follow': { req: TODO; res: TODO; };
-	'channels/followed': { req: TODO; res: TODO; };
-	'channels/owned': { req: TODO; res: TODO; };
-	'channels/pin-note': { req: TODO; res: TODO; };
-	'channels/show': { req: TODO; res: TODO; };
-	'channels/timeline': { req: TODO; res: TODO; };
-	'channels/unfollow': { req: TODO; res: TODO; };
-	'channels/update': { req: TODO; res: TODO; };
-
-	// charts
-	'charts/active-users': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: {
-		local: {
-			users: number[];
-		};
-		remote: {
-			users: number[];
-		};
-	}; };
-	'charts/drive': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: {
-		local: {
-			decCount: number[];
-			decSize: number[];
-			incCount: number[];
-			incSize: number[];
-			totalCount: number[];
-			totalSize: number[];
-		};
-		remote: {
-			decCount: number[];
-			decSize: number[];
-			incCount: number[];
-			incSize: number[];
-			totalCount: number[];
-			totalSize: number[];
-		};
-	}; };
-	'charts/federation': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: {
-		instance: {
-			dec: number[];
-			inc: number[];
-			total: number[];
-		};
-	}; };
-	'charts/hashtag': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: TODO; };
-	'charts/instance': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; host: string; }; res: {
-		drive: {
-			decFiles: number[];
-			decUsage: number[];
-			incFiles: number[];
-			incUsage: number[];
-			totalFiles: number[];
-			totalUsage: number[];
-		};
-		followers: {
-			dec: number[];
-			inc: number[];
-			total: number[];
-		};
-		following: {
-			dec: number[];
-			inc: number[];
-			total: number[];
-		};
-		notes: {
-			dec: number[];
-			inc: number[];
-			total: number[];
-			diffs: {
-				normal: number[];
-				renote: number[];
-				reply: number[];
-			};
-		};
-		requests: {
-			failed: number[];
-			received: number[];
-			succeeded: number[];
-		};
-		users: {
-			dec: number[];
-			inc: number[];
-			total: number[];
-		};
-	}; };
-	'charts/network': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: TODO; };
-	'charts/notes': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: {
-		local: {
-			dec: number[];
-			inc: number[];
-			total: number[];
-			diffs: {
-				normal: number[];
-				renote: number[];
-				reply: number[];
-			};
-		};
-		remote: {
-			dec: number[];
-			inc: number[];
-			total: number[];
-			diffs: {
-				normal: number[];
-				renote: number[];
-				reply: number[];
-			};
-		};
-	}; };
-	'charts/user/drive': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; userId: User['id']; }; res: {
-		decCount: number[];
-		decSize: number[];
-		incCount: number[];
-		incSize: number[];
-		totalCount: number[];
-		totalSize: number[];
-	}; };
-	'charts/user/following': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; userId: User['id']; }; res: TODO; };
-	'charts/user/notes': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; userId: User['id']; }; res: {
-		dec: number[];
-		inc: number[];
-		total: number[];
-		diffs: {
-			normal: number[];
-			renote: number[];
-			reply: number[];
-		};
-	}; };
-	'charts/user/reactions': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; userId: User['id']; }; res: TODO; };
-	'charts/users': { req: { span: 'day' | 'hour'; limit?: number; offset?: number | null; }; res: {
-		local: {
-			dec: number[];
-			inc: number[];
-			total: number[];
-		};
-		remote: {
-			dec: number[];
-			inc: number[];
-			total: number[];
-		};
-	}; };
-
-	// clips
-	'clips/add-note': { req: TODO; res: TODO; };
-	'clips/create': { req: TODO; res: TODO; };
-	'clips/delete': { req: { clipId: Clip['id']; }; res: null; };
-	'clips/list': { req: TODO; res: TODO; };
-	'clips/notes': { req: TODO; res: TODO; };
-	'clips/show': { req: TODO; res: TODO; };
-	'clips/update': { req: TODO; res: TODO; };
-
-	// drive
-	'drive': { req: NoParams; res: { capacity: number; usage: number; }; };
-	'drive/files': { req: { folderId?: DriveFolder['id'] | null; type?: DriveFile['type'] | null; limit?: number; sinceId?: DriveFile['id']; untilId?: DriveFile['id']; }; res: DriveFile[]; };
-	'drive/files/attached-notes': { req: TODO; res: TODO; };
-	'drive/files/check-existence': { req: TODO; res: TODO; };
-	'drive/files/create': {
-		req: {
-			folderId?: string,
-			name?: string,
-			comment?: string,
-			isSentisive?: boolean,
-			force?: boolean,
-		};
-		res: DriveFile;
-	};
-	'drive/files/delete': { req: { fileId: DriveFile['id']; }; res: null; };
-	'drive/files/find-by-hash': { req: TODO; res: TODO; };
-	'drive/files/find': { req: { name: string; folderId?: DriveFolder['id'] | null; }; res: DriveFile[]; };
-	'drive/files/show': { req: { fileId?: DriveFile['id']; url?: string; }; res: DriveFile; };
-	'drive/files/update': { req: { fileId: DriveFile['id']; folderId?: DriveFolder['id'] | null; name?: string; isSensitive?: boolean; comment?: string | null; }; res: DriveFile; };
-	'drive/files/upload-from-url': { req: { url: string; folderId?: DriveFolder['id'] | null; isSensitive?: boolean; comment?: string | null; marker?: string | null; force?: boolean; }; res: null; };
-	'drive/folders': { req: { folderId?: DriveFolder['id'] | null; limit?: number; sinceId?: DriveFile['id']; untilId?: DriveFile['id']; }; res: DriveFolder[]; };
-	'drive/folders/create': { req: { name?: string; parentId?: DriveFolder['id'] | null; }; res: DriveFolder; };
-	'drive/folders/delete': { req: { folderId: DriveFolder['id']; }; res: null; };
-	'drive/folders/find': { req: { name: string; parentId?: DriveFolder['id'] | null; }; res: DriveFolder[]; };
-	'drive/folders/show': { req: { folderId: DriveFolder['id']; }; res: DriveFolder; };
-	'drive/folders/update': { req: { folderId: DriveFolder['id']; name?: string; parentId?: DriveFolder['id'] | null; }; res: DriveFolder; };
-	'drive/stream': { req: { type?: DriveFile['type'] | null; limit?: number; sinceId?: DriveFile['id']; untilId?: DriveFile['id']; }; res: DriveFile[]; };
-
-	// endpoint
-	'endpoint': { req: { endpoint: string; }; res: { params: { name: string; type: string; }[]; }; };
-
-	// endpoints
-	'endpoints': { req: NoParams; res: string[]; };
-
-	// federation
-	'federation/dns': { req: { host: string; }; res: {
-		a: string[];
-		aaaa: string[];
-		cname: string[];
-		txt: string[];
-	}; };
-	'federation/followers': { req: { host: string; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFolloweePopulated[]; };
-	'federation/following': { req: { host: string; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFolloweePopulated[]; };
-	'federation/instances': { req: {
-		host?: string | null;
-		blocked?: boolean | null;
-		notResponding?: boolean | null;
-		suspended?: boolean | null;
-		federating?: boolean | null;
-		subscribing?: boolean | null;
-		publishing?: boolean | null;
-		limit?: number;
-		offset?: number;
-		sort?: '+pubSub' | '-pubSub' | '+notes' | '-notes' | '+users' | '-users' | '+following' | '-following' | '+followers' | '-followers' | '+caughtAt' | '-caughtAt' | '+lastCommunicatedAt' | '-lastCommunicatedAt' | '+driveUsage' | '-driveUsage' | '+driveFiles' | '-driveFiles';
-	}; res: Instance[]; };
-	'federation/show-instance': { req: { host: string; }; res: Instance; };
-	'federation/update-remote-user': { req: { userId: User['id']; }; res: null; };
-	'federation/users': { req: { host: string; limit?: number; sinceId?: User['id']; untilId?: User['id']; }; res: UserDetailed[]; };
-
-	// following
-	'following/create': { req: {
-		userId: User['id'],
-		withReplies?: boolean,
-	}; res: User; };
-	'following/delete': { req: { userId: User['id'] }; res: User; };
-	'following/requests/accept': { req: { userId: User['id'] }; res: null; };
-	'following/requests/cancel': { req: { userId: User['id'] }; res: User; };
-	'following/requests/list': { req: NoParams; res: FollowRequest[]; };
-	'following/requests/reject': { req: { userId: User['id'] }; res: null; };
-
-	// gallery
-	'gallery/featured': { req: null; res: GalleryPost[]; };
-	'gallery/popular': { req: null; res: GalleryPost[]; };
-	'gallery/posts': { req: { limit?: number; sinceId?: GalleryPost['id']; untilId?: GalleryPost['id']; }; res: GalleryPost[]; };
-	'gallery/posts/create': { req: { title: GalleryPost['title']; description?: GalleryPost['description']; fileIds: GalleryPost['fileIds']; isSensitive?: GalleryPost['isSensitive'] }; res: GalleryPost; };
-	'gallery/posts/delete': { req: { postId: GalleryPost['id'] }; res: null; };
-	'gallery/posts/like': { req: { postId: GalleryPost['id'] }; res: null; };
-	'gallery/posts/show': { req: { postId: GalleryPost['id'] }; res: GalleryPost; };
-	'gallery/posts/unlike': { req: { postId: GalleryPost['id'] }; res: null; };
-	'gallery/posts/update': { req: { postId: GalleryPost['id']; title: GalleryPost['title']; description?: GalleryPost['description']; fileIds: GalleryPost['fileIds']; isSensitive?: GalleryPost['isSensitive'] }; res: GalleryPost; };
-
-	// games
-	'games/reversi/games': { req: TODO; res: TODO; };
-	'games/reversi/games/show': { req: TODO; res: TODO; };
-	'games/reversi/games/surrender': { req: TODO; res: TODO; };
-	'games/reversi/invitations': { req: TODO; res: TODO; };
-	'games/reversi/match': { req: TODO; res: TODO; };
-	'games/reversi/match/cancel': { req: TODO; res: TODO; };
-
-	// get-online-users-count
-	'get-online-users-count': { req: NoParams; res: { count: number; }; };
-
-	// hashtags
-	'hashtags/list': { req: TODO; res: TODO; };
-	'hashtags/search': { req: TODO; res: TODO; };
-	'hashtags/show': { req: TODO; res: TODO; };
-	'hashtags/trend': { req: TODO; res: TODO; };
-	'hashtags/users': { req: TODO; res: TODO; };
-
-	// i
-	'i': { req: NoParams; res: User; };
-	'i/apps': { req: TODO; res: TODO; };
-	'i/authorized-apps': { req: TODO; res: TODO; };
-	'i/change-password': { req: TODO; res: TODO; };
-	'i/delete-account': { req: { password: string; }; res: null; };
-	'i/export-blocking': { req: TODO; res: TODO; };
-	'i/export-following': { req: TODO; res: TODO; };
-	'i/export-mute': { req: TODO; res: TODO; };
-	'i/export-notes': { req: TODO; res: TODO; };
-	'i/export-user-lists': { req: TODO; res: TODO; };
-	'i/favorites': { req: { limit?: number; sinceId?: NoteFavorite['id']; untilId?: NoteFavorite['id']; }; res: NoteFavorite[]; };
-	'i/gallery/likes': { req: TODO; res: TODO; };
-	'i/gallery/posts': { req: TODO; res: TODO; };
-	'i/import-following': { req: TODO; res: TODO; };
-	'i/import-user-lists': { req: TODO; res: TODO; };
-	'i/move': { req: TODO; res: TODO; };
-	'i/notifications': { req: {
-		limit?: number;
-		sinceId?: Notification['id'];
-		untilId?: Notification['id'];
-		following?: boolean;
-		markAsRead?: boolean;
-		includeTypes?: Notification['type'][];
-		excludeTypes?: Notification['type'][];
-	}; res: Notification[]; };
-	'i/page-likes': { req: TODO; res: TODO; };
-	'i/pages': { req: TODO; res: TODO; };
-	'i/pin': { req: { noteId: Note['id']; }; res: MeDetailed; };
-	'i/read-all-messaging-messages': { req: TODO; res: TODO; };
-	'i/read-all-unread-notes': { req: TODO; res: TODO; };
-	'i/read-announcement': { req: TODO; res: TODO; };
-	'i/regenerate-token': { req: { password: string; }; res: null; };
-	'i/registry/get-all': { req: { scope?: string[]; }; res: Record<string, any>; };
-	'i/registry/get-detail': { req: { key: string; scope?: string[]; }; res: { updatedAt: DateString; value: any; }; };
-	'i/registry/get': { req: { key: string; scope?: string[]; }; res: any; };
-	'i/registry/keys-with-type': { req: { scope?: string[]; }; res: Record<string, 'null' | 'array' | 'number' | 'string' | 'boolean' | 'object'>; };
-	'i/registry/keys': { req: { scope?: string[]; }; res: string[]; };
-	'i/registry/remove': { req: { key: string; scope?: string[]; }; res: null; };
-	'i/registry/set': { req: { key: string; value: any; scope?: string[]; }; res: null; };
-	'i/revoke-token': { req: TODO; res: TODO; };
-	'i/signin-history': { req: { limit?: number; sinceId?: Signin['id']; untilId?: Signin['id']; }; res: Signin[]; };
-	'i/unpin': { req: { noteId: Note['id']; }; res: MeDetailed; };
-	'i/update-email': { req: {
-		password: string;
-		email?: string | null;
-	}; res: MeDetailed; };
-	'i/update': { req: {
-		name?: string | null;
-		description?: string | null;
-		lang?: string | null;
-		location?: string | null;
-		birthday?: string | null;
-		avatarId?: DriveFile['id'] | null;
-		bannerId?: DriveFile['id'] | null;
-		fields?: {
-			name: string;
-			value: string;
-		}[];
-		isLocked?: boolean;
-		isExplorable?: boolean;
-		hideOnlineStatus?: boolean;
-		carefulBot?: boolean;
-		autoAcceptFollowed?: boolean;
-		noCrawle?: boolean;
-		isBot?: boolean;
-		isCat?: boolean;
-		injectFeaturedNote?: boolean;
-		receiveAnnouncementEmail?: boolean;
-		alwaysMarkNsfw?: boolean;
-		mutedWords?: (string[] | string)[];
-		hardMutedWords?: (string[] | string)[];
-		notificationRecieveConfig?: any;
-		emailNotificationTypes?: string[];
-		alsoKnownAs?: string[];
-	}; res: MeDetailed; };
-	'i/user-group-invites': { req: TODO; res: TODO; };
-	'i/2fa/done': { req: TODO; res: TODO; };
-	'i/2fa/key-done': { req: TODO; res: TODO; };
-	'i/2fa/password-less': { req: TODO; res: TODO; };
-	'i/2fa/register-key': { req: TODO; res: TODO; };
-	'i/2fa/register': { req: TODO; res: TODO; };
-	'i/2fa/remove-key': { req: TODO; res: TODO; };
-	'i/2fa/unregister': { req: TODO; res: TODO; };
-
-	// invite
-	'invite/create': { req: NoParams; res: Invite; };
-	'invite/delete': { req: { inviteId: Invite['id']; }; res: null; };
-	'invite/list': { req: { limit?: number; sinceId?: Invite['id']; untilId?: Invite['id'] }; res: Invite[]; };
-	'invite/limit': { req: NoParams; res: InviteLimit; };
-
-	// messaging
-	'messaging/history': { req: { limit?: number; group?: boolean; }; res: MessagingMessage[]; };
-	'messaging/messages': { req: { userId?: User['id']; groupId?: UserGroup['id']; limit?: number; sinceId?: MessagingMessage['id']; untilId?: MessagingMessage['id']; markAsRead?: boolean; }; res: MessagingMessage[]; };
-	'messaging/messages/create': { req: { userId?: User['id']; groupId?: UserGroup['id']; text?: string; fileId?: DriveFile['id']; }; res: MessagingMessage; };
-	'messaging/messages/delete': { req: { messageId: MessagingMessage['id']; }; res: null; };
-	'messaging/messages/read': { req: { messageId: MessagingMessage['id']; }; res: null; };
-
-	// meta
-	'meta': { req: { detail?: boolean; }; res: {
-		$switch: {
-			$cases: [[
-				{ detail: true; },
-				DetailedInstanceMetadata,
-			], [
-				{ detail: false; },
-				LiteInstanceMetadata,
-			], [
-				{ detail: boolean; },
-				LiteInstanceMetadata | DetailedInstanceMetadata,
-			]];
-			$default: LiteInstanceMetadata;
-		};
-	}; };
-
-	// miauth
-	'miauth/gen-token': { req: TODO; res: TODO; };
-
-	// mute
-	'mute/create': { req: TODO; res: TODO; };
-	'mute/delete': { req: { userId: User['id'] }; res: null; };
-	'mute/list': { req: TODO; res: TODO; };
-
-	// my
-	'my/apps': { req: TODO; res: TODO; };
-
-	// notes
-	'notes': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; }; res: Note[]; };
-	'notes/children': { req: { noteId: Note['id']; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; }; res: Note[]; };
-	'notes/clips': { req: TODO; res: TODO; };
-	'notes/conversation': { req: TODO; res: TODO; };
-	'notes/create': { req: {
-		visibility?: 'public' | 'home' | 'followers' | 'specified',
-		visibleUserIds?: User['id'][];
-		text?: null | string;
-		cw?: null | string;
-		viaMobile?: boolean;
-		localOnly?: boolean;
-		fileIds?: DriveFile['id'][];
-		replyId?: null | Note['id'];
-		renoteId?: null | Note['id'];
-		channelId?: null | Channel['id'];
-		poll?: null | {
-			choices: string[];
-			multiple?: boolean;
-			expiresAt?: null | number;
-			expiredAfter?: null | number;
-		};
-	}; res: { createdNote: Note }; };
-	'notes/delete': { req: { noteId: Note['id']; }; res: null; };
-	'notes/favorites/create': { req: { noteId: Note['id']; }; res: null; };
-	'notes/favorites/delete': { req: { noteId: Note['id']; }; res: null; };
-	'notes/featured': { req: TODO; res: Note[]; };
-	'notes/global-timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; };
-	'notes/hybrid-timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; };
-	'notes/local-timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; };
-	'notes/mentions': { req: { following?: boolean; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; }; res: Note[]; };
-	'notes/polls/recommendation': { req: TODO; res: TODO; };
-	'notes/polls/vote': { req: { noteId: Note['id']; choice: number; }; res: null; };
-	'notes/reactions': { req: { noteId: Note['id']; type?: string | null; limit?: number; }; res: NoteReaction[]; };
-	'notes/reactions/create': { req: { noteId: Note['id']; reaction: string; }; res: null; };
-	'notes/reactions/delete': { req: { noteId: Note['id']; }; res: null; };
-	'notes/renotes': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; noteId: Note['id']; }; res: Note[]; };
-	'notes/replies': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; noteId: Note['id']; }; res: Note[]; };
-	'notes/search-by-tag': { req: TODO; res: TODO; };
-	'notes/search': { req: TODO; res: TODO; };
-	'notes/show': { req: { noteId: Note['id']; }; res: Note; };
-	'notes/state': { req: TODO; res: TODO; };
-	'notes/timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; };
-	'notes/unrenote': { req: { noteId: Note['id']; }; res: null; };
-	'notes/user-list-timeline': { req: { listId: UserList['id']; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; };
-	'notes/watching/create': { req: TODO; res: TODO; };
-	'notes/watching/delete': { req: { noteId: Note['id']; }; res: null; };
-
-	// notifications
-	'notifications/create': { req: { body: string; header?: string | null; icon?: string | null; }; res: null; };
-	'notifications/test-notification': { req: NoParams; res: null; };
-	'notifications/mark-all-as-read': { req: NoParams; res: null; };
-
-	// page-push
-	'page-push': { req: { pageId: Page['id']; event: string; var?: any; }; res: null; };
-
-	// pages
-	'pages/create': { req: TODO; res: Page; };
-	'pages/delete': { req: { pageId: Page['id']; }; res: null; };
-	'pages/featured': { req: NoParams; res: Page[]; };
-	'pages/like': { req: { pageId: Page['id']; }; res: null; };
-	'pages/show': { req: { pageId?: Page['id']; name?: string; username?: string; }; res: Page; };
-	'pages/unlike': { req: { pageId: Page['id']; }; res: null; };
-	'pages/update': { req: TODO; res: null; };
-
-	// ping
-	'ping': { req: NoParams; res: { pong: number; }; };
-
-	// pinned-users
-	'pinned-users': { req: TODO; res: TODO; };
-
-	// promo
-	'promo/read': { req: TODO; res: TODO; };
-
-	// request-reset-password
-	'request-reset-password': { req: { username: string; email: string; }; res: null; };
-
-	// reset-password
-	'reset-password': { req: { token: string; password: string; }; res: null; };
-
-	// room
-	'room/show': { req: TODO; res: TODO; };
-	'room/update': { req: TODO; res: TODO; };
-
-	// signup
-	'signup': {
-		req: {
-			username: string;
-			password: string;
-			host?: string;
-			invitationCode?: string;
-			emailAddress?: string;
-			'hcaptcha-response'?: string;
-			'g-recaptcha-response'?: string;
-			'turnstile-response'?: string;
-		};
-		res: MeSignup | null;
-	};
-
-	// stats
-	'stats': { req: NoParams; res: Stats; };
-
-	// server-info
-	'server-info': { req: NoParams; res: ServerInfo; };
-
-	// sw
-	'sw/register': { req: TODO; res: TODO; };
-
-	// username
-	'username/available': { req: { username: string; }; res: { available: boolean; }; };
-
-	// users
-	'users': { req: { limit?: number; offset?: number; sort?: UserSorting; origin?: OriginType; }; res: User[]; };
-	'users/clips': { req: TODO; res: TODO; };
-	'users/followers': { req: { userId?: User['id']; username?: User['username']; host?: User['host'] | null; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFollowerPopulated[]; };
-	'users/following': { req: { userId?: User['id']; username?: User['username']; host?: User['host'] | null; limit?: number; sinceId?: Following['id']; untilId?: Following['id']; }; res: FollowingFolloweePopulated[]; };
-	'users/gallery/posts': { req: TODO; res: TODO; };
-	'users/get-frequently-replied-users': { req: TODO; res: TODO; };
-	'users/groups/create': { req: TODO; res: TODO; };
-	'users/groups/delete': { req: { groupId: UserGroup['id'] }; res: null; };
-	'users/groups/invitations/accept': { req: TODO; res: TODO; };
-	'users/groups/invitations/reject': { req: TODO; res: TODO; };
-	'users/groups/invite': { req: TODO; res: TODO; };
-	'users/groups/joined': { req: TODO; res: TODO; };
-	'users/groups/owned': { req: TODO; res: TODO; };
-	'users/groups/pull': { req: TODO; res: TODO; };
-	'users/groups/show': { req: TODO; res: TODO; };
-	'users/groups/transfer': { req: TODO; res: TODO; };
-	'users/groups/update': { req: TODO; res: TODO; };
-	'users/lists/create': { req: { name: string; }; res: UserList; };
-	'users/lists/delete': { req: { listId: UserList['id']; }; res: null; };
-	'users/lists/list': { req: NoParams; res: UserList[]; };
-	'users/lists/pull': { req: { listId: UserList['id']; userId: User['id']; }; res: null; };
-	'users/lists/push': { req: { listId: UserList['id']; userId: User['id']; }; res: null; };
-	'users/lists/show': { req: { listId: UserList['id']; }; res: UserList; };
-	'users/lists/update': { req: { listId: UserList['id']; name: string; }; res: UserList; };
-	'users/notes': { req: { userId: User['id']; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; };
-	'users/pages': { req: TODO; res: TODO; };
-	'users/flashs': { req: TODO; res: TODO; };
-	'users/recommendation': { req: TODO; res: TODO; };
-	'users/relation': { req: TODO; res: TODO; };
-	'users/report-abuse': { req: TODO; res: TODO; };
-	'users/search-by-username-and-host': { req: TODO; res: TODO; };
-	'users/search': { req: TODO; res: TODO; };
-	'users/show': { req: ShowUserReq | { userIds: User['id'][]; }; res: {
-		$switch: {
-			$cases: [[
-				{ userIds: User['id'][]; },
-				UserDetailed[],
-			]];
-			$default: UserDetailed;
-		};
-	}; };
-
-	// fetching external data
-	'fetch-rss': { req: { url: string; }; res: TODO; };
-	'fetch-external-resources': {
-		req: { url: string; hash: string; };
-		res: { type: string; data: string; };
+type SwitchCase = {
+	$switch: {
+		$cases: [any, any][],
+		$default: any;
 	};
 };
+
+type IsNeverType<T> = [T] extends [never] ? true : false;
+type StrictExtract<Union, Cond> = Cond extends Union ? Union : never;
+
+type IsCaseMatched<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
+	Endpoints[E]['res'] extends SwitchCase
+		? IsNeverType<StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>> extends false ? true : false
+		: false
+
+type GetCaseResult<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
+	Endpoints[E]['res'] extends SwitchCase
+		? StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>[1]
+		: never
+
+export type SwitchCaseResponseType<E extends keyof Endpoints, P extends Endpoints[E]['req']> = Endpoints[E]['res'] extends SwitchCase
+	? IsCaseMatched<E, P, 0> extends true ? GetCaseResult<E, P, 0> :
+		IsCaseMatched<E, P, 1> extends true ? GetCaseResult<E, P, 1> :
+			IsCaseMatched<E, P, 2> extends true ? GetCaseResult<E, P, 2> :
+				IsCaseMatched<E, P, 3> extends true ? GetCaseResult<E, P, 3> :
+					IsCaseMatched<E, P, 4> extends true ? GetCaseResult<E, P, 4> :
+						IsCaseMatched<E, P, 5> extends true ? GetCaseResult<E, P, 5> :
+							IsCaseMatched<E, P, 6> extends true ? GetCaseResult<E, P, 6> :
+								IsCaseMatched<E, P, 7> extends true ? GetCaseResult<E, P, 7> :
+									IsCaseMatched<E, P, 8> extends true ? GetCaseResult<E, P, 8> :
+										IsCaseMatched<E, P, 9> extends true ? GetCaseResult<E, P, 9> :
+											Endpoints[E]['res']['$switch']['$default'] : Endpoints[E]['res'];
+
+export type Endpoints = Overwrite<
+	Gen,
+	{
+		'users/show': {
+			req: UsersShowRequest;
+			res: {
+				$switch: {
+					$cases: [[
+						{
+							userIds?: string[];
+						}, UserDetailed[],
+					]];
+					$default: UserDetailed;
+				};
+			};
+		}
+	}
+>
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
new file mode 100644
index 0000000000..64739a65d0
--- /dev/null
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -0,0 +1,802 @@
+/*
+ * version: 2023.11.1
+ * generatedAt: 2023-11-27T02:24:45.113Z
+ */
+
+import type {
+	EmptyRequest,
+	EmptyResponse,
+	AdminMetaResponse,
+	AdminAbuseUserReportsRequest,
+	AdminAbuseUserReportsResponse,
+	AdminAccountsCreateRequest,
+	AdminAccountsCreateResponse,
+	AdminAccountsDeleteRequest,
+	AdminAccountsFindByEmailRequest,
+	AdminAdCreateRequest,
+	AdminAdDeleteRequest,
+	AdminAdListRequest,
+	AdminAdUpdateRequest,
+	AdminAnnouncementsCreateRequest,
+	AdminAnnouncementsCreateResponse,
+	AdminAnnouncementsDeleteRequest,
+	AdminAnnouncementsListRequest,
+	AdminAnnouncementsListResponse,
+	AdminAnnouncementsUpdateRequest,
+	AdminAvatarDecorationsCreateRequest,
+	AdminAvatarDecorationsDeleteRequest,
+	AdminAvatarDecorationsListRequest,
+	AdminAvatarDecorationsListResponse,
+	AdminAvatarDecorationsUpdateRequest,
+	AdminDeleteAllFilesOfAUserRequest,
+	AdminUnsetUserAvatarRequest,
+	AdminUnsetUserBannerRequest,
+	AdminDriveFilesRequest,
+	AdminDriveFilesResponse,
+	AdminDriveShowFileRequest,
+	AdminDriveShowFileResponse,
+	AdminEmojiAddAliasesBulkRequest,
+	AdminEmojiAddRequest,
+	AdminEmojiCopyRequest,
+	AdminEmojiCopyResponse,
+	AdminEmojiDeleteBulkRequest,
+	AdminEmojiDeleteRequest,
+	AdminEmojiListRemoteRequest,
+	AdminEmojiListRemoteResponse,
+	AdminEmojiListRequest,
+	AdminEmojiListResponse,
+	AdminEmojiRemoveAliasesBulkRequest,
+	AdminEmojiSetAliasesBulkRequest,
+	AdminEmojiSetCategoryBulkRequest,
+	AdminEmojiSetLicenseBulkRequest,
+	AdminEmojiUpdateRequest,
+	AdminFederationDeleteAllFilesRequest,
+	AdminFederationRefreshRemoteInstanceMetadataRequest,
+	AdminFederationRemoveAllFollowingRequest,
+	AdminFederationUpdateInstanceRequest,
+	AdminGetTableStatsResponse,
+	AdminGetUserIpsRequest,
+	AdminInviteCreateRequest,
+	AdminInviteCreateResponse,
+	AdminInviteListRequest,
+	AdminInviteListResponse,
+	AdminPromoCreateRequest,
+	AdminQueueDeliverDelayedResponse,
+	AdminQueueInboxDelayedResponse,
+	AdminQueuePromoteRequest,
+	AdminQueueStatsResponse,
+	AdminRelaysAddRequest,
+	AdminRelaysAddResponse,
+	AdminRelaysListResponse,
+	AdminRelaysRemoveRequest,
+	AdminResetPasswordRequest,
+	AdminResetPasswordResponse,
+	AdminResolveAbuseUserReportRequest,
+	AdminSendEmailRequest,
+	AdminServerInfoResponse,
+	AdminShowModerationLogsRequest,
+	AdminShowModerationLogsResponse,
+	AdminShowUserRequest,
+	AdminShowUserResponse,
+	AdminShowUsersRequest,
+	AdminShowUsersResponse,
+	AdminSuspendUserRequest,
+	AdminUnsuspendUserRequest,
+	AdminUpdateMetaRequest,
+	AdminDeleteAccountRequest,
+	AdminDeleteAccountResponse,
+	AdminUpdateUserNoteRequest,
+	AdminRolesCreateRequest,
+	AdminRolesDeleteRequest,
+	AdminRolesShowRequest,
+	AdminRolesUpdateRequest,
+	AdminRolesAssignRequest,
+	AdminRolesUnassignRequest,
+	AdminRolesUpdateDefaultPoliciesRequest,
+	AdminRolesUsersRequest,
+	AnnouncementsRequest,
+	AnnouncementsResponse,
+	AntennasCreateRequest,
+	AntennasCreateResponse,
+	AntennasDeleteRequest,
+	AntennasListResponse,
+	AntennasNotesRequest,
+	AntennasNotesResponse,
+	AntennasShowRequest,
+	AntennasShowResponse,
+	AntennasUpdateRequest,
+	AntennasUpdateResponse,
+	ApGetRequest,
+	ApGetResponse,
+	ApShowRequest,
+	ApShowResponse,
+	AppCreateRequest,
+	AppCreateResponse,
+	AppShowRequest,
+	AppShowResponse,
+	AuthSessionGenerateRequest,
+	AuthSessionGenerateResponse,
+	AuthSessionShowRequest,
+	AuthSessionShowResponse,
+	AuthSessionUserkeyRequest,
+	AuthSessionUserkeyResponse,
+	BlockingCreateRequest,
+	BlockingCreateResponse,
+	BlockingDeleteRequest,
+	BlockingDeleteResponse,
+	BlockingListRequest,
+	BlockingListResponse,
+	ChannelsCreateRequest,
+	ChannelsCreateResponse,
+	ChannelsFeaturedResponse,
+	ChannelsFollowRequest,
+	ChannelsFollowedRequest,
+	ChannelsFollowedResponse,
+	ChannelsOwnedRequest,
+	ChannelsOwnedResponse,
+	ChannelsShowRequest,
+	ChannelsShowResponse,
+	ChannelsTimelineRequest,
+	ChannelsTimelineResponse,
+	ChannelsUnfollowRequest,
+	ChannelsUpdateRequest,
+	ChannelsUpdateResponse,
+	ChannelsFavoriteRequest,
+	ChannelsUnfavoriteRequest,
+	ChannelsMyFavoritesResponse,
+	ChannelsSearchRequest,
+	ChannelsSearchResponse,
+	ChartsActiveUsersRequest,
+	ChartsActiveUsersResponse,
+	ChartsApRequestRequest,
+	ChartsApRequestResponse,
+	ChartsDriveRequest,
+	ChartsDriveResponse,
+	ChartsFederationRequest,
+	ChartsFederationResponse,
+	ChartsInstanceRequest,
+	ChartsInstanceResponse,
+	ChartsNotesRequest,
+	ChartsNotesResponse,
+	ChartsUserDriveRequest,
+	ChartsUserDriveResponse,
+	ChartsUserFollowingRequest,
+	ChartsUserFollowingResponse,
+	ChartsUserNotesRequest,
+	ChartsUserNotesResponse,
+	ChartsUserPvRequest,
+	ChartsUserPvResponse,
+	ChartsUserReactionsRequest,
+	ChartsUserReactionsResponse,
+	ChartsUsersRequest,
+	ChartsUsersResponse,
+	ClipsAddNoteRequest,
+	ClipsRemoveNoteRequest,
+	ClipsCreateRequest,
+	ClipsCreateResponse,
+	ClipsDeleteRequest,
+	ClipsListResponse,
+	ClipsNotesRequest,
+	ClipsNotesResponse,
+	ClipsShowRequest,
+	ClipsShowResponse,
+	ClipsUpdateRequest,
+	ClipsUpdateResponse,
+	ClipsFavoriteRequest,
+	ClipsUnfavoriteRequest,
+	ClipsMyFavoritesResponse,
+	DriveResponse,
+	DriveFilesRequest,
+	DriveFilesResponse,
+	DriveFilesAttachedNotesRequest,
+	DriveFilesAttachedNotesResponse,
+	DriveFilesCheckExistenceRequest,
+	DriveFilesCheckExistenceResponse,
+	DriveFilesCreateRequest,
+	DriveFilesCreateResponse,
+	DriveFilesDeleteRequest,
+	DriveFilesFindByHashRequest,
+	DriveFilesFindByHashResponse,
+	DriveFilesFindRequest,
+	DriveFilesFindResponse,
+	DriveFilesShowRequest,
+	DriveFilesShowResponse,
+	DriveFilesUpdateRequest,
+	DriveFilesUpdateResponse,
+	DriveFilesUploadFromUrlRequest,
+	DriveFoldersRequest,
+	DriveFoldersResponse,
+	DriveFoldersCreateRequest,
+	DriveFoldersCreateResponse,
+	DriveFoldersDeleteRequest,
+	DriveFoldersFindRequest,
+	DriveFoldersFindResponse,
+	DriveFoldersShowRequest,
+	DriveFoldersShowResponse,
+	DriveFoldersUpdateRequest,
+	DriveFoldersUpdateResponse,
+	DriveStreamRequest,
+	DriveStreamResponse,
+	EmailAddressAvailableRequest,
+	EmailAddressAvailableResponse,
+	EndpointRequest,
+	EndpointsResponse,
+	FederationFollowersRequest,
+	FederationFollowersResponse,
+	FederationFollowingRequest,
+	FederationFollowingResponse,
+	FederationInstancesRequest,
+	FederationInstancesResponse,
+	FederationShowInstanceRequest,
+	FederationShowInstanceResponse,
+	FederationUpdateRemoteUserRequest,
+	FederationUsersRequest,
+	FederationUsersResponse,
+	FederationStatsRequest,
+	FollowingCreateRequest,
+	FollowingCreateResponse,
+	FollowingDeleteRequest,
+	FollowingDeleteResponse,
+	FollowingUpdateRequest,
+	FollowingUpdateResponse,
+	FollowingUpdateAllRequest,
+	FollowingInvalidateRequest,
+	FollowingInvalidateResponse,
+	FollowingRequestsAcceptRequest,
+	FollowingRequestsCancelRequest,
+	FollowingRequestsCancelResponse,
+	FollowingRequestsListRequest,
+	FollowingRequestsListResponse,
+	FollowingRequestsRejectRequest,
+	GalleryFeaturedRequest,
+	GalleryFeaturedResponse,
+	GalleryPopularResponse,
+	GalleryPostsRequest,
+	GalleryPostsResponse,
+	GalleryPostsCreateRequest,
+	GalleryPostsCreateResponse,
+	GalleryPostsDeleteRequest,
+	GalleryPostsLikeRequest,
+	GalleryPostsShowRequest,
+	GalleryPostsShowResponse,
+	GalleryPostsUnlikeRequest,
+	GalleryPostsUpdateRequest,
+	GalleryPostsUpdateResponse,
+	GetAvatarDecorationsResponse,
+	HashtagsListRequest,
+	HashtagsListResponse,
+	HashtagsSearchRequest,
+	HashtagsSearchResponse,
+	HashtagsShowRequest,
+	HashtagsShowResponse,
+	HashtagsTrendResponse,
+	HashtagsUsersRequest,
+	HashtagsUsersResponse,
+	IResponse,
+	IClaimAchievementRequest,
+	IFavoritesRequest,
+	IFavoritesResponse,
+	IGalleryLikesRequest,
+	IGalleryLikesResponse,
+	IGalleryPostsRequest,
+	IGalleryPostsResponse,
+	INotificationsRequest,
+	INotificationsResponse,
+	INotificationsGroupedRequest,
+	INotificationsGroupedResponse,
+	IPageLikesRequest,
+	IPageLikesResponse,
+	IPagesRequest,
+	IPagesResponse,
+	IPinRequest,
+	IPinResponse,
+	IReadAnnouncementRequest,
+	IRegistryGetAllRequest,
+	IRegistryGetDetailRequest,
+	IRegistryGetRequest,
+	IRegistryKeysWithTypeRequest,
+	IRegistryKeysRequest,
+	IRegistryRemoveRequest,
+	IRegistrySetRequest,
+	IUnpinRequest,
+	IUnpinResponse,
+	IUpdateRequest,
+	IUpdateResponse,
+	IWebhooksCreateRequest,
+	IWebhooksShowRequest,
+	IWebhooksUpdateRequest,
+	IWebhooksDeleteRequest,
+	InviteCreateResponse,
+	InviteDeleteRequest,
+	InviteListRequest,
+	InviteListResponse,
+	InviteLimitResponse,
+	MetaRequest,
+	MetaResponse,
+	EmojisResponse,
+	EmojiRequest,
+	EmojiResponse,
+	MuteCreateRequest,
+	MuteDeleteRequest,
+	MuteListRequest,
+	MuteListResponse,
+	RenoteMuteCreateRequest,
+	RenoteMuteDeleteRequest,
+	RenoteMuteListRequest,
+	RenoteMuteListResponse,
+	MyAppsRequest,
+	MyAppsResponse,
+	NotesRequest,
+	NotesResponse,
+	NotesChildrenRequest,
+	NotesChildrenResponse,
+	NotesClipsRequest,
+	NotesClipsResponse,
+	NotesConversationRequest,
+	NotesConversationResponse,
+	NotesCreateRequest,
+	NotesCreateResponse,
+	NotesDeleteRequest,
+	NotesFavoritesCreateRequest,
+	NotesFavoritesDeleteRequest,
+	NotesFeaturedRequest,
+	NotesFeaturedResponse,
+	NotesGlobalTimelineRequest,
+	NotesGlobalTimelineResponse,
+	NotesHybridTimelineRequest,
+	NotesHybridTimelineResponse,
+	NotesLocalTimelineRequest,
+	NotesLocalTimelineResponse,
+	NotesMentionsRequest,
+	NotesMentionsResponse,
+	NotesPollsRecommendationRequest,
+	NotesPollsRecommendationResponse,
+	NotesPollsVoteRequest,
+	NotesReactionsRequest,
+	NotesReactionsResponse,
+	NotesReactionsCreateRequest,
+	NotesReactionsDeleteRequest,
+	NotesRenotesRequest,
+	NotesRenotesResponse,
+	NotesRepliesRequest,
+	NotesRepliesResponse,
+	NotesSearchByTagRequest,
+	NotesSearchByTagResponse,
+	NotesSearchRequest,
+	NotesSearchResponse,
+	NotesShowRequest,
+	NotesShowResponse,
+	NotesStateRequest,
+	NotesStateResponse,
+	NotesThreadMutingCreateRequest,
+	NotesThreadMutingDeleteRequest,
+	NotesTimelineRequest,
+	NotesTimelineResponse,
+	NotesTranslateRequest,
+	NotesTranslateResponse,
+	NotesUnrenoteRequest,
+	NotesUserListTimelineRequest,
+	NotesUserListTimelineResponse,
+	NotificationsCreateRequest,
+	PagesCreateRequest,
+	PagesCreateResponse,
+	PagesDeleteRequest,
+	PagesFeaturedResponse,
+	PagesLikeRequest,
+	PagesShowRequest,
+	PagesShowResponse,
+	PagesUnlikeRequest,
+	PagesUpdateRequest,
+	FlashCreateRequest,
+	FlashDeleteRequest,
+	FlashFeaturedResponse,
+	FlashLikeRequest,
+	FlashShowRequest,
+	FlashShowResponse,
+	FlashUnlikeRequest,
+	FlashUpdateRequest,
+	FlashMyRequest,
+	FlashMyResponse,
+	FlashMyLikesRequest,
+	FlashMyLikesResponse,
+	PingResponse,
+	PinnedUsersResponse,
+	PromoReadRequest,
+	RolesShowRequest,
+	RolesUsersRequest,
+	RolesNotesRequest,
+	RolesNotesResponse,
+	RequestResetPasswordRequest,
+	ResetPasswordRequest,
+	StatsResponse,
+	SwShowRegistrationRequest,
+	SwShowRegistrationResponse,
+	SwUpdateRegistrationRequest,
+	SwUpdateRegistrationResponse,
+	SwRegisterRequest,
+	SwRegisterResponse,
+	SwUnregisterRequest,
+	TestRequest,
+	UsernameAvailableRequest,
+	UsernameAvailableResponse,
+	UsersRequest,
+	UsersResponse,
+	UsersClipsRequest,
+	UsersClipsResponse,
+	UsersFollowersRequest,
+	UsersFollowersResponse,
+	UsersFollowingRequest,
+	UsersFollowingResponse,
+	UsersGalleryPostsRequest,
+	UsersGalleryPostsResponse,
+	UsersGetFrequentlyRepliedUsersRequest,
+	UsersGetFrequentlyRepliedUsersResponse,
+	UsersFeaturedNotesRequest,
+	UsersFeaturedNotesResponse,
+	UsersListsCreateRequest,
+	UsersListsCreateResponse,
+	UsersListsDeleteRequest,
+	UsersListsListRequest,
+	UsersListsListResponse,
+	UsersListsPullRequest,
+	UsersListsPushRequest,
+	UsersListsShowRequest,
+	UsersListsShowResponse,
+	UsersListsFavoriteRequest,
+	UsersListsUnfavoriteRequest,
+	UsersListsUpdateRequest,
+	UsersListsUpdateResponse,
+	UsersListsCreateFromPublicRequest,
+	UsersListsCreateFromPublicResponse,
+	UsersListsUpdateMembershipRequest,
+	UsersListsGetMembershipsRequest,
+	UsersNotesRequest,
+	UsersNotesResponse,
+	UsersPagesRequest,
+	UsersPagesResponse,
+	UsersFlashsRequest,
+	UsersFlashsResponse,
+	UsersReactionsRequest,
+	UsersReactionsResponse,
+	UsersRecommendationRequest,
+	UsersRecommendationResponse,
+	UsersRelationRequest,
+	UsersRelationResponse,
+	UsersReportAbuseRequest,
+	UsersSearchByUsernameAndHostRequest,
+	UsersSearchByUsernameAndHostResponse,
+	UsersSearchRequest,
+	UsersSearchResponse,
+	UsersShowRequest,
+	UsersShowResponse,
+	UsersAchievementsRequest,
+	UsersUpdateMemoRequest,
+	FetchRssRequest,
+	FetchExternalResourcesRequest,
+	RetentionResponse,
+} from './entities.js';
+
+export type Endpoints = {
+	'admin/meta': { req: EmptyRequest; res: AdminMetaResponse };
+	'admin/abuse-user-reports': { req: AdminAbuseUserReportsRequest; res: AdminAbuseUserReportsResponse };
+	'admin/accounts/create': { req: AdminAccountsCreateRequest; res: AdminAccountsCreateResponse };
+	'admin/accounts/delete': { req: AdminAccountsDeleteRequest; res: EmptyResponse };
+	'admin/accounts/find-by-email': { req: AdminAccountsFindByEmailRequest; res: EmptyResponse };
+	'admin/ad/create': { req: AdminAdCreateRequest; res: EmptyResponse };
+	'admin/ad/delete': { req: AdminAdDeleteRequest; res: EmptyResponse };
+	'admin/ad/list': { req: AdminAdListRequest; res: EmptyResponse };
+	'admin/ad/update': { req: AdminAdUpdateRequest; res: EmptyResponse };
+	'admin/announcements/create': { req: AdminAnnouncementsCreateRequest; res: AdminAnnouncementsCreateResponse };
+	'admin/announcements/delete': { req: AdminAnnouncementsDeleteRequest; res: EmptyResponse };
+	'admin/announcements/list': { req: AdminAnnouncementsListRequest; res: AdminAnnouncementsListResponse };
+	'admin/announcements/update': { req: AdminAnnouncementsUpdateRequest; res: EmptyResponse };
+	'admin/avatar-decorations/create': { req: AdminAvatarDecorationsCreateRequest; res: EmptyResponse };
+	'admin/avatar-decorations/delete': { req: AdminAvatarDecorationsDeleteRequest; res: EmptyResponse };
+	'admin/avatar-decorations/list': { req: AdminAvatarDecorationsListRequest; res: AdminAvatarDecorationsListResponse };
+	'admin/avatar-decorations/update': { req: AdminAvatarDecorationsUpdateRequest; res: EmptyResponse };
+	'admin/delete-all-files-of-a-user': { req: AdminDeleteAllFilesOfAUserRequest; res: EmptyResponse };
+	'admin/unset-user-avatar': { req: AdminUnsetUserAvatarRequest; res: EmptyResponse };
+	'admin/unset-user-banner': { req: AdminUnsetUserBannerRequest; res: EmptyResponse };
+	'admin/drive/clean-remote-files': { req: EmptyRequest; res: EmptyResponse };
+	'admin/drive/cleanup': { req: EmptyRequest; res: EmptyResponse };
+	'admin/drive/files': { req: AdminDriveFilesRequest; res: AdminDriveFilesResponse };
+	'admin/drive/show-file': { req: AdminDriveShowFileRequest; res: AdminDriveShowFileResponse };
+	'admin/emoji/add-aliases-bulk': { req: AdminEmojiAddAliasesBulkRequest; res: EmptyResponse };
+	'admin/emoji/add': { req: AdminEmojiAddRequest; res: EmptyResponse };
+	'admin/emoji/copy': { req: AdminEmojiCopyRequest; res: AdminEmojiCopyResponse };
+	'admin/emoji/delete-bulk': { req: AdminEmojiDeleteBulkRequest; res: EmptyResponse };
+	'admin/emoji/delete': { req: AdminEmojiDeleteRequest; res: EmptyResponse };
+	'admin/emoji/list-remote': { req: AdminEmojiListRemoteRequest; res: AdminEmojiListRemoteResponse };
+	'admin/emoji/list': { req: AdminEmojiListRequest; res: AdminEmojiListResponse };
+	'admin/emoji/remove-aliases-bulk': { req: AdminEmojiRemoveAliasesBulkRequest; res: EmptyResponse };
+	'admin/emoji/set-aliases-bulk': { req: AdminEmojiSetAliasesBulkRequest; res: EmptyResponse };
+	'admin/emoji/set-category-bulk': { req: AdminEmojiSetCategoryBulkRequest; res: EmptyResponse };
+	'admin/emoji/set-license-bulk': { req: AdminEmojiSetLicenseBulkRequest; res: EmptyResponse };
+	'admin/emoji/update': { req: AdminEmojiUpdateRequest; res: EmptyResponse };
+	'admin/federation/delete-all-files': { req: AdminFederationDeleteAllFilesRequest; res: EmptyResponse };
+	'admin/federation/refresh-remote-instance-metadata': { req: AdminFederationRefreshRemoteInstanceMetadataRequest; res: EmptyResponse };
+	'admin/federation/remove-all-following': { req: AdminFederationRemoveAllFollowingRequest; res: EmptyResponse };
+	'admin/federation/update-instance': { req: AdminFederationUpdateInstanceRequest; res: EmptyResponse };
+	'admin/get-index-stats': { req: EmptyRequest; res: EmptyResponse };
+	'admin/get-table-stats': { req: EmptyRequest; res: AdminGetTableStatsResponse };
+	'admin/get-user-ips': { req: AdminGetUserIpsRequest; res: EmptyResponse };
+	'admin/invite/create': { req: AdminInviteCreateRequest; res: AdminInviteCreateResponse };
+	'admin/invite/list': { req: AdminInviteListRequest; res: AdminInviteListResponse };
+	'admin/promo/create': { req: AdminPromoCreateRequest; res: EmptyResponse };
+	'admin/queue/clear': { req: EmptyRequest; res: EmptyResponse };
+	'admin/queue/deliver-delayed': { req: EmptyRequest; res: AdminQueueDeliverDelayedResponse };
+	'admin/queue/inbox-delayed': { req: EmptyRequest; res: AdminQueueInboxDelayedResponse };
+	'admin/queue/promote': { req: AdminQueuePromoteRequest; res: EmptyResponse };
+	'admin/queue/stats': { req: EmptyRequest; res: AdminQueueStatsResponse };
+	'admin/relays/add': { req: AdminRelaysAddRequest; res: AdminRelaysAddResponse };
+	'admin/relays/list': { req: EmptyRequest; res: AdminRelaysListResponse };
+	'admin/relays/remove': { req: AdminRelaysRemoveRequest; res: EmptyResponse };
+	'admin/reset-password': { req: AdminResetPasswordRequest; res: AdminResetPasswordResponse };
+	'admin/resolve-abuse-user-report': { req: AdminResolveAbuseUserReportRequest; res: EmptyResponse };
+	'admin/send-email': { req: AdminSendEmailRequest; res: EmptyResponse };
+	'admin/server-info': { req: EmptyRequest; res: AdminServerInfoResponse };
+	'admin/show-moderation-logs': { req: AdminShowModerationLogsRequest; res: AdminShowModerationLogsResponse };
+	'admin/show-user': { req: AdminShowUserRequest; res: AdminShowUserResponse };
+	'admin/show-users': { req: AdminShowUsersRequest; res: AdminShowUsersResponse };
+	'admin/suspend-user': { req: AdminSuspendUserRequest; res: EmptyResponse };
+	'admin/unsuspend-user': { req: AdminUnsuspendUserRequest; res: EmptyResponse };
+	'admin/update-meta': { req: AdminUpdateMetaRequest; res: EmptyResponse };
+	'admin/delete-account': { req: AdminDeleteAccountRequest; res: AdminDeleteAccountResponse };
+	'admin/update-user-note': { req: AdminUpdateUserNoteRequest; res: EmptyResponse };
+	'admin/roles/create': { req: AdminRolesCreateRequest; res: EmptyResponse };
+	'admin/roles/delete': { req: AdminRolesDeleteRequest; res: EmptyResponse };
+	'admin/roles/list': { req: EmptyRequest; res: EmptyResponse };
+	'admin/roles/show': { req: AdminRolesShowRequest; res: EmptyResponse };
+	'admin/roles/update': { req: AdminRolesUpdateRequest; res: EmptyResponse };
+	'admin/roles/assign': { req: AdminRolesAssignRequest; res: EmptyResponse };
+	'admin/roles/unassign': { req: AdminRolesUnassignRequest; res: EmptyResponse };
+	'admin/roles/update-default-policies': { req: AdminRolesUpdateDefaultPoliciesRequest; res: EmptyResponse };
+	'admin/roles/users': { req: AdminRolesUsersRequest; res: EmptyResponse };
+	'announcements': { req: AnnouncementsRequest; res: AnnouncementsResponse };
+	'antennas/create': { req: AntennasCreateRequest; res: AntennasCreateResponse };
+	'antennas/delete': { req: AntennasDeleteRequest; res: EmptyResponse };
+	'antennas/list': { req: EmptyRequest; res: AntennasListResponse };
+	'antennas/notes': { req: AntennasNotesRequest; res: AntennasNotesResponse };
+	'antennas/show': { req: AntennasShowRequest; res: AntennasShowResponse };
+	'antennas/update': { req: AntennasUpdateRequest; res: AntennasUpdateResponse };
+	'ap/get': { req: ApGetRequest; res: ApGetResponse };
+	'ap/show': { req: ApShowRequest; res: ApShowResponse };
+	'app/create': { req: AppCreateRequest; res: AppCreateResponse };
+	'app/show': { req: AppShowRequest; res: AppShowResponse };
+	'auth/session/generate': { req: AuthSessionGenerateRequest; res: AuthSessionGenerateResponse };
+	'auth/session/show': { req: AuthSessionShowRequest; res: AuthSessionShowResponse };
+	'auth/session/userkey': { req: AuthSessionUserkeyRequest; res: AuthSessionUserkeyResponse };
+	'blocking/create': { req: BlockingCreateRequest; res: BlockingCreateResponse };
+	'blocking/delete': { req: BlockingDeleteRequest; res: BlockingDeleteResponse };
+	'blocking/list': { req: BlockingListRequest; res: BlockingListResponse };
+	'channels/create': { req: ChannelsCreateRequest; res: ChannelsCreateResponse };
+	'channels/featured': { req: EmptyRequest; res: ChannelsFeaturedResponse };
+	'channels/follow': { req: ChannelsFollowRequest; res: EmptyResponse };
+	'channels/followed': { req: ChannelsFollowedRequest; res: ChannelsFollowedResponse };
+	'channels/owned': { req: ChannelsOwnedRequest; res: ChannelsOwnedResponse };
+	'channels/show': { req: ChannelsShowRequest; res: ChannelsShowResponse };
+	'channels/timeline': { req: ChannelsTimelineRequest; res: ChannelsTimelineResponse };
+	'channels/unfollow': { req: ChannelsUnfollowRequest; res: EmptyResponse };
+	'channels/update': { req: ChannelsUpdateRequest; res: ChannelsUpdateResponse };
+	'channels/favorite': { req: ChannelsFavoriteRequest; res: EmptyResponse };
+	'channels/unfavorite': { req: ChannelsUnfavoriteRequest; res: EmptyResponse };
+	'channels/my-favorites': { req: EmptyRequest; res: ChannelsMyFavoritesResponse };
+	'channels/search': { req: ChannelsSearchRequest; res: ChannelsSearchResponse };
+	'charts/active-users': { req: ChartsActiveUsersRequest; res: ChartsActiveUsersResponse };
+	'charts/ap-request': { req: ChartsApRequestRequest; res: ChartsApRequestResponse };
+	'charts/drive': { req: ChartsDriveRequest; res: ChartsDriveResponse };
+	'charts/federation': { req: ChartsFederationRequest; res: ChartsFederationResponse };
+	'charts/instance': { req: ChartsInstanceRequest; res: ChartsInstanceResponse };
+	'charts/notes': { req: ChartsNotesRequest; res: ChartsNotesResponse };
+	'charts/user/drive': { req: ChartsUserDriveRequest; res: ChartsUserDriveResponse };
+	'charts/user/following': { req: ChartsUserFollowingRequest; res: ChartsUserFollowingResponse };
+	'charts/user/notes': { req: ChartsUserNotesRequest; res: ChartsUserNotesResponse };
+	'charts/user/pv': { req: ChartsUserPvRequest; res: ChartsUserPvResponse };
+	'charts/user/reactions': { req: ChartsUserReactionsRequest; res: ChartsUserReactionsResponse };
+	'charts/users': { req: ChartsUsersRequest; res: ChartsUsersResponse };
+	'clips/add-note': { req: ClipsAddNoteRequest; res: EmptyResponse };
+	'clips/remove-note': { req: ClipsRemoveNoteRequest; res: EmptyResponse };
+	'clips/create': { req: ClipsCreateRequest; res: ClipsCreateResponse };
+	'clips/delete': { req: ClipsDeleteRequest; res: EmptyResponse };
+	'clips/list': { req: EmptyRequest; res: ClipsListResponse };
+	'clips/notes': { req: ClipsNotesRequest; res: ClipsNotesResponse };
+	'clips/show': { req: ClipsShowRequest; res: ClipsShowResponse };
+	'clips/update': { req: ClipsUpdateRequest; res: ClipsUpdateResponse };
+	'clips/favorite': { req: ClipsFavoriteRequest; res: EmptyResponse };
+	'clips/unfavorite': { req: ClipsUnfavoriteRequest; res: EmptyResponse };
+	'clips/my-favorites': { req: EmptyRequest; res: ClipsMyFavoritesResponse };
+	'drive': { req: EmptyRequest; res: DriveResponse };
+	'drive/files': { req: DriveFilesRequest; res: DriveFilesResponse };
+	'drive/files/attached-notes': { req: DriveFilesAttachedNotesRequest; res: DriveFilesAttachedNotesResponse };
+	'drive/files/check-existence': { req: DriveFilesCheckExistenceRequest; res: DriveFilesCheckExistenceResponse };
+	'drive/files/create': { req: DriveFilesCreateRequest; res: DriveFilesCreateResponse };
+	'drive/files/delete': { req: DriveFilesDeleteRequest; res: EmptyResponse };
+	'drive/files/find-by-hash': { req: DriveFilesFindByHashRequest; res: DriveFilesFindByHashResponse };
+	'drive/files/find': { req: DriveFilesFindRequest; res: DriveFilesFindResponse };
+	'drive/files/show': { req: DriveFilesShowRequest; res: DriveFilesShowResponse };
+	'drive/files/update': { req: DriveFilesUpdateRequest; res: DriveFilesUpdateResponse };
+	'drive/files/upload-from-url': { req: DriveFilesUploadFromUrlRequest; res: EmptyResponse };
+	'drive/folders': { req: DriveFoldersRequest; res: DriveFoldersResponse };
+	'drive/folders/create': { req: DriveFoldersCreateRequest; res: DriveFoldersCreateResponse };
+	'drive/folders/delete': { req: DriveFoldersDeleteRequest; res: EmptyResponse };
+	'drive/folders/find': { req: DriveFoldersFindRequest; res: DriveFoldersFindResponse };
+	'drive/folders/show': { req: DriveFoldersShowRequest; res: DriveFoldersShowResponse };
+	'drive/folders/update': { req: DriveFoldersUpdateRequest; res: DriveFoldersUpdateResponse };
+	'drive/stream': { req: DriveStreamRequest; res: DriveStreamResponse };
+	'email-address/available': { req: EmailAddressAvailableRequest; res: EmailAddressAvailableResponse };
+	'endpoint': { req: EndpointRequest; res: EmptyResponse };
+	'endpoints': { req: EmptyRequest; res: EndpointsResponse };
+	'federation/followers': { req: FederationFollowersRequest; res: FederationFollowersResponse };
+	'federation/following': { req: FederationFollowingRequest; res: FederationFollowingResponse };
+	'federation/instances': { req: FederationInstancesRequest; res: FederationInstancesResponse };
+	'federation/show-instance': { req: FederationShowInstanceRequest; res: FederationShowInstanceResponse };
+	'federation/update-remote-user': { req: FederationUpdateRemoteUserRequest; res: EmptyResponse };
+	'federation/users': { req: FederationUsersRequest; res: FederationUsersResponse };
+	'federation/stats': { req: FederationStatsRequest; res: EmptyResponse };
+	'following/create': { req: FollowingCreateRequest; res: FollowingCreateResponse };
+	'following/delete': { req: FollowingDeleteRequest; res: FollowingDeleteResponse };
+	'following/update': { req: FollowingUpdateRequest; res: FollowingUpdateResponse };
+	'following/update-all': { req: FollowingUpdateAllRequest; res: EmptyResponse };
+	'following/invalidate': { req: FollowingInvalidateRequest; res: FollowingInvalidateResponse };
+	'following/requests/accept': { req: FollowingRequestsAcceptRequest; res: EmptyResponse };
+	'following/requests/cancel': { req: FollowingRequestsCancelRequest; res: FollowingRequestsCancelResponse };
+	'following/requests/list': { req: FollowingRequestsListRequest; res: FollowingRequestsListResponse };
+	'following/requests/reject': { req: FollowingRequestsRejectRequest; res: EmptyResponse };
+	'gallery/featured': { req: GalleryFeaturedRequest; res: GalleryFeaturedResponse };
+	'gallery/popular': { req: EmptyRequest; res: GalleryPopularResponse };
+	'gallery/posts': { req: GalleryPostsRequest; res: GalleryPostsResponse };
+	'gallery/posts/create': { req: GalleryPostsCreateRequest; res: GalleryPostsCreateResponse };
+	'gallery/posts/delete': { req: GalleryPostsDeleteRequest; res: EmptyResponse };
+	'gallery/posts/like': { req: GalleryPostsLikeRequest; res: EmptyResponse };
+	'gallery/posts/show': { req: GalleryPostsShowRequest; res: GalleryPostsShowResponse };
+	'gallery/posts/unlike': { req: GalleryPostsUnlikeRequest; res: EmptyResponse };
+	'gallery/posts/update': { req: GalleryPostsUpdateRequest; res: GalleryPostsUpdateResponse };
+	'get-online-users-count': { req: EmptyRequest; res: EmptyResponse };
+	'get-avatar-decorations': { req: EmptyRequest; res: GetAvatarDecorationsResponse };
+	'hashtags/list': { req: HashtagsListRequest; res: HashtagsListResponse };
+	'hashtags/search': { req: HashtagsSearchRequest; res: HashtagsSearchResponse };
+	'hashtags/show': { req: HashtagsShowRequest; res: HashtagsShowResponse };
+	'hashtags/trend': { req: EmptyRequest; res: HashtagsTrendResponse };
+	'hashtags/users': { req: HashtagsUsersRequest; res: HashtagsUsersResponse };
+	'i': { req: EmptyRequest; res: IResponse };
+	'i/claim-achievement': { req: IClaimAchievementRequest; res: EmptyResponse };
+	'i/favorites': { req: IFavoritesRequest; res: IFavoritesResponse };
+	'i/gallery/likes': { req: IGalleryLikesRequest; res: IGalleryLikesResponse };
+	'i/gallery/posts': { req: IGalleryPostsRequest; res: IGalleryPostsResponse };
+	'i/notifications': { req: INotificationsRequest; res: INotificationsResponse };
+	'i/notifications-grouped': { req: INotificationsGroupedRequest; res: INotificationsGroupedResponse };
+	'i/page-likes': { req: IPageLikesRequest; res: IPageLikesResponse };
+	'i/pages': { req: IPagesRequest; res: IPagesResponse };
+	'i/pin': { req: IPinRequest; res: IPinResponse };
+	'i/read-all-unread-notes': { req: EmptyRequest; res: EmptyResponse };
+	'i/read-announcement': { req: IReadAnnouncementRequest; res: EmptyResponse };
+	'i/registry/get-all': { req: IRegistryGetAllRequest; res: EmptyResponse };
+	'i/registry/get-detail': { req: IRegistryGetDetailRequest; res: EmptyResponse };
+	'i/registry/get': { req: IRegistryGetRequest; res: EmptyResponse };
+	'i/registry/keys-with-type': { req: IRegistryKeysWithTypeRequest; res: EmptyResponse };
+	'i/registry/keys': { req: IRegistryKeysRequest; res: EmptyResponse };
+	'i/registry/remove': { req: IRegistryRemoveRequest; res: EmptyResponse };
+	'i/registry/set': { req: IRegistrySetRequest; res: EmptyResponse };
+	'i/unpin': { req: IUnpinRequest; res: IUnpinResponse };
+	'i/update': { req: IUpdateRequest; res: IUpdateResponse };
+	'i/webhooks/create': { req: IWebhooksCreateRequest; res: EmptyResponse };
+	'i/webhooks/list': { req: EmptyRequest; res: EmptyResponse };
+	'i/webhooks/show': { req: IWebhooksShowRequest; res: EmptyResponse };
+	'i/webhooks/update': { req: IWebhooksUpdateRequest; res: EmptyResponse };
+	'i/webhooks/delete': { req: IWebhooksDeleteRequest; res: EmptyResponse };
+	'invite/create': { req: EmptyRequest; res: InviteCreateResponse };
+	'invite/delete': { req: InviteDeleteRequest; res: EmptyResponse };
+	'invite/list': { req: InviteListRequest; res: InviteListResponse };
+	'invite/limit': { req: EmptyRequest; res: InviteLimitResponse };
+	'meta': { req: MetaRequest; res: MetaResponse };
+	'emojis': { req: EmptyRequest; res: EmojisResponse };
+	'emoji': { req: EmojiRequest; res: EmojiResponse };
+	'mute/create': { req: MuteCreateRequest; res: EmptyResponse };
+	'mute/delete': { req: MuteDeleteRequest; res: EmptyResponse };
+	'mute/list': { req: MuteListRequest; res: MuteListResponse };
+	'renote-mute/create': { req: RenoteMuteCreateRequest; res: EmptyResponse };
+	'renote-mute/delete': { req: RenoteMuteDeleteRequest; res: EmptyResponse };
+	'renote-mute/list': { req: RenoteMuteListRequest; res: RenoteMuteListResponse };
+	'my/apps': { req: MyAppsRequest; res: MyAppsResponse };
+	'notes': { req: NotesRequest; res: NotesResponse };
+	'notes/children': { req: NotesChildrenRequest; res: NotesChildrenResponse };
+	'notes/clips': { req: NotesClipsRequest; res: NotesClipsResponse };
+	'notes/conversation': { req: NotesConversationRequest; res: NotesConversationResponse };
+	'notes/create': { req: NotesCreateRequest; res: NotesCreateResponse };
+	'notes/delete': { req: NotesDeleteRequest; res: EmptyResponse };
+	'notes/favorites/create': { req: NotesFavoritesCreateRequest; res: EmptyResponse };
+	'notes/favorites/delete': { req: NotesFavoritesDeleteRequest; res: EmptyResponse };
+	'notes/featured': { req: NotesFeaturedRequest; res: NotesFeaturedResponse };
+	'notes/global-timeline': { req: NotesGlobalTimelineRequest; res: NotesGlobalTimelineResponse };
+	'notes/hybrid-timeline': { req: NotesHybridTimelineRequest; res: NotesHybridTimelineResponse };
+	'notes/local-timeline': { req: NotesLocalTimelineRequest; res: NotesLocalTimelineResponse };
+	'notes/mentions': { req: NotesMentionsRequest; res: NotesMentionsResponse };
+	'notes/polls/recommendation': { req: NotesPollsRecommendationRequest; res: NotesPollsRecommendationResponse };
+	'notes/polls/vote': { req: NotesPollsVoteRequest; res: EmptyResponse };
+	'notes/reactions': { req: NotesReactionsRequest; res: NotesReactionsResponse };
+	'notes/reactions/create': { req: NotesReactionsCreateRequest; res: EmptyResponse };
+	'notes/reactions/delete': { req: NotesReactionsDeleteRequest; res: EmptyResponse };
+	'notes/renotes': { req: NotesRenotesRequest; res: NotesRenotesResponse };
+	'notes/replies': { req: NotesRepliesRequest; res: NotesRepliesResponse };
+	'notes/search-by-tag': { req: NotesSearchByTagRequest; res: NotesSearchByTagResponse };
+	'notes/search': { req: NotesSearchRequest; res: NotesSearchResponse };
+	'notes/show': { req: NotesShowRequest; res: NotesShowResponse };
+	'notes/state': { req: NotesStateRequest; res: NotesStateResponse };
+	'notes/thread-muting/create': { req: NotesThreadMutingCreateRequest; res: EmptyResponse };
+	'notes/thread-muting/delete': { req: NotesThreadMutingDeleteRequest; res: EmptyResponse };
+	'notes/timeline': { req: NotesTimelineRequest; res: NotesTimelineResponse };
+	'notes/translate': { req: NotesTranslateRequest; res: NotesTranslateResponse };
+	'notes/unrenote': { req: NotesUnrenoteRequest; res: EmptyResponse };
+	'notes/user-list-timeline': { req: NotesUserListTimelineRequest; res: NotesUserListTimelineResponse };
+	'notifications/create': { req: NotificationsCreateRequest; res: EmptyResponse };
+	'notifications/mark-all-as-read': { req: EmptyRequest; res: EmptyResponse };
+	'notifications/test-notification': { req: EmptyRequest; res: EmptyResponse };
+	'pages/create': { req: PagesCreateRequest; res: PagesCreateResponse };
+	'pages/delete': { req: PagesDeleteRequest; res: EmptyResponse };
+	'pages/featured': { req: EmptyRequest; res: PagesFeaturedResponse };
+	'pages/like': { req: PagesLikeRequest; res: EmptyResponse };
+	'pages/show': { req: PagesShowRequest; res: PagesShowResponse };
+	'pages/unlike': { req: PagesUnlikeRequest; res: EmptyResponse };
+	'pages/update': { req: PagesUpdateRequest; res: EmptyResponse };
+	'flash/create': { req: FlashCreateRequest; res: EmptyResponse };
+	'flash/delete': { req: FlashDeleteRequest; res: EmptyResponse };
+	'flash/featured': { req: EmptyRequest; res: FlashFeaturedResponse };
+	'flash/like': { req: FlashLikeRequest; res: EmptyResponse };
+	'flash/show': { req: FlashShowRequest; res: FlashShowResponse };
+	'flash/unlike': { req: FlashUnlikeRequest; res: EmptyResponse };
+	'flash/update': { req: FlashUpdateRequest; res: EmptyResponse };
+	'flash/my': { req: FlashMyRequest; res: FlashMyResponse };
+	'flash/my-likes': { req: FlashMyLikesRequest; res: FlashMyLikesResponse };
+	'ping': { req: EmptyRequest; res: PingResponse };
+	'pinned-users': { req: EmptyRequest; res: PinnedUsersResponse };
+	'promo/read': { req: PromoReadRequest; res: EmptyResponse };
+	'roles/list': { req: EmptyRequest; res: EmptyResponse };
+	'roles/show': { req: RolesShowRequest; res: EmptyResponse };
+	'roles/users': { req: RolesUsersRequest; res: EmptyResponse };
+	'roles/notes': { req: RolesNotesRequest; res: RolesNotesResponse };
+	'request-reset-password': { req: RequestResetPasswordRequest; res: EmptyResponse };
+	'reset-db': { req: EmptyRequest; res: EmptyResponse };
+	'reset-password': { req: ResetPasswordRequest; res: EmptyResponse };
+	'server-info': { req: EmptyRequest; res: EmptyResponse };
+	'stats': { req: EmptyRequest; res: StatsResponse };
+	'sw/show-registration': { req: SwShowRegistrationRequest; res: SwShowRegistrationResponse };
+	'sw/update-registration': { req: SwUpdateRegistrationRequest; res: SwUpdateRegistrationResponse };
+	'sw/register': { req: SwRegisterRequest; res: SwRegisterResponse };
+	'sw/unregister': { req: SwUnregisterRequest; res: EmptyResponse };
+	'test': { req: TestRequest; res: EmptyResponse };
+	'username/available': { req: UsernameAvailableRequest; res: UsernameAvailableResponse };
+	'users': { req: UsersRequest; res: UsersResponse };
+	'users/clips': { req: UsersClipsRequest; res: UsersClipsResponse };
+	'users/followers': { req: UsersFollowersRequest; res: UsersFollowersResponse };
+	'users/following': { req: UsersFollowingRequest; res: UsersFollowingResponse };
+	'users/gallery/posts': { req: UsersGalleryPostsRequest; res: UsersGalleryPostsResponse };
+	'users/get-frequently-replied-users': { req: UsersGetFrequentlyRepliedUsersRequest; res: UsersGetFrequentlyRepliedUsersResponse };
+	'users/featured-notes': { req: UsersFeaturedNotesRequest; res: UsersFeaturedNotesResponse };
+	'users/lists/create': { req: UsersListsCreateRequest; res: UsersListsCreateResponse };
+	'users/lists/delete': { req: UsersListsDeleteRequest; res: EmptyResponse };
+	'users/lists/list': { req: UsersListsListRequest; res: UsersListsListResponse };
+	'users/lists/pull': { req: UsersListsPullRequest; res: EmptyResponse };
+	'users/lists/push': { req: UsersListsPushRequest; res: EmptyResponse };
+	'users/lists/show': { req: UsersListsShowRequest; res: UsersListsShowResponse };
+	'users/lists/favorite': { req: UsersListsFavoriteRequest; res: EmptyResponse };
+	'users/lists/unfavorite': { req: UsersListsUnfavoriteRequest; res: EmptyResponse };
+	'users/lists/update': { req: UsersListsUpdateRequest; res: UsersListsUpdateResponse };
+	'users/lists/create-from-public': { req: UsersListsCreateFromPublicRequest; res: UsersListsCreateFromPublicResponse };
+	'users/lists/update-membership': { req: UsersListsUpdateMembershipRequest; res: EmptyResponse };
+	'users/lists/get-memberships': { req: UsersListsGetMembershipsRequest; res: EmptyResponse };
+	'users/notes': { req: UsersNotesRequest; res: UsersNotesResponse };
+	'users/pages': { req: UsersPagesRequest; res: UsersPagesResponse };
+	'users/flashs': { req: UsersFlashsRequest; res: UsersFlashsResponse };
+	'users/reactions': { req: UsersReactionsRequest; res: UsersReactionsResponse };
+	'users/recommendation': { req: UsersRecommendationRequest; res: UsersRecommendationResponse };
+	'users/relation': { req: UsersRelationRequest; res: UsersRelationResponse };
+	'users/report-abuse': { req: UsersReportAbuseRequest; res: EmptyResponse };
+	'users/search-by-username-and-host': { req: UsersSearchByUsernameAndHostRequest; res: UsersSearchByUsernameAndHostResponse };
+	'users/search': { req: UsersSearchRequest; res: UsersSearchResponse };
+	'users/show': { req: UsersShowRequest; res: UsersShowResponse };
+	'users/achievements': { req: UsersAchievementsRequest; res: EmptyResponse };
+	'users/update-memo': { req: UsersUpdateMemoRequest; res: EmptyResponse };
+	'fetch-rss': { req: FetchRssRequest; res: EmptyResponse };
+	'fetch-external-resources': { req: FetchExternalResourcesRequest; res: EmptyResponse };
+	'retention': { req: EmptyRequest; res: RetentionResponse };
+}
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
new file mode 100644
index 0000000000..133a30b4ca
--- /dev/null
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -0,0 +1,478 @@
+/*
+ * version: 2023.11.1
+ * generatedAt: 2023-11-27T02:24:45.111Z
+ */
+
+import { operations } from './types.js';
+
+export type EmptyRequest = Record<string, unknown> | undefined;
+export type EmptyResponse = Record<string, unknown> | undefined;
+
+export type AdminMetaResponse = operations['admin/meta']['responses']['200']['content']['application/json'];
+export type AdminAbuseUserReportsRequest = operations['admin/abuse-user-reports']['requestBody']['content']['application/json'];
+export type AdminAbuseUserReportsResponse = operations['admin/abuse-user-reports']['responses']['200']['content']['application/json'];
+export type AdminAccountsCreateRequest = operations['admin/accounts/create']['requestBody']['content']['application/json'];
+export type AdminAccountsCreateResponse = operations['admin/accounts/create']['responses']['200']['content']['application/json'];
+export type AdminAccountsDeleteRequest = operations['admin/accounts/delete']['requestBody']['content']['application/json'];
+export type AdminAccountsFindByEmailRequest = operations['admin/accounts/find-by-email']['requestBody']['content']['application/json'];
+export type AdminAdCreateRequest = operations['admin/ad/create']['requestBody']['content']['application/json'];
+export type AdminAdDeleteRequest = operations['admin/ad/delete']['requestBody']['content']['application/json'];
+export type AdminAdListRequest = operations['admin/ad/list']['requestBody']['content']['application/json'];
+export type AdminAdUpdateRequest = operations['admin/ad/update']['requestBody']['content']['application/json'];
+export type AdminAnnouncementsCreateRequest = operations['admin/announcements/create']['requestBody']['content']['application/json'];
+export type AdminAnnouncementsCreateResponse = operations['admin/announcements/create']['responses']['200']['content']['application/json'];
+export type AdminAnnouncementsDeleteRequest = operations['admin/announcements/delete']['requestBody']['content']['application/json'];
+export type AdminAnnouncementsListRequest = operations['admin/announcements/list']['requestBody']['content']['application/json'];
+export type AdminAnnouncementsListResponse = operations['admin/announcements/list']['responses']['200']['content']['application/json'];
+export type AdminAnnouncementsUpdateRequest = operations['admin/announcements/update']['requestBody']['content']['application/json'];
+export type AdminAvatarDecorationsCreateRequest = operations['admin/avatar-decorations/create']['requestBody']['content']['application/json'];
+export type AdminAvatarDecorationsDeleteRequest = operations['admin/avatar-decorations/delete']['requestBody']['content']['application/json'];
+export type AdminAvatarDecorationsListRequest = operations['admin/avatar-decorations/list']['requestBody']['content']['application/json'];
+export type AdminAvatarDecorationsListResponse = operations['admin/avatar-decorations/list']['responses']['200']['content']['application/json'];
+export type AdminAvatarDecorationsUpdateRequest = operations['admin/avatar-decorations/update']['requestBody']['content']['application/json'];
+export type AdminDeleteAllFilesOfAUserRequest = operations['admin/delete-all-files-of-a-user']['requestBody']['content']['application/json'];
+export type AdminUnsetUserAvatarRequest = operations['admin/unset-user-avatar']['requestBody']['content']['application/json'];
+export type AdminUnsetUserBannerRequest = operations['admin/unset-user-banner']['requestBody']['content']['application/json'];
+export type AdminDriveFilesRequest = operations['admin/drive/files']['requestBody']['content']['application/json'];
+export type AdminDriveFilesResponse = operations['admin/drive/files']['responses']['200']['content']['application/json'];
+export type AdminDriveShowFileRequest = operations['admin/drive/show-file']['requestBody']['content']['application/json'];
+export type AdminDriveShowFileResponse = operations['admin/drive/show-file']['responses']['200']['content']['application/json'];
+export type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json'];
+export type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json'];
+export type AdminEmojiCopyResponse = operations['admin/emoji/copy']['responses']['200']['content']['application/json'];
+export type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiDeleteRequest = operations['admin/emoji/delete']['requestBody']['content']['application/json'];
+export type AdminEmojiListRemoteRequest = operations['admin/emoji/list-remote']['requestBody']['content']['application/json'];
+export type AdminEmojiListRemoteResponse = operations['admin/emoji/list-remote']['responses']['200']['content']['application/json'];
+export type AdminEmojiListRequest = operations['admin/emoji/list']['requestBody']['content']['application/json'];
+export type AdminEmojiListResponse = operations['admin/emoji/list']['responses']['200']['content']['application/json'];
+export type AdminEmojiRemoveAliasesBulkRequest = operations['admin/emoji/remove-aliases-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiSetAliasesBulkRequest = operations['admin/emoji/set-aliases-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiSetCategoryBulkRequest = operations['admin/emoji/set-category-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiSetLicenseBulkRequest = operations['admin/emoji/set-license-bulk']['requestBody']['content']['application/json'];
+export type AdminEmojiUpdateRequest = operations['admin/emoji/update']['requestBody']['content']['application/json'];
+export type AdminFederationDeleteAllFilesRequest = operations['admin/federation/delete-all-files']['requestBody']['content']['application/json'];
+export type AdminFederationRefreshRemoteInstanceMetadataRequest = operations['admin/federation/refresh-remote-instance-metadata']['requestBody']['content']['application/json'];
+export type AdminFederationRemoveAllFollowingRequest = operations['admin/federation/remove-all-following']['requestBody']['content']['application/json'];
+export type AdminFederationUpdateInstanceRequest = operations['admin/federation/update-instance']['requestBody']['content']['application/json'];
+export type AdminGetTableStatsResponse = operations['admin/get-table-stats']['responses']['200']['content']['application/json'];
+export type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['content']['application/json'];
+export type AdminInviteCreateRequest = operations['admin/invite/create']['requestBody']['content']['application/json'];
+export type AdminInviteCreateResponse = operations['admin/invite/create']['responses']['200']['content']['application/json'];
+export type AdminInviteListRequest = operations['admin/invite/list']['requestBody']['content']['application/json'];
+export type AdminInviteListResponse = operations['admin/invite/list']['responses']['200']['content']['application/json'];
+export type AdminPromoCreateRequest = operations['admin/promo/create']['requestBody']['content']['application/json'];
+export type AdminQueueDeliverDelayedResponse = operations['admin/queue/deliver-delayed']['responses']['200']['content']['application/json'];
+export type AdminQueueInboxDelayedResponse = operations['admin/queue/inbox-delayed']['responses']['200']['content']['application/json'];
+export type AdminQueuePromoteRequest = operations['admin/queue/promote']['requestBody']['content']['application/json'];
+export type AdminQueueStatsResponse = operations['admin/queue/stats']['responses']['200']['content']['application/json'];
+export type AdminRelaysAddRequest = operations['admin/relays/add']['requestBody']['content']['application/json'];
+export type AdminRelaysAddResponse = operations['admin/relays/add']['responses']['200']['content']['application/json'];
+export type AdminRelaysListResponse = operations['admin/relays/list']['responses']['200']['content']['application/json'];
+export type AdminRelaysRemoveRequest = operations['admin/relays/remove']['requestBody']['content']['application/json'];
+export type AdminResetPasswordRequest = operations['admin/reset-password']['requestBody']['content']['application/json'];
+export type AdminResetPasswordResponse = operations['admin/reset-password']['responses']['200']['content']['application/json'];
+export type AdminResolveAbuseUserReportRequest = operations['admin/resolve-abuse-user-report']['requestBody']['content']['application/json'];
+export type AdminSendEmailRequest = operations['admin/send-email']['requestBody']['content']['application/json'];
+export type AdminServerInfoResponse = operations['admin/server-info']['responses']['200']['content']['application/json'];
+export type AdminShowModerationLogsRequest = operations['admin/show-moderation-logs']['requestBody']['content']['application/json'];
+export type AdminShowModerationLogsResponse = operations['admin/show-moderation-logs']['responses']['200']['content']['application/json'];
+export type AdminShowUserRequest = operations['admin/show-user']['requestBody']['content']['application/json'];
+export type AdminShowUserResponse = operations['admin/show-user']['responses']['200']['content']['application/json'];
+export type AdminShowUsersRequest = operations['admin/show-users']['requestBody']['content']['application/json'];
+export type AdminShowUsersResponse = operations['admin/show-users']['responses']['200']['content']['application/json'];
+export type AdminSuspendUserRequest = operations['admin/suspend-user']['requestBody']['content']['application/json'];
+export type AdminUnsuspendUserRequest = operations['admin/unsuspend-user']['requestBody']['content']['application/json'];
+export type AdminUpdateMetaRequest = operations['admin/update-meta']['requestBody']['content']['application/json'];
+export type AdminDeleteAccountRequest = operations['admin/delete-account']['requestBody']['content']['application/json'];
+export type AdminDeleteAccountResponse = operations['admin/delete-account']['responses']['200']['content']['application/json'];
+export type AdminUpdateUserNoteRequest = operations['admin/update-user-note']['requestBody']['content']['application/json'];
+export type AdminRolesCreateRequest = operations['admin/roles/create']['requestBody']['content']['application/json'];
+export type AdminRolesDeleteRequest = operations['admin/roles/delete']['requestBody']['content']['application/json'];
+export type AdminRolesShowRequest = operations['admin/roles/show']['requestBody']['content']['application/json'];
+export type AdminRolesUpdateRequest = operations['admin/roles/update']['requestBody']['content']['application/json'];
+export type AdminRolesAssignRequest = operations['admin/roles/assign']['requestBody']['content']['application/json'];
+export type AdminRolesUnassignRequest = operations['admin/roles/unassign']['requestBody']['content']['application/json'];
+export type AdminRolesUpdateDefaultPoliciesRequest = operations['admin/roles/update-default-policies']['requestBody']['content']['application/json'];
+export type AdminRolesUsersRequest = operations['admin/roles/users']['requestBody']['content']['application/json'];
+export type AnnouncementsRequest = operations['announcements']['requestBody']['content']['application/json'];
+export type AnnouncementsResponse = operations['announcements']['responses']['200']['content']['application/json'];
+export type AntennasCreateRequest = operations['antennas/create']['requestBody']['content']['application/json'];
+export type AntennasCreateResponse = operations['antennas/create']['responses']['200']['content']['application/json'];
+export type AntennasDeleteRequest = operations['antennas/delete']['requestBody']['content']['application/json'];
+export type AntennasListResponse = operations['antennas/list']['responses']['200']['content']['application/json'];
+export type AntennasNotesRequest = operations['antennas/notes']['requestBody']['content']['application/json'];
+export type AntennasNotesResponse = operations['antennas/notes']['responses']['200']['content']['application/json'];
+export type AntennasShowRequest = operations['antennas/show']['requestBody']['content']['application/json'];
+export type AntennasShowResponse = operations['antennas/show']['responses']['200']['content']['application/json'];
+export type AntennasUpdateRequest = operations['antennas/update']['requestBody']['content']['application/json'];
+export type AntennasUpdateResponse = operations['antennas/update']['responses']['200']['content']['application/json'];
+export type ApGetRequest = operations['ap/get']['requestBody']['content']['application/json'];
+export type ApGetResponse = operations['ap/get']['responses']['200']['content']['application/json'];
+export type ApShowRequest = operations['ap/show']['requestBody']['content']['application/json'];
+export type ApShowResponse = operations['ap/show']['responses']['200']['content']['application/json'];
+export type AppCreateRequest = operations['app/create']['requestBody']['content']['application/json'];
+export type AppCreateResponse = operations['app/create']['responses']['200']['content']['application/json'];
+export type AppShowRequest = operations['app/show']['requestBody']['content']['application/json'];
+export type AppShowResponse = operations['app/show']['responses']['200']['content']['application/json'];
+export type AuthSessionGenerateRequest = operations['auth/session/generate']['requestBody']['content']['application/json'];
+export type AuthSessionGenerateResponse = operations['auth/session/generate']['responses']['200']['content']['application/json'];
+export type AuthSessionShowRequest = operations['auth/session/show']['requestBody']['content']['application/json'];
+export type AuthSessionShowResponse = operations['auth/session/show']['responses']['200']['content']['application/json'];
+export type AuthSessionUserkeyRequest = operations['auth/session/userkey']['requestBody']['content']['application/json'];
+export type AuthSessionUserkeyResponse = operations['auth/session/userkey']['responses']['200']['content']['application/json'];
+export type BlockingCreateRequest = operations['blocking/create']['requestBody']['content']['application/json'];
+export type BlockingCreateResponse = operations['blocking/create']['responses']['200']['content']['application/json'];
+export type BlockingDeleteRequest = operations['blocking/delete']['requestBody']['content']['application/json'];
+export type BlockingDeleteResponse = operations['blocking/delete']['responses']['200']['content']['application/json'];
+export type BlockingListRequest = operations['blocking/list']['requestBody']['content']['application/json'];
+export type BlockingListResponse = operations['blocking/list']['responses']['200']['content']['application/json'];
+export type ChannelsCreateRequest = operations['channels/create']['requestBody']['content']['application/json'];
+export type ChannelsCreateResponse = operations['channels/create']['responses']['200']['content']['application/json'];
+export type ChannelsFeaturedResponse = operations['channels/featured']['responses']['200']['content']['application/json'];
+export type ChannelsFollowRequest = operations['channels/follow']['requestBody']['content']['application/json'];
+export type ChannelsFollowedRequest = operations['channels/followed']['requestBody']['content']['application/json'];
+export type ChannelsFollowedResponse = operations['channels/followed']['responses']['200']['content']['application/json'];
+export type ChannelsOwnedRequest = operations['channels/owned']['requestBody']['content']['application/json'];
+export type ChannelsOwnedResponse = operations['channels/owned']['responses']['200']['content']['application/json'];
+export type ChannelsShowRequest = operations['channels/show']['requestBody']['content']['application/json'];
+export type ChannelsShowResponse = operations['channels/show']['responses']['200']['content']['application/json'];
+export type ChannelsTimelineRequest = operations['channels/timeline']['requestBody']['content']['application/json'];
+export type ChannelsTimelineResponse = operations['channels/timeline']['responses']['200']['content']['application/json'];
+export type ChannelsUnfollowRequest = operations['channels/unfollow']['requestBody']['content']['application/json'];
+export type ChannelsUpdateRequest = operations['channels/update']['requestBody']['content']['application/json'];
+export type ChannelsUpdateResponse = operations['channels/update']['responses']['200']['content']['application/json'];
+export type ChannelsFavoriteRequest = operations['channels/favorite']['requestBody']['content']['application/json'];
+export type ChannelsUnfavoriteRequest = operations['channels/unfavorite']['requestBody']['content']['application/json'];
+export type ChannelsMyFavoritesResponse = operations['channels/my-favorites']['responses']['200']['content']['application/json'];
+export type ChannelsSearchRequest = operations['channels/search']['requestBody']['content']['application/json'];
+export type ChannelsSearchResponse = operations['channels/search']['responses']['200']['content']['application/json'];
+export type ChartsActiveUsersRequest = operations['charts/active-users']['requestBody']['content']['application/json'];
+export type ChartsActiveUsersResponse = operations['charts/active-users']['responses']['200']['content']['application/json'];
+export type ChartsApRequestRequest = operations['charts/ap-request']['requestBody']['content']['application/json'];
+export type ChartsApRequestResponse = operations['charts/ap-request']['responses']['200']['content']['application/json'];
+export type ChartsDriveRequest = operations['charts/drive']['requestBody']['content']['application/json'];
+export type ChartsDriveResponse = operations['charts/drive']['responses']['200']['content']['application/json'];
+export type ChartsFederationRequest = operations['charts/federation']['requestBody']['content']['application/json'];
+export type ChartsFederationResponse = operations['charts/federation']['responses']['200']['content']['application/json'];
+export type ChartsInstanceRequest = operations['charts/instance']['requestBody']['content']['application/json'];
+export type ChartsInstanceResponse = operations['charts/instance']['responses']['200']['content']['application/json'];
+export type ChartsNotesRequest = operations['charts/notes']['requestBody']['content']['application/json'];
+export type ChartsNotesResponse = operations['charts/notes']['responses']['200']['content']['application/json'];
+export type ChartsUserDriveRequest = operations['charts/user/drive']['requestBody']['content']['application/json'];
+export type ChartsUserDriveResponse = operations['charts/user/drive']['responses']['200']['content']['application/json'];
+export type ChartsUserFollowingRequest = operations['charts/user/following']['requestBody']['content']['application/json'];
+export type ChartsUserFollowingResponse = operations['charts/user/following']['responses']['200']['content']['application/json'];
+export type ChartsUserNotesRequest = operations['charts/user/notes']['requestBody']['content']['application/json'];
+export type ChartsUserNotesResponse = operations['charts/user/notes']['responses']['200']['content']['application/json'];
+export type ChartsUserPvRequest = operations['charts/user/pv']['requestBody']['content']['application/json'];
+export type ChartsUserPvResponse = operations['charts/user/pv']['responses']['200']['content']['application/json'];
+export type ChartsUserReactionsRequest = operations['charts/user/reactions']['requestBody']['content']['application/json'];
+export type ChartsUserReactionsResponse = operations['charts/user/reactions']['responses']['200']['content']['application/json'];
+export type ChartsUsersRequest = operations['charts/users']['requestBody']['content']['application/json'];
+export type ChartsUsersResponse = operations['charts/users']['responses']['200']['content']['application/json'];
+export type ClipsAddNoteRequest = operations['clips/add-note']['requestBody']['content']['application/json'];
+export type ClipsRemoveNoteRequest = operations['clips/remove-note']['requestBody']['content']['application/json'];
+export type ClipsCreateRequest = operations['clips/create']['requestBody']['content']['application/json'];
+export type ClipsCreateResponse = operations['clips/create']['responses']['200']['content']['application/json'];
+export type ClipsDeleteRequest = operations['clips/delete']['requestBody']['content']['application/json'];
+export type ClipsListResponse = operations['clips/list']['responses']['200']['content']['application/json'];
+export type ClipsNotesRequest = operations['clips/notes']['requestBody']['content']['application/json'];
+export type ClipsNotesResponse = operations['clips/notes']['responses']['200']['content']['application/json'];
+export type ClipsShowRequest = operations['clips/show']['requestBody']['content']['application/json'];
+export type ClipsShowResponse = operations['clips/show']['responses']['200']['content']['application/json'];
+export type ClipsUpdateRequest = operations['clips/update']['requestBody']['content']['application/json'];
+export type ClipsUpdateResponse = operations['clips/update']['responses']['200']['content']['application/json'];
+export type ClipsFavoriteRequest = operations['clips/favorite']['requestBody']['content']['application/json'];
+export type ClipsUnfavoriteRequest = operations['clips/unfavorite']['requestBody']['content']['application/json'];
+export type ClipsMyFavoritesResponse = operations['clips/my-favorites']['responses']['200']['content']['application/json'];
+export type DriveResponse = operations['drive']['responses']['200']['content']['application/json'];
+export type DriveFilesRequest = operations['drive/files']['requestBody']['content']['application/json'];
+export type DriveFilesResponse = operations['drive/files']['responses']['200']['content']['application/json'];
+export type DriveFilesAttachedNotesRequest = operations['drive/files/attached-notes']['requestBody']['content']['application/json'];
+export type DriveFilesAttachedNotesResponse = operations['drive/files/attached-notes']['responses']['200']['content']['application/json'];
+export type DriveFilesCheckExistenceRequest = operations['drive/files/check-existence']['requestBody']['content']['application/json'];
+export type DriveFilesCheckExistenceResponse = operations['drive/files/check-existence']['responses']['200']['content']['application/json'];
+export type DriveFilesCreateRequest = operations['drive/files/create']['requestBody']['content']['multipart/form-data'];
+export type DriveFilesCreateResponse = operations['drive/files/create']['responses']['200']['content']['application/json'];
+export type DriveFilesDeleteRequest = operations['drive/files/delete']['requestBody']['content']['application/json'];
+export type DriveFilesFindByHashRequest = operations['drive/files/find-by-hash']['requestBody']['content']['application/json'];
+export type DriveFilesFindByHashResponse = operations['drive/files/find-by-hash']['responses']['200']['content']['application/json'];
+export type DriveFilesFindRequest = operations['drive/files/find']['requestBody']['content']['application/json'];
+export type DriveFilesFindResponse = operations['drive/files/find']['responses']['200']['content']['application/json'];
+export type DriveFilesShowRequest = operations['drive/files/show']['requestBody']['content']['application/json'];
+export type DriveFilesShowResponse = operations['drive/files/show']['responses']['200']['content']['application/json'];
+export type DriveFilesUpdateRequest = operations['drive/files/update']['requestBody']['content']['application/json'];
+export type DriveFilesUpdateResponse = operations['drive/files/update']['responses']['200']['content']['application/json'];
+export type DriveFilesUploadFromUrlRequest = operations['drive/files/upload-from-url']['requestBody']['content']['application/json'];
+export type DriveFoldersRequest = operations['drive/folders']['requestBody']['content']['application/json'];
+export type DriveFoldersResponse = operations['drive/folders']['responses']['200']['content']['application/json'];
+export type DriveFoldersCreateRequest = operations['drive/folders/create']['requestBody']['content']['application/json'];
+export type DriveFoldersCreateResponse = operations['drive/folders/create']['responses']['200']['content']['application/json'];
+export type DriveFoldersDeleteRequest = operations['drive/folders/delete']['requestBody']['content']['application/json'];
+export type DriveFoldersFindRequest = operations['drive/folders/find']['requestBody']['content']['application/json'];
+export type DriveFoldersFindResponse = operations['drive/folders/find']['responses']['200']['content']['application/json'];
+export type DriveFoldersShowRequest = operations['drive/folders/show']['requestBody']['content']['application/json'];
+export type DriveFoldersShowResponse = operations['drive/folders/show']['responses']['200']['content']['application/json'];
+export type DriveFoldersUpdateRequest = operations['drive/folders/update']['requestBody']['content']['application/json'];
+export type DriveFoldersUpdateResponse = operations['drive/folders/update']['responses']['200']['content']['application/json'];
+export type DriveStreamRequest = operations['drive/stream']['requestBody']['content']['application/json'];
+export type DriveStreamResponse = operations['drive/stream']['responses']['200']['content']['application/json'];
+export type EmailAddressAvailableRequest = operations['email-address/available']['requestBody']['content']['application/json'];
+export type EmailAddressAvailableResponse = operations['email-address/available']['responses']['200']['content']['application/json'];
+export type EndpointRequest = operations['endpoint']['requestBody']['content']['application/json'];
+export type EndpointsResponse = operations['endpoints']['responses']['200']['content']['application/json'];
+export type FederationFollowersRequest = operations['federation/followers']['requestBody']['content']['application/json'];
+export type FederationFollowersResponse = operations['federation/followers']['responses']['200']['content']['application/json'];
+export type FederationFollowingRequest = operations['federation/following']['requestBody']['content']['application/json'];
+export type FederationFollowingResponse = operations['federation/following']['responses']['200']['content']['application/json'];
+export type FederationInstancesRequest = operations['federation/instances']['requestBody']['content']['application/json'];
+export type FederationInstancesResponse = operations['federation/instances']['responses']['200']['content']['application/json'];
+export type FederationShowInstanceRequest = operations['federation/show-instance']['requestBody']['content']['application/json'];
+export type FederationShowInstanceResponse = operations['federation/show-instance']['responses']['200']['content']['application/json'];
+export type FederationUpdateRemoteUserRequest = operations['federation/update-remote-user']['requestBody']['content']['application/json'];
+export type FederationUsersRequest = operations['federation/users']['requestBody']['content']['application/json'];
+export type FederationUsersResponse = operations['federation/users']['responses']['200']['content']['application/json'];
+export type FederationStatsRequest = operations['federation/stats']['requestBody']['content']['application/json'];
+export type FollowingCreateRequest = operations['following/create']['requestBody']['content']['application/json'];
+export type FollowingCreateResponse = operations['following/create']['responses']['200']['content']['application/json'];
+export type FollowingDeleteRequest = operations['following/delete']['requestBody']['content']['application/json'];
+export type FollowingDeleteResponse = operations['following/delete']['responses']['200']['content']['application/json'];
+export type FollowingUpdateRequest = operations['following/update']['requestBody']['content']['application/json'];
+export type FollowingUpdateResponse = operations['following/update']['responses']['200']['content']['application/json'];
+export type FollowingUpdateAllRequest = operations['following/update-all']['requestBody']['content']['application/json'];
+export type FollowingInvalidateRequest = operations['following/invalidate']['requestBody']['content']['application/json'];
+export type FollowingInvalidateResponse = operations['following/invalidate']['responses']['200']['content']['application/json'];
+export type FollowingRequestsAcceptRequest = operations['following/requests/accept']['requestBody']['content']['application/json'];
+export type FollowingRequestsCancelRequest = operations['following/requests/cancel']['requestBody']['content']['application/json'];
+export type FollowingRequestsCancelResponse = operations['following/requests/cancel']['responses']['200']['content']['application/json'];
+export type FollowingRequestsListRequest = operations['following/requests/list']['requestBody']['content']['application/json'];
+export type FollowingRequestsListResponse = operations['following/requests/list']['responses']['200']['content']['application/json'];
+export type FollowingRequestsRejectRequest = operations['following/requests/reject']['requestBody']['content']['application/json'];
+export type GalleryFeaturedRequest = operations['gallery/featured']['requestBody']['content']['application/json'];
+export type GalleryFeaturedResponse = operations['gallery/featured']['responses']['200']['content']['application/json'];
+export type GalleryPopularResponse = operations['gallery/popular']['responses']['200']['content']['application/json'];
+export type GalleryPostsRequest = operations['gallery/posts']['requestBody']['content']['application/json'];
+export type GalleryPostsResponse = operations['gallery/posts']['responses']['200']['content']['application/json'];
+export type GalleryPostsCreateRequest = operations['gallery/posts/create']['requestBody']['content']['application/json'];
+export type GalleryPostsCreateResponse = operations['gallery/posts/create']['responses']['200']['content']['application/json'];
+export type GalleryPostsDeleteRequest = operations['gallery/posts/delete']['requestBody']['content']['application/json'];
+export type GalleryPostsLikeRequest = operations['gallery/posts/like']['requestBody']['content']['application/json'];
+export type GalleryPostsShowRequest = operations['gallery/posts/show']['requestBody']['content']['application/json'];
+export type GalleryPostsShowResponse = operations['gallery/posts/show']['responses']['200']['content']['application/json'];
+export type GalleryPostsUnlikeRequest = operations['gallery/posts/unlike']['requestBody']['content']['application/json'];
+export type GalleryPostsUpdateRequest = operations['gallery/posts/update']['requestBody']['content']['application/json'];
+export type GalleryPostsUpdateResponse = operations['gallery/posts/update']['responses']['200']['content']['application/json'];
+export type GetAvatarDecorationsResponse = operations['get-avatar-decorations']['responses']['200']['content']['application/json'];
+export type HashtagsListRequest = operations['hashtags/list']['requestBody']['content']['application/json'];
+export type HashtagsListResponse = operations['hashtags/list']['responses']['200']['content']['application/json'];
+export type HashtagsSearchRequest = operations['hashtags/search']['requestBody']['content']['application/json'];
+export type HashtagsSearchResponse = operations['hashtags/search']['responses']['200']['content']['application/json'];
+export type HashtagsShowRequest = operations['hashtags/show']['requestBody']['content']['application/json'];
+export type HashtagsShowResponse = operations['hashtags/show']['responses']['200']['content']['application/json'];
+export type HashtagsTrendResponse = operations['hashtags/trend']['responses']['200']['content']['application/json'];
+export type HashtagsUsersRequest = operations['hashtags/users']['requestBody']['content']['application/json'];
+export type HashtagsUsersResponse = operations['hashtags/users']['responses']['200']['content']['application/json'];
+export type IResponse = operations['i']['responses']['200']['content']['application/json'];
+export type IClaimAchievementRequest = operations['i/claim-achievement']['requestBody']['content']['application/json'];
+export type IFavoritesRequest = operations['i/favorites']['requestBody']['content']['application/json'];
+export type IFavoritesResponse = operations['i/favorites']['responses']['200']['content']['application/json'];
+export type IGalleryLikesRequest = operations['i/gallery/likes']['requestBody']['content']['application/json'];
+export type IGalleryLikesResponse = operations['i/gallery/likes']['responses']['200']['content']['application/json'];
+export type IGalleryPostsRequest = operations['i/gallery/posts']['requestBody']['content']['application/json'];
+export type IGalleryPostsResponse = operations['i/gallery/posts']['responses']['200']['content']['application/json'];
+export type INotificationsRequest = operations['i/notifications']['requestBody']['content']['application/json'];
+export type INotificationsResponse = operations['i/notifications']['responses']['200']['content']['application/json'];
+export type INotificationsGroupedRequest = operations['i/notifications-grouped']['requestBody']['content']['application/json'];
+export type INotificationsGroupedResponse = operations['i/notifications-grouped']['responses']['200']['content']['application/json'];
+export type IPageLikesRequest = operations['i/page-likes']['requestBody']['content']['application/json'];
+export type IPageLikesResponse = operations['i/page-likes']['responses']['200']['content']['application/json'];
+export type IPagesRequest = operations['i/pages']['requestBody']['content']['application/json'];
+export type IPagesResponse = operations['i/pages']['responses']['200']['content']['application/json'];
+export type IPinRequest = operations['i/pin']['requestBody']['content']['application/json'];
+export type IPinResponse = operations['i/pin']['responses']['200']['content']['application/json'];
+export type IReadAnnouncementRequest = operations['i/read-announcement']['requestBody']['content']['application/json'];
+export type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json'];
+export type IRegistryGetDetailRequest = operations['i/registry/get-detail']['requestBody']['content']['application/json'];
+export type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content']['application/json'];
+export type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type']['requestBody']['content']['application/json'];
+export type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json'];
+export type IRegistryRemoveRequest = operations['i/registry/remove']['requestBody']['content']['application/json'];
+export type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content']['application/json'];
+export type IUnpinRequest = operations['i/unpin']['requestBody']['content']['application/json'];
+export type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['application/json'];
+export type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json'];
+export type IUpdateResponse = operations['i/update']['responses']['200']['content']['application/json'];
+export type IWebhooksCreateRequest = operations['i/webhooks/create']['requestBody']['content']['application/json'];
+export type IWebhooksShowRequest = operations['i/webhooks/show']['requestBody']['content']['application/json'];
+export type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
+export type IWebhooksDeleteRequest = operations['i/webhooks/delete']['requestBody']['content']['application/json'];
+export type InviteCreateResponse = operations['invite/create']['responses']['200']['content']['application/json'];
+export type InviteDeleteRequest = operations['invite/delete']['requestBody']['content']['application/json'];
+export type InviteListRequest = operations['invite/list']['requestBody']['content']['application/json'];
+export type InviteListResponse = operations['invite/list']['responses']['200']['content']['application/json'];
+export type InviteLimitResponse = operations['invite/limit']['responses']['200']['content']['application/json'];
+export type MetaRequest = operations['meta']['requestBody']['content']['application/json'];
+export type MetaResponse = operations['meta']['responses']['200']['content']['application/json'];
+export type EmojisResponse = operations['emojis']['responses']['200']['content']['application/json'];
+export type EmojiRequest = operations['emoji']['requestBody']['content']['application/json'];
+export type EmojiResponse = operations['emoji']['responses']['200']['content']['application/json'];
+export type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json'];
+export type MuteDeleteRequest = operations['mute/delete']['requestBody']['content']['application/json'];
+export type MuteListRequest = operations['mute/list']['requestBody']['content']['application/json'];
+export type MuteListResponse = operations['mute/list']['responses']['200']['content']['application/json'];
+export type RenoteMuteCreateRequest = operations['renote-mute/create']['requestBody']['content']['application/json'];
+export type RenoteMuteDeleteRequest = operations['renote-mute/delete']['requestBody']['content']['application/json'];
+export type RenoteMuteListRequest = operations['renote-mute/list']['requestBody']['content']['application/json'];
+export type RenoteMuteListResponse = operations['renote-mute/list']['responses']['200']['content']['application/json'];
+export type MyAppsRequest = operations['my/apps']['requestBody']['content']['application/json'];
+export type MyAppsResponse = operations['my/apps']['responses']['200']['content']['application/json'];
+export type NotesRequest = operations['notes']['requestBody']['content']['application/json'];
+export type NotesResponse = operations['notes']['responses']['200']['content']['application/json'];
+export type NotesChildrenRequest = operations['notes/children']['requestBody']['content']['application/json'];
+export type NotesChildrenResponse = operations['notes/children']['responses']['200']['content']['application/json'];
+export type NotesClipsRequest = operations['notes/clips']['requestBody']['content']['application/json'];
+export type NotesClipsResponse = operations['notes/clips']['responses']['200']['content']['application/json'];
+export type NotesConversationRequest = operations['notes/conversation']['requestBody']['content']['application/json'];
+export type NotesConversationResponse = operations['notes/conversation']['responses']['200']['content']['application/json'];
+export type NotesCreateRequest = operations['notes/create']['requestBody']['content']['application/json'];
+export type NotesCreateResponse = operations['notes/create']['responses']['200']['content']['application/json'];
+export type NotesDeleteRequest = operations['notes/delete']['requestBody']['content']['application/json'];
+export type NotesFavoritesCreateRequest = operations['notes/favorites/create']['requestBody']['content']['application/json'];
+export type NotesFavoritesDeleteRequest = operations['notes/favorites/delete']['requestBody']['content']['application/json'];
+export type NotesFeaturedRequest = operations['notes/featured']['requestBody']['content']['application/json'];
+export type NotesFeaturedResponse = operations['notes/featured']['responses']['200']['content']['application/json'];
+export type NotesGlobalTimelineRequest = operations['notes/global-timeline']['requestBody']['content']['application/json'];
+export type NotesGlobalTimelineResponse = operations['notes/global-timeline']['responses']['200']['content']['application/json'];
+export type NotesHybridTimelineRequest = operations['notes/hybrid-timeline']['requestBody']['content']['application/json'];
+export type NotesHybridTimelineResponse = operations['notes/hybrid-timeline']['responses']['200']['content']['application/json'];
+export type NotesLocalTimelineRequest = operations['notes/local-timeline']['requestBody']['content']['application/json'];
+export type NotesLocalTimelineResponse = operations['notes/local-timeline']['responses']['200']['content']['application/json'];
+export type NotesMentionsRequest = operations['notes/mentions']['requestBody']['content']['application/json'];
+export type NotesMentionsResponse = operations['notes/mentions']['responses']['200']['content']['application/json'];
+export type NotesPollsRecommendationRequest = operations['notes/polls/recommendation']['requestBody']['content']['application/json'];
+export type NotesPollsRecommendationResponse = operations['notes/polls/recommendation']['responses']['200']['content']['application/json'];
+export type NotesPollsVoteRequest = operations['notes/polls/vote']['requestBody']['content']['application/json'];
+export type NotesReactionsRequest = operations['notes/reactions']['requestBody']['content']['application/json'];
+export type NotesReactionsResponse = operations['notes/reactions']['responses']['200']['content']['application/json'];
+export type NotesReactionsCreateRequest = operations['notes/reactions/create']['requestBody']['content']['application/json'];
+export type NotesReactionsDeleteRequest = operations['notes/reactions/delete']['requestBody']['content']['application/json'];
+export type NotesRenotesRequest = operations['notes/renotes']['requestBody']['content']['application/json'];
+export type NotesRenotesResponse = operations['notes/renotes']['responses']['200']['content']['application/json'];
+export type NotesRepliesRequest = operations['notes/replies']['requestBody']['content']['application/json'];
+export type NotesRepliesResponse = operations['notes/replies']['responses']['200']['content']['application/json'];
+export type NotesSearchByTagRequest = operations['notes/search-by-tag']['requestBody']['content']['application/json'];
+export type NotesSearchByTagResponse = operations['notes/search-by-tag']['responses']['200']['content']['application/json'];
+export type NotesSearchRequest = operations['notes/search']['requestBody']['content']['application/json'];
+export type NotesSearchResponse = operations['notes/search']['responses']['200']['content']['application/json'];
+export type NotesShowRequest = operations['notes/show']['requestBody']['content']['application/json'];
+export type NotesShowResponse = operations['notes/show']['responses']['200']['content']['application/json'];
+export type NotesStateRequest = operations['notes/state']['requestBody']['content']['application/json'];
+export type NotesStateResponse = operations['notes/state']['responses']['200']['content']['application/json'];
+export type NotesThreadMutingCreateRequest = operations['notes/thread-muting/create']['requestBody']['content']['application/json'];
+export type NotesThreadMutingDeleteRequest = operations['notes/thread-muting/delete']['requestBody']['content']['application/json'];
+export type NotesTimelineRequest = operations['notes/timeline']['requestBody']['content']['application/json'];
+export type NotesTimelineResponse = operations['notes/timeline']['responses']['200']['content']['application/json'];
+export type NotesTranslateRequest = operations['notes/translate']['requestBody']['content']['application/json'];
+export type NotesTranslateResponse = operations['notes/translate']['responses']['200']['content']['application/json'];
+export type NotesUnrenoteRequest = operations['notes/unrenote']['requestBody']['content']['application/json'];
+export type NotesUserListTimelineRequest = operations['notes/user-list-timeline']['requestBody']['content']['application/json'];
+export type NotesUserListTimelineResponse = operations['notes/user-list-timeline']['responses']['200']['content']['application/json'];
+export type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json'];
+export type PagesCreateRequest = operations['pages/create']['requestBody']['content']['application/json'];
+export type PagesCreateResponse = operations['pages/create']['responses']['200']['content']['application/json'];
+export type PagesDeleteRequest = operations['pages/delete']['requestBody']['content']['application/json'];
+export type PagesFeaturedResponse = operations['pages/featured']['responses']['200']['content']['application/json'];
+export type PagesLikeRequest = operations['pages/like']['requestBody']['content']['application/json'];
+export type PagesShowRequest = operations['pages/show']['requestBody']['content']['application/json'];
+export type PagesShowResponse = operations['pages/show']['responses']['200']['content']['application/json'];
+export type PagesUnlikeRequest = operations['pages/unlike']['requestBody']['content']['application/json'];
+export type PagesUpdateRequest = operations['pages/update']['requestBody']['content']['application/json'];
+export type FlashCreateRequest = operations['flash/create']['requestBody']['content']['application/json'];
+export type FlashDeleteRequest = operations['flash/delete']['requestBody']['content']['application/json'];
+export type FlashFeaturedResponse = operations['flash/featured']['responses']['200']['content']['application/json'];
+export type FlashLikeRequest = operations['flash/like']['requestBody']['content']['application/json'];
+export type FlashShowRequest = operations['flash/show']['requestBody']['content']['application/json'];
+export type FlashShowResponse = operations['flash/show']['responses']['200']['content']['application/json'];
+export type FlashUnlikeRequest = operations['flash/unlike']['requestBody']['content']['application/json'];
+export type FlashUpdateRequest = operations['flash/update']['requestBody']['content']['application/json'];
+export type FlashMyRequest = operations['flash/my']['requestBody']['content']['application/json'];
+export type FlashMyResponse = operations['flash/my']['responses']['200']['content']['application/json'];
+export type FlashMyLikesRequest = operations['flash/my-likes']['requestBody']['content']['application/json'];
+export type FlashMyLikesResponse = operations['flash/my-likes']['responses']['200']['content']['application/json'];
+export type PingResponse = operations['ping']['responses']['200']['content']['application/json'];
+export type PinnedUsersResponse = operations['pinned-users']['responses']['200']['content']['application/json'];
+export type PromoReadRequest = operations['promo/read']['requestBody']['content']['application/json'];
+export type RolesShowRequest = operations['roles/show']['requestBody']['content']['application/json'];
+export type RolesUsersRequest = operations['roles/users']['requestBody']['content']['application/json'];
+export type RolesNotesRequest = operations['roles/notes']['requestBody']['content']['application/json'];
+export type RolesNotesResponse = operations['roles/notes']['responses']['200']['content']['application/json'];
+export type RequestResetPasswordRequest = operations['request-reset-password']['requestBody']['content']['application/json'];
+export type ResetPasswordRequest = operations['reset-password']['requestBody']['content']['application/json'];
+export type StatsResponse = operations['stats']['responses']['200']['content']['application/json'];
+export type SwShowRegistrationRequest = operations['sw/show-registration']['requestBody']['content']['application/json'];
+export type SwShowRegistrationResponse = operations['sw/show-registration']['responses']['200']['content']['application/json'];
+export type SwUpdateRegistrationRequest = operations['sw/update-registration']['requestBody']['content']['application/json'];
+export type SwUpdateRegistrationResponse = operations['sw/update-registration']['responses']['200']['content']['application/json'];
+export type SwRegisterRequest = operations['sw/register']['requestBody']['content']['application/json'];
+export type SwRegisterResponse = operations['sw/register']['responses']['200']['content']['application/json'];
+export type SwUnregisterRequest = operations['sw/unregister']['requestBody']['content']['application/json'];
+export type TestRequest = operations['test']['requestBody']['content']['application/json'];
+export type UsernameAvailableRequest = operations['username/available']['requestBody']['content']['application/json'];
+export type UsernameAvailableResponse = operations['username/available']['responses']['200']['content']['application/json'];
+export type UsersRequest = operations['users']['requestBody']['content']['application/json'];
+export type UsersResponse = operations['users']['responses']['200']['content']['application/json'];
+export type UsersClipsRequest = operations['users/clips']['requestBody']['content']['application/json'];
+export type UsersClipsResponse = operations['users/clips']['responses']['200']['content']['application/json'];
+export type UsersFollowersRequest = operations['users/followers']['requestBody']['content']['application/json'];
+export type UsersFollowersResponse = operations['users/followers']['responses']['200']['content']['application/json'];
+export type UsersFollowingRequest = operations['users/following']['requestBody']['content']['application/json'];
+export type UsersFollowingResponse = operations['users/following']['responses']['200']['content']['application/json'];
+export type UsersGalleryPostsRequest = operations['users/gallery/posts']['requestBody']['content']['application/json'];
+export type UsersGalleryPostsResponse = operations['users/gallery/posts']['responses']['200']['content']['application/json'];
+export type UsersGetFrequentlyRepliedUsersRequest = operations['users/get-frequently-replied-users']['requestBody']['content']['application/json'];
+export type UsersGetFrequentlyRepliedUsersResponse = operations['users/get-frequently-replied-users']['responses']['200']['content']['application/json'];
+export type UsersFeaturedNotesRequest = operations['users/featured-notes']['requestBody']['content']['application/json'];
+export type UsersFeaturedNotesResponse = operations['users/featured-notes']['responses']['200']['content']['application/json'];
+export type UsersListsCreateRequest = operations['users/lists/create']['requestBody']['content']['application/json'];
+export type UsersListsCreateResponse = operations['users/lists/create']['responses']['200']['content']['application/json'];
+export type UsersListsDeleteRequest = operations['users/lists/delete']['requestBody']['content']['application/json'];
+export type UsersListsListRequest = operations['users/lists/list']['requestBody']['content']['application/json'];
+export type UsersListsListResponse = operations['users/lists/list']['responses']['200']['content']['application/json'];
+export type UsersListsPullRequest = operations['users/lists/pull']['requestBody']['content']['application/json'];
+export type UsersListsPushRequest = operations['users/lists/push']['requestBody']['content']['application/json'];
+export type UsersListsShowRequest = operations['users/lists/show']['requestBody']['content']['application/json'];
+export type UsersListsShowResponse = operations['users/lists/show']['responses']['200']['content']['application/json'];
+export type UsersListsFavoriteRequest = operations['users/lists/favorite']['requestBody']['content']['application/json'];
+export type UsersListsUnfavoriteRequest = operations['users/lists/unfavorite']['requestBody']['content']['application/json'];
+export type UsersListsUpdateRequest = operations['users/lists/update']['requestBody']['content']['application/json'];
+export type UsersListsUpdateResponse = operations['users/lists/update']['responses']['200']['content']['application/json'];
+export type UsersListsCreateFromPublicRequest = operations['users/lists/create-from-public']['requestBody']['content']['application/json'];
+export type UsersListsCreateFromPublicResponse = operations['users/lists/create-from-public']['responses']['200']['content']['application/json'];
+export type UsersListsUpdateMembershipRequest = operations['users/lists/update-membership']['requestBody']['content']['application/json'];
+export type UsersListsGetMembershipsRequest = operations['users/lists/get-memberships']['requestBody']['content']['application/json'];
+export type UsersNotesRequest = operations['users/notes']['requestBody']['content']['application/json'];
+export type UsersNotesResponse = operations['users/notes']['responses']['200']['content']['application/json'];
+export type UsersPagesRequest = operations['users/pages']['requestBody']['content']['application/json'];
+export type UsersPagesResponse = operations['users/pages']['responses']['200']['content']['application/json'];
+export type UsersFlashsRequest = operations['users/flashs']['requestBody']['content']['application/json'];
+export type UsersFlashsResponse = operations['users/flashs']['responses']['200']['content']['application/json'];
+export type UsersReactionsRequest = operations['users/reactions']['requestBody']['content']['application/json'];
+export type UsersReactionsResponse = operations['users/reactions']['responses']['200']['content']['application/json'];
+export type UsersRecommendationRequest = operations['users/recommendation']['requestBody']['content']['application/json'];
+export type UsersRecommendationResponse = operations['users/recommendation']['responses']['200']['content']['application/json'];
+export type UsersRelationRequest = operations['users/relation']['requestBody']['content']['application/json'];
+export type UsersRelationResponse = operations['users/relation']['responses']['200']['content']['application/json'];
+export type UsersReportAbuseRequest = operations['users/report-abuse']['requestBody']['content']['application/json'];
+export type UsersSearchByUsernameAndHostRequest = operations['users/search-by-username-and-host']['requestBody']['content']['application/json'];
+export type UsersSearchByUsernameAndHostResponse = operations['users/search-by-username-and-host']['responses']['200']['content']['application/json'];
+export type UsersSearchRequest = operations['users/search']['requestBody']['content']['application/json'];
+export type UsersSearchResponse = operations['users/search']['responses']['200']['content']['application/json'];
+export type UsersShowRequest = operations['users/show']['requestBody']['content']['application/json'];
+export type UsersShowResponse = operations['users/show']['responses']['200']['content']['application/json'];
+export type UsersAchievementsRequest = operations['users/achievements']['requestBody']['content']['application/json'];
+export type UsersUpdateMemoRequest = operations['users/update-memo']['requestBody']['content']['application/json'];
+export type FetchRssRequest = operations['fetch-rss']['requestBody']['content']['application/json'];
+export type FetchExternalResourcesRequest = operations['fetch-external-resources']['requestBody']['content']['application/json'];
+export type RetentionResponse = operations['retention']['responses']['200']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
new file mode 100644
index 0000000000..bc7ab1f3b9
--- /dev/null
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -0,0 +1,39 @@
+/*
+ * version: 2023.11.1
+ * generatedAt: 2023-11-27T02:24:45.109Z
+ */
+
+import { components } from './types.js';
+export type Error = components['schemas']['Error'];
+export type UserLite = components['schemas']['UserLite'];
+export type UserDetailedNotMeOnly = components['schemas']['UserDetailedNotMeOnly'];
+export type MeDetailedOnly = components['schemas']['MeDetailedOnly'];
+export type UserDetailedNotMe = components['schemas']['UserDetailedNotMe'];
+export type MeDetailed = components['schemas']['MeDetailed'];
+export type UserDetailed = components['schemas']['UserDetailed'];
+export type User = components['schemas']['User'];
+export type UserList = components['schemas']['UserList'];
+export type Announcement = components['schemas']['Announcement'];
+export type App = components['schemas']['App'];
+export type Note = components['schemas']['Note'];
+export type NoteReaction = components['schemas']['NoteReaction'];
+export type NoteFavorite = components['schemas']['NoteFavorite'];
+export type Notification = components['schemas']['Notification'];
+export type DriveFile = components['schemas']['DriveFile'];
+export type DriveFolder = components['schemas']['DriveFolder'];
+export type Following = components['schemas']['Following'];
+export type Muting = components['schemas']['Muting'];
+export type RenoteMuting = components['schemas']['RenoteMuting'];
+export type Blocking = components['schemas']['Blocking'];
+export type Hashtag = components['schemas']['Hashtag'];
+export type InviteCode = components['schemas']['InviteCode'];
+export type Page = components['schemas']['Page'];
+export type Channel = components['schemas']['Channel'];
+export type QueueCount = components['schemas']['QueueCount'];
+export type Antenna = components['schemas']['Antenna'];
+export type Clip = components['schemas']['Clip'];
+export type FederationInstance = components['schemas']['FederationInstance'];
+export type GalleryPost = components['schemas']['GalleryPost'];
+export type EmojiSimple = components['schemas']['EmojiSimple'];
+export type EmojiDetailed = components['schemas']['EmojiDetailed'];
+export type Flash = components['schemas']['Flash'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
new file mode 100644
index 0000000000..c7f5c6c82d
--- /dev/null
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -0,0 +1,22560 @@
+/* eslint @typescript-eslint/naming-convention: 0 */
+/* eslint @typescript-eslint/no-explicit-any: 0 */
+
+/*
+ * version: 2023.11.1
+ * generatedAt: 2023-11-27T02:24:44.994Z
+ */
+
+/**
+ * This file was auto-generated by openapi-typescript.
+ * Do not make direct changes to the file.
+ */
+
+/** OneOf type helpers */
+type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
+type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
+type OneOf<T extends any[]> = T extends [infer Only] ? Only : T extends [infer A, infer B, ...infer Rest] ? OneOf<[XOR<A, B>, ...Rest]> : never;
+
+export type paths = {
+  '/admin/meta': {
+    /**
+     * admin/meta
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/meta'];
+  };
+  '/admin/abuse-user-reports': {
+    /**
+     * admin/abuse-user-reports
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/abuse-user-reports'];
+  };
+  '/admin/accounts/create': {
+    /**
+     * admin/accounts/create
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['admin/accounts/create'];
+  };
+  '/admin/accounts/delete': {
+    /**
+     * admin/accounts/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/accounts/delete'];
+  };
+  '/admin/accounts/find-by-email': {
+    /**
+     * admin/accounts/find-by-email
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/accounts/find-by-email'];
+  };
+  '/admin/ad/create': {
+    /**
+     * admin/ad/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/ad/create'];
+  };
+  '/admin/ad/delete': {
+    /**
+     * admin/ad/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/ad/delete'];
+  };
+  '/admin/ad/list': {
+    /**
+     * admin/ad/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/ad/list'];
+  };
+  '/admin/ad/update': {
+    /**
+     * admin/ad/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/ad/update'];
+  };
+  '/admin/announcements/create': {
+    /**
+     * admin/announcements/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/announcements/create'];
+  };
+  '/admin/announcements/delete': {
+    /**
+     * admin/announcements/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/announcements/delete'];
+  };
+  '/admin/announcements/list': {
+    /**
+     * admin/announcements/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/announcements/list'];
+  };
+  '/admin/announcements/update': {
+    /**
+     * admin/announcements/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/announcements/update'];
+  };
+  '/admin/avatar-decorations/create': {
+    /**
+     * admin/avatar-decorations/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/avatar-decorations/create'];
+  };
+  '/admin/avatar-decorations/delete': {
+    /**
+     * admin/avatar-decorations/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/avatar-decorations/delete'];
+  };
+  '/admin/avatar-decorations/list': {
+    /**
+     * admin/avatar-decorations/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/avatar-decorations/list'];
+  };
+  '/admin/avatar-decorations/update': {
+    /**
+     * admin/avatar-decorations/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/avatar-decorations/update'];
+  };
+  '/admin/delete-all-files-of-a-user': {
+    /**
+     * admin/delete-all-files-of-a-user
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/delete-all-files-of-a-user'];
+  };
+  '/admin/unset-user-avatar': {
+    /**
+     * admin/unset-user-avatar
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/unset-user-avatar'];
+  };
+  '/admin/unset-user-banner': {
+    /**
+     * admin/unset-user-banner
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/unset-user-banner'];
+  };
+  '/admin/drive/clean-remote-files': {
+    /**
+     * admin/drive/clean-remote-files
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/drive/clean-remote-files'];
+  };
+  '/admin/drive/cleanup': {
+    /**
+     * admin/drive/cleanup
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/drive/cleanup'];
+  };
+  '/admin/drive/files': {
+    /**
+     * admin/drive/files
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/drive/files'];
+  };
+  '/admin/drive/show-file': {
+    /**
+     * admin/drive/show-file
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/drive/show-file'];
+  };
+  '/admin/emoji/add-aliases-bulk': {
+    /**
+     * admin/emoji/add-aliases-bulk
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/add-aliases-bulk'];
+  };
+  '/admin/emoji/add': {
+    /**
+     * admin/emoji/add
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/add'];
+  };
+  '/admin/emoji/copy': {
+    /**
+     * admin/emoji/copy
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/copy'];
+  };
+  '/admin/emoji/delete-bulk': {
+    /**
+     * admin/emoji/delete-bulk
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/delete-bulk'];
+  };
+  '/admin/emoji/delete': {
+    /**
+     * admin/emoji/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/delete'];
+  };
+  '/admin/emoji/list-remote': {
+    /**
+     * admin/emoji/list-remote
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/list-remote'];
+  };
+  '/admin/emoji/list': {
+    /**
+     * admin/emoji/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/list'];
+  };
+  '/admin/emoji/remove-aliases-bulk': {
+    /**
+     * admin/emoji/remove-aliases-bulk
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/remove-aliases-bulk'];
+  };
+  '/admin/emoji/set-aliases-bulk': {
+    /**
+     * admin/emoji/set-aliases-bulk
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/set-aliases-bulk'];
+  };
+  '/admin/emoji/set-category-bulk': {
+    /**
+     * admin/emoji/set-category-bulk
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/set-category-bulk'];
+  };
+  '/admin/emoji/set-license-bulk': {
+    /**
+     * admin/emoji/set-license-bulk
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/set-license-bulk'];
+  };
+  '/admin/emoji/update': {
+    /**
+     * admin/emoji/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/update'];
+  };
+  '/admin/federation/delete-all-files': {
+    /**
+     * admin/federation/delete-all-files
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/federation/delete-all-files'];
+  };
+  '/admin/federation/refresh-remote-instance-metadata': {
+    /**
+     * admin/federation/refresh-remote-instance-metadata
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/federation/refresh-remote-instance-metadata'];
+  };
+  '/admin/federation/remove-all-following': {
+    /**
+     * admin/federation/remove-all-following
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/federation/remove-all-following'];
+  };
+  '/admin/federation/update-instance': {
+    /**
+     * admin/federation/update-instance
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/federation/update-instance'];
+  };
+  '/admin/get-index-stats': {
+    /**
+     * admin/get-index-stats
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/get-index-stats'];
+  };
+  '/admin/get-table-stats': {
+    /**
+     * admin/get-table-stats
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/get-table-stats'];
+  };
+  '/admin/get-user-ips': {
+    /**
+     * admin/get-user-ips
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/get-user-ips'];
+  };
+  '/admin/invite/create': {
+    /**
+     * admin/invite/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/invite/create'];
+  };
+  '/admin/invite/list': {
+    /**
+     * admin/invite/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/invite/list'];
+  };
+  '/admin/promo/create': {
+    /**
+     * admin/promo/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/promo/create'];
+  };
+  '/admin/queue/clear': {
+    /**
+     * admin/queue/clear
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/queue/clear'];
+  };
+  '/admin/queue/deliver-delayed': {
+    /**
+     * admin/queue/deliver-delayed
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/queue/deliver-delayed'];
+  };
+  '/admin/queue/inbox-delayed': {
+    /**
+     * admin/queue/inbox-delayed
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/queue/inbox-delayed'];
+  };
+  '/admin/queue/promote': {
+    /**
+     * admin/queue/promote
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/queue/promote'];
+  };
+  '/admin/queue/stats': {
+    /**
+     * admin/queue/stats
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/queue/stats'];
+  };
+  '/admin/relays/add': {
+    /**
+     * admin/relays/add
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/relays/add'];
+  };
+  '/admin/relays/list': {
+    /**
+     * admin/relays/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/relays/list'];
+  };
+  '/admin/relays/remove': {
+    /**
+     * admin/relays/remove
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/relays/remove'];
+  };
+  '/admin/reset-password': {
+    /**
+     * admin/reset-password
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/reset-password'];
+  };
+  '/admin/resolve-abuse-user-report': {
+    /**
+     * admin/resolve-abuse-user-report
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/resolve-abuse-user-report'];
+  };
+  '/admin/send-email': {
+    /**
+     * admin/send-email
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/send-email'];
+  };
+  '/admin/server-info': {
+    /**
+     * admin/server-info
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/server-info'];
+  };
+  '/admin/show-moderation-logs': {
+    /**
+     * admin/show-moderation-logs
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/show-moderation-logs'];
+  };
+  '/admin/show-user': {
+    /**
+     * admin/show-user
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/show-user'];
+  };
+  '/admin/show-users': {
+    /**
+     * admin/show-users
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/show-users'];
+  };
+  '/admin/suspend-user': {
+    /**
+     * admin/suspend-user
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/suspend-user'];
+  };
+  '/admin/unsuspend-user': {
+    /**
+     * admin/unsuspend-user
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/unsuspend-user'];
+  };
+  '/admin/update-meta': {
+    /**
+     * admin/update-meta
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/update-meta'];
+  };
+  '/admin/delete-account': {
+    /**
+     * admin/delete-account
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/delete-account'];
+  };
+  '/admin/update-user-note': {
+    /**
+     * admin/update-user-note
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/update-user-note'];
+  };
+  '/admin/roles/create': {
+    /**
+     * admin/roles/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/roles/create'];
+  };
+  '/admin/roles/delete': {
+    /**
+     * admin/roles/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/roles/delete'];
+  };
+  '/admin/roles/list': {
+    /**
+     * admin/roles/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/roles/list'];
+  };
+  '/admin/roles/show': {
+    /**
+     * admin/roles/show
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/roles/show'];
+  };
+  '/admin/roles/update': {
+    /**
+     * admin/roles/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/roles/update'];
+  };
+  '/admin/roles/assign': {
+    /**
+     * admin/roles/assign
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/roles/assign'];
+  };
+  '/admin/roles/unassign': {
+    /**
+     * admin/roles/unassign
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/roles/unassign'];
+  };
+  '/admin/roles/update-default-policies': {
+    /**
+     * admin/roles/update-default-policies
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/roles/update-default-policies'];
+  };
+  '/admin/roles/users': {
+    /**
+     * admin/roles/users
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['admin/roles/users'];
+  };
+  '/announcements': {
+    /**
+     * announcements
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['announcements'];
+  };
+  '/antennas/create': {
+    /**
+     * antennas/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['antennas/create'];
+  };
+  '/antennas/delete': {
+    /**
+     * antennas/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['antennas/delete'];
+  };
+  '/antennas/list': {
+    /**
+     * antennas/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    post: operations['antennas/list'];
+  };
+  '/antennas/notes': {
+    /**
+     * antennas/notes
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    post: operations['antennas/notes'];
+  };
+  '/antennas/show': {
+    /**
+     * antennas/show
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    post: operations['antennas/show'];
+  };
+  '/antennas/update': {
+    /**
+     * antennas/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['antennas/update'];
+  };
+  '/ap/get': {
+    /**
+     * ap/get
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['ap/get'];
+  };
+  '/ap/show': {
+    /**
+     * ap/show
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['ap/show'];
+  };
+  '/app/create': {
+    /**
+     * app/create
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['app/create'];
+  };
+  '/app/show': {
+    /**
+     * app/show
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['app/show'];
+  };
+  '/auth/session/generate': {
+    /**
+     * auth/session/generate
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['auth/session/generate'];
+  };
+  '/auth/session/show': {
+    /**
+     * auth/session/show
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['auth/session/show'];
+  };
+  '/auth/session/userkey': {
+    /**
+     * auth/session/userkey
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['auth/session/userkey'];
+  };
+  '/blocking/create': {
+    /**
+     * blocking/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:blocks*
+     */
+    post: operations['blocking/create'];
+  };
+  '/blocking/delete': {
+    /**
+     * blocking/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:blocks*
+     */
+    post: operations['blocking/delete'];
+  };
+  '/blocking/list': {
+    /**
+     * blocking/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:blocks*
+     */
+    post: operations['blocking/list'];
+  };
+  '/channels/create': {
+    /**
+     * channels/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    post: operations['channels/create'];
+  };
+  '/channels/featured': {
+    /**
+     * channels/featured
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['channels/featured'];
+  };
+  '/channels/follow': {
+    /**
+     * channels/follow
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    post: operations['channels/follow'];
+  };
+  '/channels/followed': {
+    /**
+     * channels/followed
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:channels*
+     */
+    post: operations['channels/followed'];
+  };
+  '/channels/owned': {
+    /**
+     * channels/owned
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:channels*
+     */
+    post: operations['channels/owned'];
+  };
+  '/channels/show': {
+    /**
+     * channels/show
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['channels/show'];
+  };
+  '/channels/timeline': {
+    /**
+     * channels/timeline
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['channels/timeline'];
+  };
+  '/channels/unfollow': {
+    /**
+     * channels/unfollow
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    post: operations['channels/unfollow'];
+  };
+  '/channels/update': {
+    /**
+     * channels/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    post: operations['channels/update'];
+  };
+  '/channels/favorite': {
+    /**
+     * channels/favorite
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    post: operations['channels/favorite'];
+  };
+  '/channels/unfavorite': {
+    /**
+     * channels/unfavorite
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    post: operations['channels/unfavorite'];
+  };
+  '/channels/my-favorites': {
+    /**
+     * channels/my-favorites
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:channels*
+     */
+    post: operations['channels/my-favorites'];
+  };
+  '/channels/search': {
+    /**
+     * channels/search
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['channels/search'];
+  };
+  '/charts/active-users': {
+    /**
+     * charts/active-users
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/active-users'];
+    /**
+     * charts/active-users
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/active-users'];
+  };
+  '/charts/ap-request': {
+    /**
+     * charts/ap-request
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/ap-request'];
+    /**
+     * charts/ap-request
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/ap-request'];
+  };
+  '/charts/drive': {
+    /**
+     * charts/drive
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/drive'];
+    /**
+     * charts/drive
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/drive'];
+  };
+  '/charts/federation': {
+    /**
+     * charts/federation
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/federation'];
+    /**
+     * charts/federation
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/federation'];
+  };
+  '/charts/instance': {
+    /**
+     * charts/instance
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/instance'];
+    /**
+     * charts/instance
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/instance'];
+  };
+  '/charts/notes': {
+    /**
+     * charts/notes
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/notes'];
+    /**
+     * charts/notes
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/notes'];
+  };
+  '/charts/user/drive': {
+    /**
+     * charts/user/drive
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/user/drive'];
+    /**
+     * charts/user/drive
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/user/drive'];
+  };
+  '/charts/user/following': {
+    /**
+     * charts/user/following
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/user/following'];
+    /**
+     * charts/user/following
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/user/following'];
+  };
+  '/charts/user/notes': {
+    /**
+     * charts/user/notes
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/user/notes'];
+    /**
+     * charts/user/notes
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/user/notes'];
+  };
+  '/charts/user/pv': {
+    /**
+     * charts/user/pv
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/user/pv'];
+    /**
+     * charts/user/pv
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/user/pv'];
+  };
+  '/charts/user/reactions': {
+    /**
+     * charts/user/reactions
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/user/reactions'];
+    /**
+     * charts/user/reactions
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/user/reactions'];
+  };
+  '/charts/users': {
+    /**
+     * charts/users
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['charts/users'];
+    /**
+     * charts/users
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['charts/users'];
+  };
+  '/clips/add-note': {
+    /**
+     * clips/add-note
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['clips/add-note'];
+  };
+  '/clips/remove-note': {
+    /**
+     * clips/remove-note
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['clips/remove-note'];
+  };
+  '/clips/create': {
+    /**
+     * clips/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['clips/create'];
+  };
+  '/clips/delete': {
+    /**
+     * clips/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['clips/delete'];
+  };
+  '/clips/list': {
+    /**
+     * clips/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    post: operations['clips/list'];
+  };
+  '/clips/notes': {
+    /**
+     * clips/notes
+     * @description No description provided.
+     *
+     * **Credential required**: *No* / **Permission**: *read:account*
+     */
+    post: operations['clips/notes'];
+  };
+  '/clips/show': {
+    /**
+     * clips/show
+     * @description No description provided.
+     *
+     * **Credential required**: *No* / **Permission**: *read:account*
+     */
+    post: operations['clips/show'];
+  };
+  '/clips/update': {
+    /**
+     * clips/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['clips/update'];
+  };
+  '/clips/favorite': {
+    /**
+     * clips/favorite
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
+     */
+    post: operations['clips/favorite'];
+  };
+  '/clips/unfavorite': {
+    /**
+     * clips/unfavorite
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
+     */
+    post: operations['clips/unfavorite'];
+  };
+  '/clips/my-favorites': {
+    /**
+     * clips/my-favorites
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:clip-favorite*
+     */
+    post: operations['clips/my-favorites'];
+  };
+  '/drive': {
+    /**
+     * drive
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    post: operations['drive'];
+  };
+  '/drive/files': {
+    /**
+     * drive/files
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    post: operations['drive/files'];
+  };
+  '/drive/files/attached-notes': {
+    /**
+     * drive/files/attached-notes
+     * @description Find the notes to which the given file is attached.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    post: operations['drive/files/attached-notes'];
+  };
+  '/drive/files/check-existence': {
+    /**
+     * drive/files/check-existence
+     * @description Check if a given file exists.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    post: operations['drive/files/check-existence'];
+  };
+  '/drive/files/create': {
+    /**
+     * drive/files/create
+     * @description Upload a new drive file.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    post: operations['drive/files/create'];
+  };
+  '/drive/files/delete': {
+    /**
+     * drive/files/delete
+     * @description Delete an existing drive file.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    post: operations['drive/files/delete'];
+  };
+  '/drive/files/find-by-hash': {
+    /**
+     * drive/files/find-by-hash
+     * @description Search for a drive file by a hash of the contents.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    post: operations['drive/files/find-by-hash'];
+  };
+  '/drive/files/find': {
+    /**
+     * drive/files/find
+     * @description Search for a drive file by the given parameters.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    post: operations['drive/files/find'];
+  };
+  '/drive/files/show': {
+    /**
+     * drive/files/show
+     * @description Show the properties of a drive file.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    post: operations['drive/files/show'];
+  };
+  '/drive/files/update': {
+    /**
+     * drive/files/update
+     * @description Update the properties of a drive file.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    post: operations['drive/files/update'];
+  };
+  '/drive/files/upload-from-url': {
+    /**
+     * drive/files/upload-from-url
+     * @description Request the server to download a new drive file from the specified URL.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    post: operations['drive/files/upload-from-url'];
+  };
+  '/drive/folders': {
+    /**
+     * drive/folders
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    post: operations['drive/folders'];
+  };
+  '/drive/folders/create': {
+    /**
+     * drive/folders/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    post: operations['drive/folders/create'];
+  };
+  '/drive/folders/delete': {
+    /**
+     * drive/folders/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    post: operations['drive/folders/delete'];
+  };
+  '/drive/folders/find': {
+    /**
+     * drive/folders/find
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    post: operations['drive/folders/find'];
+  };
+  '/drive/folders/show': {
+    /**
+     * drive/folders/show
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    post: operations['drive/folders/show'];
+  };
+  '/drive/folders/update': {
+    /**
+     * drive/folders/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    post: operations['drive/folders/update'];
+  };
+  '/drive/stream': {
+    /**
+     * drive/stream
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    post: operations['drive/stream'];
+  };
+  '/email-address/available': {
+    /**
+     * email-address/available
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['email-address/available'];
+  };
+  '/endpoint': {
+    /**
+     * endpoint
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['endpoint'];
+  };
+  '/endpoints': {
+    /**
+     * endpoints
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['endpoints'];
+  };
+  '/federation/followers': {
+    /**
+     * federation/followers
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['federation/followers'];
+  };
+  '/federation/following': {
+    /**
+     * federation/following
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['federation/following'];
+  };
+  '/federation/instances': {
+    /**
+     * federation/instances
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['federation/instances'];
+    /**
+     * federation/instances
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['federation/instances'];
+  };
+  '/federation/show-instance': {
+    /**
+     * federation/show-instance
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['federation/show-instance'];
+  };
+  '/federation/update-remote-user': {
+    /**
+     * federation/update-remote-user
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['federation/update-remote-user'];
+  };
+  '/federation/users': {
+    /**
+     * federation/users
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['federation/users'];
+  };
+  '/federation/stats': {
+    /**
+     * federation/stats
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['federation/stats'];
+    /**
+     * federation/stats
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['federation/stats'];
+  };
+  '/following/create': {
+    /**
+     * following/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    post: operations['following/create'];
+  };
+  '/following/delete': {
+    /**
+     * following/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    post: operations['following/delete'];
+  };
+  '/following/update': {
+    /**
+     * following/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    post: operations['following/update'];
+  };
+  '/following/update-all': {
+    /**
+     * following/update-all
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    post: operations['following/update-all'];
+  };
+  '/following/invalidate': {
+    /**
+     * following/invalidate
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    post: operations['following/invalidate'];
+  };
+  '/following/requests/accept': {
+    /**
+     * following/requests/accept
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    post: operations['following/requests/accept'];
+  };
+  '/following/requests/cancel': {
+    /**
+     * following/requests/cancel
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    post: operations['following/requests/cancel'];
+  };
+  '/following/requests/list': {
+    /**
+     * following/requests/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:following*
+     */
+    post: operations['following/requests/list'];
+  };
+  '/following/requests/reject': {
+    /**
+     * following/requests/reject
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    post: operations['following/requests/reject'];
+  };
+  '/gallery/featured': {
+    /**
+     * gallery/featured
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['gallery/featured'];
+  };
+  '/gallery/popular': {
+    /**
+     * gallery/popular
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['gallery/popular'];
+  };
+  '/gallery/posts': {
+    /**
+     * gallery/posts
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['gallery/posts'];
+  };
+  '/gallery/posts/create': {
+    /**
+     * gallery/posts/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:gallery*
+     */
+    post: operations['gallery/posts/create'];
+  };
+  '/gallery/posts/delete': {
+    /**
+     * gallery/posts/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:gallery*
+     */
+    post: operations['gallery/posts/delete'];
+  };
+  '/gallery/posts/like': {
+    /**
+     * gallery/posts/like
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
+     */
+    post: operations['gallery/posts/like'];
+  };
+  '/gallery/posts/show': {
+    /**
+     * gallery/posts/show
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['gallery/posts/show'];
+  };
+  '/gallery/posts/unlike': {
+    /**
+     * gallery/posts/unlike
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
+     */
+    post: operations['gallery/posts/unlike'];
+  };
+  '/gallery/posts/update': {
+    /**
+     * gallery/posts/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:gallery*
+     */
+    post: operations['gallery/posts/update'];
+  };
+  '/get-online-users-count': {
+    /**
+     * get-online-users-count
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['get-online-users-count'];
+    /**
+     * get-online-users-count
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['get-online-users-count'];
+  };
+  '/get-avatar-decorations': {
+    /**
+     * get-avatar-decorations
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['get-avatar-decorations'];
+  };
+  '/hashtags/list': {
+    /**
+     * hashtags/list
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['hashtags/list'];
+  };
+  '/hashtags/search': {
+    /**
+     * hashtags/search
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['hashtags/search'];
+  };
+  '/hashtags/show': {
+    /**
+     * hashtags/show
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['hashtags/show'];
+  };
+  '/hashtags/trend': {
+    /**
+     * hashtags/trend
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['hashtags/trend'];
+    /**
+     * hashtags/trend
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['hashtags/trend'];
+  };
+  '/hashtags/users': {
+    /**
+     * hashtags/users
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['hashtags/users'];
+  };
+  '/i': {
+    /**
+     * i
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['i'];
+  };
+  '/i/claim-achievement': {
+    /**
+     * i/claim-achievement
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/claim-achievement'];
+  };
+  '/i/favorites': {
+    /**
+     * i/favorites
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:favorites*
+     */
+    post: operations['i/favorites'];
+  };
+  '/i/gallery/likes': {
+    /**
+     * i/gallery/likes
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:gallery-likes*
+     */
+    post: operations['i/gallery/likes'];
+  };
+  '/i/gallery/posts': {
+    /**
+     * i/gallery/posts
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:gallery*
+     */
+    post: operations['i/gallery/posts'];
+  };
+  '/i/notifications': {
+    /**
+     * i/notifications
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:notifications*
+     */
+    post: operations['i/notifications'];
+  };
+  '/i/notifications-grouped': {
+    /**
+     * i/notifications-grouped
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:notifications*
+     */
+    post: operations['i/notifications-grouped'];
+  };
+  '/i/page-likes': {
+    /**
+     * i/page-likes
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:page-likes*
+     */
+    post: operations['i/page-likes'];
+  };
+  '/i/pages': {
+    /**
+     * i/pages
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:pages*
+     */
+    post: operations['i/pages'];
+  };
+  '/i/pin': {
+    /**
+     * i/pin
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['i/pin'];
+  };
+  '/i/read-all-unread-notes': {
+    /**
+     * i/read-all-unread-notes
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['i/read-all-unread-notes'];
+  };
+  '/i/read-announcement': {
+    /**
+     * i/read-announcement
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['i/read-announcement'];
+  };
+  '/i/registry/get-all': {
+    /**
+     * i/registry/get-all
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/registry/get-all'];
+  };
+  '/i/registry/get-detail': {
+    /**
+     * i/registry/get-detail
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/registry/get-detail'];
+  };
+  '/i/registry/get': {
+    /**
+     * i/registry/get
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/registry/get'];
+  };
+  '/i/registry/keys-with-type': {
+    /**
+     * i/registry/keys-with-type
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/registry/keys-with-type'];
+  };
+  '/i/registry/keys': {
+    /**
+     * i/registry/keys
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/registry/keys'];
+  };
+  '/i/registry/remove': {
+    /**
+     * i/registry/remove
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/registry/remove'];
+  };
+  '/i/registry/set': {
+    /**
+     * i/registry/set
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/registry/set'];
+  };
+  '/i/unpin': {
+    /**
+     * i/unpin
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['i/unpin'];
+  };
+  '/i/update': {
+    /**
+     * i/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['i/update'];
+  };
+  '/i/webhooks/create': {
+    /**
+     * i/webhooks/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['i/webhooks/create'];
+  };
+  '/i/webhooks/list': {
+    /**
+     * i/webhooks/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    post: operations['i/webhooks/list'];
+  };
+  '/i/webhooks/show': {
+    /**
+     * i/webhooks/show
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    post: operations['i/webhooks/show'];
+  };
+  '/i/webhooks/update': {
+    /**
+     * i/webhooks/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['i/webhooks/update'];
+  };
+  '/i/webhooks/delete': {
+    /**
+     * i/webhooks/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['i/webhooks/delete'];
+  };
+  '/invite/create': {
+    /**
+     * invite/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['invite/create'];
+  };
+  '/invite/delete': {
+    /**
+     * invite/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['invite/delete'];
+  };
+  '/invite/list': {
+    /**
+     * invite/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['invite/list'];
+  };
+  '/invite/limit': {
+    /**
+     * invite/limit
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['invite/limit'];
+  };
+  '/meta': {
+    /**
+     * meta
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['meta'];
+  };
+  '/emojis': {
+    /**
+     * emojis
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['emojis'];
+    /**
+     * emojis
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['emojis'];
+  };
+  '/emoji': {
+    /**
+     * emoji
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['emoji'];
+    /**
+     * emoji
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['emoji'];
+  };
+  '/mute/create': {
+    /**
+     * mute/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:mutes*
+     */
+    post: operations['mute/create'];
+  };
+  '/mute/delete': {
+    /**
+     * mute/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:mutes*
+     */
+    post: operations['mute/delete'];
+  };
+  '/mute/list': {
+    /**
+     * mute/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:mutes*
+     */
+    post: operations['mute/list'];
+  };
+  '/renote-mute/create': {
+    /**
+     * renote-mute/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:mutes*
+     */
+    post: operations['renote-mute/create'];
+  };
+  '/renote-mute/delete': {
+    /**
+     * renote-mute/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:mutes*
+     */
+    post: operations['renote-mute/delete'];
+  };
+  '/renote-mute/list': {
+    /**
+     * renote-mute/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:mutes*
+     */
+    post: operations['renote-mute/list'];
+  };
+  '/my/apps': {
+    /**
+     * my/apps
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['my/apps'];
+  };
+  '/notes': {
+    /**
+     * notes
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes'];
+  };
+  '/notes/children': {
+    /**
+     * notes/children
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/children'];
+  };
+  '/notes/clips': {
+    /**
+     * notes/clips
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/clips'];
+  };
+  '/notes/conversation': {
+    /**
+     * notes/conversation
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/conversation'];
+  };
+  '/notes/create': {
+    /**
+     * notes/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:notes*
+     */
+    post: operations['notes/create'];
+  };
+  '/notes/delete': {
+    /**
+     * notes/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:notes*
+     */
+    post: operations['notes/delete'];
+  };
+  '/notes/favorites/create': {
+    /**
+     * notes/favorites/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:favorites*
+     */
+    post: operations['notes/favorites/create'];
+  };
+  '/notes/favorites/delete': {
+    /**
+     * notes/favorites/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:favorites*
+     */
+    post: operations['notes/favorites/delete'];
+  };
+  '/notes/featured': {
+    /**
+     * notes/featured
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['notes/featured'];
+    /**
+     * notes/featured
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/featured'];
+  };
+  '/notes/global-timeline': {
+    /**
+     * notes/global-timeline
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/global-timeline'];
+  };
+  '/notes/hybrid-timeline': {
+    /**
+     * notes/hybrid-timeline
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['notes/hybrid-timeline'];
+  };
+  '/notes/local-timeline': {
+    /**
+     * notes/local-timeline
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/local-timeline'];
+  };
+  '/notes/mentions': {
+    /**
+     * notes/mentions
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['notes/mentions'];
+  };
+  '/notes/polls/recommendation': {
+    /**
+     * notes/polls/recommendation
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['notes/polls/recommendation'];
+  };
+  '/notes/polls/vote': {
+    /**
+     * notes/polls/vote
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:votes*
+     */
+    post: operations['notes/polls/vote'];
+  };
+  '/notes/reactions': {
+    /**
+     * notes/reactions
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['notes/reactions'];
+    /**
+     * notes/reactions
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/reactions'];
+  };
+  '/notes/reactions/create': {
+    /**
+     * notes/reactions/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:reactions*
+     */
+    post: operations['notes/reactions/create'];
+  };
+  '/notes/reactions/delete': {
+    /**
+     * notes/reactions/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:reactions*
+     */
+    post: operations['notes/reactions/delete'];
+  };
+  '/notes/renotes': {
+    /**
+     * notes/renotes
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/renotes'];
+  };
+  '/notes/replies': {
+    /**
+     * notes/replies
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/replies'];
+  };
+  '/notes/search-by-tag': {
+    /**
+     * notes/search-by-tag
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/search-by-tag'];
+  };
+  '/notes/search': {
+    /**
+     * notes/search
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/search'];
+  };
+  '/notes/show': {
+    /**
+     * notes/show
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['notes/show'];
+  };
+  '/notes/state': {
+    /**
+     * notes/state
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['notes/state'];
+  };
+  '/notes/thread-muting/create': {
+    /**
+     * notes/thread-muting/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['notes/thread-muting/create'];
+  };
+  '/notes/thread-muting/delete': {
+    /**
+     * notes/thread-muting/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['notes/thread-muting/delete'];
+  };
+  '/notes/timeline': {
+    /**
+     * notes/timeline
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['notes/timeline'];
+  };
+  '/notes/translate': {
+    /**
+     * notes/translate
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['notes/translate'];
+  };
+  '/notes/unrenote': {
+    /**
+     * notes/unrenote
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:notes*
+     */
+    post: operations['notes/unrenote'];
+  };
+  '/notes/user-list-timeline': {
+    /**
+     * notes/user-list-timeline
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['notes/user-list-timeline'];
+  };
+  '/notifications/create': {
+    /**
+     * notifications/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:notifications*
+     */
+    post: operations['notifications/create'];
+  };
+  '/notifications/mark-all-as-read': {
+    /**
+     * notifications/mark-all-as-read
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:notifications*
+     */
+    post: operations['notifications/mark-all-as-read'];
+  };
+  '/notifications/test-notification': {
+    /**
+     * notifications/test-notification
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:notifications*
+     */
+    post: operations['notifications/test-notification'];
+  };
+  '/pages/create': {
+    /**
+     * pages/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:pages*
+     */
+    post: operations['pages/create'];
+  };
+  '/pages/delete': {
+    /**
+     * pages/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:pages*
+     */
+    post: operations['pages/delete'];
+  };
+  '/pages/featured': {
+    /**
+     * pages/featured
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['pages/featured'];
+  };
+  '/pages/like': {
+    /**
+     * pages/like
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:page-likes*
+     */
+    post: operations['pages/like'];
+  };
+  '/pages/show': {
+    /**
+     * pages/show
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['pages/show'];
+  };
+  '/pages/unlike': {
+    /**
+     * pages/unlike
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:page-likes*
+     */
+    post: operations['pages/unlike'];
+  };
+  '/pages/update': {
+    /**
+     * pages/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:pages*
+     */
+    post: operations['pages/update'];
+  };
+  '/flash/create': {
+    /**
+     * flash/create
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:flash*
+     */
+    post: operations['flash/create'];
+  };
+  '/flash/delete': {
+    /**
+     * flash/delete
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:flash*
+     */
+    post: operations['flash/delete'];
+  };
+  '/flash/featured': {
+    /**
+     * flash/featured
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['flash/featured'];
+  };
+  '/flash/like': {
+    /**
+     * flash/like
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
+     */
+    post: operations['flash/like'];
+  };
+  '/flash/show': {
+    /**
+     * flash/show
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['flash/show'];
+  };
+  '/flash/unlike': {
+    /**
+     * flash/unlike
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
+     */
+    post: operations['flash/unlike'];
+  };
+  '/flash/update': {
+    /**
+     * flash/update
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:flash*
+     */
+    post: operations['flash/update'];
+  };
+  '/flash/my': {
+    /**
+     * flash/my
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:flash*
+     */
+    post: operations['flash/my'];
+  };
+  '/flash/my-likes': {
+    /**
+     * flash/my-likes
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:flash-likes*
+     */
+    post: operations['flash/my-likes'];
+  };
+  '/ping': {
+    /**
+     * ping
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['ping'];
+  };
+  '/pinned-users': {
+    /**
+     * pinned-users
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['pinned-users'];
+  };
+  '/promo/read': {
+    /**
+     * promo/read
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['promo/read'];
+  };
+  '/roles/list': {
+    /**
+     * roles/list
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['roles/list'];
+  };
+  '/roles/show': {
+    /**
+     * roles/show
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['roles/show'];
+  };
+  '/roles/users': {
+    /**
+     * roles/users
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['roles/users'];
+  };
+  '/roles/notes': {
+    /**
+     * roles/notes
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['roles/notes'];
+  };
+  '/request-reset-password': {
+    /**
+     * request-reset-password
+     * @description Request a users password to be reset.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['request-reset-password'];
+  };
+  '/reset-db': {
+    /**
+     * reset-db
+     * @description Only available when running with <code>NODE_ENV=testing</code>. Reset the database and flush Redis.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['reset-db'];
+  };
+  '/reset-password': {
+    /**
+     * reset-password
+     * @description Complete the password reset that was previously requested.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['reset-password'];
+  };
+  '/server-info': {
+    /**
+     * server-info
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['server-info'];
+    /**
+     * server-info
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['server-info'];
+  };
+  '/stats': {
+    /**
+     * stats
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['stats'];
+  };
+  '/sw/show-registration': {
+    /**
+     * sw/show-registration
+     * @description Check push notification registration exists.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['sw/show-registration'];
+  };
+  '/sw/update-registration': {
+    /**
+     * sw/update-registration
+     * @description Update push notification registration.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['sw/update-registration'];
+  };
+  '/sw/register': {
+    /**
+     * sw/register
+     * @description Register to receive push notifications.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['sw/register'];
+  };
+  '/sw/unregister': {
+    /**
+     * sw/unregister
+     * @description Unregister from receiving push notifications.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['sw/unregister'];
+  };
+  '/test': {
+    /**
+     * test
+     * @description Endpoint for testing input validation.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['test'];
+  };
+  '/username/available': {
+    /**
+     * username/available
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['username/available'];
+  };
+  '/users': {
+    /**
+     * users
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users'];
+  };
+  '/users/clips': {
+    /**
+     * users/clips
+     * @description Show all clips this user owns.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/clips'];
+  };
+  '/users/followers': {
+    /**
+     * users/followers
+     * @description Show everyone that follows this user.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/followers'];
+  };
+  '/users/following': {
+    /**
+     * users/following
+     * @description Show everyone that this user is following.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/following'];
+  };
+  '/users/gallery/posts': {
+    /**
+     * users/gallery/posts
+     * @description Show all gallery posts by the given user.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/gallery/posts'];
+  };
+  '/users/get-frequently-replied-users': {
+    /**
+     * users/get-frequently-replied-users
+     * @description Get a list of other users that the specified user frequently replies to.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/get-frequently-replied-users'];
+  };
+  '/users/featured-notes': {
+    /**
+     * users/featured-notes
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['users/featured-notes'];
+    /**
+     * users/featured-notes
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/featured-notes'];
+  };
+  '/users/lists/create': {
+    /**
+     * users/lists/create
+     * @description Create a new list of users.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['users/lists/create'];
+  };
+  '/users/lists/delete': {
+    /**
+     * users/lists/delete
+     * @description Delete an existing list of users.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['users/lists/delete'];
+  };
+  '/users/lists/list': {
+    /**
+     * users/lists/list
+     * @description Show all lists that the authenticated user has created.
+     *
+     * **Credential required**: *No* / **Permission**: *read:account*
+     */
+    post: operations['users/lists/list'];
+  };
+  '/users/lists/pull': {
+    /**
+     * users/lists/pull
+     * @description Remove a user from a list.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['users/lists/pull'];
+  };
+  '/users/lists/push': {
+    /**
+     * users/lists/push
+     * @description Add a user to an existing list.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['users/lists/push'];
+  };
+  '/users/lists/show': {
+    /**
+     * users/lists/show
+     * @description Show the properties of a list.
+     *
+     * **Credential required**: *No* / **Permission**: *read:account*
+     */
+    post: operations['users/lists/show'];
+  };
+  '/users/lists/favorite': {
+    /**
+     * users/lists/favorite
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['users/lists/favorite'];
+  };
+  '/users/lists/unfavorite': {
+    /**
+     * users/lists/unfavorite
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['users/lists/unfavorite'];
+  };
+  '/users/lists/update': {
+    /**
+     * users/lists/update
+     * @description Update the properties of a list.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['users/lists/update'];
+  };
+  '/users/lists/create-from-public': {
+    /**
+     * users/lists/create-from-public
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['users/lists/create-from-public'];
+  };
+  '/users/lists/update-membership': {
+    /**
+     * users/lists/update-membership
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['users/lists/update-membership'];
+  };
+  '/users/lists/get-memberships': {
+    /**
+     * users/lists/get-memberships
+     * @description No description provided.
+     *
+     * **Credential required**: *No* / **Permission**: *read:account*
+     */
+    post: operations['users/lists/get-memberships'];
+  };
+  '/users/notes': {
+    /**
+     * users/notes
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/notes'];
+  };
+  '/users/pages': {
+    /**
+     * users/pages
+     * @description Show all pages this user created.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/pages'];
+  };
+  '/users/flashs': {
+    /**
+     * users/flashs
+     * @description Show all flashs this user created.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/flashs'];
+  };
+  '/users/reactions': {
+    /**
+     * users/reactions
+     * @description Show all reactions this user made.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/reactions'];
+  };
+  '/users/recommendation': {
+    /**
+     * users/recommendation
+     * @description Show users that the authenticated user might be interested to follow.
+     *
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    post: operations['users/recommendation'];
+  };
+  '/users/relation': {
+    /**
+     * users/relation
+     * @description Show the different kinds of relations between the authenticated user and the specified user(s).
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['users/relation'];
+  };
+  '/users/report-abuse': {
+    /**
+     * users/report-abuse
+     * @description File a report.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['users/report-abuse'];
+  };
+  '/users/search-by-username-and-host': {
+    /**
+     * users/search-by-username-and-host
+     * @description Search for a user by username and/or host.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/search-by-username-and-host'];
+  };
+  '/users/search': {
+    /**
+     * users/search
+     * @description Search for users.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/search'];
+  };
+  '/users/show': {
+    /**
+     * users/show
+     * @description Show the properties of a user.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['users/show'];
+  };
+  '/users/achievements': {
+    /**
+     * users/achievements
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['users/achievements'];
+  };
+  '/users/update-memo': {
+    /**
+     * users/update-memo
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    post: operations['users/update-memo'];
+  };
+  '/fetch-rss': {
+    /**
+     * fetch-rss
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['fetch-rss'];
+    /**
+     * fetch-rss
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['fetch-rss'];
+  };
+  '/fetch-external-resources': {
+    /**
+     * fetch-external-resources
+     * @description No description provided.
+     *
+     * **Credential required**: *Yes*
+     */
+    post: operations['fetch-external-resources'];
+  };
+  '/retention': {
+    /**
+     * retention
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    get: operations['retention'];
+    /**
+     * retention
+     * @description No description provided.
+     *
+     * **Credential required**: *No*
+     */
+    post: operations['retention'];
+  };
+};
+
+export type webhooks = Record<string, never>;
+
+export type components = {
+  schemas: {
+    Error: {
+      /** @description An error object. */
+      error: {
+        /** @description An error code. Unique within the endpoint. */
+        code: string;
+        /** @description An error message. */
+        message: string;
+        /**
+         * Format: uuid
+         * @description An error ID. This ID is static.
+         */
+        id: string;
+      };
+    };
+    UserLite: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** @example 藍 */
+      name: string | null;
+      /** @example ai */
+      username: string;
+      /**
+       * @description The local host is represented with `null`.
+       * @example misskey.example.com
+       */
+      host: string | null;
+      /** Format: url */
+      avatarUrl: string | null;
+      avatarBlurhash: string | null;
+      avatarDecorations: {
+          /** Format: id */
+          id: string;
+          angle?: number;
+          flipH?: boolean;
+          /** Format: url */
+          url: string;
+        }[];
+      isBot?: boolean;
+      isCat?: boolean;
+      instance?: {
+        name: string | null;
+        softwareName: string | null;
+        softwareVersion: string | null;
+        iconUrl: string | null;
+        faviconUrl: string | null;
+        themeColor: string | null;
+      };
+      emojis: Record<string, never>;
+      /** @enum {string} */
+      onlineStatus: 'unknown' | 'online' | 'active' | 'offline';
+      badgeRoles?: ({
+          name: string;
+          iconUrl: string | null;
+          displayOrder: number;
+        })[];
+    };
+    UserDetailedNotMeOnly: {
+      /** Format: url */
+      url: string | null;
+      /** Format: uri */
+      uri: string | null;
+      /** Format: uri */
+      movedTo: string | null;
+      alsoKnownAs: string[] | null;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: date-time */
+      updatedAt: string | null;
+      /** Format: date-time */
+      lastFetchedAt: string | null;
+      /** Format: url */
+      bannerUrl: string | null;
+      bannerBlurhash: string | null;
+      isLocked: boolean;
+      isSilenced: boolean;
+      /** @example false */
+      isSuspended: boolean;
+      /** @example Hi masters, I am Ai! */
+      description: string | null;
+      location: string | null;
+      /** @example 2018-03-12 */
+      birthday: string | null;
+      /** @example ja-JP */
+      lang: string | null;
+      fields: {
+          name: string;
+          value: string;
+        }[];
+      verifiedLinks: string[];
+      followersCount: number;
+      followingCount: number;
+      notesCount: number;
+      pinnedNoteIds: string[];
+      pinnedNotes: components['schemas']['Note'][];
+      pinnedPageId: string | null;
+      pinnedPage: components['schemas']['Page'] | null;
+      publicReactions: boolean;
+      /** @enum {string} */
+      ffVisibility: 'public' | 'followers' | 'private';
+      /** @default false */
+      twoFactorEnabled: boolean;
+      /** @default false */
+      usePasswordLessLogin: boolean;
+      /** @default false */
+      securityKeys: boolean;
+      roles: ({
+          /** Format: id */
+          id: string;
+          name: string;
+          color: string | null;
+          iconUrl: string | null;
+          description: string;
+          isModerator: boolean;
+          isAdministrator: boolean;
+          displayOrder: number;
+        })[];
+      memo: string | null;
+      moderationNote?: string;
+      isFollowing?: boolean;
+      isFollowed?: boolean;
+      hasPendingFollowRequestFromYou?: boolean;
+      hasPendingFollowRequestToYou?: boolean;
+      isBlocking?: boolean;
+      isBlocked?: boolean;
+      isMuted?: boolean;
+      isRenoteMuted?: boolean;
+      notify?: string;
+      withReplies?: boolean;
+    };
+    MeDetailedOnly: {
+      /** Format: id */
+      avatarId: string | null;
+      /** Format: id */
+      bannerId: string | null;
+      isModerator: boolean | null;
+      isAdmin: boolean | null;
+      injectFeaturedNote: boolean;
+      receiveAnnouncementEmail: boolean;
+      alwaysMarkNsfw: boolean;
+      autoSensitive: boolean;
+      carefulBot: boolean;
+      autoAcceptFollowed: boolean;
+      noCrawle: boolean;
+      preventAiLearning: boolean;
+      isExplorable: boolean;
+      isDeleted: boolean;
+      /** @enum {string} */
+      twoFactorBackupCodesStock: 'full' | 'partial' | 'none';
+      hideOnlineStatus: boolean;
+      hasUnreadSpecifiedNotes: boolean;
+      hasUnreadMentions: boolean;
+      hasUnreadAnnouncement: boolean;
+      unreadAnnouncements: components['schemas']['Announcement'][];
+      hasUnreadAntenna: boolean;
+      hasUnreadChannel: boolean;
+      hasUnreadNotification: boolean;
+      hasPendingReceivedFollowRequest: boolean;
+      unreadNotificationsCount: number;
+      mutedWords: string[][];
+      hardMutedWords: string[][];
+      mutedInstances: string[] | null;
+      notificationRecieveConfig: Record<string, never>;
+      emailNotificationTypes: string[];
+      achievements: {
+          name: string;
+          unlockedAt: number;
+        }[];
+      loggedInDays: number;
+      policies: {
+        gtlAvailable: boolean;
+        ltlAvailable: boolean;
+        canPublicNote: boolean;
+        canInvite: boolean;
+        inviteLimit: number;
+        inviteLimitCycle: number;
+        inviteExpirationTime: number;
+        canManageCustomEmojis: boolean;
+        canManageAvatarDecorations: boolean;
+        canSearchNotes: boolean;
+        canUseTranslator: boolean;
+        canHideAds: boolean;
+        driveCapacityMb: number;
+        alwaysMarkNsfw: boolean;
+        pinLimit: number;
+        antennaLimit: number;
+        wordMuteLimit: number;
+        webhookLimit: number;
+        clipLimit: number;
+        noteEachClipsLimit: number;
+        userListLimit: number;
+        userEachUserListsLimit: number;
+        rateLimitFactor: number;
+      };
+      email?: string | null;
+      emailVerified?: boolean | null;
+      securityKeysList?: Record<string, never>[];
+    };
+    UserDetailedNotMe: components['schemas']['UserLite'] & components['schemas']['UserDetailedNotMeOnly'];
+    MeDetailed: components['schemas']['UserLite'] & components['schemas']['UserDetailedNotMeOnly'] & components['schemas']['MeDetailedOnly'];
+    UserDetailed: components['schemas']['UserDetailedNotMe'] | components['schemas']['MeDetailed'];
+    User: components['schemas']['UserLite'] | components['schemas']['UserDetailed'] | components['schemas']['UserDetailedNotMe'] | components['schemas']['MeDetailed'];
+    UserList: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      name: string;
+      userIds?: string[];
+      isPublic: boolean;
+    };
+    Announcement: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: date-time */
+      updatedAt: string | null;
+      text: string;
+      title: string;
+      imageUrl: string | null;
+      icon: string;
+      display: string;
+      needConfirmationToRead: boolean;
+      silence: boolean;
+      forYou: boolean;
+      isRead?: boolean;
+    };
+    App: {
+      id: string;
+      name: string;
+      callbackUrl: string | null;
+      permission: string[];
+      secret?: string;
+      isAuthorized?: boolean;
+    };
+    Note: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: date-time */
+      deletedAt?: string | null;
+      text: string | null;
+      cw?: string | null;
+      /** Format: id */
+      userId: string;
+      user: components['schemas']['UserLite'];
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      replyId?: string | null;
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      renoteId?: string | null;
+      reply?: components['schemas']['Note'] | null;
+      renote?: components['schemas']['Note'] | null;
+      isHidden?: boolean;
+      visibility: string;
+      mentions?: string[];
+      visibleUserIds?: string[];
+      fileIds?: string[];
+      files?: components['schemas']['DriveFile'][];
+      tags?: string[];
+      poll?: Record<string, unknown> | null;
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      channelId?: string | null;
+      channel?: {
+        id: string;
+        name: string;
+        color: string;
+        isSensitive: boolean;
+        allowRenoteToExternal: boolean;
+      } | null;
+      localOnly?: boolean;
+      reactionAcceptance: string | null;
+      reactions: Record<string, never>;
+      renoteCount: number;
+      repliesCount: number;
+      uri?: string;
+      url?: string;
+      reactionAndUserPairCache?: string[];
+      myReaction?: Record<string, unknown> | null;
+    };
+    NoteReaction: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      user: components['schemas']['UserLite'];
+      type: string;
+    };
+    NoteFavorite: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      note: components['schemas']['Note'];
+      /** Format: id */
+      noteId: string;
+    };
+    Notification: {
+      /** Format: id */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** @enum {string} */
+      type: 'note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'achievementEarned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped';
+      user?: components['schemas']['UserLite'] | null;
+      /** Format: id */
+      userId?: string | null;
+      note?: components['schemas']['Note'] | null;
+      reaction?: string | null;
+      achievement?: string;
+      body?: string | null;
+      header?: string | null;
+      icon?: string | null;
+      reactions?: {
+          user: components['schemas']['UserLite'];
+          reaction: string;
+        }[] | null;
+      users?: components['schemas']['UserLite'][] | null;
+    };
+    DriveFile: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** @example lenna.jpg */
+      name: string;
+      /** @example image/jpeg */
+      type: string;
+      /**
+       * Format: md5
+       * @example 15eca7fba0480996e2245f5185bf39f2
+       */
+      md5: string;
+      /** @example 51469 */
+      size: number;
+      isSensitive: boolean;
+      blurhash: string | null;
+      properties: {
+        /** @example 1280 */
+        width?: number;
+        /** @example 720 */
+        height?: number;
+        /** @example 8 */
+        orientation?: number;
+        /** @example rgb(40,65,87) */
+        avgColor?: string;
+      };
+      /** Format: url */
+      url: string;
+      /** Format: url */
+      thumbnailUrl: string | null;
+      comment: string | null;
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      folderId: string | null;
+      folder?: components['schemas']['DriveFolder'] | null;
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      userId: string | null;
+      user?: components['schemas']['UserLite'] | null;
+    };
+    DriveFolder: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      name: string;
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      parentId: string | null;
+      foldersCount?: number;
+      filesCount?: number;
+      parent?: components['schemas']['DriveFolder'] | null;
+    };
+    Following: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: id */
+      followeeId: string;
+      /** Format: id */
+      followerId: string;
+      followee?: components['schemas']['UserDetailed'];
+      follower?: components['schemas']['UserDetailed'];
+    };
+    Muting: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: date-time */
+      expiresAt: string | null;
+      /** Format: id */
+      muteeId: string;
+      mutee: components['schemas']['UserDetailed'];
+    };
+    RenoteMuting: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: id */
+      muteeId: string;
+      mutee: components['schemas']['UserDetailed'];
+    };
+    Blocking: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: id */
+      blockeeId: string;
+      blockee: components['schemas']['UserDetailed'];
+    };
+    Hashtag: {
+      /** @example misskey */
+      tag: string;
+      mentionedUsersCount: number;
+      mentionedLocalUsersCount: number;
+      mentionedRemoteUsersCount: number;
+      attachedUsersCount: number;
+      attachedLocalUsersCount: number;
+      attachedRemoteUsersCount: number;
+    };
+    InviteCode: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** @example GR6S02ERUA5VR */
+      code: string;
+      /** Format: date-time */
+      expiresAt: string | null;
+      /** Format: date-time */
+      createdAt: string;
+      createdBy: components['schemas']['UserLite'] | null;
+      usedBy: components['schemas']['UserLite'] | null;
+      /** Format: date-time */
+      usedAt: string | null;
+      used: boolean;
+    };
+    Page: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: date-time */
+      updatedAt: string;
+      /** Format: id */
+      userId: string;
+      user: components['schemas']['UserLite'];
+      content: Record<string, never>[];
+      variables: Record<string, never>[];
+      title: string;
+      name: string;
+      summary: string | null;
+      hideTitleWhenPinned: boolean;
+      alignCenter: boolean;
+      font: string;
+      script: string;
+      eyeCatchingImageId: string | null;
+      eyeCatchingImage: components['schemas']['DriveFile'] | null;
+      attachedFiles: components['schemas']['DriveFile'][];
+      likedCount: number;
+      isLiked?: boolean;
+    };
+    Channel: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: date-time */
+      lastNotedAt: string | null;
+      name: string;
+      description: string | null;
+      /** Format: id */
+      userId: string | null;
+      /** Format: url */
+      bannerUrl: string | null;
+      pinnedNoteIds: string[];
+      color: string;
+      isArchived: boolean;
+      usersCount: number;
+      notesCount: number;
+      isSensitive: boolean;
+      allowRenoteToExternal: boolean;
+      isFollowing?: boolean;
+      isFavorited?: boolean;
+      pinnedNotes?: components['schemas']['Note'][];
+    };
+    QueueCount: {
+      waiting: number;
+      active: number;
+      completed: number;
+      failed: number;
+      delayed: number;
+    };
+    Antenna: {
+      /** Format: id */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      name: string;
+      keywords: string[][];
+      excludeKeywords: string[][];
+      /** @enum {string} */
+      src: 'home' | 'all' | 'users' | 'list' | 'users_blacklist';
+      /** Format: id */
+      userListId: string | null;
+      users: string[];
+      /** @default false */
+      caseSensitive: boolean;
+      /** @default false */
+      localOnly: boolean;
+      notify: boolean;
+      /** @default false */
+      withReplies: boolean;
+      withFile: boolean;
+      isActive: boolean;
+      /** @default false */
+      hasUnreadNote: boolean;
+    };
+    Clip: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: date-time */
+      lastClippedAt: string | null;
+      /** Format: id */
+      userId: string;
+      user: components['schemas']['UserLite'];
+      name: string;
+      description: string | null;
+      isPublic: boolean;
+      favoritedCount: number;
+      isFavorited?: boolean;
+    };
+    FederationInstance: {
+      /** Format: id */
+      id: string;
+      /** Format: date-time */
+      firstRetrievedAt: string;
+      /** @example misskey.example.com */
+      host: string;
+      usersCount: number;
+      notesCount: number;
+      followingCount: number;
+      followersCount: number;
+      isNotResponding: boolean;
+      isSuspended: boolean;
+      isBlocked: boolean;
+      /** @example misskey */
+      softwareName: string | null;
+      softwareVersion: string | null;
+      /** @example true */
+      openRegistrations: boolean | null;
+      name: string | null;
+      description: string | null;
+      maintainerName: string | null;
+      maintainerEmail: string | null;
+      isSilenced: boolean;
+      /** Format: url */
+      iconUrl: string | null;
+      /** Format: url */
+      faviconUrl: string | null;
+      themeColor: string | null;
+      /** Format: date-time */
+      infoUpdatedAt: string | null;
+      /** Format: date-time */
+      latestRequestReceivedAt: string | null;
+    };
+    GalleryPost: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: date-time */
+      updatedAt: string;
+      /** Format: id */
+      userId: string;
+      user: components['schemas']['UserLite'];
+      title: string;
+      description: string | null;
+      fileIds?: string[];
+      files?: components['schemas']['DriveFile'][];
+      tags?: string[];
+      isSensitive: boolean;
+      likedCount: number;
+      isLiked?: boolean;
+    };
+    EmojiSimple: {
+      aliases: string[];
+      name: string;
+      category: string | null;
+      url: string;
+      isSensitive?: boolean;
+      roleIdsThatCanBeUsedThisEmojiAsReaction?: string[];
+    };
+    EmojiDetailed: {
+      /** Format: id */
+      id: string;
+      aliases: string[];
+      name: string;
+      category: string | null;
+      /** @description The local host is represented with `null`. */
+      host: string | null;
+      url: string;
+      license: string | null;
+      isSensitive: boolean;
+      localOnly: boolean;
+      roleIdsThatCanBeUsedThisEmojiAsReaction: string[];
+    };
+    Flash: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: date-time */
+      updatedAt: string;
+      /** Format: id */
+      userId: string;
+      user: components['schemas']['UserLite'];
+      title: string;
+      summary: string;
+      script: string;
+      likedCount: number | null;
+      isLiked?: boolean;
+    };
+  };
+  responses: never;
+  parameters: never;
+  requestBodies: never;
+  headers: never;
+  pathItems: never;
+};
+
+export type $defs = Record<string, never>;
+
+export type external = Record<string, never>;
+
+export type operations = {
+
+  /**
+   * admin/meta
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/meta': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            cacheRemoteFiles: boolean;
+            cacheRemoteSensitiveFiles: boolean;
+            emailRequiredForSignup: boolean;
+            enableHcaptcha: boolean;
+            hcaptchaSiteKey: string | null;
+            enableRecaptcha: boolean;
+            recaptchaSiteKey: string | null;
+            enableTurnstile: boolean;
+            turnstileSiteKey: string | null;
+            swPublickey: string | null;
+            /** @default /assets/ai.png */
+            mascotImageUrl: string | null;
+            bannerUrl: string | null;
+            serverErrorImageUrl: string | null;
+            infoImageUrl: string | null;
+            notFoundImageUrl: string | null;
+            iconUrl: string | null;
+            app192IconUrl: string | null;
+            app512IconUrl: string | null;
+            enableEmail: boolean;
+            enableServiceWorker: boolean;
+            translatorAvailable: boolean;
+            silencedHosts?: string[];
+            pinnedUsers: string[];
+            hiddenTags: string[];
+            blockedHosts: string[];
+            sensitiveWords: string[];
+            preservedUsernames: string[];
+            hcaptchaSecretKey: string | null;
+            recaptchaSecretKey: string | null;
+            turnstileSecretKey: string | null;
+            sensitiveMediaDetection: string;
+            sensitiveMediaDetectionSensitivity: string;
+            setSensitiveFlagAutomatically: boolean;
+            enableSensitiveMediaDetectionForVideos: boolean;
+            /** Format: id */
+            proxyAccountId: string | null;
+            email: string | null;
+            smtpSecure: boolean;
+            smtpHost: string | null;
+            smtpPort: number | null;
+            smtpUser: string | null;
+            smtpPass: string | null;
+            swPrivateKey: string | null;
+            useObjectStorage: boolean;
+            objectStorageBaseUrl: string | null;
+            objectStorageBucket: string | null;
+            objectStoragePrefix: string | null;
+            objectStorageEndpoint: string | null;
+            objectStorageRegion: string | null;
+            objectStoragePort: number | null;
+            objectStorageAccessKey: string | null;
+            objectStorageSecretKey: string | null;
+            objectStorageUseSSL: boolean;
+            objectStorageUseProxy: boolean;
+            objectStorageSetPublicRead: boolean;
+            enableIpLogging: boolean;
+            enableActiveEmailValidation: boolean;
+            enableVerifymailApi: boolean;
+            verifymailAuthKey: string | null;
+            enableChartsForRemoteUser: boolean;
+            enableChartsForFederatedInstances: boolean;
+            enableServerMachineStats: boolean;
+            enableIdenticonGeneration: boolean;
+            manifestJsonOverride: string;
+            policies: Record<string, never>;
+            enableFanoutTimeline: boolean;
+            enableFanoutTimelineDbFallback: boolean;
+            perLocalUserUserTimelineCacheMax: number;
+            perRemoteUserUserTimelineCacheMax: number;
+            perUserHomeTimelineCacheMax: number;
+            perUserListTimelineCacheMax: number;
+            notesPerOneAd: number;
+            backgroundImageUrl: string | null;
+            deeplAuthKey: string | null;
+            deeplIsPro: boolean;
+            defaultDarkTheme: string | null;
+            defaultLightTheme: string | null;
+            description: string | null;
+            disableRegistration: boolean;
+            impressumUrl: string | null;
+            maintainerEmail: string | null;
+            maintainerName: string | null;
+            name: string | null;
+            objectStorageS3ForcePathStyle: boolean;
+            privacyPolicyUrl: string | null;
+            repositoryUrl: string;
+            summalyProxy: string | null;
+            themeColor: string | null;
+            tosUrl: string | null;
+            uri: string;
+            version: string;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/abuse-user-reports
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/abuse-user-reports': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default null */
+          state?: string | null;
+          /**
+           * @default combined
+           * @enum {string}
+           */
+          reporterOrigin?: 'combined' | 'local' | 'remote';
+          /**
+           * @default combined
+           * @enum {string}
+           */
+          targetUserOrigin?: 'combined' | 'local' | 'remote';
+          /** @default false */
+          forwarded?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ({
+              /**
+               * Format: id
+               * @example xxxxxxxxxx
+               */
+              id: string;
+              /** Format: date-time */
+              createdAt: string;
+              comment: string;
+              /** @example false */
+              resolved: boolean;
+              /** Format: id */
+              reporterId: string;
+              /** Format: id */
+              targetUserId: string;
+              /** Format: id */
+              assigneeId: string | null;
+              reporter: components['schemas']['User'];
+              targetUser: components['schemas']['User'];
+              assignee?: components['schemas']['User'] | null;
+            })[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/accounts/create
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'admin/accounts/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          username: string;
+          password: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['User'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/accounts/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/accounts/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/accounts/find-by-email
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/accounts/find-by-email': {
+    requestBody: {
+      content: {
+        'application/json': {
+          email: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/ad/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/ad/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          url: string;
+          memo: string;
+          place: string;
+          priority: string;
+          ratio: number;
+          expiresAt: number;
+          startsAt: number;
+          imageUrl: string;
+          dayOfWeek: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/ad/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/ad/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/ad/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/ad/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default null */
+          publishing?: boolean | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/ad/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/ad/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+          memo: string;
+          url: string;
+          imageUrl: string;
+          place: string;
+          priority: string;
+          ratio: number;
+          expiresAt: number;
+          startsAt: number;
+          dayOfWeek: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/announcements/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/announcements/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          title: string;
+          text: string;
+          imageUrl: string | null;
+          /**
+           * @default info
+           * @enum {string}
+           */
+          icon?: 'info' | 'warning' | 'error' | 'success';
+          /**
+           * @default normal
+           * @enum {string}
+           */
+          display?: 'normal' | 'banner' | 'dialog';
+          /** @default false */
+          forExistingUsers?: boolean;
+          /** @default false */
+          silence?: boolean;
+          /** @default false */
+          needConfirmationToRead?: boolean;
+          /**
+           * Format: misskey:id
+           * @default null
+           */
+          userId?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            /**
+             * Format: id
+             * @example xxxxxxxxxx
+             */
+            id: string;
+            /** Format: date-time */
+            createdAt: string;
+            /** Format: date-time */
+            updatedAt: string | null;
+            title: string;
+            text: string;
+            imageUrl: string | null;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/announcements/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/announcements/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/announcements/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/announcements/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** Format: misskey:id */
+          userId?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ({
+              /**
+               * Format: id
+               * @example xxxxxxxxxx
+               */
+              id: string;
+              /** Format: date-time */
+              createdAt: string;
+              /** Format: date-time */
+              updatedAt: string | null;
+              text: string;
+              title: string;
+              imageUrl: string | null;
+              reads: number;
+            })[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/announcements/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/announcements/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+          title?: string;
+          text?: string;
+          imageUrl?: string | null;
+          /** @enum {string} */
+          icon?: 'info' | 'warning' | 'error' | 'success';
+          /** @enum {string} */
+          display?: 'normal' | 'banner' | 'dialog';
+          forExistingUsers?: boolean;
+          silence?: boolean;
+          needConfirmationToRead?: boolean;
+          isActive?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/avatar-decorations/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/avatar-decorations/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          description: string;
+          url: string;
+          roleIdsThatCanBeUsedThisDecoration?: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/avatar-decorations/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/avatar-decorations/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/avatar-decorations/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/avatar-decorations/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** Format: misskey:id */
+          userId?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ({
+              /**
+               * Format: id
+               * @example xxxxxxxxxx
+               */
+              id: string;
+              /** Format: date-time */
+              createdAt: string;
+              /** Format: date-time */
+              updatedAt: string | null;
+              name: string;
+              description: string;
+              url: string;
+              roleIdsThatCanBeUsedThisDecoration: string[];
+            })[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/avatar-decorations/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/avatar-decorations/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+          name?: string;
+          description?: string;
+          url?: string;
+          roleIdsThatCanBeUsedThisDecoration?: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/delete-all-files-of-a-user
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/delete-all-files-of-a-user': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/unset-user-avatar
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/unset-user-avatar': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/unset-user-banner
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/unset-user-banner': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/drive/clean-remote-files
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/drive/clean-remote-files': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/drive/cleanup
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/drive/cleanup': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/drive/files
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/drive/files': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** Format: misskey:id */
+          userId?: string | null;
+          type?: string | null;
+          /**
+           * @default local
+           * @enum {string}
+           */
+          origin?: 'combined' | 'local' | 'remote';
+          /**
+           * @description The local host is represented with `null`.
+           * @default null
+           */
+          hostname?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFile'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/drive/show-file
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/drive/show-file': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          fileId?: string;
+          url?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            /**
+             * Format: id
+             * @example xxxxxxxxxx
+             */
+            id: string;
+            /** Format: date-time */
+            createdAt: string;
+            /**
+             * Format: id
+             * @example xxxxxxxxxx
+             */
+            userId: string | null;
+            /** @description The local host is represented with `null`. */
+            userHost: string | null;
+            /**
+             * Format: md5
+             * @example 15eca7fba0480996e2245f5185bf39f2
+             */
+            md5: string;
+            /** @example lenna.jpg */
+            name: string;
+            /** @example image/jpeg */
+            type: string;
+            /** @example 51469 */
+            size: number;
+            comment: string | null;
+            blurhash: string | null;
+            properties: Record<string, never>;
+            /** @example true */
+            storedInternal: boolean | null;
+            /** Format: url */
+            url: string | null;
+            /** Format: url */
+            thumbnailUrl: string | null;
+            /** Format: url */
+            webpublicUrl: string | null;
+            accessKey: string | null;
+            thumbnailAccessKey: string | null;
+            webpublicAccessKey: string | null;
+            uri: string | null;
+            src: string | null;
+            /**
+             * Format: id
+             * @example xxxxxxxxxx
+             */
+            folderId: string | null;
+            isSensitive: boolean;
+            isLink: boolean;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/add-aliases-bulk
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/add-aliases-bulk': {
+    requestBody: {
+      content: {
+        'application/json': {
+          ids: string[];
+          aliases: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/add
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/add': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          /** Format: misskey:id */
+          fileId: string;
+          /** @description Use `null` to reset the category. */
+          category?: string | null;
+          aliases?: string[];
+          license?: string | null;
+          isSensitive?: boolean;
+          localOnly?: boolean;
+          roleIdsThatCanBeUsedThisEmojiAsReaction?: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/copy
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/copy': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          emojiId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            /** Format: id */
+            id: string;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/delete-bulk
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/delete-bulk': {
+    requestBody: {
+      content: {
+        'application/json': {
+          ids: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/list-remote
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/list-remote': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default null */
+          query?: string | null;
+          /**
+           * @description Use `null` to represent the local host.
+           * @default null
+           */
+          host?: string | null;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ({
+              /** Format: id */
+              id: string;
+              aliases: string[];
+              name: string;
+              category: string | null;
+              /** @description The local host is represented with `null`. */
+              host: string | null;
+              url: string;
+            })[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default null */
+          query?: string | null;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ({
+              /** Format: id */
+              id: string;
+              aliases: string[];
+              name: string;
+              category: string | null;
+              /** @description The local host is represented with `null`. The field exists for compatibility with other API endpoints that return files. */
+              host: string | null;
+              url: string;
+            })[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/remove-aliases-bulk
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/remove-aliases-bulk': {
+    requestBody: {
+      content: {
+        'application/json': {
+          ids: string[];
+          aliases: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/set-aliases-bulk
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/set-aliases-bulk': {
+    requestBody: {
+      content: {
+        'application/json': {
+          ids: string[];
+          aliases: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/set-category-bulk
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/set-category-bulk': {
+    requestBody: {
+      content: {
+        'application/json': {
+          ids: string[];
+          /** @description Use `null` to reset the category. */
+          category?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/set-license-bulk
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/set-license-bulk': {
+    requestBody: {
+      content: {
+        'application/json': {
+          ids: string[];
+          /** @description Use `null` to reset the license. */
+          license?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/emoji/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          id: string;
+          name: string;
+          /** Format: misskey:id */
+          fileId?: string;
+          /** @description Use `null` to reset the category. */
+          category?: string | null;
+          aliases: string[];
+          license?: string | null;
+          isSensitive?: boolean;
+          localOnly?: boolean;
+          roleIdsThatCanBeUsedThisEmojiAsReaction?: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/federation/delete-all-files
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/federation/delete-all-files': {
+    requestBody: {
+      content: {
+        'application/json': {
+          host: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/federation/refresh-remote-instance-metadata
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/federation/refresh-remote-instance-metadata': {
+    requestBody: {
+      content: {
+        'application/json': {
+          host: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/federation/remove-all-following
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/federation/remove-all-following': {
+    requestBody: {
+      content: {
+        'application/json': {
+          host: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/federation/update-instance
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/federation/update-instance': {
+    requestBody: {
+      content: {
+        'application/json': {
+          host: string;
+          isSuspended: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/get-index-stats
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/get-index-stats': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/get-table-stats
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/get-table-stats': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': Record<string, never>;
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/get-user-ips
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/get-user-ips': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/invite/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/invite/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 1 */
+          count?: number;
+          expiresAt?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              /** @example GR6S02ERUA5VR */
+              code: string;
+            }[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/invite/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/invite/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 30 */
+          limit?: number;
+          /** @default 0 */
+          offset?: number;
+          /**
+           * @default all
+           * @enum {string}
+           */
+          type?: 'unused' | 'used' | 'expired' | 'all';
+          /** @enum {string} */
+          sort?: '+createdAt' | '-createdAt' | '+usedAt' | '-usedAt';
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': Record<string, never>[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/promo/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/promo/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+          expiresAt: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/queue/clear
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/queue/clear': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/queue/deliver-delayed
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/queue/deliver-delayed': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ((string | number)[])[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/queue/inbox-delayed
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/queue/inbox-delayed': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ((string | number)[])[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/queue/promote
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/queue/promote': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          type: 'deliver' | 'inbox';
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/queue/stats
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/queue/stats': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            deliver: components['schemas']['QueueCount'];
+            inbox: components['schemas']['QueueCount'];
+            db: components['schemas']['QueueCount'];
+            objectStorage: components['schemas']['QueueCount'];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/relays/add
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/relays/add': {
+    requestBody: {
+      content: {
+        'application/json': {
+          inbox: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            /** Format: id */
+            id: string;
+            /** Format: url */
+            inbox: string;
+            /**
+             * @default requesting
+             * @enum {string}
+             */
+            status: 'requesting' | 'accepted' | 'rejected';
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/relays/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/relays/list': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ({
+              /** Format: id */
+              id: string;
+              /** Format: url */
+              inbox: string;
+              /**
+               * @default requesting
+               * @enum {string}
+               */
+              status: 'requesting' | 'accepted' | 'rejected';
+            })[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/relays/remove
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/relays/remove': {
+    requestBody: {
+      content: {
+        'application/json': {
+          inbox: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/reset-password
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/reset-password': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            password: string;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/resolve-abuse-user-report
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/resolve-abuse-user-report': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          reportId: string;
+          /** @default false */
+          forward?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/send-email
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/send-email': {
+    requestBody: {
+      content: {
+        'application/json': {
+          to: string;
+          subject: string;
+          text: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/server-info
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/server-info': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            machine: string;
+            /** @example linux */
+            os: string;
+            node: string;
+            psql: string;
+            cpu: {
+              model: string;
+              cores: number;
+            };
+            mem: {
+              /** Format: bytes */
+              total: number;
+            };
+            fs: {
+              /** Format: bytes */
+              total: number;
+              /** Format: bytes */
+              used: number;
+            };
+            net: {
+              /** @example eth0 */
+              interface: string;
+            };
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/show-moderation-logs
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/show-moderation-logs': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          type?: string | null;
+          /** Format: misskey:id */
+          userId?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              /** Format: id */
+              id: string;
+              /** Format: date-time */
+              createdAt: string;
+              type: string;
+              info: Record<string, never>;
+              /** Format: id */
+              userId: string;
+              user: components['schemas']['UserDetailed'];
+            }[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/show-user
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/show-user': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': Record<string, never>;
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/show-users
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/show-users': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** @default 0 */
+          offset?: number;
+          /** @enum {string} */
+          sort?: '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+updatedAt' | '-updatedAt' | '+lastActiveDate' | '-lastActiveDate';
+          /**
+           * @default all
+           * @enum {string}
+           */
+          state?: 'all' | 'alive' | 'available' | 'admin' | 'moderator' | 'adminOrModerator' | 'suspended';
+          /**
+           * @default combined
+           * @enum {string}
+           */
+          origin?: 'combined' | 'local' | 'remote';
+          /** @default null */
+          username?: string | null;
+          /**
+           * @description The local host is represented with `null`.
+           * @default null
+           */
+          hostname?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserDetailed'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/suspend-user
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/suspend-user': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/unsuspend-user
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/unsuspend-user': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/update-meta
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/update-meta': {
+    requestBody: {
+      content: {
+        'application/json': {
+          disableRegistration?: boolean | null;
+          pinnedUsers?: string[] | null;
+          hiddenTags?: string[] | null;
+          blockedHosts?: string[] | null;
+          sensitiveWords?: string[] | null;
+          themeColor?: string | null;
+          mascotImageUrl?: string | null;
+          bannerUrl?: string | null;
+          serverErrorImageUrl?: string | null;
+          infoImageUrl?: string | null;
+          notFoundImageUrl?: string | null;
+          iconUrl?: string | null;
+          app192IconUrl?: string | null;
+          app512IconUrl?: string | null;
+          backgroundImageUrl?: string | null;
+          logoImageUrl?: string | null;
+          name?: string | null;
+          shortName?: string | null;
+          description?: string | null;
+          defaultLightTheme?: string | null;
+          defaultDarkTheme?: string | null;
+          cacheRemoteFiles?: boolean;
+          cacheRemoteSensitiveFiles?: boolean;
+          emailRequiredForSignup?: boolean;
+          enableHcaptcha?: boolean;
+          hcaptchaSiteKey?: string | null;
+          hcaptchaSecretKey?: string | null;
+          enableRecaptcha?: boolean;
+          recaptchaSiteKey?: string | null;
+          recaptchaSecretKey?: string | null;
+          enableTurnstile?: boolean;
+          turnstileSiteKey?: string | null;
+          turnstileSecretKey?: string | null;
+          /** @enum {string} */
+          sensitiveMediaDetection?: 'none' | 'all' | 'local' | 'remote';
+          /** @enum {string} */
+          sensitiveMediaDetectionSensitivity?: 'medium' | 'low' | 'high' | 'veryLow' | 'veryHigh';
+          setSensitiveFlagAutomatically?: boolean;
+          enableSensitiveMediaDetectionForVideos?: boolean;
+          /** Format: misskey:id */
+          proxyAccountId?: string | null;
+          maintainerName?: string | null;
+          maintainerEmail?: string | null;
+          langs?: string[];
+          summalyProxy?: string | null;
+          deeplAuthKey?: string | null;
+          deeplIsPro?: boolean;
+          enableEmail?: boolean;
+          email?: string | null;
+          smtpSecure?: boolean;
+          smtpHost?: string | null;
+          smtpPort?: number | null;
+          smtpUser?: string | null;
+          smtpPass?: string | null;
+          enableServiceWorker?: boolean;
+          swPublicKey?: string | null;
+          swPrivateKey?: string | null;
+          tosUrl?: string | null;
+          repositoryUrl?: string;
+          feedbackUrl?: string;
+          impressumUrl?: string | null;
+          privacyPolicyUrl?: string | null;
+          useObjectStorage?: boolean;
+          objectStorageBaseUrl?: string | null;
+          objectStorageBucket?: string | null;
+          objectStoragePrefix?: string | null;
+          objectStorageEndpoint?: string | null;
+          objectStorageRegion?: string | null;
+          objectStoragePort?: number | null;
+          objectStorageAccessKey?: string | null;
+          objectStorageSecretKey?: string | null;
+          objectStorageUseSSL?: boolean;
+          objectStorageUseProxy?: boolean;
+          objectStorageSetPublicRead?: boolean;
+          objectStorageS3ForcePathStyle?: boolean;
+          enableIpLogging?: boolean;
+          enableActiveEmailValidation?: boolean;
+          enableVerifymailApi?: boolean;
+          verifymailAuthKey?: string | null;
+          enableChartsForRemoteUser?: boolean;
+          enableChartsForFederatedInstances?: boolean;
+          enableServerMachineStats?: boolean;
+          enableIdenticonGeneration?: boolean;
+          serverRules?: string[];
+          preservedUsernames?: string[];
+          manifestJsonOverride?: string;
+          enableFanoutTimeline?: boolean;
+          enableFanoutTimelineDbFallback?: boolean;
+          perLocalUserUserTimelineCacheMax?: number;
+          perRemoteUserUserTimelineCacheMax?: number;
+          perUserHomeTimelineCacheMax?: number;
+          perUserListTimelineCacheMax?: number;
+          notesPerOneAd?: number;
+          silencedHosts?: string[] | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/delete-account
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/delete-account': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': unknown;
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/update-user-note
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/update-user-note': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          text: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/roles/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/roles/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          description: string;
+          color: string | null;
+          iconUrl: string | null;
+          /** @enum {string} */
+          target: 'manual' | 'conditional';
+          condFormula: Record<string, never>;
+          isPublic: boolean;
+          isModerator: boolean;
+          isAdministrator: boolean;
+          /** @default false */
+          isExplorable?: boolean;
+          asBadge: boolean;
+          canEditMembersByModerator: boolean;
+          displayOrder: number;
+          policies: Record<string, never>;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/roles/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/roles/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          roleId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/roles/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/roles/list': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/roles/show
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/roles/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          roleId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/roles/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/roles/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          roleId: string;
+          name: string;
+          description: string;
+          color: string | null;
+          iconUrl: string | null;
+          /** @enum {string} */
+          target: 'manual' | 'conditional';
+          condFormula: Record<string, never>;
+          isPublic: boolean;
+          isModerator: boolean;
+          isAdministrator: boolean;
+          isExplorable?: boolean;
+          asBadge: boolean;
+          canEditMembersByModerator: boolean;
+          displayOrder: number;
+          policies: Record<string, never>;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/roles/assign
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/roles/assign': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          roleId: string;
+          /** Format: misskey:id */
+          userId: string;
+          expiresAt?: number | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/roles/unassign
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/roles/unassign': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          roleId: string;
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/roles/update-default-policies
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'admin/roles/update-default-policies': {
+    requestBody: {
+      content: {
+        'application/json': {
+          policies: Record<string, never>;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * admin/roles/users
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'admin/roles/users': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          roleId: string;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * announcements
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  announcements: {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default true */
+          isActive?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Announcement'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * antennas/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'antennas/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          /** @enum {string} */
+          src: 'home' | 'all' | 'users' | 'list' | 'users_blacklist';
+          /** Format: misskey:id */
+          userListId?: string | null;
+          keywords: string[][];
+          excludeKeywords: string[][];
+          users: string[];
+          caseSensitive: boolean;
+          localOnly?: boolean;
+          withReplies: boolean;
+          withFile: boolean;
+          notify: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Antenna'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * antennas/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'antennas/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          antennaId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * antennas/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:account*
+   */
+  'antennas/list': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Antenna'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * antennas/notes
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:account*
+   */
+  'antennas/notes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          antennaId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          sinceDate?: number;
+          untilDate?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * antennas/show
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:account*
+   */
+  'antennas/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          antennaId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Antenna'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * antennas/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'antennas/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          antennaId: string;
+          name: string;
+          /** @enum {string} */
+          src: 'home' | 'all' | 'users' | 'list' | 'users_blacklist';
+          /** Format: misskey:id */
+          userListId?: string | null;
+          keywords: string[][];
+          excludeKeywords: string[][];
+          users: string[];
+          caseSensitive: boolean;
+          localOnly?: boolean;
+          withReplies: boolean;
+          withFile: boolean;
+          notify: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Antenna'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * ap/get
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'ap/get': {
+    requestBody: {
+      content: {
+        'application/json': {
+          uri: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': Record<string, never>;
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * ap/show
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'ap/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          uri: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': OneOf<[{
+            /** @enum {string} */
+            type: 'User';
+            object: components['schemas']['UserDetailedNotMe'];
+          }, {
+            /** @enum {string} */
+            type: 'Note';
+            object: components['schemas']['Note'];
+          }]>;
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * app/create
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'app/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          description: string;
+          permission: string[];
+          callbackUrl?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['App'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * app/show
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'app/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          appId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['App'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * auth/session/generate
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'auth/session/generate': {
+    requestBody: {
+      content: {
+        'application/json': {
+          appSecret: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            token: string;
+            /** Format: url */
+            url: string;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * auth/session/show
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'auth/session/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          token: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            /** Format: id */
+            id: string;
+            app: components['schemas']['App'];
+            token: string;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * auth/session/userkey
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'auth/session/userkey': {
+    requestBody: {
+      content: {
+        'application/json': {
+          appSecret: string;
+          token: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            accessToken: string;
+            user: components['schemas']['UserDetailedNotMe'];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * blocking/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:blocks*
+   */
+  'blocking/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserDetailedNotMe'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * blocking/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:blocks*
+   */
+  'blocking/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserDetailedNotMe'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * blocking/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:blocks*
+   */
+  'blocking/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 30 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Blocking'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:channels*
+   */
+  'channels/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          description?: string | null;
+          /** Format: misskey:id */
+          bannerId?: string | null;
+          color?: string;
+          isSensitive?: boolean | null;
+          allowRenoteToExternal?: boolean | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Channel'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/featured
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'channels/featured': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Channel'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/follow
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:channels*
+   */
+  'channels/follow': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          channelId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/followed
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:channels*
+   */
+  'channels/followed': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 5 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Channel'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/owned
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:channels*
+   */
+  'channels/owned': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 5 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Channel'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/show
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'channels/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          channelId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Channel'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/timeline
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'channels/timeline': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          channelId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          sinceDate?: number;
+          untilDate?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/unfollow
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:channels*
+   */
+  'channels/unfollow': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          channelId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:channels*
+   */
+  'channels/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          channelId: string;
+          name?: string;
+          description?: string | null;
+          /** Format: misskey:id */
+          bannerId?: string | null;
+          isArchived?: boolean | null;
+          pinnedNoteIds?: string[];
+          color?: string;
+          isSensitive?: boolean | null;
+          allowRenoteToExternal?: boolean | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Channel'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/favorite
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:channels*
+   */
+  'channels/favorite': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          channelId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/unfavorite
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:channels*
+   */
+  'channels/unfavorite': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          channelId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/my-favorites
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:channels*
+   */
+  'channels/my-favorites': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Channel'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * channels/search
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'channels/search': {
+    requestBody: {
+      content: {
+        'application/json': {
+          query: string;
+          /**
+           * @default nameAndDescription
+           * @enum {string}
+           */
+          type?: 'nameAndDescription' | 'nameOnly';
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 5 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Channel'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/active-users
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/active-users': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            readWrite: number[];
+            read: number[];
+            write: number[];
+            registeredWithinWeek: number[];
+            registeredWithinMonth: number[];
+            registeredWithinYear: number[];
+            registeredOutsideWeek: number[];
+            registeredOutsideMonth: number[];
+            registeredOutsideYear: number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/ap-request
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/ap-request': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            deliverFailed: number[];
+            deliverSucceeded: number[];
+            inboxReceived: number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/drive
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/drive': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            'local.incCount': number[];
+            'local.incSize': number[];
+            'local.decCount': number[];
+            'local.decSize': number[];
+            'remote.incCount': number[];
+            'remote.incSize': number[];
+            'remote.decCount': number[];
+            'remote.decSize': number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/federation
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/federation': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            deliveredInstances: number[];
+            inboxInstances: number[];
+            stalled: number[];
+            sub: number[];
+            pub: number[];
+            pubsub: number[];
+            subActive: number[];
+            pubActive: number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/instance
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/instance': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+          host: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            'requests.failed': number[];
+            'requests.succeeded': number[];
+            'requests.received': number[];
+            'notes.total': number[];
+            'notes.inc': number[];
+            'notes.dec': number[];
+            'notes.diffs.normal': number[];
+            'notes.diffs.reply': number[];
+            'notes.diffs.renote': number[];
+            'notes.diffs.withFile': number[];
+            'users.total': number[];
+            'users.inc': number[];
+            'users.dec': number[];
+            'following.total': number[];
+            'following.inc': number[];
+            'following.dec': number[];
+            'followers.total': number[];
+            'followers.inc': number[];
+            'followers.dec': number[];
+            'drive.totalFiles': number[];
+            'drive.incFiles': number[];
+            'drive.decFiles': number[];
+            'drive.incUsage': number[];
+            'drive.decUsage': number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/notes
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/notes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            'local.total': number[];
+            'local.inc': number[];
+            'local.dec': number[];
+            'local.diffs.normal': number[];
+            'local.diffs.reply': number[];
+            'local.diffs.renote': number[];
+            'local.diffs.withFile': number[];
+            'remote.total': number[];
+            'remote.inc': number[];
+            'remote.dec': number[];
+            'remote.diffs.normal': number[];
+            'remote.diffs.reply': number[];
+            'remote.diffs.renote': number[];
+            'remote.diffs.withFile': number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/user/drive
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/user/drive': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            totalCount: number[];
+            totalSize: number[];
+            incCount: number[];
+            incSize: number[];
+            decCount: number[];
+            decSize: number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/user/following
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/user/following': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            'local.followings.total': number[];
+            'local.followings.inc': number[];
+            'local.followings.dec': number[];
+            'local.followers.total': number[];
+            'local.followers.inc': number[];
+            'local.followers.dec': number[];
+            'remote.followings.total': number[];
+            'remote.followings.inc': number[];
+            'remote.followings.dec': number[];
+            'remote.followers.total': number[];
+            'remote.followers.inc': number[];
+            'remote.followers.dec': number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/user/notes
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/user/notes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            total: number[];
+            inc: number[];
+            dec: number[];
+            'diffs.normal': number[];
+            'diffs.reply': number[];
+            'diffs.renote': number[];
+            'diffs.withFile': number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/user/pv
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/user/pv': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            'upv.user': number[];
+            'pv.user': number[];
+            'upv.visitor': number[];
+            'pv.visitor': number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/user/reactions
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/user/reactions': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            'local.count': number[];
+            'remote.count': number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * charts/users
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'charts/users': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          span: 'day' | 'hour';
+          /** @default 30 */
+          limit?: number;
+          /** @default null */
+          offset?: number | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            'local.total': number[];
+            'local.inc': number[];
+            'local.dec': number[];
+            'remote.total': number[];
+            'remote.inc': number[];
+            'remote.dec': number[];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * clips/add-note
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'clips/add-note': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          clipId: string;
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * clips/remove-note
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'clips/remove-note': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          clipId: string;
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * clips/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'clips/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          /** @default false */
+          isPublic?: boolean;
+          description?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Clip'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * clips/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'clips/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          clipId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * clips/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:account*
+   */
+  'clips/list': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Clip'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * clips/notes
+   * @description No description provided.
+   *
+   * **Credential required**: *No* / **Permission**: *read:account*
+   */
+  'clips/notes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          clipId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * clips/show
+   * @description No description provided.
+   *
+   * **Credential required**: *No* / **Permission**: *read:account*
+   */
+  'clips/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          clipId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Clip'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * clips/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'clips/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          clipId: string;
+          name: string;
+          isPublic?: boolean;
+          description?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Clip'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * clips/favorite
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
+   */
+  'clips/favorite': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          clipId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * clips/unfavorite
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
+   */
+  'clips/unfavorite': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          clipId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * clips/my-favorites
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:clip-favorite*
+   */
+  'clips/my-favorites': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Clip'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:drive*
+   */
+  drive: {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            capacity: number;
+            usage: number;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/files
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:drive*
+   */
+  'drive/files': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /**
+           * Format: misskey:id
+           * @default null
+           */
+          folderId?: string | null;
+          type?: string | null;
+          /** @enum {string|null} */
+          sort?: '+createdAt' | '-createdAt' | '+name' | '-name' | '+size' | '-size' | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFile'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/files/attached-notes
+   * @description Find the notes to which the given file is attached.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:drive*
+   */
+  'drive/files/attached-notes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          fileId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/files/check-existence
+   * @description Check if a given file exists.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:drive*
+   */
+  'drive/files/check-existence': {
+    requestBody: {
+      content: {
+        'application/json': {
+          md5: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': boolean;
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/files/create
+   * @description Upload a new drive file.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:drive*
+   */
+  'drive/files/create': {
+    requestBody: {
+      content: {
+        'multipart/form-data': {
+          /**
+           * Format: misskey:id
+           * @default null
+           */
+          folderId?: string | null;
+          /** @default null */
+          name?: string | null;
+          /** @default null */
+          comment?: string | null;
+          /** @default false */
+          isSensitive?: boolean;
+          /** @default false */
+          force?: boolean;
+          /**
+           * Format: binary
+           * @description The file contents.
+           */
+          file: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFile'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/files/delete
+   * @description Delete an existing drive file.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:drive*
+   */
+  'drive/files/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          fileId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/files/find-by-hash
+   * @description Search for a drive file by a hash of the contents.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:drive*
+   */
+  'drive/files/find-by-hash': {
+    requestBody: {
+      content: {
+        'application/json': {
+          md5: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFile'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/files/find
+   * @description Search for a drive file by the given parameters.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:drive*
+   */
+  'drive/files/find': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          /**
+           * Format: misskey:id
+           * @default null
+           */
+          folderId?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFile'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/files/show
+   * @description Show the properties of a drive file.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:drive*
+   */
+  'drive/files/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          fileId?: string;
+          url?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFile'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/files/update
+   * @description Update the properties of a drive file.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:drive*
+   */
+  'drive/files/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          fileId: string;
+          /** Format: misskey:id */
+          folderId?: string | null;
+          name?: string;
+          isSensitive?: boolean;
+          comment?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFile'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/files/upload-from-url
+   * @description Request the server to download a new drive file from the specified URL.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:drive*
+   */
+  'drive/files/upload-from-url': {
+    requestBody: {
+      content: {
+        'application/json': {
+          url: string;
+          /**
+           * Format: misskey:id
+           * @default null
+           */
+          folderId?: string | null;
+          /** @default false */
+          isSensitive?: boolean;
+          /** @default null */
+          comment?: string | null;
+          /** @default null */
+          marker?: string | null;
+          /** @default false */
+          force?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/folders
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:drive*
+   */
+  'drive/folders': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /**
+           * Format: misskey:id
+           * @default null
+           */
+          folderId?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFolder'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/folders/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:drive*
+   */
+  'drive/folders/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default Untitled */
+          name?: string;
+          /** Format: misskey:id */
+          parentId?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFolder'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/folders/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:drive*
+   */
+  'drive/folders/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          folderId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/folders/find
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:drive*
+   */
+  'drive/folders/find': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          /**
+           * Format: misskey:id
+           * @default null
+           */
+          parentId?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFolder'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/folders/show
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:drive*
+   */
+  'drive/folders/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          folderId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFolder'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/folders/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:drive*
+   */
+  'drive/folders/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          folderId: string;
+          name?: string;
+          /** Format: misskey:id */
+          parentId?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFolder'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * drive/stream
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:drive*
+   */
+  'drive/stream': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          type?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['DriveFile'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * email-address/available
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'email-address/available': {
+    requestBody: {
+      content: {
+        'application/json': {
+          emailAddress: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            available: boolean;
+            reason: string | null;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * endpoint
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  endpoint: {
+    requestBody: {
+      content: {
+        'application/json': {
+          endpoint: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * endpoints
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  endpoints: {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': string[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * federation/followers
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'federation/followers': {
+    requestBody: {
+      content: {
+        'application/json': {
+          host: string;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Following'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * federation/following
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'federation/following': {
+    requestBody: {
+      content: {
+        'application/json': {
+          host: string;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Following'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * federation/instances
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'federation/instances': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @description Omit or use `null` to not filter by host. */
+          host?: string | null;
+          blocked?: boolean | null;
+          notResponding?: boolean | null;
+          suspended?: boolean | null;
+          silenced?: boolean | null;
+          federating?: boolean | null;
+          subscribing?: boolean | null;
+          publishing?: boolean | null;
+          /** @default 30 */
+          limit?: number;
+          /** @default 0 */
+          offset?: number;
+          /** @enum {string|null} */
+          sort?: '+pubSub' | '-pubSub' | '+notes' | '-notes' | '+users' | '-users' | '+following' | '-following' | '+followers' | '-followers' | '+firstRetrievedAt' | '-firstRetrievedAt' | '+latestRequestReceivedAt' | '-latestRequestReceivedAt' | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['FederationInstance'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * federation/show-instance
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'federation/show-instance': {
+    requestBody: {
+      content: {
+        'application/json': {
+          host: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['FederationInstance'] | null;
+        };
+      };
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * federation/update-remote-user
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'federation/update-remote-user': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * federation/users
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'federation/users': {
+    requestBody: {
+      content: {
+        'application/json': {
+          host: string;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserDetailedNotMe'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * federation/stats
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'federation/stats': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * following/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:following*
+   */
+  'following/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          withReplies?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserLite'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * following/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:following*
+   */
+  'following/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserLite'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * following/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:following*
+   */
+  'following/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          /** @enum {string} */
+          notify?: 'normal' | 'none';
+          withReplies?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserLite'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * following/update-all
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:following*
+   */
+  'following/update-all': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          notify?: 'normal' | 'none';
+          withReplies?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * following/invalidate
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:following*
+   */
+  'following/invalidate': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserLite'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * following/requests/accept
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:following*
+   */
+  'following/requests/accept': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * following/requests/cancel
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:following*
+   */
+  'following/requests/cancel': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserLite'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * following/requests/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:following*
+   */
+  'following/requests/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              /** Format: id */
+              id: string;
+              follower: components['schemas']['UserLite'];
+              followee: components['schemas']['UserLite'];
+            }[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * following/requests/reject
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:following*
+   */
+  'following/requests/reject': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * gallery/featured
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'gallery/featured': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['GalleryPost'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * gallery/popular
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'gallery/popular': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['GalleryPost'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * gallery/posts
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'gallery/posts': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['GalleryPost'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * gallery/posts/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:gallery*
+   */
+  'gallery/posts/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          title: string;
+          description?: string | null;
+          fileIds: string[];
+          /** @default false */
+          isSensitive?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['GalleryPost'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * gallery/posts/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:gallery*
+   */
+  'gallery/posts/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          postId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * gallery/posts/like
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
+   */
+  'gallery/posts/like': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          postId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * gallery/posts/show
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'gallery/posts/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          postId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['GalleryPost'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * gallery/posts/unlike
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
+   */
+  'gallery/posts/unlike': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          postId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * gallery/posts/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:gallery*
+   */
+  'gallery/posts/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          postId: string;
+          title: string;
+          description?: string | null;
+          fileIds: string[];
+          /** @default false */
+          isSensitive?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['GalleryPost'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * get-online-users-count
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'get-online-users-count': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * get-avatar-decorations
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'get-avatar-decorations': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              /**
+               * Format: id
+               * @example xxxxxxxxxx
+               */
+              id: string;
+              name: string;
+              description: string;
+              url: string;
+              roleIdsThatCanBeUsedThisDecoration: string[];
+            }[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * hashtags/list
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'hashtags/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** @default false */
+          attachedToUserOnly?: boolean;
+          /** @default false */
+          attachedToLocalUserOnly?: boolean;
+          /** @default false */
+          attachedToRemoteUserOnly?: boolean;
+          /** @enum {string} */
+          sort: '+mentionedUsers' | '-mentionedUsers' | '+mentionedLocalUsers' | '-mentionedLocalUsers' | '+mentionedRemoteUsers' | '-mentionedRemoteUsers' | '+attachedUsers' | '-attachedUsers' | '+attachedLocalUsers' | '-attachedLocalUsers' | '+attachedRemoteUsers' | '-attachedRemoteUsers';
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Hashtag'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * hashtags/search
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'hashtags/search': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          query: string;
+          /** @default 0 */
+          offset?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': string[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * hashtags/show
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'hashtags/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          tag: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Hashtag'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * hashtags/trend
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'hashtags/trend': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              tag: string;
+              chart: number[];
+              usersCount: number;
+            }[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * hashtags/users
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'hashtags/users': {
+    requestBody: {
+      content: {
+        'application/json': {
+          tag: string;
+          /** @default 10 */
+          limit?: number;
+          /** @enum {string} */
+          sort: '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+updatedAt' | '-updatedAt';
+          /**
+           * @default all
+           * @enum {string}
+           */
+          state?: 'all' | 'alive';
+          /**
+           * @default local
+           * @enum {string}
+           */
+          origin?: 'combined' | 'local' | 'remote';
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserDetailed'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  i: {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['MeDetailed'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/claim-achievement
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'i/claim-achievement': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          name: 'notes1' | 'notes10' | 'notes100' | 'notes500' | 'notes1000' | 'notes5000' | 'notes10000' | 'notes20000' | 'notes30000' | 'notes40000' | 'notes50000' | 'notes60000' | 'notes70000' | 'notes80000' | 'notes90000' | 'notes100000' | 'login3' | 'login7' | 'login15' | 'login30' | 'login60' | 'login100' | 'login200' | 'login300' | 'login400' | 'login500' | 'login600' | 'login700' | 'login800' | 'login900' | 'login1000' | 'passedSinceAccountCreated1' | 'passedSinceAccountCreated2' | 'passedSinceAccountCreated3' | 'loggedInOnBirthday' | 'loggedInOnNewYearsDay' | 'noteClipped1' | 'noteFavorited1' | 'myNoteFavorited1' | 'profileFilled' | 'markedAsCat' | 'following1' | 'following10' | 'following50' | 'following100' | 'following300' | 'followers1' | 'followers10' | 'followers50' | 'followers100' | 'followers300' | 'followers500' | 'followers1000' | 'collectAchievements30' | 'viewAchievements3min' | 'iLoveMisskey' | 'foundTreasure' | 'client30min' | 'client60min' | 'noteDeletedWithin1min' | 'postedAtLateNight' | 'postedAt0min0sec' | 'selfQuote' | 'htl20npm' | 'viewInstanceChart' | 'outputHelloWorldOnScratchpad' | 'open3windows' | 'driveFolderCircularReference' | 'reactWithoutRead' | 'clickedClickHere' | 'justPlainLucky' | 'setNameToSyuilo' | 'cookieClicked' | 'brainDiver' | 'smashTestNotificationButton' | 'tutorialCompleted';
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/favorites
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:favorites*
+   */
+  'i/favorites': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['NoteFavorite'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/gallery/likes
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:gallery-likes*
+   */
+  'i/gallery/likes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              /** Format: id */
+              id: string;
+              post: components['schemas']['GalleryPost'];
+            }[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/gallery/posts
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:gallery*
+   */
+  'i/gallery/posts': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['GalleryPost'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/notifications
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:notifications*
+   */
+  'i/notifications': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default true */
+          markAsRead?: boolean;
+          includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
+          excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Notification'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/notifications-grouped
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:notifications*
+   */
+  'i/notifications-grouped': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default true */
+          markAsRead?: boolean;
+          includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
+          excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Notification'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/page-likes
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:page-likes*
+   */
+  'i/page-likes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              /** Format: id */
+              id: string;
+              page: components['schemas']['Page'];
+            }[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/pages
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:pages*
+   */
+  'i/pages': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Page'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/pin
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'i/pin': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['MeDetailed'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/read-all-unread-notes
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'i/read-all-unread-notes': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/read-announcement
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'i/read-announcement': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          announcementId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/registry/get-all
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'i/registry/get-all': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default [] */
+          scope: string[];
+          domain?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/registry/get-detail
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'i/registry/get-detail': {
+    requestBody: {
+      content: {
+        'application/json': {
+          key: string;
+          /** @default [] */
+          scope: string[];
+          domain?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/registry/get
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'i/registry/get': {
+    requestBody: {
+      content: {
+        'application/json': {
+          key: string;
+          /** @default [] */
+          scope: string[];
+          domain?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/registry/keys-with-type
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'i/registry/keys-with-type': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default [] */
+          scope: string[];
+          domain?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/registry/keys
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'i/registry/keys': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default [] */
+          scope: string[];
+          domain?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/registry/remove
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'i/registry/remove': {
+    requestBody: {
+      content: {
+        'application/json': {
+          key: string;
+          /** @default [] */
+          scope: string[];
+          domain?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/registry/set
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'i/registry/set': {
+    requestBody: {
+      content: {
+        'application/json': {
+          key: string;
+          value: unknown;
+          /** @default [] */
+          scope: string[];
+          domain?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/unpin
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'i/unpin': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['MeDetailed'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'i/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name?: string | null;
+          description?: string | null;
+          location?: string | null;
+          birthday?: string | null;
+          /** @enum {string|null} */
+          lang?: null | 'ach' | 'ady' | 'af' | 'af-NA' | 'af-ZA' | 'ak' | 'ar' | 'ar-AR' | 'ar-MA' | 'ar-SA' | 'ay-BO' | 'az' | 'az-AZ' | 'be-BY' | 'bg' | 'bg-BG' | 'bn' | 'bn-IN' | 'bn-BD' | 'br' | 'bs-BA' | 'ca' | 'ca-ES' | 'cak' | 'ck-US' | 'cs' | 'cs-CZ' | 'cy' | 'cy-GB' | 'da' | 'da-DK' | 'de' | 'de-AT' | 'de-DE' | 'de-CH' | 'dsb' | 'el' | 'el-GR' | 'en' | 'en-GB' | 'en-AU' | 'en-CA' | 'en-IE' | 'en-IN' | 'en-PI' | 'en-SG' | 'en-UD' | 'en-US' | 'en-ZA' | 'en@pirate' | 'eo' | 'eo-EO' | 'es' | 'es-AR' | 'es-419' | 'es-CL' | 'es-CO' | 'es-EC' | 'es-ES' | 'es-LA' | 'es-NI' | 'es-MX' | 'es-US' | 'es-VE' | 'et' | 'et-EE' | 'eu' | 'eu-ES' | 'fa' | 'fa-IR' | 'fb-LT' | 'ff' | 'fi' | 'fi-FI' | 'fo' | 'fo-FO' | 'fr' | 'fr-CA' | 'fr-FR' | 'fr-BE' | 'fr-CH' | 'fy-NL' | 'ga' | 'ga-IE' | 'gd' | 'gl' | 'gl-ES' | 'gn-PY' | 'gu-IN' | 'gv' | 'gx-GR' | 'he' | 'he-IL' | 'hi' | 'hi-IN' | 'hr' | 'hr-HR' | 'hsb' | 'ht' | 'hu' | 'hu-HU' | 'hy' | 'hy-AM' | 'id' | 'id-ID' | 'is' | 'is-IS' | 'it' | 'it-IT' | 'ja' | 'ja-JP' | 'jv-ID' | 'ka-GE' | 'kk-KZ' | 'km' | 'kl' | 'km-KH' | 'kab' | 'kn' | 'kn-IN' | 'ko' | 'ko-KR' | 'ku-TR' | 'kw' | 'la' | 'la-VA' | 'lb' | 'li-NL' | 'lt' | 'lt-LT' | 'lv' | 'lv-LV' | 'mai' | 'mg-MG' | 'mk' | 'mk-MK' | 'ml' | 'ml-IN' | 'mn-MN' | 'mr' | 'mr-IN' | 'ms' | 'ms-MY' | 'mt' | 'mt-MT' | 'my' | 'no' | 'nb' | 'nb-NO' | 'ne' | 'ne-NP' | 'nl' | 'nl-BE' | 'nl-NL' | 'nn-NO' | 'oc' | 'or-IN' | 'pa' | 'pa-IN' | 'pl' | 'pl-PL' | 'ps-AF' | 'pt' | 'pt-BR' | 'pt-PT' | 'qu-PE' | 'rm-CH' | 'ro' | 'ro-RO' | 'ru' | 'ru-RU' | 'sa-IN' | 'se-NO' | 'sh' | 'si-LK' | 'sk' | 'sk-SK' | 'sl' | 'sl-SI' | 'so-SO' | 'sq' | 'sq-AL' | 'sr' | 'sr-RS' | 'su' | 'sv' | 'sv-SE' | 'sw' | 'sw-KE' | 'ta' | 'ta-IN' | 'te' | 'te-IN' | 'tg' | 'tg-TJ' | 'th' | 'th-TH' | 'fil' | 'tlh' | 'tr' | 'tr-TR' | 'tt-RU' | 'uk' | 'uk-UA' | 'ur' | 'ur-PK' | 'uz' | 'uz-UZ' | 'vi' | 'vi-VN' | 'xh-ZA' | 'yi' | 'yi-DE' | 'zh' | 'zh-Hans' | 'zh-Hant' | 'zh-CN' | 'zh-HK' | 'zh-SG' | 'zh-TW' | 'zu-ZA';
+          /** Format: misskey:id */
+          avatarId?: string | null;
+          avatarDecorations?: ({
+              /** Format: misskey:id */
+              id: string;
+              angle?: number | null;
+              flipH?: boolean | null;
+            })[];
+          /** Format: misskey:id */
+          bannerId?: string | null;
+          fields?: {
+              name: string;
+              value: string;
+            }[];
+          isLocked?: boolean;
+          isExplorable?: boolean;
+          hideOnlineStatus?: boolean;
+          publicReactions?: boolean;
+          carefulBot?: boolean;
+          autoAcceptFollowed?: boolean;
+          noCrawle?: boolean;
+          preventAiLearning?: boolean;
+          isBot?: boolean;
+          isCat?: boolean;
+          injectFeaturedNote?: boolean;
+          receiveAnnouncementEmail?: boolean;
+          alwaysMarkNsfw?: boolean;
+          autoSensitive?: boolean;
+          /** @enum {string} */
+          ffVisibility?: 'public' | 'followers' | 'private';
+          /** Format: misskey:id */
+          pinnedPageId?: string | null;
+          mutedWords?: (string[] | string)[];
+          hardMutedWords?: (string[] | string)[];
+          mutedInstances?: string[];
+          notificationRecieveConfig?: Record<string, never>;
+          emailNotificationTypes?: string[];
+          alsoKnownAs?: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['MeDetailed'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/webhooks/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'i/webhooks/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          url: string;
+          /** @default */
+          secret?: string;
+          on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/webhooks/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:account*
+   */
+  'i/webhooks/list': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/webhooks/show
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:account*
+   */
+  'i/webhooks/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          webhookId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/webhooks/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'i/webhooks/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          webhookId: string;
+          name: string;
+          url: string;
+          /** @default */
+          secret?: string;
+          on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
+          active: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/webhooks/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'i/webhooks/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          webhookId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * invite/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'invite/create': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            /** @example GR6S02ERUA5VR */
+            code: string;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * invite/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'invite/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          inviteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * invite/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'invite/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 30 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['InviteCode'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * invite/limit
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'invite/limit': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            remaining: number | null;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * meta
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  meta: {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default true */
+          detail?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            maintainerName: string | null;
+            maintainerEmail: string | null;
+            version: string;
+            name: string;
+            shortName: string | null;
+            /**
+             * Format: url
+             * @example https://misskey.example.com
+             */
+            uri: string;
+            description: string | null;
+            langs: string[];
+            tosUrl: string | null;
+            /** @default https://github.com/misskey-dev/misskey */
+            repositoryUrl: string;
+            /** @default https://github.com/misskey-dev/misskey/issues/new */
+            feedbackUrl: string;
+            defaultDarkTheme: string | null;
+            defaultLightTheme: string | null;
+            disableRegistration: boolean;
+            cacheRemoteFiles: boolean;
+            cacheRemoteSensitiveFiles: boolean;
+            emailRequiredForSignup: boolean;
+            enableHcaptcha: boolean;
+            hcaptchaSiteKey: string | null;
+            enableRecaptcha: boolean;
+            recaptchaSiteKey: string | null;
+            enableTurnstile: boolean;
+            turnstileSiteKey: string | null;
+            swPublickey: string | null;
+            /** @default /assets/ai.png */
+            mascotImageUrl: string;
+            bannerUrl: string;
+            serverErrorImageUrl: string | null;
+            infoImageUrl: string | null;
+            notFoundImageUrl: string | null;
+            iconUrl: string | null;
+            maxNoteTextLength: number;
+            ads: {
+                place: string;
+                /** Format: url */
+                url: string;
+                /** Format: url */
+                imageUrl: string;
+              }[];
+            /** @default 0 */
+            notesPerOneAd: number;
+            /** @example false */
+            requireSetup: boolean;
+            enableEmail: boolean;
+            enableServiceWorker: boolean;
+            translatorAvailable: boolean;
+            proxyAccountName: string | null;
+            mediaProxy: string;
+            features?: {
+              registration: boolean;
+              localTimeline: boolean;
+              globalTimeline: boolean;
+              hcaptcha: boolean;
+              recaptcha: boolean;
+              objectStorage: boolean;
+              serviceWorker: boolean;
+              /** @default true */
+              miauth?: boolean;
+            };
+            backgroundImageUrl: string | null;
+            impressumUrl: string | null;
+            logoImageUrl: string | null;
+            privacyPolicyUrl: string | null;
+            serverRules: string[];
+            themeColor: string | null;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * emojis
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  emojis: {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            emojis: components['schemas']['EmojiSimple'][];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * emoji
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  emoji: {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['EmojiDetailed'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * mute/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:mutes*
+   */
+  'mute/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          /** @description A Unix Epoch timestamp that must lie in the future. `null` means an indefinite mute. */
+          expiresAt?: number | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * mute/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:mutes*
+   */
+  'mute/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * mute/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:mutes*
+   */
+  'mute/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 30 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Muting'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * renote-mute/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:mutes*
+   */
+  'renote-mute/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * renote-mute/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:mutes*
+   */
+  'renote-mute/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * renote-mute/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:mutes*
+   */
+  'renote-mute/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 30 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['RenoteMuting'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * my/apps
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'my/apps': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** @default 0 */
+          offset?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['App'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  notes: {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default false */
+          local?: boolean;
+          reply?: boolean;
+          renote?: boolean;
+          withFiles?: boolean;
+          poll?: boolean;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/children
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/children': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/clips
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/clips': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Clip'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/conversation
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/conversation': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+          /** @default 10 */
+          limit?: number;
+          /** @default 0 */
+          offset?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:notes*
+   */
+  'notes/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /**
+           * @default public
+           * @enum {string}
+           */
+          visibility?: 'public' | 'home' | 'followers' | 'specified';
+          visibleUserIds?: string[];
+          cw?: string | null;
+          /** @default false */
+          localOnly?: boolean;
+          /**
+           * @default null
+           * @enum {string|null}
+           */
+          reactionAcceptance?: null | 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote';
+          /** @default false */
+          noExtractMentions?: boolean;
+          /** @default false */
+          noExtractHashtags?: boolean;
+          /** @default false */
+          noExtractEmojis?: boolean;
+          /** Format: misskey:id */
+          replyId?: string | null;
+          /** Format: misskey:id */
+          renoteId?: string | null;
+          /** Format: misskey:id */
+          channelId?: string | null;
+          text?: string | null;
+          fileIds?: string[];
+          mediaIds?: string[];
+          poll?: ({
+            choices: string[];
+            multiple?: boolean;
+            expiresAt?: number | null;
+            expiredAfter?: number | null;
+          }) | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            createdNote: components['schemas']['Note'];
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:notes*
+   */
+  'notes/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/favorites/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:favorites*
+   */
+  'notes/favorites/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/favorites/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:favorites*
+   */
+  'notes/favorites/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/featured
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/featured': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** Format: misskey:id */
+          channelId?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/global-timeline
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/global-timeline': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default false */
+          withFiles?: boolean;
+          /** @default true */
+          withRenotes?: boolean;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          sinceDate?: number;
+          untilDate?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/hybrid-timeline
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'notes/hybrid-timeline': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          sinceDate?: number;
+          untilDate?: number;
+          /** @default true */
+          includeMyRenotes?: boolean;
+          /** @default true */
+          includeRenotedMyNotes?: boolean;
+          /** @default true */
+          includeLocalRenotes?: boolean;
+          /** @default false */
+          withFiles?: boolean;
+          /** @default true */
+          withRenotes?: boolean;
+          /** @default false */
+          withReplies?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/local-timeline
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/local-timeline': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default false */
+          withFiles?: boolean;
+          /** @default true */
+          withRenotes?: boolean;
+          /** @default false */
+          withReplies?: boolean;
+          /** @default false */
+          excludeNsfw?: boolean;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          sinceDate?: number;
+          untilDate?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/mentions
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'notes/mentions': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default false */
+          following?: boolean;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          visibility?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/polls/recommendation
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'notes/polls/recommendation': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** @default 0 */
+          offset?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/polls/vote
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:votes*
+   */
+  'notes/polls/vote': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+          choice: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/reactions
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/reactions': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+          type?: string | null;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['NoteReaction'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/reactions/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:reactions*
+   */
+  'notes/reactions/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+          reaction: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/reactions/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:reactions*
+   */
+  'notes/reactions/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/renotes
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/renotes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/replies
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/replies': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/search-by-tag
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/search-by-tag': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default null */
+          reply?: boolean | null;
+          /** @default null */
+          renote?: boolean | null;
+          /**
+           * @description Only show notes that have attached files.
+           * @default false
+           */
+          withFiles?: boolean;
+          /** @default null */
+          poll?: boolean | null;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+          tag?: string;
+          /** @description The outer arrays are chained with OR, the inner arrays are chained with AND. */
+          query?: string[][];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/search
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/search': {
+    requestBody: {
+      content: {
+        'application/json': {
+          query: string;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+          /** @default 0 */
+          offset?: number;
+          /** @description The local host is represented with `.`. */
+          host?: string;
+          /**
+           * Format: misskey:id
+           * @default null
+           */
+          userId?: string | null;
+          /**
+           * Format: misskey:id
+           * @default null
+           */
+          channelId?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/show
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'notes/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/state
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'notes/state': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            isFavorited: boolean;
+            isMutedThread: boolean;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/thread-muting/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'notes/thread-muting/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/thread-muting/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'notes/thread-muting/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/timeline
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'notes/timeline': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          sinceDate?: number;
+          untilDate?: number;
+          /** @default true */
+          includeMyRenotes?: boolean;
+          /** @default true */
+          includeRenotedMyNotes?: boolean;
+          /** @default true */
+          includeLocalRenotes?: boolean;
+          /** @default false */
+          withFiles?: boolean;
+          /** @default true */
+          withRenotes?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/translate
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'notes/translate': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+          targetLang: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': Record<string, never>;
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/unrenote
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:notes*
+   */
+  'notes/unrenote': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notes/user-list-timeline
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'notes/user-list-timeline': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          listId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          sinceDate?: number;
+          untilDate?: number;
+          /** @default true */
+          includeMyRenotes?: boolean;
+          /** @default true */
+          includeRenotedMyNotes?: boolean;
+          /** @default true */
+          includeLocalRenotes?: boolean;
+          /** @default true */
+          withRenotes?: boolean;
+          /**
+           * @description Only show notes that have attached files.
+           * @default false
+           */
+          withFiles?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notifications/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:notifications*
+   */
+  'notifications/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          body: string;
+          header?: string | null;
+          icon?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notifications/mark-all-as-read
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:notifications*
+   */
+  'notifications/mark-all-as-read': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * notifications/test-notification
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:notifications*
+   */
+  'notifications/test-notification': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * pages/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:pages*
+   */
+  'pages/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          title: string;
+          name: string;
+          summary?: string | null;
+          content: {
+              [key: string]: unknown;
+            }[];
+          variables: {
+              [key: string]: unknown;
+            }[];
+          script: string;
+          /** Format: misskey:id */
+          eyeCatchingImageId?: string | null;
+          /**
+           * @default sans-serif
+           * @enum {string}
+           */
+          font?: 'serif' | 'sans-serif';
+          /** @default false */
+          alignCenter?: boolean;
+          /** @default false */
+          hideTitleWhenPinned?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Page'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * pages/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:pages*
+   */
+  'pages/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          pageId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * pages/featured
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'pages/featured': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Page'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * pages/like
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:page-likes*
+   */
+  'pages/like': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          pageId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * pages/show
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'pages/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          pageId?: string;
+          name?: string;
+          username?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Page'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * pages/unlike
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:page-likes*
+   */
+  'pages/unlike': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          pageId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * pages/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:pages*
+   */
+  'pages/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          pageId: string;
+          title: string;
+          name: string;
+          summary?: string | null;
+          content: {
+              [key: string]: unknown;
+            }[];
+          variables: {
+              [key: string]: unknown;
+            }[];
+          script: string;
+          /** Format: misskey:id */
+          eyeCatchingImageId?: string | null;
+          /** @enum {string} */
+          font?: 'serif' | 'sans-serif';
+          alignCenter?: boolean;
+          hideTitleWhenPinned?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * flash/create
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:flash*
+   */
+  'flash/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          title: string;
+          summary: string;
+          script: string;
+          permissions: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * flash/delete
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:flash*
+   */
+  'flash/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          flashId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * flash/featured
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'flash/featured': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Flash'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * flash/like
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
+   */
+  'flash/like': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          flashId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * flash/show
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'flash/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          flashId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Flash'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * flash/unlike
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
+   */
+  'flash/unlike': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          flashId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * flash/update
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:flash*
+   */
+  'flash/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          flashId: string;
+          title: string;
+          summary: string;
+          script: string;
+          permissions: string[];
+          /** @enum {string} */
+          visibility?: 'public' | 'private';
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * flash/my
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:flash*
+   */
+  'flash/my': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Flash'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * flash/my-likes
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:flash-likes*
+   */
+  'flash/my-likes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              /** Format: id */
+              id: string;
+              flash: components['schemas']['Flash'];
+            }[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * ping
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  ping: {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            pong: number;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * pinned-users
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'pinned-users': {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserDetailed'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * promo/read
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'promo/read': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          noteId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * roles/list
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'roles/list': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * roles/show
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'roles/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          roleId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * roles/users
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'roles/users': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          roleId: string;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * roles/notes
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'roles/notes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          roleId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          sinceDate?: number;
+          untilDate?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * request-reset-password
+   * @description Request a users password to be reset.
+   *
+   * **Credential required**: *No*
+   */
+  'request-reset-password': {
+    requestBody: {
+      content: {
+        'application/json': {
+          username: string;
+          email: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * reset-db
+   * @description Only available when running with <code>NODE_ENV=testing</code>. Reset the database and flush Redis.
+   *
+   * **Credential required**: *No*
+   */
+  'reset-db': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * reset-password
+   * @description Complete the password reset that was previously requested.
+   *
+   * **Credential required**: *No*
+   */
+  'reset-password': {
+    requestBody: {
+      content: {
+        'application/json': {
+          token: string;
+          password: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * server-info
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'server-info': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * stats
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  stats: {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            notesCount: number;
+            originalNotesCount: number;
+            usersCount: number;
+            originalUsersCount: number;
+            instances: number;
+            driveUsageLocal: number;
+            driveUsageRemote: number;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * sw/show-registration
+   * @description Check push notification registration exists.
+   *
+   * **Credential required**: *Yes*
+   */
+  'sw/show-registration': {
+    requestBody: {
+      content: {
+        'application/json': {
+          endpoint: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            userId: string;
+            endpoint: string;
+            sendReadMessage: boolean;
+          } | null;
+        };
+      };
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * sw/update-registration
+   * @description Update push notification registration.
+   *
+   * **Credential required**: *Yes*
+   */
+  'sw/update-registration': {
+    requestBody: {
+      content: {
+        'application/json': {
+          endpoint: string;
+          sendReadMessage?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            userId: string;
+            endpoint: string;
+            sendReadMessage: boolean;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * sw/register
+   * @description Register to receive push notifications.
+   *
+   * **Credential required**: *Yes*
+   */
+  'sw/register': {
+    requestBody: {
+      content: {
+        'application/json': {
+          endpoint: string;
+          auth: string;
+          publickey: string;
+          /** @default false */
+          sendReadMessage?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            /** @enum {string} */
+            state?: 'already-subscribed' | 'subscribed';
+            key: string | null;
+            userId: string;
+            endpoint: string;
+            sendReadMessage: boolean;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * sw/unregister
+   * @description Unregister from receiving push notifications.
+   *
+   * **Credential required**: *No*
+   */
+  'sw/unregister': {
+    requestBody: {
+      content: {
+        'application/json': {
+          endpoint: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * test
+   * @description Endpoint for testing input validation.
+   *
+   * **Credential required**: *No*
+   */
+  test: {
+    requestBody: {
+      content: {
+        'application/json': {
+          required: boolean;
+          string?: string;
+          /** @default hello */
+          default?: string;
+          /** @default hello */
+          nullableDefault?: string | null;
+          /** Format: misskey:id */
+          id?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * username/available
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'username/available': {
+    requestBody: {
+      content: {
+        'application/json': {
+          username: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            available: boolean;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  users: {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** @default 0 */
+          offset?: number;
+          /** @enum {string} */
+          sort?: '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+updatedAt' | '-updatedAt';
+          /**
+           * @default all
+           * @enum {string}
+           */
+          state?: 'all' | 'alive';
+          /**
+           * @default local
+           * @enum {string}
+           */
+          origin?: 'combined' | 'local' | 'remote';
+          /**
+           * @description The local host is represented with `null`.
+           * @default null
+           */
+          hostname?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserDetailed'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/clips
+   * @description Show all clips this user owns.
+   *
+   * **Credential required**: *No*
+   */
+  'users/clips': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Clip'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/followers
+   * @description Show everyone that follows this user.
+   *
+   * **Credential required**: *No*
+   */
+  'users/followers': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          userId?: string;
+          username?: string;
+          /** @description The local host is represented with `null`. */
+          host?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Following'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/following
+   * @description Show everyone that this user is following.
+   *
+   * **Credential required**: *No*
+   */
+  'users/following': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          userId?: string;
+          username?: string;
+          /** @description The local host is represented with `null`. */
+          host?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Following'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/gallery/posts
+   * @description Show all gallery posts by the given user.
+   *
+   * **Credential required**: *No*
+   */
+  'users/gallery/posts': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['GalleryPost'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/get-frequently-replied-users
+   * @description Get a list of other users that the specified user frequently replies to.
+   *
+   * **Credential required**: *No*
+   */
+  'users/get-frequently-replied-users': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          /** @default 10 */
+          limit?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              user: components['schemas']['UserDetailed'];
+              weight: number;
+            }[];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/featured-notes
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'users/featured-notes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          untilId?: string;
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/create
+   * @description Create a new list of users.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'users/lists/create': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserList'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/delete
+   * @description Delete an existing list of users.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'users/lists/delete': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          listId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/list
+   * @description Show all lists that the authenticated user has created.
+   *
+   * **Credential required**: *No* / **Permission**: *read:account*
+   */
+  'users/lists/list': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserList'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/pull
+   * @description Remove a user from a list.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'users/lists/pull': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          listId: string;
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/push
+   * @description Add a user to an existing list.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'users/lists/push': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          listId: string;
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/show
+   * @description Show the properties of a list.
+   *
+   * **Credential required**: *No* / **Permission**: *read:account*
+   */
+  'users/lists/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          listId: string;
+          /** @default false */
+          forPublic?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserList'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/favorite
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'users/lists/favorite': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          listId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/unfavorite
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'users/lists/unfavorite': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          listId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/update
+   * @description Update the properties of a list.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'users/lists/update': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          listId: string;
+          name?: string;
+          isPublic?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserList'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/create-from-public
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'users/lists/create-from-public': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          /** Format: misskey:id */
+          listId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserList'];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/update-membership
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'users/lists/update-membership': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          listId: string;
+          /** Format: misskey:id */
+          userId: string;
+          withReplies?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/lists/get-memberships
+   * @description No description provided.
+   *
+   * **Credential required**: *No* / **Permission**: *read:account*
+   */
+  'users/lists/get-memberships': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          listId: string;
+          /** @default false */
+          forPublic?: boolean;
+          /** @default 30 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/notes
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'users/notes': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          /** @default false */
+          withReplies?: boolean;
+          /** @default true */
+          withRenotes?: boolean;
+          /** @default false */
+          withChannelNotes?: boolean;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          sinceDate?: number;
+          untilDate?: number;
+          /** @default false */
+          withFiles?: boolean;
+          /** @default false */
+          excludeNsfw?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Note'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/pages
+   * @description Show all pages this user created.
+   *
+   * **Credential required**: *No*
+   */
+  'users/pages': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Page'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/flashs
+   * @description Show all flashs this user created.
+   *
+   * **Credential required**: *No*
+   */
+  'users/flashs': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Flash'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/reactions
+   * @description Show all reactions this user made.
+   *
+   * **Credential required**: *No*
+   */
+  'users/reactions': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+          sinceDate?: number;
+          untilDate?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['NoteReaction'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/recommendation
+   * @description Show users that the authenticated user might be interested to follow.
+   *
+   * **Credential required**: *Yes* / **Permission**: *read:account*
+   */
+  'users/recommendation': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** @default 0 */
+          offset?: number;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserDetailed'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/relation
+   * @description Show the different kinds of relations between the authenticated user and the specified user(s).
+   *
+   * **Credential required**: *Yes*
+   */
+  'users/relation': {
+    requestBody: {
+      content: {
+        'application/json': {
+          userId: string | string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': OneOf<[{
+            /** Format: id */
+            id: string;
+            isFollowing: boolean;
+            hasPendingFollowRequestFromYou: boolean;
+            hasPendingFollowRequestToYou: boolean;
+            isFollowed: boolean;
+            isBlocking: boolean;
+            isBlocked: boolean;
+            isMuted: boolean;
+            isRenoteMuted: boolean;
+          }, {
+              /** Format: id */
+              id: string;
+              isFollowing: boolean;
+              hasPendingFollowRequestFromYou: boolean;
+              hasPendingFollowRequestToYou: boolean;
+              isFollowed: boolean;
+              isBlocking: boolean;
+              isBlocked: boolean;
+              isMuted: boolean;
+              isRenoteMuted: boolean;
+            }[]]>;
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/report-abuse
+   * @description File a report.
+   *
+   * **Credential required**: *Yes*
+   */
+  'users/report-abuse': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          comment: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/search-by-username-and-host
+   * @description Search for a user by username and/or host.
+   *
+   * **Credential required**: *No*
+   */
+  'users/search-by-username-and-host': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** @default true */
+          detail?: boolean;
+          username?: string | null;
+          host?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['User'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/search
+   * @description Search for users.
+   *
+   * **Credential required**: *No*
+   */
+  'users/search': {
+    requestBody: {
+      content: {
+        'application/json': {
+          query: string;
+          /** @default 0 */
+          offset?: number;
+          /** @default 10 */
+          limit?: number;
+          /**
+           * @default combined
+           * @enum {string}
+           */
+          origin?: 'local' | 'remote' | 'combined';
+          /** @default true */
+          detail?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['User'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/show
+   * @description Show the properties of a user.
+   *
+   * **Credential required**: *No*
+   */
+  'users/show': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId?: string;
+          userIds?: string[];
+          username?: string;
+          /** @description The local host is represented with `null`. */
+          host?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserDetailed'] | components['schemas']['UserDetailed'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/achievements
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'users/achievements': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * users/update-memo
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes* / **Permission**: *write:account*
+   */
+  'users/update-memo': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          userId: string;
+          /** @description A personal memo for the target user. If null or empty, delete the memo. */
+          memo: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * fetch-rss
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  'fetch-rss': {
+    requestBody: {
+      content: {
+        'application/json': {
+          url: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * fetch-external-resources
+   * @description No description provided.
+   *
+   * **Credential required**: *Yes*
+   */
+  'fetch-external-resources': {
+    requestBody: {
+      content: {
+        'application/json': {
+          url: string;
+          hash: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * retention
+   * @description No description provided.
+   *
+   * **Credential required**: *No*
+   */
+  retention: {
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': unknown;
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+};
+
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 2ddcf93f5d..b1d6b5c10f 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -1,455 +1,11 @@
 import { ModerationLogPayloads, notificationTypes } from './consts.js';
+import { Page, User, UserDetailed } from './autogen/models';
+export * from './autogen/entities';
+export * from './autogen/models';
 
 export type ID = string;
 export type DateString = string;
 
-type TODO = Record<string, any>;
-
-// NOTE: 極力この型を使うのは避け、UserLite か UserDetailed か明示するように
-export type User = UserLite | UserDetailed;
-
-export type UserLite = {
-	id: ID;
-	username: string;
-	host: string | null;
-	name: string | null;
-	onlineStatus: 'online' | 'active' | 'offline' | 'unknown';
-	avatarUrl: string;
-	avatarBlurhash: string;
-	avatarDecorations: {
-		id: ID;
-		url: string;
-		angle?: number;
-		flipH?: boolean;
-	}[];
-	emojis: {
-		name: string;
-		url: string;
-	}[];
-	instance?: {
-		name: Instance['name'];
-		softwareName: Instance['softwareName'];
-		softwareVersion: Instance['softwareVersion'];
-		iconUrl: Instance['iconUrl'];
-		faviconUrl: Instance['faviconUrl'];
-		themeColor: Instance['themeColor'];
-	};
-	isCat?: boolean;
-	isBot?: boolean;
-};
-
-export type UserDetailed = UserLite & {
-	alsoKnownAs: string[];
-	bannerBlurhash: string | null;
-	bannerColor: string | null;
-	bannerUrl: string | null;
-	birthday: string | null;
-	createdAt: DateString;
-	description: string | null;
-	ffVisibility: 'public' | 'followers' | 'private';
-	fields: {name: string; value: string}[];
-	verifiedLinks: string[];
-	followersCount: number;
-	followingCount: number;
-	hasPendingFollowRequestFromYou: boolean;
-	hasPendingFollowRequestToYou: boolean;
-	isAdmin: boolean;
-	isBlocked: boolean;
-	isBlocking: boolean;
-	isBot: boolean;
-	isCat: boolean;
-	isFollowed: boolean;
-	isFollowing: boolean;
-	isLocked: boolean;
-	isModerator: boolean;
-	isMuted: boolean;
-	isSilenced: boolean;
-	isSuspended: boolean;
-	lang: string | null;
-	lastFetchedAt?: DateString;
-	location: string | null;
-	movedTo: string;
-	notesCount: number;
-	pinnedNoteIds: ID[];
-	pinnedNotes: Note[];
-	pinnedPage: Page | null;
-	pinnedPageId: string | null;
-	publicReactions: boolean;
-	securityKeys: boolean;
-	twoFactorEnabled: boolean;
-	updatedAt: DateString | null;
-	uri: string | null;
-	url: string | null;
-	notify: 'normal' | 'none';
-};
-
-export type UserGroup = TODO;
-
-export type UserList = {
-	id: ID;
-	createdAt: DateString;
-	name: string;
-	userIds: User['id'][];
-};
-
-export type MeDetailed = UserDetailed & {
-	avatarId: DriveFile['id'];
-	bannerId: DriveFile['id'];
-	autoAcceptFollowed: boolean;
-	alwaysMarkNsfw: boolean;
-	carefulBot: boolean;
-	emailNotificationTypes: string[];
-	hasPendingReceivedFollowRequest: boolean;
-	hasUnreadAnnouncement: boolean;
-	hasUnreadAntenna: boolean;
-	hasUnreadMentions: boolean;
-	hasUnreadMessagingMessage: boolean;
-	hasUnreadNotification: boolean;
-	hasUnreadSpecifiedNotes: boolean;
-	unreadNotificationsCount: number;
-	hideOnlineStatus: boolean;
-	injectFeaturedNote: boolean;
-	integrations: Record<string, any>;
-	isDeleted: boolean;
-	isExplorable: boolean;
-	mutedWords: (string[] | string)[];
-	hardMutedWords: (string[] | string)[];
-	notificationRecieveConfig: {
-		[notificationType in typeof notificationTypes[number]]?: {
-			type: 'all';
-		} | {
-			type: 'never';
-		} | {
-			type: 'following';
-		} | {
-			type: 'follower';
-		} | {
-			type: 'mutualFollow';
-		} | {
-			type: 'list';
-			userListId: string;
-		};
-	};
-	noCrawle: boolean;
-	receiveAnnouncementEmail: boolean;
-	usePasswordLessLogin: boolean;
-	unreadAnnouncements: Announcement[];
-	twoFactorBackupCodesStock: 'full' | 'partial' | 'none';
-	[other: string]: any;
-};
-
-export type MeDetailedWithSecret = MeDetailed & {
-	email: string;
-	emailVerified: boolean;
-	securityKeysList: {
-		id: string;
-		name: string;
-		lastUsed: string;
-	}[];
-};
-
-export type MeSignup = MeDetailedWithSecret & {
-	token: string;
-};
-
-export type DriveFile = {
-	id: ID;
-	createdAt: DateString;
-	isSensitive: boolean;
-	name: string;
-	thumbnailUrl: string;
-	url: string;
-	type: string;
-	size: number;
-	md5: string;
-	blurhash: string;
-	comment: string | null;
-	properties: Record<string, any>;
-};
-
-export type DriveFolder = TODO;
-
-export type GalleryPost = {
-	id: ID;
-	createdAt: DateString;
-	updatedAt: DateString;
-	userId: User['id'];
-	user: User;
-	title: string;
-	description: string | null;
-	fileIds: DriveFile['id'][];
-	files: DriveFile[];
-	isSensitive: boolean;
-	likedCount: number;
-	isLiked?: boolean;
-};
-
-export type Note = {
-	id: ID;
-	createdAt: DateString;
-	text: string | null;
-	cw: string | null;
-	user: User;
-	userId: User['id'];
-	reply?: Note;
-	replyId: Note['id'];
-	renote?: Note;
-	renoteId: Note['id'];
-	files: DriveFile[];
-	fileIds: DriveFile['id'][];
-	visibility: 'public' | 'home' | 'followers' | 'specified';
-	visibleUserIds?: User['id'][];
-	channel?: Channel;
-	channelId?: Channel['id'];
-	localOnly?: boolean;
-	myReaction?: string;
-	reactions: Record<string, number>;
-	renoteCount: number;
-	repliesCount: number;
-	clippedCount?: number;
-	poll?: {
-		expiresAt: DateString | null;
-		multiple: boolean;
-		choices: {
-			isVoted: boolean;
-			text: string;
-			votes: number;
-		}[];
-	};
-	emojis: {
-		name: string;
-		url: string;
-	}[];
-	uri?: string;
-	url?: string;
-	isHidden?: boolean;
-};
-
-export type NoteReaction = {
-	id: ID;
-	createdAt: DateString;
-	user: UserLite;
-	type: string;
-};
-
-export type Notification = {
-	id: ID;
-	createdAt: DateString;
-	isRead: boolean;
-} & ({
-	type: 'reaction';
-	reaction: string;
-	user: User;
-	userId: User['id'];
-	note: Note;
-} | {
-	type: 'reply';
-	user: User;
-	userId: User['id'];
-	note: Note;
-} | {
-	type: 'renote';
-	user: User;
-	userId: User['id'];
-	note: Note;
-} | {
-	type: 'quote';
-	user: User;
-	userId: User['id'];
-	note: Note;
-} | {
-	type: 'mention';
-	user: User;
-	userId: User['id'];
-	note: Note;
-} | {
-	type: 'note';
-	user: User;
-	userId: User['id'];
-	note: Note;
-} | {
-	type: 'pollEnded';
-	user: User;
-	userId: User['id'];
-	note: Note;
-} | {
-	type: 'follow';
-	user: User;
-	userId: User['id'];
-} | {
-	type: 'followRequestAccepted';
-	user: User;
-	userId: User['id'];
-} | {
-	type: 'receiveFollowRequest';
-	user: User;
-	userId: User['id'];
-} | {
-	type: 'groupInvited';
-	invitation: UserGroup;
-	user: User;
-	userId: User['id'];
-} | {
-	type: 'achievementEarned';
-	achievement: string;
-} | {
-	type: 'app';
-	header?: string | null;
-	body: string;
-	icon?: string | null;
-} | {
-	type: 'test';
-});
-
-export type MessagingMessage = {
-	id: ID;
-	createdAt: DateString;
-	file: DriveFile | null;
-	fileId: DriveFile['id'] | null;
-	isRead: boolean;
-	reads: User['id'][];
-	text: string | null;
-	user: User;
-	userId: User['id'];
-	recipient?: User | null;
-	recipientId: User['id'] | null;
-	group?: UserGroup | null;
-	groupId: UserGroup['id'] | null;
-};
-
-export type CustomEmoji = {
-	id: string;
-	name: string;
-	url: string;
-	category: string;
-	aliases: string[];
-};
-
-export type LiteInstanceMetadata = {
-	maintainerName: string | null;
-	maintainerEmail: string | null;
-	version: string;
-	name: string | null;
-	shortName: string | null;
-	uri: string;
-	description: string | null;
-	langs: string[];
-	tosUrl: string | null;
-	repositoryUrl: string;
-	feedbackUrl: string;
-	impressumUrl: string | null;
-	privacyPolicyUrl: string | null;
-	disableRegistration: boolean;
-	disableLocalTimeline: boolean;
-	disableGlobalTimeline: boolean;
-	driveCapacityPerLocalUserMb: number;
-	driveCapacityPerRemoteUserMb: number;
-	emailRequiredForSignup: boolean;
-	enableHcaptcha: boolean;
-	hcaptchaSiteKey: string | null;
-	enableRecaptcha: boolean;
-	recaptchaSiteKey: string | null;
-	enableTurnstile: boolean;
-	turnstileSiteKey: string | null;
-	swPublickey: string | null;
-	themeColor: string | null;
-	mascotImageUrl: string | null;
-	bannerUrl: string | null;
-	serverErrorImageUrl: string | null;
-	infoImageUrl: string | null;
-	notFoundImageUrl: string | null;
-	iconUrl: string | null;
-	backgroundImageUrl: string | null;
-	logoImageUrl: string | null;
-	maxNoteTextLength: number;
-	enableEmail: boolean;
-	enableTwitterIntegration: boolean;
-	enableGithubIntegration: boolean;
-	enableDiscordIntegration: boolean;
-	enableServiceWorker: boolean;
-	emojis: CustomEmoji[];
-	defaultDarkTheme: string | null;
-	defaultLightTheme: string | null;
-	ads: {
-		id: ID;
-		ratio: number;
-		place: string;
-		url: string;
-		imageUrl: string;
-	}[];
-	notesPerOneAd: number;
-	translatorAvailable: boolean;
-	serverRules: string[];
-};
-
-export type DetailedInstanceMetadata = LiteInstanceMetadata & {
-	pinnedPages: string[];
-	pinnedClipId: string | null;
-	cacheRemoteFiles: boolean;
-	cacheRemoteSensitiveFiles: boolean;
-	requireSetup: boolean;
-	proxyAccountName: string | null;
-	features: Record<string, any>;
-};
-
-export type InstanceMetadata = LiteInstanceMetadata | DetailedInstanceMetadata;
-
-export type AdminInstanceMetadata = DetailedInstanceMetadata & {
-	// TODO: There are more fields.
-	blockedHosts: string[];
-	silencedHosts: string[];
-	app192IconUrl: string | null;
-	app512IconUrl: string | null;
-	manifestJsonOverride: string;
-};
-
-export type ServerInfo = {
-	machine: string;
-	cpu: {
-		model: string;
-		cores: number;
-	};
-	mem: {
-		total: number;
-	};
-	fs: {
-		total: number;
-		used: number;
-	};
-};
-
-export type Stats = {
-	notesCount: number;
-	originalNotesCount: number;
-	usersCount: number;
-	originalUsersCount: number;
-	instances: number;
-	driveUsageLocal: number;
-	driveUsageRemote: number;
-};
-
-export type Page = {
-	id: ID;
-	createdAt: DateString;
-	updatedAt: DateString;
-	userId: User['id'];
-	user: User;
-	content: Record<string, any>[];
-	variables: Record<string, any>[];
-	title: string;
-	name: string;
-	summary: string | null;
-	hideTitleWhenPinned: boolean;
-	alignCenter: boolean;
-	font: string;
-	script: string;
-	eyeCatchingImageId: DriveFile['id'] | null;
-	eyeCatchingImage: DriveFile | null;
-	attachedFiles: any;
-	likedCount: number;
-	isLiked?: boolean;
-};
-
 export type PageEvent = {
 	pageId: Page['id'];
 	event: string;
@@ -458,166 +14,6 @@ export type PageEvent = {
 	user: User;
 };
 
-export type Announcement = {
-	id: ID;
-	createdAt: DateString;
-	updatedAt: DateString | null;
-	text: string;
-	title: string;
-	imageUrl: string | null;
-	display: 'normal' | 'banner' | 'dialog';
-	icon: 'info' | 'warning' | 'error' | 'success';
-	needConfirmationToRead: boolean;
-	forYou: boolean;
-	isRead?: boolean;
-};
-
-export type Antenna = {
-	id: ID;
-	createdAt: DateString;
-	name: string;
-	keywords: string[][]; // TODO
-	excludeKeywords: string[][]; // TODO
-	src: 'home' | 'all' | 'users' | 'list' | 'group';
-	userListId: ID | null; // TODO
-	userGroupId: ID | null; // TODO
-	users: string[]; // TODO
-	caseSensitive: boolean;
-	localOnly: boolean;
-	notify: boolean;
-	withReplies: boolean;
-	withFile: boolean;
-	hasUnreadNote: boolean;
-};
-
-export type App = TODO;
-
-export type AuthSession = {
-	id: ID;
-	app: App;
-	token: string;
-};
-
-export type Ad = TODO;
-
-export type Clip = TODO;
-
-export type NoteFavorite = {
-	id: ID;
-	createdAt: DateString;
-	noteId: Note['id'];
-	note: Note;
-};
-
-export type FollowRequest = {
-	id: ID;
-	follower: User;
-	followee: User;
-};
-
-export type Channel = {
-	id: ID;
-	lastNotedAt: Date | null;
-	userId: User['id'] | null;
-	user: User | null;
-	name: string;
-	description: string | null;
-	bannerId: DriveFile['id'] | null;
-	banner: DriveFile | null;
-	pinnedNoteIds: string[];
-	color: string;
-	isArchived: boolean;
-	notesCount: number;
-	usersCount: number;
-	isSensitive: boolean;
-	allowRenoteToExternal: boolean;
-};
-
-export type Following = {
-	id: ID;
-	createdAt: DateString;
-	followerId: User['id'];
-	followeeId: User['id'];
-};
-
-export type FollowingFolloweePopulated = Following & {
-	followee: UserDetailed;
-};
-
-export type FollowingFollowerPopulated = Following & {
-	follower: UserDetailed;
-};
-
-export type Blocking = {
-	id: ID;
-	createdAt: DateString;
-	blockeeId: User['id'];
-	blockee: UserDetailed;
-};
-
-export type Instance = {
-	id: ID;
-	firstRetrievedAt: DateString;
-	host: string;
-	usersCount: number;
-	notesCount: number;
-	followingCount: number;
-	followersCount: number;
-	driveUsage: number;
-	driveFiles: number;
-	latestRequestSentAt: DateString | null;
-	latestStatus: number | null;
-	latestRequestReceivedAt: DateString | null;
-	lastCommunicatedAt: DateString;
-	isNotResponding: boolean;
-	isSuspended: boolean;
-	isSilenced: boolean;
-	isBlocked: boolean;
-	softwareName: string | null;
-	softwareVersion: string | null;
-	openRegistrations: boolean | null;
-	name: string | null;
-	description: string | null;
-	maintainerName: string | null;
-	maintainerEmail: string | null;
-	iconUrl: string | null;
-	faviconUrl: string | null;
-	themeColor: string | null;
-	infoUpdatedAt: DateString | null;
-};
-
-export type Signin = {
-	id: ID;
-	createdAt: DateString;
-	ip: string;
-	headers: Record<string, any>;
-	success: boolean;
-};
-
-export type Invite = {
-	id: ID;
-	code: string;
-	expiresAt: DateString | null;
-	createdAt: DateString;
-	createdBy: UserLite | null;
-	usedBy: UserLite | null;
-	usedAt: DateString | null;
-	used: boolean;
-}
-
-export type InviteLimit = {
-	remaining: number;
-}
-
-export type UserSorting =
-	| '+follower'
-	| '-follower'
-	| '+createdAt'
-	| '-createdAt'
-	| '+updatedAt'
-	| '-updatedAt';
-export type OriginType = 'combined' | 'local' | 'remote';
-
 export type ModerationLog = {
 	id: ID;
 	createdAt: DateString;
diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts
index 96ac7787e1..7981c280f6 100644
--- a/packages/misskey-js/src/streaming.types.ts
+++ b/packages/misskey-js/src/streaming.types.ts
@@ -1,4 +1,5 @@
-import type { Antenna, CustomEmoji, DriveFile, MeDetailed, MessagingMessage, Note, Notification, PageEvent, User, UserGroup } from './entities.js';
+import { Antenna, DriveFile, EmojiDetailed, MeDetailed, Note, User, Notification } from './autogen/models.js';
+import { PageEvent } from './entities.js';
 
 type FIXME = any;
 
@@ -22,9 +23,6 @@ export type Channels = {
 			readAllUnreadMentions: () => void;
 			unreadSpecifiedNote: (payload: Note['id']) => void;
 			readAllUnreadSpecifiedNotes: () => void;
-			readAllMessagingMessages: () => void;
-			messagingMessage: (payload: MessagingMessage) => void;
-			unreadMessagingMessage: (payload: MessagingMessage) => void;
 			readAllAntennas: () => void;
 			unreadAntenna: (payload: Antenna) => void;
 			readAllAnnouncements: () => void;
@@ -70,23 +68,6 @@ export type Channels = {
 		};
 		receives: null;
 	};
-	messaging: {
-		params: {
-			otherparty?: User['id'] | null;
-			group?: UserGroup['id'] | null;
-		};
-		events: {
-			message: (payload: MessagingMessage) => void;
-			deleted: (payload: MessagingMessage['id']) => void;
-			read: (payload: MessagingMessage['id'][]) => void;
-			typers: (payload: User[]) => void;
-		};
-		receives: {
-			read: {
-				id: MessagingMessage['id'];
-			};
-		};
-	};
 	serverStats: {
 		params: null;
 		events: {
@@ -145,6 +126,6 @@ export type NoteUpdatedEvent = {
 export type BroadcastEvents = {
 	noteUpdated: (payload: NoteUpdatedEvent) => void;
 	emojiAdded: (payload: {
-		emoji: CustomEmoji;
+		emoji: EmojiDetailed;
 	}) => void;
 };
diff --git a/packages/misskey-js/test-d/api.ts b/packages/misskey-js/test-d/api.ts
index ce793f6fd1..1c2a142e8b 100644
--- a/packages/misskey-js/test-d/api.ts
+++ b/packages/misskey-js/test-d/api.ts
@@ -8,7 +8,7 @@ describe('API', () => {
 			credential: 'TOKEN'
 		});
 		const res = await cli.request('meta', { detail: true });
-		expectType<Misskey.entities.DetailedInstanceMetadata>(res);
+		expectType<Misskey.entities.MetaResponse>(res);
 	});
 
 	test('conditional respose type (meta)', async () => {
@@ -18,16 +18,16 @@ describe('API', () => {
 		});
 
 		const res = await cli.request('meta', { detail: true });
-		expectType<Misskey.entities.DetailedInstanceMetadata>(res);
+		expectType<Misskey.entities.MetaResponse>(res);
 
 		const res2 = await cli.request('meta', { detail: false });
-		expectType<Misskey.entities.LiteInstanceMetadata>(res2);
+		expectType<Misskey.entities.MetaResponse>(res2);
 
 		const res3 = await cli.request('meta', { });
-		expectType<Misskey.entities.LiteInstanceMetadata>(res3);
+		expectType<Misskey.entities.MetaResponse>(res3);
 
 		const res4 = await cli.request('meta', { detail: true as boolean });
-		expectType<Misskey.entities.LiteInstanceMetadata | Misskey.entities.DetailedInstanceMetadata>(res4);
+		expectType<Misskey.entities.MetaResponse>(res4);
 	});
 
 	test('conditional respose type (users/show)', async () => {
diff --git a/packages/misskey-js/test-d/streaming.ts b/packages/misskey-js/test-d/streaming.ts
index 853f376e6c..6b186bd45a 100644
--- a/packages/misskey-js/test-d/streaming.ts
+++ b/packages/misskey-js/test-d/streaming.ts
@@ -9,18 +9,4 @@ describe('Streaming', () => {
 			expectType<Misskey.entities.Notification>(notification);
 		});
 	});
-
-	test('params type', async () => {
-		const stream = new Misskey.Stream('https://misskey.test', { token: 'TOKEN' });
-		// TODO: 「stream.useChannel の第二引数として受け入れる型が
-		// {
-		//   otherparty?: User['id'] | null;
-		//   group?: UserGroup['id'] | null;
-		// }
-		// になっている」というテストを行いたいけどtsdでの書き方がわからない
-		const messagingChannel = stream.useChannel('messaging', { otherparty: 'aaa' });
-		messagingChannel.on('message', message => {
-			expectType<Misskey.entities.MessagingMessage>(message);
-		});
-	});
 });
diff --git a/packages/misskey-js/tsconfig.json b/packages/misskey-js/tsconfig.json
index e2b755075b..f56b65e868 100644
--- a/packages/misskey-js/tsconfig.json
+++ b/packages/misskey-js/tsconfig.json
@@ -1,5 +1,5 @@
 {
-	"$schema": "http://json.schemastore.org/tsconfig",
+	"$schema": "https://json.schemastore.org/tsconfig",
 	"compilerOptions": {
 		"target": "ES2022",
 		"module": "nodenext",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 68d70ba4c1..8831c43036 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -50,6 +50,9 @@ importers:
       eslint:
         specifier: 8.54.0
         version: 8.54.0
+      ncp:
+        specifier: 2.0.0
+        version: 2.0.0
       start-server-and-test:
         specifier: 2.0.3
         version: 2.0.3
@@ -358,7 +361,7 @@ importers:
         version: 2.1.0
       summaly:
         specifier: github:misskey-dev/summaly
-        version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7
+        version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8
       systeminformation:
         specifier: 5.21.18
         version: 5.21.18
@@ -877,10 +880,10 @@ importers:
         version: 7.5.3
       '@storybook/vue3':
         specifier: 7.5.3
-        version: 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.9)
+        version: 7.5.3(@vue/compiler-core@3.3.9)(vue@3.3.9)
       '@storybook/vue3-vite':
         specifier: 7.5.3
-        version: 7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9)
+        version: 7.5.3(@vue/compiler-core@3.3.9)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9)
       '@testing-library/vue':
         specifier: 8.0.1
         version: 8.0.1(@vue/compiler-sfc@3.3.9)(vue@3.3.9)
@@ -988,7 +991,7 @@ importers:
         version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.5.3)(@storybook/components@7.5.3)(@storybook/core-events@7.5.3)(@storybook/manager-api@7.5.3)(@storybook/preview-api@7.5.3)(@storybook/theming@7.5.3)(@storybook/types@7.5.3)(react-dom@18.2.0)(react@18.2.0)
       summaly:
         specifier: github:misskey-dev/summaly
-        version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7
+        version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8
       vite-plugin-turbosnap:
         specifier: 1.0.3
         version: 1.0.3
@@ -1053,6 +1056,9 @@ importers:
       mock-socket:
         specifier: 9.3.1
         version: 9.3.1
+      ncp:
+        specifier: 2.0.0
+        version: 2.0.0
       tsd:
         specifier: 0.29.0
         version: 0.29.0
@@ -1060,6 +1066,39 @@ importers:
         specifier: 5.3.2
         version: 5.3.2
 
+  packages/misskey-js/generator:
+    devDependencies:
+      '@apidevtools/swagger-parser':
+        specifier: 10.1.0
+        version: 10.1.0(openapi-types@12.1.3)
+      '@types/node':
+        specifier: 20.9.1
+        version: 20.9.1
+      '@typescript-eslint/eslint-plugin':
+        specifier: 6.11.0
+        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2)
+      '@typescript-eslint/parser':
+        specifier: 6.11.0
+        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      eslint:
+        specifier: 8.53.0
+        version: 8.53.0
+      openapi-types:
+        specifier: 12.1.3
+        version: 12.1.3
+      openapi-typescript:
+        specifier: 6.7.1
+        version: 6.7.1
+      ts-case-convert:
+        specifier: 2.0.2
+        version: 2.0.2
+      tsx:
+        specifier: 4.4.0
+        version: 4.4.0
+      typescript:
+        specifier: 5.3.2
+        version: 5.3.2
+
   packages/sw:
     dependencies:
       esbuild:
@@ -1107,6 +1146,38 @@ packages:
       '@jridgewell/trace-mapping': 0.3.18
     dev: true
 
+  /@apidevtools/json-schema-ref-parser@9.0.6:
+    resolution: {integrity: sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==}
+    dependencies:
+      '@jsdevtools/ono': 7.1.3
+      call-me-maybe: 1.0.2
+      js-yaml: 3.14.1
+    dev: true
+
+  /@apidevtools/openapi-schemas@2.1.0:
+    resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /@apidevtools/swagger-methods@3.0.2:
+    resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==}
+    dev: true
+
+  /@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3):
+    resolution: {integrity: sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==}
+    peerDependencies:
+      openapi-types: '>=7'
+    dependencies:
+      '@apidevtools/json-schema-ref-parser': 9.0.6
+      '@apidevtools/openapi-schemas': 2.1.0
+      '@apidevtools/swagger-methods': 3.0.2
+      '@jsdevtools/ono': 7.1.3
+      ajv: 8.12.0
+      ajv-draft-04: 1.0.0(ajv@8.12.0)
+      call-me-maybe: 1.0.2
+      openapi-types: 12.1.3
+    dev: true
+
   /@aw-web-design/x-default-browser@1.4.126:
     resolution: {integrity: sha512-Xk1sIhyNC/esHGGVjL/niHLowM0csl/kFO5uawBy4IrWwy0o1G8LGt3jP6nmWGz+USxeeqbihAmp/oVZju6wug==}
     hasBin: true
@@ -3328,6 +3399,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/android-arm64@0.18.20:
+    resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/android-arm64@0.19.8:
     resolution: {integrity: sha512-B8JbS61bEunhfx8kasogFENgQfr/dIp+ggYXwTqdbMAgGDhRa3AaPpQMuQU0rNxDLECj6FhDzk1cF9WHMVwrtA==}
     engines: {node: '>=12'}
@@ -3345,6 +3425,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/android-arm@0.18.20:
+    resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/android-arm@0.19.8:
     resolution: {integrity: sha512-31E2lxlGM1KEfivQl8Yf5aYU/mflz9g06H6S15ITUFQueMFtFjESRMoDSkvMo8thYvLBax+VKTPlpnx+sPicOA==}
     engines: {node: '>=12'}
@@ -3362,6 +3451,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/android-x64@0.18.20:
+    resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/android-x64@0.19.8:
     resolution: {integrity: sha512-rdqqYfRIn4jWOp+lzQttYMa2Xar3OK9Yt2fhOhzFXqg0rVWEfSclJvZq5fZslnz6ypHvVf3CT7qyf0A5pM682A==}
     engines: {node: '>=12'}
@@ -3379,6 +3477,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/darwin-arm64@0.18.20:
+    resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/darwin-arm64@0.19.8:
     resolution: {integrity: sha512-RQw9DemMbIq35Bprbboyf8SmOr4UXsRVxJ97LgB55VKKeJOOdvsIPy0nFyF2l8U+h4PtBx/1kRf0BelOYCiQcw==}
     engines: {node: '>=12'}
@@ -3396,6 +3503,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/darwin-x64@0.18.20:
+    resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/darwin-x64@0.19.8:
     resolution: {integrity: sha512-3sur80OT9YdeZwIVgERAysAbwncom7b4bCI2XKLjMfPymTud7e/oY4y+ci1XVp5TfQp/bppn7xLw1n/oSQY3/Q==}
     engines: {node: '>=12'}
@@ -3413,6 +3529,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/freebsd-arm64@0.18.20:
+    resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/freebsd-arm64@0.19.8:
     resolution: {integrity: sha512-WAnPJSDattvS/XtPCTj1tPoTxERjcTpH6HsMr6ujTT+X6rylVe8ggxk8pVxzf5U1wh5sPODpawNicF5ta/9Tmw==}
     engines: {node: '>=12'}
@@ -3430,6 +3555,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/freebsd-x64@0.18.20:
+    resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/freebsd-x64@0.19.8:
     resolution: {integrity: sha512-ICvZyOplIjmmhjd6mxi+zxSdpPTKFfyPPQMQTK/w+8eNK6WV01AjIztJALDtwNNfFhfZLux0tZLC+U9nSyA5Zg==}
     engines: {node: '>=12'}
@@ -3447,6 +3581,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/linux-arm64@0.18.20:
+    resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/linux-arm64@0.19.8:
     resolution: {integrity: sha512-z1zMZivxDLHWnyGOctT9JP70h0beY54xDDDJt4VpTX+iwA77IFsE1vCXWmprajJGa+ZYSqkSbRQ4eyLCpCmiCQ==}
     engines: {node: '>=12'}
@@ -3464,6 +3607,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/linux-arm@0.18.20:
+    resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/linux-arm@0.19.8:
     resolution: {integrity: sha512-H4vmI5PYqSvosPaTJuEppU9oz1dq2A7Mr2vyg5TF9Ga+3+MGgBdGzcyBP7qK9MrwFQZlvNyJrvz6GuCaj3OukQ==}
     engines: {node: '>=12'}
@@ -3481,6 +3633,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/linux-ia32@0.18.20:
+    resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/linux-ia32@0.19.8:
     resolution: {integrity: sha512-1a8suQiFJmZz1khm/rDglOc8lavtzEMRo0v6WhPgxkrjcU0LkHj+TwBrALwoz/OtMExvsqbbMI0ChyelKabSvQ==}
     engines: {node: '>=12'}
@@ -3498,6 +3659,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/linux-loong64@0.18.20:
+    resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/linux-loong64@0.19.8:
     resolution: {integrity: sha512-fHZWS2JJxnXt1uYJsDv9+b60WCc2RlvVAy1F76qOLtXRO+H4mjt3Tr6MJ5l7Q78X8KgCFudnTuiQRBhULUyBKQ==}
     engines: {node: '>=12'}
@@ -3515,6 +3685,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/linux-mips64el@0.18.20:
+    resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/linux-mips64el@0.19.8:
     resolution: {integrity: sha512-Wy/z0EL5qZYLX66dVnEg9riiwls5IYnziwuju2oUiuxVc+/edvqXa04qNtbrs0Ukatg5HEzqT94Zs7J207dN5Q==}
     engines: {node: '>=12'}
@@ -3532,6 +3711,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/linux-ppc64@0.18.20:
+    resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/linux-ppc64@0.19.8:
     resolution: {integrity: sha512-ETaW6245wK23YIEufhMQ3HSeHO7NgsLx8gygBVldRHKhOlD1oNeNy/P67mIh1zPn2Hr2HLieQrt6tWrVwuqrxg==}
     engines: {node: '>=12'}
@@ -3549,6 +3737,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/linux-riscv64@0.18.20:
+    resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/linux-riscv64@0.19.8:
     resolution: {integrity: sha512-T2DRQk55SgoleTP+DtPlMrxi/5r9AeFgkhkZ/B0ap99zmxtxdOixOMI570VjdRCs9pE4Wdkz7JYrsPvsl7eESg==}
     engines: {node: '>=12'}
@@ -3566,6 +3763,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/linux-s390x@0.18.20:
+    resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/linux-s390x@0.19.8:
     resolution: {integrity: sha512-NPxbdmmo3Bk7mbNeHmcCd7R7fptJaczPYBaELk6NcXxy7HLNyWwCyDJ/Xx+/YcNH7Im5dHdx9gZ5xIwyliQCbg==}
     engines: {node: '>=12'}
@@ -3583,6 +3789,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/linux-x64@0.18.20:
+    resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/linux-x64@0.19.8:
     resolution: {integrity: sha512-lytMAVOM3b1gPypL2TRmZ5rnXl7+6IIk8uB3eLsV1JwcizuolblXRrc5ShPrO9ls/b+RTp+E6gbsuLWHWi2zGg==}
     engines: {node: '>=12'}
@@ -3600,6 +3815,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/netbsd-x64@0.18.20:
+    resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/netbsd-x64@0.19.8:
     resolution: {integrity: sha512-hvWVo2VsXz/8NVt1UhLzxwAfo5sioj92uo0bCfLibB0xlOmimU/DeAEsQILlBQvkhrGjamP0/el5HU76HAitGw==}
     engines: {node: '>=12'}
@@ -3617,6 +3841,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/openbsd-x64@0.18.20:
+    resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/openbsd-x64@0.19.8:
     resolution: {integrity: sha512-/7Y7u77rdvmGTxR83PgaSvSBJCC2L3Kb1M/+dmSIvRvQPXXCuC97QAwMugBNG0yGcbEGfFBH7ojPzAOxfGNkwQ==}
     engines: {node: '>=12'}
@@ -3634,6 +3867,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/sunos-x64@0.18.20:
+    resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/sunos-x64@0.19.8:
     resolution: {integrity: sha512-9Lc4s7Oi98GqFA4HzA/W2JHIYfnXbUYgekUP/Sm4BG9sfLjyv6GKKHKKVs83SMicBF2JwAX6A1PuOLMqpD001w==}
     engines: {node: '>=12'}
@@ -3651,6 +3893,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/win32-arm64@0.18.20:
+    resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/win32-arm64@0.19.8:
     resolution: {integrity: sha512-rq6WzBGjSzihI9deW3fC2Gqiak68+b7qo5/3kmB6Gvbh/NYPA0sJhrnp7wgV4bNwjqM+R2AApXGxMO7ZoGhIJg==}
     engines: {node: '>=12'}
@@ -3668,6 +3919,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/win32-ia32@0.18.20:
+    resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/win32-ia32@0.19.8:
     resolution: {integrity: sha512-AIAbverbg5jMvJznYiGhrd3sumfwWs8572mIJL5NQjJa06P8KfCPWZQ0NwZbPQnbQi9OWSZhFVSUWjjIrn4hSw==}
     engines: {node: '>=12'}
@@ -3685,6 +3945,15 @@ packages:
     dev: true
     optional: true
 
+  /@esbuild/win32-x64@0.18.20:
+    resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
   /@esbuild/win32-x64@0.19.8:
     resolution: {integrity: sha512-bfZ0cQ1uZs2PqpulNL5j/3w+GDhP36k1K5c38QdQg+Swy51jFZWWeIkteNsufkQxp986wnqRRsb/bHbY1WQ7TA==}
     engines: {node: '>=12'}
@@ -3693,6 +3962,16 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@eslint-community/eslint-utils@4.4.0(eslint@8.53.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.53.0
+      eslint-visitor-keys: 3.4.3
+    dev: true
+
   /@eslint-community/eslint-utils@4.4.0(eslint@8.54.0):
     resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -3725,6 +4004,11 @@ packages:
       - supports-color
     dev: true
 
+  /@eslint/js@8.53.0:
+    resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dev: true
+
   /@eslint/js@8.54.0:
     resolution: {integrity: sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -3761,6 +4045,11 @@ packages:
       text-decoding: 1.0.0
     dev: false
 
+  /@fastify/busboy@2.1.0:
+    resolution: {integrity: sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==}
+    engines: {node: '>=14'}
+    dev: true
+
   /@fastify/cookie@9.2.0:
     resolution: {integrity: sha512-fkg1yjjQRHPFAxSHeLC8CqYuNzvR6Lwlj/KjrzQcGjNBK+K82nW+UfCjfN71g1GkoVoc1GTOgIWkFJpcMfMkHQ==}
     dependencies:
@@ -4305,6 +4594,10 @@ packages:
       '@jridgewell/resolve-uri': 3.1.0
       '@jridgewell/sourcemap-codec': 1.4.14
 
+  /@jsdevtools/ono@7.1.3:
+    resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
+    dev: true
+
   /@juggle/resize-observer@3.4.0:
     resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==}
     dev: true
@@ -6924,7 +7217,7 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.5.3(@vue/compiler-core@3.3.8)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9):
+  /@storybook/vue3-vite@7.5.3(@vue/compiler-core@3.3.9)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9):
     resolution: {integrity: sha512-gkNwDDn2AKthAtaoPrHb0+2gi33UluxpfSq/M5COoMEVFphj6y/jyDa+OEYlceXgnD8g2xvX4/yv2TbTNDzmcQ==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
@@ -6934,7 +7227,7 @@ packages:
     dependencies:
       '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@5.0.2)
       '@storybook/core-server': 7.5.3
-      '@storybook/vue3': 7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.9)
+      '@storybook/vue3': 7.5.3(@vue/compiler-core@3.3.9)(vue@3.3.9)
       '@vitejs/plugin-vue': 4.5.0(vite@5.0.2)(vue@3.3.9)
       magic-string: 0.30.5
       react: 18.2.0
@@ -6953,7 +7246,7 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.5.3(@vue/compiler-core@3.3.8)(vue@3.3.9):
+  /@storybook/vue3@7.5.3(@vue/compiler-core@3.3.9)(vue@3.3.9):
     resolution: {integrity: sha512-JaxtOl3UD9YhPrOqHuKtpqHMnFril3sBUxx/no2yM/mZYmNpAVd/C6PFM839WCay1mAywPuUoebJvmwWxWijkw==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
@@ -6965,7 +7258,7 @@ packages:
       '@storybook/global': 5.0.0
       '@storybook/preview-api': 7.5.3
       '@storybook/types': 7.5.3
-      '@vue/compiler-core': 3.3.8
+      '@vue/compiler-core': 3.3.9
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
@@ -6982,7 +7275,7 @@ packages:
     hasBin: true
     peerDependencies:
       '@swc/core': ^1.2.66
-      chokidar: 3.5.3
+      chokidar: ^3.5.1
     peerDependenciesMeta:
       chokidar:
         optional: true
@@ -7832,6 +8125,12 @@ packages:
     dependencies:
       undici-types: 5.26.5
 
+  /@types/node@20.9.1:
+    resolution: {integrity: sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==}
+    dependencies:
+      undici-types: 5.26.5
+    dev: true
+
   /@types/nodemailer@6.4.14:
     resolution: {integrity: sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==}
     dependencies:
@@ -8091,6 +8390,35 @@ packages:
     dev: true
     optional: true
 
+  /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
+      eslint: ^7.0.0 || ^8.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@eslint-community/regexpp': 4.6.2
+      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      '@typescript-eslint/scope-manager': 6.11.0
+      '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      '@typescript-eslint/visitor-keys': 6.11.0
+      debug: 4.3.4(supports-color@8.1.1)
+      eslint: 8.53.0
+      graphemer: 1.4.0
+      ignore: 5.2.4
+      natural-compare: 1.4.0
+      semver: 7.5.4
+      ts-api-utils: 1.0.1(typescript@5.3.2)
+      typescript: 5.3.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /@typescript-eslint/eslint-plugin@6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2):
     resolution: {integrity: sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==}
     engines: {node: ^16.0.0 || >=18.0.0}
@@ -8120,6 +8448,27 @@ packages:
       - supports-color
     dev: true
 
+  /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      eslint: ^7.0.0 || ^8.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/scope-manager': 6.11.0
+      '@typescript-eslint/types': 6.11.0
+      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
+      '@typescript-eslint/visitor-keys': 6.11.0
+      debug: 4.3.4(supports-color@8.1.1)
+      eslint: 8.53.0
+      typescript: 5.3.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /@typescript-eslint/parser@6.12.0(eslint@8.54.0)(typescript@5.3.2):
     resolution: {integrity: sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==}
     engines: {node: ^16.0.0 || >=18.0.0}
@@ -8141,6 +8490,14 @@ packages:
       - supports-color
     dev: true
 
+  /@typescript-eslint/scope-manager@6.11.0:
+    resolution: {integrity: sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    dependencies:
+      '@typescript-eslint/types': 6.11.0
+      '@typescript-eslint/visitor-keys': 6.11.0
+    dev: true
+
   /@typescript-eslint/scope-manager@6.12.0:
     resolution: {integrity: sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==}
     engines: {node: ^16.0.0 || >=18.0.0}
@@ -8149,6 +8506,26 @@ packages:
       '@typescript-eslint/visitor-keys': 6.12.0
     dev: true
 
+  /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      eslint: ^7.0.0 || ^8.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
+      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      debug: 4.3.4(supports-color@8.1.1)
+      eslint: 8.53.0
+      ts-api-utils: 1.0.1(typescript@5.3.2)
+      typescript: 5.3.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /@typescript-eslint/type-utils@6.12.0(eslint@8.54.0)(typescript@5.3.2):
     resolution: {integrity: sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==}
     engines: {node: ^16.0.0 || >=18.0.0}
@@ -8169,11 +8546,37 @@ packages:
       - supports-color
     dev: true
 
+  /@typescript-eslint/types@6.11.0:
+    resolution: {integrity: sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    dev: true
+
   /@typescript-eslint/types@6.12.0:
     resolution: {integrity: sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
+  /@typescript-eslint/typescript-estree@6.11.0(typescript@5.3.2):
+    resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/types': 6.11.0
+      '@typescript-eslint/visitor-keys': 6.11.0
+      debug: 4.3.4(supports-color@8.1.1)
+      globby: 11.1.0
+      is-glob: 4.0.3
+      semver: 7.5.4
+      ts-api-utils: 1.0.1(typescript@5.3.2)
+      typescript: 5.3.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /@typescript-eslint/typescript-estree@6.12.0(typescript@5.3.2):
     resolution: {integrity: sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==}
     engines: {node: ^16.0.0 || >=18.0.0}
@@ -8195,6 +8598,25 @@ packages:
       - supports-color
     dev: true
 
+  /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==}
+    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.53.0)
+      '@types/json-schema': 7.0.12
+      '@types/semver': 7.5.6
+      '@typescript-eslint/scope-manager': 6.11.0
+      '@typescript-eslint/types': 6.11.0
+      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
+      eslint: 8.53.0
+      semver: 7.5.4
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+    dev: true
+
   /@typescript-eslint/utils@6.12.0(eslint@8.54.0)(typescript@5.3.2):
     resolution: {integrity: sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
@@ -8214,6 +8636,14 @@ packages:
       - typescript
     dev: true
 
+  /@typescript-eslint/visitor-keys@6.11.0:
+    resolution: {integrity: sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==}
+    engines: {node: ^16.0.0 || >=18.0.0}
+    dependencies:
+      '@typescript-eslint/types': 6.11.0
+      eslint-visitor-keys: 3.4.3
+    dev: true
+
   /@typescript-eslint/visitor-keys@6.12.0:
     resolution: {integrity: sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==}
     engines: {node: ^16.0.0 || >=18.0.0}
@@ -8392,6 +8822,7 @@ packages:
       '@vue/shared': 3.3.8
       estree-walker: 2.0.2
       source-map-js: 1.0.2
+    dev: false
 
   /@vue/compiler-core@3.3.9:
     resolution: {integrity: sha512-+/Lf68Vr/nFBA6ol4xOtJrW+BQWv3QWKfRwGSm70jtXwfhZNF4R/eRgyVJYoxFRhdCTk/F6g99BP0ffPgZihfQ==}
@@ -8506,6 +8937,7 @@ packages:
 
   /@vue/shared@3.3.8:
     resolution: {integrity: sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==}
+    dev: false
 
   /@vue/shared@3.3.9:
     resolution: {integrity: sha512-ZE0VTIR0LmYgeyhurPTpy4KzKsuDyQbMSdM49eKkMnT5X4VfFBLysMzjIZhLEFQYjjOVVfbvUDHckwjDFiO2eA==}
@@ -8678,6 +9110,17 @@ packages:
       clean-stack: 2.2.0
       indent-string: 4.0.0
 
+  /ajv-draft-04@1.0.0(ajv@8.12.0):
+    resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==}
+    peerDependencies:
+      ajv: ^8.5.0
+    peerDependenciesMeta:
+      ajv:
+        optional: true
+    dependencies:
+      ajv: 8.12.0
+    dev: true
+
   /ajv-formats@2.1.1(ajv@8.12.0):
     resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
     peerDependencies:
@@ -8704,7 +9147,6 @@ packages:
       json-schema-traverse: 1.0.0
       require-from-string: 2.0.2
       uri-js: 4.4.1
-    dev: false
 
   /ansi-colors@4.1.3:
     resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
@@ -9538,6 +9980,10 @@ packages:
       function-bind: 1.1.1
       get-intrinsic: 1.2.0
 
+  /call-me-maybe@1.0.2:
+    resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
+    dev: true
+
   /callsites@3.1.0:
     resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
     engines: {node: '>=6'}
@@ -9773,7 +10219,7 @@ packages:
       normalize-path: 3.0.0
       readdirp: 3.6.0
     optionalDependencies:
-      fsevents: 2.3.2
+      fsevents: 2.3.3
 
   /chownr@1.1.4:
     resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
@@ -11060,6 +11506,36 @@ packages:
       '@esbuild/win32-x64': 0.18.17
     dev: true
 
+  /esbuild@0.18.20:
+    resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    optionalDependencies:
+      '@esbuild/android-arm': 0.18.20
+      '@esbuild/android-arm64': 0.18.20
+      '@esbuild/android-x64': 0.18.20
+      '@esbuild/darwin-arm64': 0.18.20
+      '@esbuild/darwin-x64': 0.18.20
+      '@esbuild/freebsd-arm64': 0.18.20
+      '@esbuild/freebsd-x64': 0.18.20
+      '@esbuild/linux-arm': 0.18.20
+      '@esbuild/linux-arm64': 0.18.20
+      '@esbuild/linux-ia32': 0.18.20
+      '@esbuild/linux-loong64': 0.18.20
+      '@esbuild/linux-mips64el': 0.18.20
+      '@esbuild/linux-ppc64': 0.18.20
+      '@esbuild/linux-riscv64': 0.18.20
+      '@esbuild/linux-s390x': 0.18.20
+      '@esbuild/linux-x64': 0.18.20
+      '@esbuild/netbsd-x64': 0.18.20
+      '@esbuild/openbsd-x64': 0.18.20
+      '@esbuild/sunos-x64': 0.18.20
+      '@esbuild/win32-arm64': 0.18.20
+      '@esbuild/win32-ia32': 0.18.20
+      '@esbuild/win32-x64': 0.18.20
+    dev: true
+
   /esbuild@0.19.8:
     resolution: {integrity: sha512-l7iffQpT2OrZfH2rXIp7/FkmaeZM0vxbxN9KfiCwGYuZqzMg/JdvX26R31Zxn/Pxvsrg3Y9N6XTcnknqDyyv4w==}
     engines: {node: '>=12'}
@@ -11253,6 +11729,53 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
+  /eslint@8.53.0:
+    resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    hasBin: true
+    dependencies:
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
+      '@eslint-community/regexpp': 4.6.2
+      '@eslint/eslintrc': 2.1.3
+      '@eslint/js': 8.53.0
+      '@humanwhocodes/config-array': 0.11.13
+      '@humanwhocodes/module-importer': 1.0.1
+      '@nodelib/fs.walk': 1.2.8
+      '@ungap/structured-clone': 1.2.0
+      ajv: 6.12.6
+      chalk: 4.1.2
+      cross-spawn: 7.0.3
+      debug: 4.3.4(supports-color@8.1.1)
+      doctrine: 3.0.0
+      escape-string-regexp: 4.0.0
+      eslint-scope: 7.2.2
+      eslint-visitor-keys: 3.4.3
+      espree: 9.6.1
+      esquery: 1.4.2
+      esutils: 2.0.3
+      fast-deep-equal: 3.1.3
+      file-entry-cache: 6.0.1
+      find-up: 5.0.0
+      glob-parent: 6.0.2
+      globals: 13.19.0
+      graphemer: 1.4.0
+      ignore: 5.2.4
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      is-path-inside: 3.0.3
+      js-yaml: 4.1.0
+      json-stable-stringify-without-jsonify: 1.0.1
+      levn: 0.4.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.2
+      natural-compare: 1.4.0
+      optionator: 0.9.3
+      strip-ansi: 6.0.1
+      text-table: 0.2.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /eslint@8.54.0:
     resolution: {integrity: sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -12020,13 +12543,6 @@ packages:
   /fs.realpath@1.0.0:
     resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
 
-  /fsevents@2.3.2:
-    resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
-    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
-    os: [darwin]
-    requiresBuild: true
-    optional: true
-
   /fsevents@2.3.3:
     resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
     engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -12164,6 +12680,12 @@ packages:
       get-intrinsic: 1.2.1
     dev: true
 
+  /get-tsconfig@4.7.2:
+    resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
+    dependencies:
+      resolve-pkg-maps: 1.0.0
+    dev: true
+
   /getos@3.2.1:
     resolution: {integrity: sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==}
     dependencies:
@@ -13557,7 +14079,7 @@ packages:
       micromatch: 4.0.5
       walker: 1.0.8
     optionalDependencies:
-      fsevents: 2.3.2
+      fsevents: 2.3.3
     dev: true
 
   /jest-leak-detector@29.7.0:
@@ -14004,7 +14526,6 @@ packages:
 
   /json-schema-traverse@1.0.0:
     resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
-    dev: false
 
   /json-schema@0.4.0:
     resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
@@ -14950,6 +15471,11 @@ packages:
     resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
     dev: true
 
+  /ncp@2.0.0:
+    resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==}
+    hasBin: true
+    dev: true
+
   /ndarray-ops@1.2.2:
     resolution: {integrity: sha512-BppWAFRjMYF7N/r6Ie51q6D4fs0iiGmeXIACKY66fLpnwIui3Wc3CXiD/30mgLbDjPpSLrsqcp3Z62+IcHZsDw==}
     dependencies:
@@ -15422,7 +15948,18 @@ packages:
 
   /openapi-types@12.1.3:
     resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
-    dev: false
+
+  /openapi-typescript@6.7.1:
+    resolution: {integrity: sha512-Q3Ltt0KUm2smcPrsaR8qKmSwQ1KM4yGDJVoQdpYa0yvKPeN8huDx5utMT7DvwvJastHHzUxajjivK3WN2+fobg==}
+    hasBin: true
+    dependencies:
+      ansi-colors: 4.1.3
+      fast-glob: 3.3.2
+      js-yaml: 4.1.0
+      supports-color: 9.4.0
+      undici: 5.28.1
+      yargs-parser: 21.1.1
+    dev: true
 
   /opentype.js@0.4.11:
     resolution: {integrity: sha512-GthxucX/6aftfLdeU5Ho7o7zmQcC8uVtqdcelVq12X++ndxwBZG8Xb5rFEKT7nEcWDD2P1x+TNuJ70jtj1Mbpw==}
@@ -17217,7 +17754,6 @@ packages:
   /require-from-string@2.0.2:
     resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
     engines: {node: '>=0.10.0'}
-    dev: false
 
   /require-main-filename@2.0.0:
     resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
@@ -17246,6 +17782,10 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /resolve-pkg-maps@1.0.0:
+    resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+    dev: true
+
   /resolve.exports@2.0.0:
     resolution: {integrity: sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==}
     engines: {node: '>=10'}
@@ -17345,7 +17885,7 @@ packages:
     engines: {node: '>=14.18.0', npm: '>=8.0.0'}
     hasBin: true
     optionalDependencies:
-      fsevents: 2.3.2
+      fsevents: 2.3.3
     dev: true
 
   /rollup@4.6.0:
@@ -17365,7 +17905,7 @@ packages:
       '@rollup/rollup-win32-arm64-msvc': 4.6.0
       '@rollup/rollup-win32-ia32-msvc': 4.6.0
       '@rollup/rollup-win32-x64-msvc': 4.6.0
-      fsevents: 2.3.2
+      fsevents: 2.3.3
 
   /rrweb-cssom@0.6.0:
     resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==}
@@ -18293,6 +18833,11 @@ packages:
     dependencies:
       has-flag: 4.0.0
 
+  /supports-color@9.4.0:
+    resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==}
+    engines: {node: '>=12'}
+    dev: true
+
   /supports-hyperlinks@2.3.0:
     resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
     engines: {node: '>=8'}
@@ -18638,6 +19183,10 @@ packages:
       typescript: 5.3.2
     dev: true
 
+  /ts-case-convert@2.0.2:
+    resolution: {integrity: sha512-vdKfx1VAdpvEBOBv5OpVu5ZFqRg9HdTI4sYt6qqMeICBeNyXvitrarCnFWNDAki51IKwCyx+ZssY46Q9jH5otA==}
+    dev: true
+
   /ts-dedent@2.2.0:
     resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
     engines: {node: '>=6.10'}
@@ -18707,6 +19256,17 @@ packages:
   /tslib@2.6.2:
     resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
 
+  /tsx@4.4.0:
+    resolution: {integrity: sha512-4fwcEjRUxW20ciSaMB8zkpGwCPxuRGnadDuj/pBk5S9uT29zvWz15PK36GrKJo45mSJomDxVejZ73c6lr3811Q==}
+    engines: {node: '>=18.0.0'}
+    hasBin: true
+    dependencies:
+      esbuild: 0.18.20
+      get-tsconfig: 4.7.2
+    optionalDependencies:
+      fsevents: 2.3.3
+    dev: true
+
   /tunnel-agent@0.6.0:
     resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
     dependencies:
@@ -18957,6 +19517,13 @@ packages:
       busboy: 1.6.0
     dev: false
 
+  /undici@5.28.1:
+    resolution: {integrity: sha512-xcIIvj1LOQH9zAL54iWFkuDEaIVEjLrru7qRpa3GrEEHk6OBhb/LycuUY2m7VCcTuDeLziXCxobQVyKExyGeIA==}
+    engines: {node: '>=14.0'}
+    dependencies:
+      '@fastify/busboy': 2.1.0
+    dev: true
+
   /unicode-canonical-property-names-ecmascript@2.0.0:
     resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==}
     engines: {node: '>=4'}
@@ -19999,8 +20566,8 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7:
-    resolution: {tarball: https://codeload.github.com/misskey-dev/summaly/tar.gz/d2d8db49943ccb201c1b1b283e9d0a630519fac7}
+  github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8:
+    resolution: {tarball: https://codeload.github.com/misskey-dev/summaly/tar.gz/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8}
     name: summaly
     version: 4.0.2
     dependencies:
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index ead1764a56..3b2ecec7fd 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -3,3 +3,4 @@ packages:
  - 'packages/frontend'
  - 'packages/sw'
  - 'packages/misskey-js'
+ - 'packages/misskey-js/generator'

From f0fe8eceaf863ca66a2744cabcbbca7407592124 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Sat, 2 Dec 2023 13:09:25 +0100
Subject: [PATCH 096/435] upd: add option to select between note designs

Adds the ability to choose between `Sharkey` or `Misskey`
---
 .../src/components/MkInstanceTicker.vue       |   49 +-
 packages/frontend/src/components/MkNote.vue   |   49 +-
 .../src/components/MkNoteDetailed.vue         |   69 +-
 .../frontend/src/components/MkNoteHeader.vue  |  153 +--
 .../frontend/src/components/MkNoteSimple.vue  |    2 +-
 .../frontend/src/components/MkNoteSub.vue     |   80 +-
 packages/frontend/src/components/MkNotes.vue  |   20 +-
 .../src/components/SkInstanceTicker.vue       |   82 ++
 packages/frontend/src/components/SkNote.vue   | 1201 +++++++++++++++++
 .../src/components/SkNoteDetailed.vue         | 1082 +++++++++++++++
 .../frontend/src/components/SkNoteHeader.vue  |  265 ++++
 .../frontend/src/components/SkNoteSimple.vue  |  113 ++
 .../frontend/src/components/SkNoteSub.vue     |  545 ++++++++
 packages/frontend/src/pages/note.vue          |    7 +-
 .../frontend/src/pages/settings/general.vue   |    6 +
 packages/frontend/src/store.ts                |    4 +
 16 files changed, 3404 insertions(+), 323 deletions(-)
 create mode 100644 packages/frontend/src/components/SkInstanceTicker.vue
 create mode 100644 packages/frontend/src/components/SkNote.vue
 create mode 100644 packages/frontend/src/components/SkNoteDetailed.vue
 create mode 100644 packages/frontend/src/components/SkNoteHeader.vue
 create mode 100644 packages/frontend/src/components/SkNoteSimple.vue
 create mode 100644 packages/frontend/src/components/SkNoteSub.vue

diff --git a/packages/frontend/src/components/MkInstanceTicker.vue b/packages/frontend/src/components/MkInstanceTicker.vue
index 4e2856388e..f0650e48f1 100644
--- a/packages/frontend/src/components/MkInstanceTicker.vue
+++ b/packages/frontend/src/components/MkInstanceTicker.vue
@@ -35,48 +35,51 @@ const faviconUrl = $computed(() => props.instance ? getProxiedImageUrlNullable(p
 const themeColor = instance.themeColor ?? '#777777';
 
 const bg = {
-	//background: `linear-gradient(90deg, ${themeColor}, ${themeColor}00)`,
-	background: `${themeColor}`,
+	background: `linear-gradient(90deg, ${themeColor}, ${themeColor}00)`,
 };
 </script>
 
 <style lang="scss" module>
+$height: 2ex;
+
 .root {
 	display: flex;
 	align-items: center;
-	height: 1.5ex;
-	border-radius: var(--radius-xl);
-	margin-top: 5px;
-	padding: 4px;
+	height: $height;
+	border-radius: var(--radius-xs) 0 0 var(--radius-xs);
 	overflow: clip;
 	color: #fff;
-	text-shadow: -1px -1px 0 var(--bg),1px -1px 0 var(--bg),-1px 1px 0 var(--bg),1px 1px 0 var(--bg)
+	text-shadow: /* .866 ≈ sin(60deg) */
+		1px 0 1px #000,
+		.866px .5px 1px #000,
+		.5px .866px 1px #000,
+		0 1px 1px #000,
+		-.5px .866px 1px #000,
+		-.866px .5px 1px #000,
+		-1px 0 1px #000,
+		-.866px -.5px 1px #000,
+		-.5px -.866px 1px #000,
+		0 -1px 1px #000,
+		.5px -.866px 1px #000,
+		.866px -.5px 1px #000;
+	mask-image: linear-gradient(90deg,
+		rgb(0,0,0),
+		rgb(0,0,0) calc(100% - 16px),
+		rgba(0,0,0,0) 100%
+	);
 }
 
 .icon {
-	height: 2ex;
+	height: $height;
 	flex-shrink: 0;
 }
 
 .name {
 	margin-left: 4px;
 	line-height: 1;
-	font-size: 0.8em;
+	font-size: 0.9em;
 	font-weight: bold;
 	white-space: nowrap;
-	overflow: hidden;
-	overflow-wrap: anywhere;
-	max-width: 300px;
-	text-overflow: ellipsis;
-
-	&::-webkit-scrollbar {
-		display: none;
-	}
-}
-
-@container (max-width: 400px) {
-	.name {
-		max-width: 50px;
-	}
+	overflow: visible;
 }
 </style>
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 869164273e..4da8f16df1 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -47,14 +47,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :nyaize="'respect'" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/>
 	</div>
 	<article v-else :class="$style.article" @contextmenu.stop="onContextmenu">
-		<div style="display: flex; padding-bottom: 10px;">
-			<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
-			<MkAvatar :class="[$style.avatar, { [$style.avatarReplyTo]: appearNote.reply }]" :user="appearNote.user" :link="!mock" :preview="!mock"/>
-			<div :class="$style.main">
-				<MkNoteHeader :note="appearNote" :mini="true"/>
-			</div>
-		</div>
-		<div :class="[{ [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined">
+		<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
+		<MkAvatar :class="$style.avatar" :user="appearNote.user" :link="!mock" :preview="!mock"/>
+		<div :class="[$style.main, { [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined">
+			<MkNoteHeader :note="appearNote" :mini="true" v-on:click.stop/>
+			<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
 			<div style="container-type: inline-size;">
 				<p v-if="appearNote.cw != null" :class="$style.cw">
 					<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
@@ -63,6 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]" >
 					<div :class="$style.text">
 						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
+						<MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA>
 						<Mfm
 							v-if="appearNote.text"
 							:parsedNodes="parsed"
@@ -178,6 +176,7 @@ import MkCwButton from '@/components/MkCwButton.vue';
 import MkPoll from '@/components/MkPoll.vue';
 import MkUsersTooltip from '@/components/MkUsersTooltip.vue';
 import MkUrlPreview from '@/components/MkUrlPreview.vue';
+import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
 import MkButton from '@/components/MkButton.vue';
 import { pleaseLogin } from '@/scripts/please-login.js';
 import { focusPrev, focusNext } from '@/scripts/focus.js';
@@ -831,7 +830,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	position: relative;
 	display: flex;
 	align-items: center;
-	padding: 24px 38px 16px;
+	padding: 16px 32px 8px 32px;
 	line-height: 28px;
 	white-space: pre;
 	color: var(--renote);
@@ -883,7 +882,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	align-items: center;
 	line-height: 28px;
 	white-space: pre;
-	padding: 8px 38px 24px;
+	padding: 0 32px 18px;
 }
 
 .collapsedRenoteTargetAvatar {
@@ -910,6 +909,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 
 .article {
 	position: relative;
+	display: flex;
 	padding: 28px 32px;
 }
 
@@ -926,19 +926,12 @@ function emitUpdReaction(emoji: string, delta: number) {
 .avatar {
 	flex-shrink: 0;
 	display: block !important;
-	position: sticky !important;
 	margin: 0 14px 0 0;
 	width: 58px;
 	height: 58px;
 	position: sticky !important;
 	top: calc(22px + var(--stickyTop, 0px));
 	left: 0;
-	transition: top 0.5s;
-
-	&.avatarReplyTo {
-		position: relative !important;
-		top: 0 !important;
-	}
 }
 
 .main {
@@ -1001,6 +994,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 
 .text {
 	overflow-wrap: break-word;
+	overflow: hidden;
 }
 
 .replyIcon {
@@ -1033,8 +1027,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 
 .quoteNote {
 	padding: 16px;
-	// Made border solid, stylistic choice
-	border: solid 1px var(--renote);
+	border: dashed 1px var(--renote);
 	border-radius: var(--radius-sm);
 	overflow: clip;
 }
@@ -1074,11 +1067,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 	}
 
 	.renote {
-		padding: 24px 28px 16px;
-	}
-
-	.collapsedRenoteTarget {
-		padding: 8px 28px 24px;
+		padding: 12px 26px 0 26px;
 	}
 
 	.article {
@@ -1096,8 +1085,12 @@ function emitUpdReaction(emoji: string, delta: number) {
 		font-size: 0.9em;
 	}
 
+	.renote {
+		padding: 10px 22px 0 22px;
+	}
+
 	.article {
-		padding: 23px 25px;
+		padding: 20px 22px;
 	}
 
 	.footer {
@@ -1107,7 +1100,7 @@ function emitUpdReaction(emoji: string, delta: number) {
 
 @container (max-width: 480px) {
 	.renote {
-		padding: 20px 24px 8px;
+		padding: 8px 16px 0 16px;
 	}
 
 	.tip {
@@ -1115,12 +1108,12 @@ function emitUpdReaction(emoji: string, delta: number) {
 	}
 
 	.collapsedRenoteTarget {
-		padding: 8px 24px 20px;
+		padding: 0 16px 9px;
 		margin-top: 4px;
 	}
 
 	.article {
-		padding: 22px 24px;
+		padding: 14px 16px;
 	}
 }
 
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 95cbe38b1b..30198d2a6a 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -11,9 +11,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 	v-hotkey="keymap"
 	:class="$style.root"
 >
-	<div v-if="appearNote.reply && appearNote.reply.replyId && !conversationLoaded" style="padding: 16px">
-		<MkButton style="margin: 0 auto;" primary rounded @click="loadConversation">{{ i18n.ts.loadConversation }}</MkButton>
+	<div v-if="appearNote.reply && appearNote.reply.replyId">
+		<div v-if="!conversationLoaded" style="padding: 16px">
+			<MkButton style="margin: 0 auto;" primary rounded @click="loadConversation">{{ i18n.ts.loadConversation }}</MkButton>
+		</div>
+		<MkNoteSub v-for="note in conversation" :key="note.id" :class="$style.replyToMore" :note="note" :expandAllCws="props.expandAllCws"/>
 	</div>
+	<MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo" :expandAllCws="props.expandAllCws"/>
 	<div v-if="isRenote" :class="$style.renote">
 		<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
 		<i class="ph-rocket-launch ph-bold ph-lg" style="margin-right: 4px;"></i>
@@ -39,29 +43,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
 		</div>
 	</div>
-	<template v-if="appearNote.reply && appearNote.reply.replyId">
-		<MkNoteSub v-for="note in conversation" :key="note.id" :class="$style.replyToMore" :note="note" :expandAllCws="props.expandAllCws"/>
-	</template>
-	<MkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo" :expandAllCws="props.expandAllCws"/>
 	<article :class="$style.note" @contextmenu.stop="onContextmenu">
 		<header :class="$style.noteHeader">
 			<MkAvatar :class="$style.noteHeaderAvatar" :user="appearNote.user" indicator link preview/>
-			<div style="display: flex; align-items: center; white-space: nowrap; overflow: hidden;">
-				<div :class="$style.noteHeaderBody">
-					<div>
-						<MkA v-user-preview="appearNote.user.id" :class="$style.noteHeaderName" :to="userPage(appearNote.user)">
-							<MkUserName :nowrap="false" :user="appearNote.user"/>
-						</MkA>
-						<span v-if="appearNote.user.isBot" :class="$style.isBot">bot</span>
-						<span v-if="appearNote.user.badgeRoles" :class="$style.badgeRoles">
-							<img v-for="role in appearNote.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
-						</span>
-					</div>
-					<div :class="$style.noteHeaderUsername"><MkAcct :user="appearNote.user"/></div>
-				</div>
-			</div>
-			<div style="display: flex; align-items: flex-end; margin-left: auto;">
-				<div :class="$style.noteHeaderBody">
+			<div :class="$style.noteHeaderBody">
+				<div>
+					<MkA v-user-preview="appearNote.user.id" :class="$style.noteHeaderName" :to="userPage(appearNote.user)">
+						<MkUserName :nowrap="false" :user="appearNote.user"/>
+					</MkA>
+					<span v-if="appearNote.user.isBot" :class="$style.isBot">bot</span>
 					<div :class="$style.noteHeaderInfo">
 						<span v-if="appearNote.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[appearNote.visibility]">
 							<i v-if="appearNote.visibility === 'home'" class="ph-house ph-bold ph-lg"></i>
@@ -71,8 +61,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<span v-if="appearNote.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
 						<span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
 					</div>
-					<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
 				</div>
+				<div :class="$style.noteHeaderUsername"><MkAcct :user="appearNote.user"/></div>
+				<MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
 			</div>
 		</header>
 		<div :class="$style.noteContent">
@@ -82,6 +73,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</p>
 			<div v-show="appearNote.cw == null || showContent">
 				<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
+				<MkA v-if="appearNote.replyId" :class="$style.noteReplyTarget" :to="`/notes/${appearNote.replyId}`"><i class="ph-arrow-bend-left-up ph-bold ph-lg"></i></MkA>
 				<Mfm
 					v-if="appearNote.text"
 					:parsedNodes="parsed"
@@ -472,7 +464,7 @@ function renote() {
 		if (appearNote.channel?.isSensitive) {
 			visibility = smallerVisibility(visibility, 'home');
 		}
-
+		
 		os.api('notes/create', {
 			localOnly,
 			visibility,
@@ -859,19 +851,12 @@ function animatedMFM() {
 
 .noteHeaderInfo {
 	float: right;
-	text-align: right;
 }
 
 .noteHeaderUsername {
 	margin-bottom: 2px;
 	line-height: 1.3;
 	word-wrap: anywhere;
-	text-overflow: ellipsis;
-	white-space: nowrap;
-
-	&::-webkit-scrollbar {
-		display: none;
-	}
 }
 
 .playMFMButton {
@@ -1052,31 +1037,9 @@ function animatedMFM() {
 	}
 }
 
-.avatar {
-	flex-shrink: 0 !important;
-	display: block !important;
-	margin: 0 10px 0 0 !important;
-	width: 40px !important;
-	height: 40px !important;
-	border-radius: var(--radius-sm) !important;
-}
-
 .muted {
 	padding: 8px;
 	text-align: center;
 	opacity: 0.7;
 }
-
-.badgeRoles {
-	margin: 0 .5em 0 0;
-}
-
-.badgeRole {
-	height: 1.3em;
-	vertical-align: -20%;
-
-	& + .badgeRole {
-		margin-left: 0.2em;
-	}
-}
 </style>
diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue
index 9984c3774d..6121db3f8f 100644
--- a/packages/frontend/src/components/MkNoteHeader.vue
+++ b/packages/frontend/src/components/MkNoteHeader.vue
@@ -4,56 +4,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<header v-if="!classic" :class="$style.root">
-	<div :class="$style.section">
-		<div style="display: flex;">
-			<div v-if="mock" :class="$style.name">
-				<MkUserName :user="note.user"/>
-			</div>
-			<MkA v-else v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)">
-				<MkUserName :user="note.user"/>
-			</MkA>
-			<div v-if="note.user.isBot" :class="$style.isBot">bot</div>
-			<div v-if="note.user.badgeRoles" :class="$style.badgeRoles">
-				<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
-			</div>
-		</div>
-		<div :class="$style.username"><MkAcct :user="note.user"/></div>
-	</div>
-	<div :class="$style.section">
-		<div :class="$style.info">
-			<div v-if="mock">
-				<MkTime :time="note.createdAt" colored/>
-			</div>
-			<MkA v-else :class="$style.time" :to="notePage(note)">
-				<MkTime :time="note.createdAt" colored/>
-			</MkA>
-			<span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]">
-				<i v-if="note.visibility === 'home'" class="ph-house ph-bold ph-lg"></i>
-				<i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
-				<i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
-			</span>
-			<span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em; cursor: pointer;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
-			<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
-			<span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span>
-		</div>
-		<div :class="$style.info"><MkInstanceTicker v-if="showTicker" style="cursor: pointer;" :instance="note.user.instance" @click.stop="showOnRemote()"/></div>
-	</div>
-</header>
-<header v-else :class="$style.classicRoot">
+<header :class="$style.root">
 	<div v-if="mock" :class="$style.name">
 		<MkUserName :user="note.user"/>
 	</div>
-	<MkA v-else v-user-preview="note.user.id" :class="$style.classicName" :to="userPage(note.user)">
+	<MkA v-else v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)">
 		<MkUserName :user="note.user"/>
 	</MkA>
 	<div v-if="note.user.isBot" :class="$style.isBot">bot</div>
-	<div :class="$style.classicUsername"><MkAcct :user="note.user"/></div>
+	<div :class="$style.username"><MkAcct :user="note.user"/></div>
 	<div v-if="note.user.badgeRoles" :class="$style.badgeRoles">
 		<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
 	</div>
-	<MkInstanceTicker v-if="showTicker && !isMobile && defaultStore.state.showTickerOnReplies" style="cursor: pointer; max-height: 5px; top: 3px; position: relative; margin-top: 0px !important;" :instance="note.user.instance" @click.stop="showOnRemote()"/>
-	<div :class="$style.classicInfo">
+	<div :class="$style.info">
 		<div v-if="mock">
 			<MkTime :time="note.createdAt" colored/>
 		</div>
@@ -73,29 +36,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { inject, shallowRef, ref } from 'vue';
+import { inject, shallowRef } from 'vue';
 import * as Misskey from 'misskey-js';
 import { i18n } from '@/i18n.js';
 import { notePage } from '@/filters/note.js';
 import { userPage } from '@/filters/user.js';
 import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js';
-import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
 import { popupMenu } from '@/os.js';
-import { defaultStore } from '@/store.js';
-import { useRouter } from '@/router.js';
-import { deviceKind } from '@/scripts/device-kind.js';
 
 const props = defineProps<{
 	note: Misskey.entities.Note;
-	classic?: boolean;
 }>();
 
 const menuVersionsButton = shallowRef<HTMLElement>();
-const router = useRouter();
-const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && props.note.user.instance);
-
-const MOBILE_THRESHOLD = 500;
-const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
 
 async function menuVersions(viaKeyboard = false): Promise<void> {
 	const { menu, cleanup } = await getNoteVersionsMenu({ note: props.note, menuVersionsButton });
@@ -104,67 +57,18 @@ async function menuVersions(viaKeyboard = false): Promise<void> {
 	}).then(focus).finally(cleanup);
 }
 
-function showOnRemote() {
-	if (props.note.url ?? props.note.uri === undefined) router.push(notePage(props.note));
-	else window.open(props.note.url ?? props.note.uri);
-}
-
 const mock = inject<boolean>('mock', false);
 </script>
 
 <style lang="scss" module>
 .root {
-	display: flex;
-	cursor: auto; /* not clickToOpen-able */
-}
-
-.classicRoot {
 	display: flex;
 	align-items: baseline;
 	white-space: nowrap;
 	cursor: auto; /* not clickToOpen-able */
 }
 
-.section {
-		align-items: flex-start;
-		white-space: nowrap;
-		flex-direction: column;
-		overflow: hidden;
-
-		&:last-child {
-			display: flex;
-			align-items: flex-end;
-			margin-left: auto;
-			padding-left: 10px;
-			overflow: clip;
-		}
-}
-
 .name {
-	flex-shrink: 1;
-	display: block;
-	// note, these margin top values were done by hand may need futher checking if it actualy aligns pixel perfect
-	margin: 3px .5em 0 0;
-	padding: 0;
-	overflow: scroll;
-	overflow-wrap: anywhere;
-	font-size: 1em;
-	font-weight: bold;
-	text-decoration: none;
-	text-overflow: ellipsis;
-	max-width: 300px;
-
-		&::-webkit-scrollbar {
-			display: none;
-		}
-
-		&:hover {
-			color: var(--nameHover);
-			text-decoration: none;
-		}
-}
-
-.classicName {
 	flex-shrink: 1;
 	display: block;
 	margin: 0 .5em 0 0;
@@ -191,20 +95,6 @@ const mock = inject<boolean>('mock', false);
 }
 
 .username {
-	flex-shrink: 9999999;
-	// note these top margins were made to align with the instance ticker
-	margin: 4px .5em 0 0;
-	overflow: hidden;
-	text-overflow: ellipsis;
-	font-size: .95em;
-	max-width: 300px;
-
-	&::-webkit-scrollbar {
-		display: none;
-	}
-}
-
-.classicUsername {
 	flex-shrink: 9999999;
 	margin: 0 .5em 0 0;
 	overflow: hidden;
@@ -212,34 +102,11 @@ const mock = inject<boolean>('mock', false);
 }
 
 .info {
-	&:first-child {
-		margin-top: 4px;
-		flex-shrink: 0;
-		margin-left: auto;
-		font-size: 0.9em;
-	}
-
-	&:not(:first-child) {
-		flex-shrink: 0;
-		margin-left: auto;
-		font-size: 0.9em;
-	}
-}
-
-.classicInfo {
 	flex-shrink: 0;
 	margin-left: auto;
 	font-size: 0.9em;
 }
 
-.time {
-	text-decoration: none;
-
-	&:hover {
-		text-decoration: none;
-	}
-}
-
 .badgeRoles {
 	margin: 0 .5em 0 0;
 }
@@ -252,14 +119,4 @@ const mock = inject<boolean>('mock', false);
 		margin-left: 0.2em;
 	}
 }
-
-.danger {
-		color: var(--accent);
-	}
-
-	@container (max-width: 500px) {
-		.name, .username {
-			max-width: 200px;
-		}
-	}
 </style>
diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue
index 8ebd24b322..b1d4ed3f7e 100644
--- a/packages/frontend/src/components/MkNoteSimple.vue
+++ b/packages/frontend/src/components/MkNoteSimple.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <div :class="$style.root">
 	<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
 	<div :class="$style.main">
-		<MkNoteHeader :class="$style.header" :classic="true" :note="note" :mini="true"/>
+		<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
 		<div>
 			<p v-if="note.cw != null" :class="$style.cw">
 				<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/>
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 822b4de83f..3e33c7aa69 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -5,12 +5,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div v-if="!muted" ref="el" :class="[$style.root, { [$style.children]: depth > 1 }]">
-	<div v-if="!hideLine" :class="$style.line"></div>
 	<div :class="$style.main">
 		<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
 		<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
 		<div :class="$style.body">
-			<MkNoteHeader :class="$style.header" :note="note" :classic="true" :mini="true"/>
+			<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
 			<div :class="$style.content">
 				<p v-if="note.cw != null" :class="$style.cw">
 					<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'"/>
@@ -107,7 +106,6 @@ import { getNoteMenu } from '@/scripts/get-note-menu.js';
 import { useNoteCapture } from '@/scripts/use-note-capture.js';
 
 const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
-const hideLine = computed(() => { return props.detail ? true : false; });
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
@@ -363,7 +361,7 @@ if (props.detail) {
 
 <style lang="scss" module>
 .root {
-	padding: 28px 32px;
+	padding: 16px 32px;
 	font-size: 0.9em;
 	position: relative;
 
@@ -373,20 +371,12 @@ if (props.detail) {
 	}
 }
 
-.line {
-	position: absolute;
-	height: 100%;
-	left: 60px;
-	// using solid instead of dotted, stylelistic choice
-	border-left: 2.5px solid rgb(174, 174, 174);
-}
-
 .footer {
-	position: relative;
-	z-index: 1;
-	margin-top: 0.4em;
-	width: max-content;
-	min-width: max-content;
+		position: relative;
+		z-index: 1;
+		margin-top: 0.4em;
+		width: max-content;
+		min-width: max-content;
 }
 
 .main {
@@ -406,9 +396,9 @@ if (props.detail) {
 .avatar {
 	flex-shrink: 0;
 	display: block;
-	margin: 0 14px 0 0;
-	width: 58px;
-	height: 58px;
+	margin: 0 8px 0 0;
+	width: 38px;
+	height: 38px;
 	border-radius: var(--radius-sm);
 }
 
@@ -421,11 +411,6 @@ if (props.detail) {
 	overflow: hidden;
 }
 
-.text {
-	margin: 0;
-	padding: 0;
-}
-
 .header {
 	margin-bottom: 2px;
 }
@@ -445,36 +430,6 @@ if (props.detail) {
 	}
 }
 
-.reply, .more {
-	border-left: solid 0.5px var(--divider);
-	margin-top: 10px;
-}
-
-.more {
-	padding: 10px 0 0 16px;
-}
-
-@container (max-width: 580px) {
-	.root {
-		padding: 28px 26px 0;
-	}
-
-	.line {
-		left: 50.5px;
-	}
-
-	.avatar {
-		width: 50px;
-		height: 50px;
-	}
-}
-
-@container (max-width: 500px) {
-	.root {
-		padding: 23px 25px;
-	}
-}
-
 @container (max-width: 400px) {
 	.noteFooterButton {
 		&:not(:last-child) {
@@ -514,9 +469,9 @@ if (props.detail) {
 	padding: 10px 0 0 16px;
 }
 
-@container (max-width: 480px) {
+@container (max-width: 450px) {
 	.root {
-		padding: 22px 24px;
+		padding: 14px 16px;
 
 		&.children {
 			padding: 10px 0 0 8px;
@@ -524,17 +479,6 @@ if (props.detail) {
 	}
 }
 
-@container (max-width: 450px) {
-	.line {
-		left: 46px;
-	}
-
-	.avatar {
-		width: 46px;
-		height: 46px;
-	}
-}
-
 .muted {
 	text-align: center;
 	padding: 8px !important;
diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue
index 76587ce141..0d2f0020d1 100644
--- a/packages/frontend/src/components/MkNotes.vue
+++ b/packages/frontend/src/components/MkNotes.vue
@@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<template #default="{ items: notes }">
 		<div :class="[$style.root, { [$style.noGap]: noGap }]">
 			<MkDateSeparatedList
+				v-if="defaultStore.state.noteDesign === 'misskey'"
 				ref="notes"
 				v-slot="{ item: note }"
 				:items="notes"
@@ -26,18 +27,35 @@ SPDX-License-Identifier: AGPL-3.0-only
 			>
 				<MkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note"/>
 			</MkDateSeparatedList>
+			<MkDateSeparatedList
+				v-else-if="defaultStore.state.noteDesign === 'sharkey'"
+				ref="notes" 
+				v-slot="{ item: note }"
+				:items="notes"
+				:direction="pagination.reversed ? 'up' : 'down'"
+				:reversed="pagination.reversed"
+				:noGap="noGap"
+				:ad="true"
+				:class="$style.notes"
+			>
+				<SkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note"/>
+			</MkDateSeparatedList>
 		</div>
 	</template>
 </MkPagination>
 </template>
 
 <script lang="ts" setup>
-import { shallowRef } from 'vue';
+import { shallowRef, ref } from 'vue';
 import MkNote from '@/components/MkNote.vue';
+import SkNote from '@/components/SkNote.vue';
 import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import { i18n } from '@/i18n.js';
 import { infoImageUrl } from '@/instance.js';
+import { defaultStore } from '@/store.js';
+
+console.log(defaultStore.state.noteDesign, defaultStore.state.noteDesign === 'sharkey');
 
 const props = defineProps<{
 	pagination: Paging;
diff --git a/packages/frontend/src/components/SkInstanceTicker.vue b/packages/frontend/src/components/SkInstanceTicker.vue
new file mode 100644
index 0000000000..4e2856388e
--- /dev/null
+++ b/packages/frontend/src/components/SkInstanceTicker.vue
@@ -0,0 +1,82 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.root" :style="bg">
+	<img v-if="faviconUrl" :class="$style.icon" :src="faviconUrl"/>
+	<div :class="$style.name">{{ instance.name }}</div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { } from 'vue';
+import { instanceName } from '@/config.js';
+import { instance as Instance } from '@/instance.js';
+import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
+
+const props = defineProps<{
+	instance?: {
+		faviconUrl?: string
+		name: string
+		themeColor?: string
+	}
+}>();
+
+// if no instance data is given, this is for the local instance
+const instance = props.instance ?? {
+	name: instanceName,
+	themeColor: (document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement).content,
+};
+
+const faviconUrl = $computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(Instance.faviconUrl, 'preview') ?? '/favicon.ico');
+
+const themeColor = instance.themeColor ?? '#777777';
+
+const bg = {
+	//background: `linear-gradient(90deg, ${themeColor}, ${themeColor}00)`,
+	background: `${themeColor}`,
+};
+</script>
+
+<style lang="scss" module>
+.root {
+	display: flex;
+	align-items: center;
+	height: 1.5ex;
+	border-radius: var(--radius-xl);
+	margin-top: 5px;
+	padding: 4px;
+	overflow: clip;
+	color: #fff;
+	text-shadow: -1px -1px 0 var(--bg),1px -1px 0 var(--bg),-1px 1px 0 var(--bg),1px 1px 0 var(--bg)
+}
+
+.icon {
+	height: 2ex;
+	flex-shrink: 0;
+}
+
+.name {
+	margin-left: 4px;
+	line-height: 1;
+	font-size: 0.8em;
+	font-weight: bold;
+	white-space: nowrap;
+	overflow: hidden;
+	overflow-wrap: anywhere;
+	max-width: 300px;
+	text-overflow: ellipsis;
+
+	&::-webkit-scrollbar {
+		display: none;
+	}
+}
+
+@container (max-width: 400px) {
+	.name {
+		max-width: 50px;
+	}
+}
+</style>
diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue
new file mode 100644
index 0000000000..924437def5
--- /dev/null
+++ b/packages/frontend/src/components/SkNote.vue
@@ -0,0 +1,1201 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div
+	v-if="!muted"
+	v-show="!isDeleted"
+	ref="el"
+	v-hotkey="keymap"
+	:class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]"
+	:tabindex="!isDeleted ? '-1' : undefined"
+>
+	<SkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
+	<div v-if="pinned" :class="$style.tip"><i class="ph-push-pin ph-bold ph-lg"></i> {{ i18n.ts.pinnedNote }}</div>
+	<!--<div v-if="appearNote._prId_" class="tip"><i class="ph-megaphone ph-bold ph-lg"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ph-x ph-bold ph-lg"></i></button></div>-->
+	<!--<div v-if="appearNote._featuredId_" class="tip"><i class="ph-lightning ph-bold ph-lg"></i> {{ i18n.ts.featured }}</div>-->
+	<div v-if="isRenote" :class="$style.renote">
+		<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
+		<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
+		<i class="ph-rocket-launch ph-bold ph-lg" style="margin-right: 4px;"></i>
+		<I18n :src="i18n.ts.renotedBy" tag="span" :class="$style.renoteText">
+			<template #user>
+				<MkA v-user-preview="note.userId" :class="$style.renoteUserName" :to="userPage(note.user)">
+					<MkUserName :user="note.user"/>
+				</MkA>
+			</template>
+		</I18n>
+		<div :class="$style.renoteInfo">
+			<button ref="renoteTime" :class="$style.renoteTime" class="_button" @click="showRenoteMenu()">
+				<i class="ph-dots-three ph-bold ph-lg" :class="$style.renoteMenu"></i>
+				<MkTime :time="note.createdAt"/>
+			</button>
+			<span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]">
+				<i v-if="note.visibility === 'home'" class="ph-house ph-bold ph-lg"></i>
+				<i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
+				<i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
+			</span>
+			<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
+			<span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span>
+			<span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
+		</div>
+	</div>
+	<div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget">
+		<MkAvatar :class="$style.collapsedRenoteTargetAvatar" :user="appearNote.user" link preview/>
+		<Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :nyaize="'respect'" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/>
+	</div>
+	<article v-else :class="$style.article" @contextmenu.stop="onContextmenu">
+		<div style="display: flex; padding-bottom: 10px;">
+			<div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
+			<MkAvatar :class="[$style.avatar, { [$style.avatarReplyTo]: appearNote.reply }]" :user="appearNote.user" :link="!mock" :preview="!mock"/>
+			<div :class="$style.main">
+				<SkNoteHeader :note="appearNote" :mini="true"/>
+			</div>
+		</div>
+		<div :class="[{ [$style.clickToOpen]: defaultStore.state.clickToOpen }]" @click="defaultStore.state.clickToOpen ? noteclick(appearNote.id) : undefined">
+			<div style="container-type: inline-size;">
+				<p v-if="appearNote.cw != null" :class="$style.cw">
+					<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
+					<MkCwButton v-model="showContent" :note="appearNote" style="margin: 4px 0;" v-on:click.stop/>
+				</p>
+				<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]" >
+					<div :class="$style.text">
+						<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
+						<Mfm
+							v-if="appearNote.text"
+							:parsedNodes="parsed"
+							:text="appearNote.text"
+							:author="appearNote.user"
+							:nyaize="'respect'"
+							:emojiUrls="appearNote.emojis"
+							:enableEmojiMenu="true"
+							:enableEmojiMenuReaction="true"
+							:isAnim="allowAnim"
+						/>
+						<div v-if="translating || translation" :class="$style.translation">
+							<MkLoading v-if="translating" mini/>
+							<div v-else>
+								<b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b>
+								<Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
+							</div>
+						</div>
+						<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" v-on:click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
+						<MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" v-on:click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
+					</div>
+					<div v-if="appearNote.files.length > 0">
+						<MkMediaList :mediaList="appearNote.files" v-on:click.stop/>
+					</div>
+					<MkPoll v-if="appearNote.poll" :note="appearNote" :class="$style.poll" v-on:click.stop />
+					<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview" v-on:click.stop/>
+					<div v-if="appearNote.renote" :class="$style.quote"><SkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div>
+					<button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" v-on:click.stop @click="collapsed = false">
+						<span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span>
+					</button>
+					<button v-else-if="isLong && !collapsed" :class="$style.showLess" class="_button" v-on:click.stop @click="collapsed = true">
+						<span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span>
+					</button>
+				</div>
+				<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ph-television ph-bold ph-lg"></i> {{ appearNote.channel.name }}</MkA>
+			</div>
+			<MkReactionsViewer :note="appearNote" :maxNumber="16" v-on:click.stop @mockUpdateMyReaction="emitUpdReaction">
+				<template #more>
+					<div :class="$style.reactionOmitted">{{ i18n.ts.more }}</div>
+				</template>
+			</MkReactionsViewer>
+			<footer :class="$style.footer">
+				<button :class="$style.footerButton" class="_button" v-on:click.stop @click="reply()">
+					<i class="ph-arrow-u-up-left ph-bold ph-lg"></i>
+					<p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p>
+				</button>
+				<button
+					v-if="canRenote"
+					ref="renoteButton"
+					:class="$style.footerButton"
+					class="_button"
+					:style="renoted ? 'color: var(--accent) !important;' : ''"
+					v-on:click.stop
+					@mousedown="renoted ? undoRenote(appearNote) : renote()"
+				>
+					<i class="ph-rocket-launch ph-bold ph-lg"></i>
+					<p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ appearNote.renoteCount }}</p>
+				</button>
+				<button v-else :class="$style.footerButton" class="_button" disabled>
+					<i class="ph-prohibit ph-bold ph-lg"></i>
+				</button>
+				<button
+					v-if="canRenote && !props.mock"
+					ref="quoteButton"
+					:class="$style.footerButton"
+					class="_button"
+					v-on:click.stop
+					@mousedown="quote()"
+				>
+					<i class="ph-quotes ph-bold ph-lg"></i>
+				</button>
+				<button v-if="appearNote.myReaction == null && appearNote.reactionAcceptance !== 'likeOnly'" ref="likeButton" :class="$style.footerButton" class="_button" v-on:click.stop @click="like()">
+					<i class="ph-heart ph-bold ph-lg"></i>
+				</button>
+				<button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.footerButton" class="_button" @mousedown="react()">
+					<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i>
+					<i v-else class="ph-smiley ph-bold ph-lg"></i>
+				</button>
+				<button v-if="appearNote.myReaction != null" ref="reactButton" :class="$style.footerButton" class="_button" v-on:click.stop @click="undoReact(appearNote)">
+					<i class="ph-minus ph-bold ph-lg"></i>
+				</button>
+				<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()">
+					<i class="ph-paperclip ph-bold ph-lg"></i>
+				</button>
+				<button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="menu()">
+					<i class="ph-dots-three ph-bold ph-lg"></i>
+				</button>
+			</footer>
+		</div>
+	</article>
+</div>
+<div v-else :class="$style.muted" @click="muted = false">
+	<I18n :src="i18n.ts.userSaysSomething" tag="small">
+		<template #name>
+			<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
+				<MkUserName :user="appearNote.user"/>
+			</MkA>
+		</template>
+	</I18n>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { computed, inject, onMounted, ref, shallowRef, Ref, defineAsyncComponent, watch, provide } from 'vue';
+import * as mfm from 'mfm-js';
+import * as Misskey from 'misskey-js';
+import SkNoteSub from '@/components/SkNoteSub.vue';
+import SkNoteHeader from '@/components/SkNoteHeader.vue';
+import SkNoteSimple from '@/components/SkNoteSimple.vue';
+import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
+import MkMediaList from '@/components/MkMediaList.vue';
+import MkCwButton from '@/components/MkCwButton.vue';
+import MkPoll from '@/components/MkPoll.vue';
+import MkUsersTooltip from '@/components/MkUsersTooltip.vue';
+import MkUrlPreview from '@/components/MkUrlPreview.vue';
+import MkButton from '@/components/MkButton.vue';
+import { pleaseLogin } from '@/scripts/please-login.js';
+import { focusPrev, focusNext } from '@/scripts/focus.js';
+import { checkWordMute } from '@/scripts/check-word-mute.js';
+import { userPage } from '@/filters/user.js';
+import * as os from '@/os.js';
+import { defaultStore, noteViewInterruptors } from '@/store.js';
+import { reactionPicker } from '@/scripts/reaction-picker.js';
+import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
+import { checkAnimationFromMfm } from '@/scripts/check-animated-mfm.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js';
+import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js';
+import { useNoteCapture } from '@/scripts/use-note-capture.js';
+import { deepClone } from '@/scripts/clone.js';
+import { useTooltip } from '@/scripts/use-tooltip.js';
+import { claimAchievement } from '@/scripts/achievements.js';
+import { getNoteSummary } from '@/scripts/get-note-summary.js';
+import { MenuItem } from '@/types/menu.js';
+import MkRippleEffect from '@/components/MkRippleEffect.vue';
+import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
+import { shouldCollapsed } from '@/scripts/collapsed.js';
+import { useRouter } from '@/router.js';
+
+const props = withDefaults(defineProps<{
+	note: Misskey.entities.Note;
+	pinned?: boolean;
+	mock?: boolean;
+}>(), {
+	mock: false,
+});
+
+provide('mock', props.mock);
+
+const emit = defineEmits<{
+	(ev: 'reaction', emoji: string): void;
+	(ev: 'removeReaction', emoji: string): void;
+}>();
+
+const router = useRouter();
+
+const inChannel = inject('inChannel', null);
+const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
+
+let note = $ref(deepClone(props.note));
+
+function noteclick(id: string) {
+	const selection = document.getSelection();
+	if (selection?.toString().length === 0) {
+		router.push(`/notes/${id}`);
+	}
+}
+
+// plugin
+if (noteViewInterruptors.length > 0) {
+	onMounted(async () => {
+		let result: Misskey.entities.Note | null = deepClone(note);
+		for (const interruptor of noteViewInterruptors) {
+			try {
+				result = await interruptor.handler(result);
+				if (result === null) {
+					isDeleted.value = true;
+					return;
+				}
+			} catch (err) {
+				console.error(err);
+			}
+		}
+		note = result;
+	});
+}
+
+const isRenote = (
+	note.renote != null &&
+	note.text == null &&
+	note.fileIds.length === 0 &&
+	note.poll == null
+);
+
+const el = shallowRef<HTMLElement>();
+const menuButton = shallowRef<HTMLElement>();
+const menuVersionsButton = shallowRef<HTMLElement>();
+const renoteButton = shallowRef<HTMLElement>();
+const renoteTime = shallowRef<HTMLElement>();
+const reactButton = shallowRef<HTMLElement>();
+const quoteButton = shallowRef<HTMLElement>();
+const clipButton = shallowRef<HTMLElement>();
+const likeButton = shallowRef<HTMLElement>();
+let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note);
+const renoteUrl = appearNote.renote ? appearNote.renote.url : null;
+const renoteUri = appearNote.renote ? appearNote.renote.uri : null;
+
+const isMyRenote = $i && ($i.id === note.userId);
+const showContent = ref(false);
+const parsed = $computed(() => appearNote.text ? mfm.parse(appearNote.text) : null);
+const urls = $computed(() => parsed ? extractUrlFromMfm(parsed).filter(u => u !== renoteUrl && u !== renoteUri) : null);
+const animated = $computed(() => parsed ? checkAnimationFromMfm(parsed) : null);
+const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
+const isLong = shouldCollapsed(appearNote, urls ?? []);
+const collapsed = ref(appearNote.cw == null && isLong);
+const isDeleted = ref(false);
+const renoted = ref(false);
+const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
+const translation = ref<any>(null);
+const translating = ref(false);
+const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
+const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || (appearNote.visibility === 'followers' && appearNote.userId === $i.id));
+let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId || $i.id === appearNote.userId)) || (appearNote.myReaction != null)));
+const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
+
+const keymap = {
+	'r': () => reply(true),
+	'e|a|plus': () => react(true),
+	'q': () => renoteButton.value.renote(true),
+	'up|k|shift+tab': focusBefore,
+	'down|j|tab': focusAfter,
+	'esc': blur,
+	'm|o': () => menu(true),
+	's': () => showContent.value !== showContent.value,
+};
+
+provide('react', (reaction: string) => {
+	os.api('notes/reactions/create', {
+		noteId: appearNote.id,
+		reaction: reaction,
+	});
+});
+
+if (props.mock) {
+	watch(() => props.note, (to) => {
+		note = deepClone(to);
+	}, { deep: true });
+} else {
+	useNoteCapture({
+		rootEl: el,
+		note: $$(appearNote),
+		pureNote: $$(note),
+		isDeletedRef: isDeleted,
+	});
+}
+
+if (!props.mock) {
+	useTooltip(renoteButton, async (showing) => {
+		const renotes = await os.api('notes/renotes', {
+			noteId: appearNote.id,
+			limit: 11,
+		});
+
+		const users = renotes.map(x => x.user);
+
+		if (users.length < 1) return;
+
+		os.popup(MkUsersTooltip, {
+			showing,
+			users,
+			count: appearNote.renoteCount,
+			targetElement: renoteButton.value,
+		}, {}, 'closed');
+	});
+
+	useTooltip(quoteButton, async (showing) => {
+		const renotes = await os.api('notes/renotes', {
+			noteId: appearNote.id,
+			limit: 11,
+			quote: true,
+		});
+
+		const users = renotes.map(x => x.user);
+
+		if (users.length < 1) return;
+
+		os.popup(MkUsersTooltip, {
+			showing,
+			users,
+			count: appearNote.renoteCount,
+			targetElement: quoteButton.value,
+		}, {}, 'closed');
+	});
+
+	if ($i) {
+		os.api("notes/renotes", {
+			noteId: appearNote.id,
+			userId: $i.id,
+			limit: 1,
+		}).then((res) => {
+			renoted.value = res.length > 0;
+		});
+	}
+}
+
+type Visibility = 'public' | 'home' | 'followers' | 'specified';
+
+// defaultStore.state.visibilityがstringなためstringも受け付けている
+function smallerVisibility(a: Visibility | string, b: Visibility | string): Visibility {
+	if (a === 'specified' || b === 'specified') return 'specified';
+	if (a === 'followers' || b === 'followers') return 'followers';
+	if (a === 'home' || b === 'home') return 'home';
+	// if (a === 'public' || b === 'public')
+	return 'public';
+}
+
+function renote() {
+	pleaseLogin();
+	showMovedDialog();
+
+	if (appearNote.channel) {
+		const el = renoteButton.value as HTMLElement | null | undefined;
+		if (el) {
+			const rect = el.getBoundingClientRect();
+			const x = rect.left + (el.offsetWidth / 2);
+			const y = rect.top + (el.offsetHeight / 2);
+			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+		}
+
+		if (!props.mock) {
+			os.api('notes/create', {
+				renoteId: appearNote.id,
+				channelId: appearNote.channelId,
+			}).then(() => {
+				os.toast(i18n.ts.renoted);
+				renoted.value = true;
+			});
+		}
+	} else if (!appearNote.channel || appearNote.channel?.allowRenoteToExternal) {
+		const el = renoteButton.value as HTMLElement | null | undefined;
+		if (el) {
+			const rect = el.getBoundingClientRect();
+			const x = rect.left + (el.offsetWidth / 2);
+			const y = rect.top + (el.offsetHeight / 2);
+			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+		}
+
+		const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
+		const localOnly = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
+
+		let visibility = appearNote.visibility;
+		visibility = smallerVisibility(visibility, configuredVisibility);
+		if (appearNote.channel?.isSensitive) {
+			visibility = smallerVisibility(visibility, 'home');
+		}
+
+		if (!props.mock) {
+			os.api('notes/create', {
+				localOnly,
+				visibility,
+				renoteId: appearNote.id,
+			}).then(() => {
+				os.toast(i18n.ts.renoted);
+				renoted.value = true;
+			});
+		}
+	}
+}
+
+function quote() {
+	pleaseLogin();
+	showMovedDialog();
+	if (props.mock) {
+		return;
+	}
+
+	if (appearNote.channel) {
+		os.post({
+			renote: appearNote,
+			channel: appearNote.channel,
+		}).then(() => {
+			os.api("notes/renotes", {
+				noteId: appearNote.id,
+				userId: $i.id,
+				limit: 1,
+				quote: true,
+			}).then((res) => {
+				if (!(res.length > 0)) return;
+				const el = quoteButton.value as HTMLElement | null | undefined;
+				if (el && res.length > 0) {
+					const rect = el.getBoundingClientRect();
+					const x = rect.left + (el.offsetWidth / 2);
+					const y = rect.top + (el.offsetHeight / 2);
+					os.popup(MkRippleEffect, { x, y }, {}, 'end');
+				}
+
+				os.toast(i18n.ts.quoted);
+			});
+		});
+	} else {
+		os.post({
+			renote: appearNote,
+		}).then(() => {
+			os.api("notes/renotes", {
+				noteId: appearNote.id,
+				userId: $i.id,
+				limit: 1,
+				quote: true,
+			}).then((res) => {
+				if (!(res.length > 0)) return;
+				const el = quoteButton.value as HTMLElement | null | undefined;
+				if (el && res.length > 0) {
+					const rect = el.getBoundingClientRect();
+					const x = rect.left + (el.offsetWidth / 2);
+					const y = rect.top + (el.offsetHeight / 2);
+					os.popup(MkRippleEffect, { x, y }, {}, 'end');
+				}
+
+				os.toast(i18n.ts.quoted);
+			});
+		});
+	}
+}
+
+function reply(viaKeyboard = false): void {
+	pleaseLogin();
+	if (props.mock) {
+		return;
+	}
+	os.post({
+		reply: appearNote,
+		channel: appearNote.channel,
+		animation: !viaKeyboard,
+	}, () => {
+		focus();
+	});
+}
+
+function like(): void {
+	pleaseLogin();
+	showMovedDialog();
+	if (props.mock) {
+		return;
+	}
+	os.api('notes/like', {
+		noteId: appearNote.id,
+		override: defaultLike.value,
+	});
+	const el = likeButton.value as HTMLElement | null | undefined;
+	if (el) {
+		const rect = el.getBoundingClientRect();
+		const x = rect.left + (el.offsetWidth / 2);
+		const y = rect.top + (el.offsetHeight / 2);
+		os.popup(MkRippleEffect, { x, y }, {}, 'end');
+	}
+}
+
+function react(viaKeyboard = false): void {
+	pleaseLogin();
+	showMovedDialog();
+	if (appearNote.reactionAcceptance === 'likeOnly') {
+		if (props.mock) {
+			return;
+		}
+
+		os.api('notes/like', {
+			noteId: appearNote.id,
+			override: defaultLike.value,
+		});
+		const el = reactButton.value as HTMLElement | null | undefined;
+		if (el) {
+			const rect = el.getBoundingClientRect();
+			const x = rect.left + (el.offsetWidth / 2);
+			const y = rect.top + (el.offsetHeight / 2);
+			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+		}
+	} else {
+		blur();
+		reactionPicker.show(reactButton.value, reaction => {
+			if (props.mock) {
+				emit('reaction', reaction);
+				return;
+			}
+
+			os.api('notes/reactions/create', {
+				noteId: appearNote.id,
+				reaction: reaction,
+			});
+			if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) {
+				claimAchievement('reactWithoutRead');
+			}
+		}, () => {
+			focus();
+		});
+	}
+}
+
+function undoReact(note): void {
+	const oldReaction = note.myReaction;
+	if (!oldReaction) return;
+
+	if (props.mock) {
+		emit('removeReaction', oldReaction);
+		return;
+	}
+
+	os.api('notes/reactions/delete', {
+		noteId: note.id,
+	});
+}
+
+function undoRenote(note) : void {
+	if (props.mock) {
+		return;
+	}
+	os.api("notes/unrenote", {
+		noteId: note.id
+	});
+	os.toast(i18n.ts.rmboost);
+	renoted.value = false;
+
+	const el = renoteButton.value as HTMLElement | null | undefined;
+	if (el) {
+		const rect = el.getBoundingClientRect();
+		const x = rect.left + (el.offsetWidth / 2);
+		const y = rect.top + (el.offsetHeight / 2);
+		os.popup(MkRippleEffect, { x, y }, {}, 'end');
+	}
+}
+
+function onContextmenu(ev: MouseEvent): void {
+	if (props.mock) {
+		return;
+	}
+
+	const isLink = (el: HTMLElement) => {
+		if (el.tagName === 'A') return true;
+		// 再生速度の選択などのために、Audio要素のコンテキストメニューはブラウザデフォルトとする。
+		if (el.tagName === 'AUDIO') return true;
+		if (el.parentElement) {
+			return isLink(el.parentElement);
+		}
+	};
+	if (isLink(ev.target)) return;
+	if (window.getSelection().toString() !== '') return;
+
+	if (defaultStore.state.useReactionPickerForContextMenu) {
+		ev.preventDefault();
+		react();
+	} else {
+		const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
+		os.contextMenu(menu, ev).then(focus).finally(cleanup);
+	}
+}
+
+function menu(viaKeyboard = false): void {
+	if (props.mock) {
+		return;
+	}
+
+	const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
+	os.popupMenu(menu, menuButton.value, {
+		viaKeyboard,
+	}).then(focus).finally(cleanup);
+}
+
+async function menuVersions(viaKeyboard = false): Promise<void> {
+	const { menu, cleanup } = await getNoteVersionsMenu({ note: note, menuVersionsButton });
+	os.popupMenu(menu, menuVersionsButton.value, {
+		viaKeyboard,
+	}).then(focus).finally(cleanup);
+}
+
+async function clip() {
+	if (props.mock) {
+		return;
+	}
+
+	os.popupMenu(await getNoteClipMenu({ note: note, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus);
+}
+
+function showRenoteMenu(viaKeyboard = false): void {
+	if (props.mock) {
+		return;
+	}
+
+	function getUnrenote(): MenuItem {
+		return {
+			text: i18n.ts.unrenote,
+			icon: 'ph-trash ph-bold ph-lg',
+			danger: true,
+			action: () => {
+				os.api('notes/delete', {
+					noteId: note.id,
+				});
+				isDeleted.value = true;
+			},
+		};
+	}
+
+	if (isMyRenote) {
+		pleaseLogin();
+		os.popupMenu([
+			getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
+			null,
+			getUnrenote(),
+		], renoteTime.value, {
+			viaKeyboard: viaKeyboard,
+		});
+	} else {
+		os.popupMenu([
+			getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
+			null,
+			getAbuseNoteMenu(note, i18n.ts.reportAbuseRenote),
+			$i.isModerator || $i.isAdmin ? getUnrenote() : undefined,
+		], renoteTime.value, {
+			viaKeyboard: viaKeyboard,
+		});
+	}
+}
+
+function animatedMFM() {
+	if (allowAnim.value) {
+		allowAnim.value = false;
+	} else {
+		os.confirm({
+			type: 'warning',
+			text: i18n.ts._animatedMFM._alert.text,
+			okText: i18n.ts._animatedMFM._alert.confirm,
+		}).then((res) => { if (!res.canceled) allowAnim.value = true; });
+	}
+}
+
+function focus() {
+	el.value.focus();
+}
+
+function blur() {
+	el.value.blur();
+}
+
+function focusBefore() {
+	focusPrev(el.value);
+}
+
+function focusAfter() {
+	focusNext(el.value);
+}
+
+function readPromo() {
+	os.api('promo/read', {
+		noteId: appearNote.id,
+	});
+	isDeleted.value = true;
+}
+
+function emitUpdReaction(emoji: string, delta: number) {
+	if (delta < 0) {
+		emit('removeReaction', emoji);
+	} else if (delta > 0) {
+		emit('reaction', emoji);
+	}
+}
+</script>
+
+<style lang="scss" module>
+.root {
+	position: relative;
+	transition: box-shadow 0.1s ease;
+	font-size: 1.05em;
+	overflow: clip;
+	contain: content;
+
+	// これらの指定はパフォーマンス向上には有効だが、ノートの高さは一定でないため、
+	// 下の方までスクロールすると上のノートの高さがここで決め打ちされたものに変化し、表示しているノートの位置が変わってしまう
+	// ノートがマウントされたときに自身の高さを取得し contain-intrinsic-size を設定しなおせばほぼ解決できそうだが、
+	// 今度はその処理自体がパフォーマンス低下の原因にならないか懸念される。また、被リアクションでも高さは変化するため、やはり多少のズレは生じる
+	// 一度レンダリングされた要素はブラウザがよしなにサイズを覚えておいてくれるような実装になるまで待った方が良さそう(なるのか?)
+	//content-visibility: auto;
+  //contain-intrinsic-size: 0 128px;
+
+	&:focus-visible {
+		outline: none;
+
+		&:after {
+			content: "";
+			pointer-events: none;
+			display: block;
+			position: absolute;
+			z-index: 10;
+			top: 0;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			margin: auto;
+			width: calc(100% - 8px);
+			height: calc(100% - 8px);
+			border: dashed 1px var(--focus);
+			border-radius: var(--radius);
+			box-sizing: border-box;
+		}
+	}
+
+	.footer {
+		position: relative;
+		z-index: 1;
+		margin-top: 0.4em;
+		width: max-content;
+		min-width: max-content;
+	}
+
+	&:hover > .article > .main > .footer > .footerButton {
+		opacity: 1;
+	}
+
+	&.showActionsOnlyHover {
+		.footer {
+			visibility: hidden;
+			position: absolute;
+			top: 12px;
+			right: 12px;
+			padding: 0 4px;
+			margin-bottom: 0 !important;
+			background: var(--popup);
+			border-radius: var(--radius-sm);
+			box-shadow: 0px 4px 32px var(--shadow);
+		}
+
+		.footerButton {
+			font-size: 90%;
+
+			&:not(:last-child) {
+				margin-right: 0;
+			}
+		}
+	}
+
+	&.showActionsOnlyHover:hover {
+		.footer {
+			visibility: visible;
+		}
+	}
+}
+
+.tip {
+	display: flex;
+	align-items: center;
+	padding: 16px 32px 8px 32px;
+	line-height: 24px;
+	font-size: 90%;
+	white-space: pre;
+	color: #d28a3f;
+}
+
+.tip + .article {
+	padding-top: 8px;
+}
+
+.replyTo {
+	opacity: 0.7;
+	padding-bottom: 0;
+}
+
+.renote {
+	position: relative;
+	display: flex;
+	align-items: center;
+	padding: 24px 38px 16px;
+	line-height: 28px;
+	white-space: pre;
+	color: var(--renote);
+
+	& + .article {
+		padding-top: 8px;
+	}
+
+	> .colorBar {
+		height: calc(100% - 6px);
+	}
+}
+
+.renoteAvatar {
+	flex-shrink: 0;
+	display: inline-block;
+	width: 28px;
+	height: 28px;
+	margin: 0 8px 0 0;
+}
+
+.renoteText {
+	overflow: hidden;
+	flex-shrink: 1;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+}
+
+.renoteUserName {
+	font-weight: bold;
+}
+
+.renoteInfo {
+	margin-left: auto;
+	font-size: 0.9em;
+}
+
+.renoteTime {
+	flex-shrink: 0;
+	color: inherit;
+}
+
+.renoteMenu {
+	margin-right: 4px;
+}
+
+.collapsedRenoteTarget {
+	display: flex;
+	align-items: center;
+	line-height: 28px;
+	white-space: pre;
+	padding: 8px 38px 24px;
+}
+
+.collapsedRenoteTargetAvatar {
+	flex-shrink: 0;
+	display: inline-block;
+	width: 28px;
+	height: 28px;
+	margin: 0 8px 0 0;
+}
+
+.collapsedRenoteTargetText {
+	overflow: hidden;
+	flex-shrink: 1;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+	font-size: 90%;
+	opacity: 0.7;
+	cursor: pointer;
+
+	&:hover {
+		text-decoration: underline;
+	}
+}
+
+.article {
+	position: relative;
+	padding: 28px 32px;
+}
+
+.colorBar {
+	position: absolute;
+	top: 8px;
+	left: 8px;
+	width: 5px;
+	height: calc(100% - 16px);
+	border-radius: var(--radius-ellipse);
+	pointer-events: none;
+}
+
+.avatar {
+	flex-shrink: 0;
+	display: block !important;
+	position: sticky !important;
+	margin: 0 14px 0 0;
+	width: 58px;
+	height: 58px;
+	position: sticky !important;
+	top: calc(22px + var(--stickyTop, 0px));
+	left: 0;
+	transition: top 0.5s;
+
+	&.avatarReplyTo {
+		position: relative !important;
+		top: 0 !important;
+	}
+}
+
+.main {
+	flex: 1;
+	min-width: 0;
+}
+
+.cw {
+	display: block;
+	margin: 0;
+	padding: 0;
+	overflow-wrap: break-word;
+}
+
+.showLess {
+	width: 100%;
+	margin-top: 14px;
+	position: sticky;
+	bottom: calc(var(--stickyBottom, 0px) - 100px);
+}
+
+.showLessLabel {
+	display: inline-block;
+	background: var(--popup);
+	padding: 6px 10px;
+	font-size: 0.8em;
+	border-radius: var(--radius-ellipse);
+	box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
+}
+
+.contentCollapsed {
+	position: relative;
+	max-height: 9em;
+	overflow: clip;
+}
+
+.collapsed {
+	display: block;
+	position: absolute;
+	bottom: 0;
+	left: 0;
+	z-index: 2;
+	width: 100%;
+	height: 64px;
+	//background: linear-gradient(0deg, var(--panel), var(--X15));
+
+	&:hover > .collapsedLabel {
+		background: var(--panelHighlight);
+	}
+}
+
+.collapsedLabel {
+	display: inline-block;
+	background: var(--panel);
+	padding: 6px 10px;
+	font-size: 0.8em;
+	border-radius: var(--radius-ellipse);
+	box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
+}
+
+.text {
+	overflow-wrap: break-word;
+}
+
+.replyIcon {
+	color: var(--accent);
+	margin-right: 0.5em;
+}
+
+.translation {
+	border: solid 0.5px var(--divider);
+	border-radius: var(--radius);
+	padding: 12px;
+	margin-top: 8px;
+}
+
+.urlPreview {
+	margin-top: 8px;
+}
+
+.playMFMButton {
+	margin-top: 5px;
+}
+
+.poll {
+	font-size: 80%;
+}
+
+.quote {
+	padding: 8px 0;
+}
+
+.quoteNote {
+	padding: 16px;
+	// Made border solid, stylistic choice
+	border: solid 1px var(--renote);
+	border-radius: var(--radius-sm);
+	overflow: clip;
+}
+
+.channel {
+	opacity: 0.7;
+	font-size: 80%;
+}
+
+.footer {
+	margin-bottom: -14px;
+}
+
+.footerButton {
+	margin: 0;
+	padding: 8px;
+	opacity: 0.7;
+
+	&:not(:last-child) {
+		margin-right: 1.5em;
+	}
+
+	&:hover {
+		color: var(--fgHighlighted);
+	}
+}
+
+.footerButtonCount {
+	display: inline;
+	margin: 0 0 0 8px;
+	opacity: 0.7;
+}
+
+@container (max-width: 580px) {
+	.root {
+		font-size: 0.95em;
+	}
+
+	.renote {
+		padding: 24px 28px 16px;
+	}
+
+	.collapsedRenoteTarget {
+		padding: 8px 28px 24px;
+	}
+
+	.article {
+		padding: 24px 26px;
+	}
+
+	.avatar {
+		width: 50px;
+		height: 50px;
+	}
+}
+
+@container (max-width: 500px) {
+	.root {
+		font-size: 0.9em;
+	}
+
+	.article {
+		padding: 23px 25px;
+	}
+
+	.footer {
+		margin-bottom: -8px;
+	}
+}
+
+@container (max-width: 480px) {
+	.renote {
+		padding: 20px 24px 8px;
+	}
+
+	.tip {
+		padding: 8px 16px 0 16px;
+	}
+
+	.collapsedRenoteTarget {
+		padding: 8px 24px 20px;
+		margin-top: 4px;
+	}
+
+	.article {
+		padding: 22px 24px;
+	}
+}
+
+@container (max-width: 450px) {
+	.avatar {
+		margin: 0 10px 0 0;
+		width: 46px;
+		height: 46px;
+		top: calc(14px + var(--stickyTop, 0px));
+	}
+}
+
+@container (max-width: 400px) {
+	.root:not(.showActionsOnlyHover) {
+		.footerButton {
+			&:not(:last-child) {
+				margin-right: 0.2em;
+			}
+		}
+	}
+}
+
+@container (max-width: 350px) {
+	.root:not(.showActionsOnlyHover) {
+		.footerButton {
+			&:not(:last-child) {
+				margin-right: 0.1em;
+			}
+		}
+	}
+
+	.colorBar {
+		top: 6px;
+		left: 6px;
+		width: 4px;
+		height: calc(100% - 12px);
+	}
+}
+
+@container (max-width: 300px) {
+	.avatar {
+		width: 44px;
+		height: 44px;
+	}
+
+	.root:not(.showActionsOnlyHover) {
+		.footerButton {
+			&:not(:last-child) {
+				margin-right: 0.1em;
+			}
+		}
+	}
+}
+
+@container (max-width: 250px) {
+	.quoteNote {
+		padding: 12px;
+	}
+}
+
+.muted {
+	padding: 8px;
+	text-align: center;
+	opacity: 0.7;
+}
+
+.reactionOmitted {
+	display: inline-block;
+	height: 32px;
+	margin: 2px;
+	padding: 0 6px;
+	opacity: .8;
+}
+
+.clickToOpen {
+	cursor: pointer;
+}
+</style>
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
new file mode 100644
index 0000000000..91db2047e0
--- /dev/null
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -0,0 +1,1082 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div
+	v-if="!muted"
+	v-show="!isDeleted"
+	ref="el"
+	v-hotkey="keymap"
+	:class="$style.root"
+>
+	<div v-if="appearNote.reply && appearNote.reply.replyId && !conversationLoaded" style="padding: 16px">
+		<MkButton style="margin: 0 auto;" primary rounded @click="loadConversation">{{ i18n.ts.loadConversation }}</MkButton>
+	</div>
+	<div v-if="isRenote" :class="$style.renote">
+		<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
+		<i class="ph-rocket-launch ph-bold ph-lg" style="margin-right: 4px;"></i>
+		<span :class="$style.renoteText">
+			<I18n :src="i18n.ts.renotedBy" tag="span">
+				<template #user>
+					<MkA v-user-preview="note.userId" :class="$style.renoteName" :to="userPage(note.user)">
+						<MkUserName :user="note.user"/>
+					</MkA>
+				</template>
+			</I18n>
+		</span>
+		<div :class="$style.renoteInfo">
+			<button ref="renoteTime" class="_button" :class="$style.renoteTime" @click="showRenoteMenu()">
+				<i v-if="isMyRenote" class="ph-dots-three ph-bold ph-lg" style="margin-right: 4px;"></i>
+				<MkTime :time="note.createdAt"/>
+			</button>
+			<span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]">
+				<i v-if="note.visibility === 'home'" class="ph-house ph-bold ph-lg"></i>
+				<i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
+				<i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
+			</span>
+			<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
+		</div>
+	</div>
+	<template v-if="appearNote.reply && appearNote.reply.replyId">
+		<SkNoteSub v-for="note in conversation" :key="note.id" :class="$style.replyToMore" :note="note" :expandAllCws="props.expandAllCws"/>
+	</template>
+	<SkNoteSub v-if="appearNote.reply" :note="appearNote.reply" :class="$style.replyTo" :expandAllCws="props.expandAllCws"/>
+	<article :class="$style.note" @contextmenu.stop="onContextmenu">
+		<header :class="$style.noteHeader">
+			<MkAvatar :class="$style.noteHeaderAvatar" :user="appearNote.user" indicator link preview/>
+			<div style="display: flex; align-items: center; white-space: nowrap; overflow: hidden;">
+				<div :class="$style.noteHeaderBody">
+					<div>
+						<MkA v-user-preview="appearNote.user.id" :class="$style.noteHeaderName" :to="userPage(appearNote.user)">
+							<MkUserName :nowrap="false" :user="appearNote.user"/>
+						</MkA>
+						<span v-if="appearNote.user.isBot" :class="$style.isBot">bot</span>
+						<span v-if="appearNote.user.badgeRoles" :class="$style.badgeRoles">
+							<img v-for="role in appearNote.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
+						</span>
+					</div>
+					<div :class="$style.noteHeaderUsername"><MkAcct :user="appearNote.user"/></div>
+				</div>
+			</div>
+			<div style="display: flex; align-items: flex-end; margin-left: auto;">
+				<div :class="$style.noteHeaderBody">
+					<div :class="$style.noteHeaderInfo">
+						<span v-if="appearNote.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[appearNote.visibility]">
+							<i v-if="appearNote.visibility === 'home'" class="ph-house ph-bold ph-lg"></i>
+							<i v-else-if="appearNote.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
+							<i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
+						</span>
+						<span v-if="appearNote.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
+						<span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
+					</div>
+					<SkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/>
+				</div>
+			</div>
+		</header>
+		<div :class="$style.noteContent">
+			<p v-if="appearNote.cw != null" :class="$style.cw">
+				<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
+				<MkCwButton v-model="showContent" :note="appearNote"/>
+			</p>
+			<div v-show="appearNote.cw == null || showContent">
+				<span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span>
+				<Mfm
+					v-if="appearNote.text"
+					:parsedNodes="parsed"
+					:text="appearNote.text"
+					:author="appearNote.user"
+					:nyaize="'respect'"
+					:emojiUrls="appearNote.emojis"
+					:enableEmojiMenu="true"
+					:enableEmojiMenuReaction="true"
+					:isAnim="allowAnim"
+				/>
+				<a v-if="appearNote.renote != null" :class="$style.rn">RN:</a>
+				<div v-if="translating || translation" :class="$style.translation">
+					<MkLoading v-if="translating" mini/>
+					<div v-else>
+						<b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b>
+						<Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/>
+					</div>
+				</div>
+				<MkButton v-if="!allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" v-on:click.stop><i class="ph-play ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.play }}</MkButton>
+				<MkButton v-else-if="!defaultStore.state.animatedMfm && allowAnim && animated" :class="$style.playMFMButton" :small="true" @click="animatedMFM()" v-on:click.stop><i class="ph-stop ph-bold ph-lg "></i> {{ i18n.ts._animatedMFM.stop }}</MkButton>
+				<div v-if="appearNote.files.length > 0">
+					<MkMediaList :mediaList="appearNote.files"/>
+				</div>
+				<MkPoll v-if="appearNote.poll" ref="pollViewer" :note="appearNote" :class="$style.poll"/>
+				<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" style="margin-top: 6px;"/>
+				<div v-if="appearNote.renote" :class="$style.quote"><SkNoteSimple :note="appearNote.renote" :class="$style.quoteNote" :expandAllCws="props.expandAllCws"/></div>
+			</div>
+			<MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ph-television ph-bold ph-lg"></i> {{ appearNote.channel.name }}</MkA>
+		</div>
+		<footer :class="$style.footer">
+			<div :class="$style.noteFooterInfo">
+				<div v-if="appearNote.updatedAt">
+					{{ i18n.ts.edited }}: <MkTime :time="appearNote.updatedAt" mode="detail"/>
+				</div>
+				<MkA :to="notePage(appearNote)">
+					<MkTime :time="appearNote.createdAt" mode="detail" colored/>
+				</MkA>
+			</div>
+			<MkReactionsViewer ref="reactionsViewer" :note="appearNote"/>
+			<button class="_button" :class="$style.noteFooterButton" @click="reply()">
+				<i class="ph-arrow-u-up-left ph-bold ph-lg"></i>
+				<p v-if="appearNote.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ appearNote.repliesCount }}</p>
+			</button>
+			<button
+				v-if="canRenote"
+				ref="renoteButton"
+				class="_button"
+				:class="$style.noteFooterButton"
+				:style="renoted ? 'color: var(--accent) !important;' : ''"
+				@mousedown="renoted ? undoRenote() : renote()"
+			>
+				<i class="ph-rocket-launch ph-bold ph-lg"></i>
+				<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ appearNote.renoteCount }}</p>
+			</button>
+			<button v-else class="_button" :class="$style.noteFooterButton" disabled>
+				<i class="ph-prohibit ph-bold ph-lg"></i>
+			</button>
+			<button
+				v-if="canRenote"
+				ref="quoteButton"
+				class="_button"
+				:class="$style.noteFooterButton"
+				@mousedown="quote()"
+			>
+				<i class="ph-quotes ph-bold ph-lg"></i>
+			</button>
+			<button v-if="appearNote.myReaction == null && appearNote.reactionAcceptance !== 'likeOnly'" ref="likeButton" :class="$style.noteFooterButton" class="_button" @mousedown="like()">
+				<i class="ph-heart ph-bold ph-lg"></i>
+			</button>
+			<button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()">
+				<i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i>
+				<i v-else class="ph-smiley ph-bold ph-lg"></i>
+			</button>
+			<button v-if="appearNote.myReaction != null" ref="reactButton" class="_button" :class="[$style.noteFooterButton, $style.reacted]" @click="undoReact(appearNote)">
+				<i class="ph-minus ph-bold ph-lg"></i>
+			</button>
+			<button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" class="_button" :class="$style.noteFooterButton" @mousedown="clip()">
+				<i class="ph-paperclip ph-bold ph-lg"></i>
+			</button>
+			<button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown="menu()">
+				<i class="ph-dots-three ph-bold ph-lg"></i>
+			</button>
+		</footer>
+	</article>
+	<div :class="$style.tabs">
+		<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'replies' }]" @click="tab = 'replies'"><i class="ph-arrow-u-up-left ph-bold ph-lg"></i> {{ i18n.ts.replies }}</button>
+		<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'renotes' }]" @click="tab = 'renotes'"><i class="ph-rocket-launch ph-bold ph-lg"></i> {{ i18n.ts.renotes }}</button>
+		<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'quotes' }]" @click="tab = 'quotes'"><i class="ph-quotes ph-bold ph-lg"></i> {{ i18n.ts._notification._types.quote }}</button>
+		<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions' }]" @click="tab = 'reactions'"><i class="ph-smiley ph-bold ph-lg"></i> {{ i18n.ts.reactions }}</button>
+	</div>
+	<div>
+		<div v-if="tab === 'replies'" :class="$style.tab_replies">
+			<div v-if="!repliesLoaded" style="padding: 16px">
+				<MkButton style="margin: 0 auto;" primary rounded @click="loadReplies">{{ i18n.ts.loadReplies }}</MkButton>
+			</div>
+			<SkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws"/>
+		</div>
+		<div v-else-if="tab === 'renotes'" :class="$style.tab_renotes">
+			<MkPagination :pagination="renotesPagination" :disableAutoLoad="true">
+				<template #default="{ items }">
+					<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>
+		<div v-if="tab === 'quotes'" :class="$style.tab_replies">
+			<div v-if="!quotesLoaded" style="padding: 16px">
+				<MkButton style="margin: 0 auto;" primary rounded @click="loadQuotes">{{ i18n.ts.loadReplies }}</MkButton>
+			</div>
+			<SkNoteSub v-for="note in quotes" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws"/>
+		</div>
+		<div v-else-if="tab === 'reactions'" :class="$style.tab_reactions">
+			<div :class="$style.reactionTabs">
+				<button v-for="reaction in Object.keys(appearNote.reactions)" :key="reaction" :class="[$style.reactionTab, { [$style.reactionTabActive]: reactionTabType === reaction }]" class="_button" @click="reactionTabType = reaction">
+					<MkReactionIcon :reaction="reaction"/>
+					<span style="margin-left: 4px;">{{ appearNote.reactions[reaction] }}</span>
+				</button>
+			</div>
+			<MkPagination v-if="reactionTabType" :key="reactionTabType" :pagination="reactionsPagination" :disableAutoLoad="true">
+				<template #default="{ items }">
+					<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>
+	</div>
+</div>
+<div v-else class="_panel" :class="$style.muted" @click="muted = false">
+	<I18n :src="i18n.ts.userSaysSomething" tag="small">
+		<template #name>
+			<MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)">
+				<MkUserName :user="appearNote.user"/>
+			</MkA>
+		</template>
+	</I18n>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { computed, inject, onMounted, provide, ref, shallowRef, watch } from 'vue';
+import * as mfm from 'mfm-js';
+import * as Misskey from 'misskey-js';
+import SkNoteSub from '@/components/SkNoteSub.vue';
+import SkNoteSimple from '@/components/SkNoteSimple.vue';
+import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
+import MkMediaList from '@/components/MkMediaList.vue';
+import MkCwButton from '@/components/MkCwButton.vue';
+import MkPoll from '@/components/MkPoll.vue';
+import MkUsersTooltip from '@/components/MkUsersTooltip.vue';
+import MkUrlPreview from '@/components/MkUrlPreview.vue';
+import SkInstanceTicker from '@/components/SkInstanceTicker.vue';
+import { pleaseLogin } from '@/scripts/please-login.js';
+import { checkWordMute } from '@/scripts/check-word-mute.js';
+import { userPage } from '@/filters/user.js';
+import { notePage } from '@/filters/note.js';
+import * as os from '@/os.js';
+import { defaultStore, noteViewInterruptors } from '@/store.js';
+import { reactionPicker } from '@/scripts/reaction-picker.js';
+import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js';
+import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js';
+import { useNoteCapture } from '@/scripts/use-note-capture.js';
+import { deepClone } from '@/scripts/clone.js';
+import { useTooltip } from '@/scripts/use-tooltip.js';
+import { claimAchievement } from '@/scripts/achievements.js';
+import { MenuItem } from '@/types/menu.js';
+import { checkAnimationFromMfm } from '@/scripts/check-animated-mfm.js';
+import MkRippleEffect from '@/components/MkRippleEffect.vue';
+import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
+import MkUserCardMini from '@/components/MkUserCardMini.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkReactionIcon from '@/components/MkReactionIcon.vue';
+import MkButton from '@/components/MkButton.vue';
+
+const props = defineProps<{
+	note: Misskey.entities.Note;
+	expandAllCws?: boolean;
+}>();
+
+const inChannel = inject('inChannel', null);
+
+let note = $ref(deepClone(props.note));
+
+// plugin
+if (noteViewInterruptors.length > 0) {
+	onMounted(async () => {
+		let result: Misskey.entities.Note | null = deepClone(note);
+		for (const interruptor of noteViewInterruptors) {
+			try {
+				result = await interruptor.handler(result);
+				if (result === null) {
+					isDeleted.value = true;
+					return;
+				}
+			} catch (err) {
+				console.error(err);
+			}
+		}
+		note = result;
+	});
+}
+
+const isRenote = (
+	note.renote != null &&
+	note.text == null &&
+	note.fileIds.length === 0 &&
+	note.poll == null
+);
+
+const el = shallowRef<HTMLElement>();
+const menuButton = shallowRef<HTMLElement>();
+const menuVersionsButton = shallowRef<HTMLElement>();
+const renoteButton = shallowRef<HTMLElement>();
+const renoteTime = shallowRef<HTMLElement>();
+const reactButton = shallowRef<HTMLElement>();
+const quoteButton = shallowRef<HTMLElement>();
+const clipButton = shallowRef<HTMLElement>();
+const likeButton = shallowRef<HTMLElement>();
+let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note);
+const renoteUrl = appearNote.renote ? appearNote.renote.url : null;
+const renoteUri = appearNote.renote ? appearNote.renote.uri : null;
+
+const isMyRenote = $i && ($i.id === note.userId);
+const showContent = ref(false);
+const isDeleted = ref(false);
+const renoted = ref(false);
+const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
+const translation = ref(null);
+const translating = ref(false);
+const parsed = $computed(() => appearNote.text ? mfm.parse(appearNote.text) : null);
+const urls = parsed ? extractUrlFromMfm(parsed).filter(u => u !== renoteUrl && u !== renoteUri) : null;
+const animated = $computed(() => parsed ? checkAnimationFromMfm(parsed) : null);
+const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
+const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
+const conversation = ref<Misskey.entities.Note[]>([]);
+const replies = ref<Misskey.entities.Note[]>([]);
+const quotes = ref<Misskey.entities.Note[]>([]);
+const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || appearNote.userId === $i.id);
+const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
+
+watch(() => props.expandAllCws, (expandAllCws) => {
+	if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
+});
+
+if ($i) {
+	os.api("notes/renotes", {
+		noteId: appearNote.id,
+		userId: $i.id,
+		limit: 1,
+	}).then((res) => {
+		renoted.value = res.length > 0;
+	});
+}
+
+const keymap = {
+	'r': () => reply(true),
+	'e|a|plus': () => react(true),
+	'q': () => renoteButton.value.renote(true),
+	'esc': blur,
+	'm|o': () => menu(true),
+	's': () => showContent.value !== showContent.value,
+};
+
+provide('react', (reaction: string) => {
+	os.api('notes/reactions/create', {
+		noteId: appearNote.id,
+		reaction: reaction,
+	});
+});
+
+let tab = $ref('replies');
+let reactionTabType = $ref(null);
+
+const renotesPagination = $computed(() => ({
+	endpoint: 'notes/renotes',
+	limit: 10,
+	params: {
+		noteId: appearNote.id,
+	},
+}));
+
+const reactionsPagination = $computed(() => ({
+	endpoint: 'notes/reactions',
+	limit: 10,
+	params: {
+		noteId: appearNote.id,
+		type: reactionTabType,
+	},
+}));
+
+useNoteCapture({
+	rootEl: el,
+	note: $$(appearNote),
+	pureNote: $$(note),
+	isDeletedRef: isDeleted,
+});
+
+useTooltip(renoteButton, async (showing) => {
+	const renotes = await os.api('notes/renotes', {
+		noteId: appearNote.id,
+		limit: 11,
+	});
+
+	const users = renotes.map(x => x.user);
+
+	if (users.length < 1) return;
+
+	os.popup(MkUsersTooltip, {
+		showing,
+		users,
+		count: appearNote.renoteCount,
+		targetElement: renoteButton.value,
+	}, {}, 'closed');
+});
+
+useTooltip(quoteButton, async (showing) => {
+	const renotes = await os.api('notes/renotes', {
+		noteId: appearNote.id,
+		limit: 11,
+		quote: true,
+	});
+
+	const users = renotes.map(x => x.user);
+
+	if (users.length < 1) return;
+
+	os.popup(MkUsersTooltip, {
+		showing,
+		users,
+		count: appearNote.renoteCount,
+		targetElement: quoteButton.value,
+	}, {}, 'closed');
+});
+
+type Visibility = 'public' | 'home' | 'followers' | 'specified';
+
+function smallerVisibility(a: Visibility | string, b: Visibility | string): Visibility {
+	if (a === 'specified' || b === 'specified') return 'specified';
+	if (a === 'followers' || b === 'followers') return 'followers';
+	if (a === 'home' || b === 'home') return 'home';
+	// if (a === 'public' || b === 'public')
+	return 'public';
+}
+
+function renote() {
+	pleaseLogin();
+	showMovedDialog();
+
+	if (appearNote.channel) {
+		const el = renoteButton.value as HTMLElement | null | undefined;
+		if (el) {
+			const rect = el.getBoundingClientRect();
+			const x = rect.left + (el.offsetWidth / 2);
+			const y = rect.top + (el.offsetHeight / 2);
+			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+		}
+
+		os.api('notes/create', {
+			renoteId: appearNote.id,
+			channelId: appearNote.channelId,
+		}).then(() => {
+			os.toast(i18n.ts.renoted);
+			renoted.value = true;
+		});
+	} else if (!appearNote.channel || appearNote.channel?.allowRenoteToExternal) {
+		const el = renoteButton.value as HTMLElement | null | undefined;
+		if (el) {
+			const rect = el.getBoundingClientRect();
+			const x = rect.left + (el.offsetWidth / 2);
+			const y = rect.top + (el.offsetHeight / 2);
+			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+		}
+
+		const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
+		const localOnly = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
+
+		let visibility = appearNote.visibility;
+		visibility = smallerVisibility(visibility, configuredVisibility);
+		if (appearNote.channel?.isSensitive) {
+			visibility = smallerVisibility(visibility, 'home');
+		}
+
+		os.api('notes/create', {
+			localOnly,
+			visibility,
+			renoteId: appearNote.id,
+		}).then(() => {
+			os.toast(i18n.ts.renoted);
+			renoted.value = true;
+		});
+	}
+}
+
+function quote() {
+	pleaseLogin();
+	showMovedDialog();
+
+	if (appearNote.channel) {
+		os.post({
+			renote: appearNote,
+			channel: appearNote.channel,
+		}).then(() => {
+			os.api("notes/renotes", {
+				noteId: appearNote.id,
+				userId: $i.id,
+				limit: 1,
+				quote: true,
+			}).then((res) => {
+				if (!(res.length > 0)) return;
+				const el = quoteButton.value as HTMLElement | null | undefined;
+				if (el && res.length > 0) {
+					const rect = el.getBoundingClientRect();
+					const x = rect.left + (el.offsetWidth / 2);
+					const y = rect.top + (el.offsetHeight / 2);
+					os.popup(MkRippleEffect, { x, y }, {}, 'end');
+				}
+
+				os.toast(i18n.ts.quoted);
+			});
+		});
+	} else {
+		os.post({
+			renote: appearNote,
+		}).then(() => {
+			os.api("notes/renotes", {
+				noteId: appearNote.id,
+				userId: $i.id,
+				limit: 1,
+				quote: true,
+			}).then((res) => {
+				if (!(res.length > 0)) return;
+				const el = quoteButton.value as HTMLElement | null | undefined;
+				if (el && res.length > 0) {
+					const rect = el.getBoundingClientRect();
+					const x = rect.left + (el.offsetWidth / 2);
+					const y = rect.top + (el.offsetHeight / 2);
+					os.popup(MkRippleEffect, { x, y }, {}, 'end');
+				}
+
+				os.toast(i18n.ts.quoted);
+			});
+		});
+	}
+}
+
+function reply(viaKeyboard = false): void {
+	pleaseLogin();
+	showMovedDialog();
+	os.post({
+		reply: appearNote,
+		channel: appearNote.channel,
+		animation: !viaKeyboard,
+	}, () => {
+		focus();
+	});
+}
+
+function react(viaKeyboard = false): void {
+	pleaseLogin();
+	showMovedDialog();
+	if (appearNote.reactionAcceptance === 'likeOnly') {
+		os.api('notes/like', {
+			noteId: appearNote.id,
+			override: defaultLike.value,
+		});
+		const el = reactButton.value as HTMLElement | null | undefined;
+		if (el) {
+			const rect = el.getBoundingClientRect();
+			const x = rect.left + (el.offsetWidth / 2);
+			const y = rect.top + (el.offsetHeight / 2);
+			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+		}
+	} else {
+		blur();
+		reactionPicker.show(reactButton.value, reaction => {
+			os.api('notes/reactions/create', {
+				noteId: appearNote.id,
+				reaction: reaction,
+			});
+			if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) {
+				claimAchievement('reactWithoutRead');
+			}
+		}, () => {
+			focus();
+		});
+	}
+}
+
+function like(): void {
+	pleaseLogin();
+	showMovedDialog();
+	os.api('notes/like', {
+		noteId: appearNote.id,
+		override: defaultLike.value,
+	});
+	const el = likeButton.value as HTMLElement | null | undefined;
+	if (el) {
+		const rect = el.getBoundingClientRect();
+		const x = rect.left + (el.offsetWidth / 2);
+		const y = rect.top + (el.offsetHeight / 2);
+		os.popup(MkRippleEffect, { x, y }, {}, 'end');
+	}
+}
+
+function undoReact(note): void {
+	const oldReaction = note.myReaction;
+	if (!oldReaction) return;
+	os.api('notes/reactions/delete', {
+		noteId: note.id,
+	});
+}
+
+function undoRenote() : void {
+	if (!renoted.value) return;
+	os.api("notes/unrenote", {
+		noteId: appearNote.id,
+	});
+	os.toast(i18n.ts.rmboost);
+	renoted.value = false;
+
+	const el = renoteButton.value as HTMLElement | null | undefined;
+	if (el) {
+		const rect = el.getBoundingClientRect();
+		const x = rect.left + (el.offsetWidth / 2);
+		const y = rect.top + (el.offsetHeight / 2);
+		os.popup(MkRippleEffect, { x, y }, {}, 'end');
+	}
+}
+
+function onContextmenu(ev: MouseEvent): void {
+	const isLink = (el: HTMLElement) => {
+		if (el.tagName === 'A') return true;
+		if (el.parentElement) {
+			return isLink(el.parentElement);
+		}
+	};
+	if (isLink(ev.target)) return;
+	if (window.getSelection().toString() !== '') return;
+
+	if (defaultStore.state.useReactionPickerForContextMenu) {
+		ev.preventDefault();
+		react();
+	} else {
+		const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted });
+		os.contextMenu(menu, ev).then(focus).finally(cleanup);
+	}
+}
+
+function menu(viaKeyboard = false): void {
+	const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted });
+	os.popupMenu(menu, menuButton.value, {
+		viaKeyboard,
+	}).then(focus).finally(cleanup);
+}
+
+async function menuVersions(viaKeyboard = false): Promise<void> {
+	const { menu, cleanup } = await getNoteVersionsMenu({ note: note, menuVersionsButton });
+	os.popupMenu(menu, menuVersionsButton.value, {
+		viaKeyboard,
+	}).then(focus).finally(cleanup);
+}
+
+async function clip() {
+	os.popupMenu(await getNoteClipMenu({ note: note, isDeleted }), clipButton.value).then(focus);
+}
+
+function showRenoteMenu(viaKeyboard = false): void {
+	if (!isMyRenote) return;
+	pleaseLogin();
+	os.popupMenu([{
+		text: i18n.ts.unrenote,
+		icon: 'ph-trash ph-bold ph-lg',
+		danger: true,
+		action: () => {
+			os.api('notes/delete', {
+				noteId: note.id,
+			});
+			isDeleted.value = true;
+		},
+	}], renoteTime.value, {
+		viaKeyboard: viaKeyboard,
+	});
+}
+
+function focus() {
+	el.value.focus();
+}
+
+function blur() {
+	el.value.blur();
+}
+
+const repliesLoaded = ref(false);
+
+function loadReplies() {
+	repliesLoaded.value = true;
+	os.api('notes/children', {
+		noteId: appearNote.id,
+		limit: 30,
+		showQuotes: false,
+	}).then(res => {
+		replies.value = res;
+	});
+}
+
+loadReplies();
+
+const quotesLoaded = ref(false);
+
+function loadQuotes() {
+	quotesLoaded.value = true;
+	os.api('notes/renotes', {
+		noteId: appearNote.id,
+		limit: 30,
+		quote: true,
+	}).then(res => {
+		quotes.value = res;
+	});
+}
+
+loadQuotes();
+
+const conversationLoaded = ref(false);
+
+function loadConversation() {
+	conversationLoaded.value = true;
+	os.api('notes/conversation', {
+		noteId: appearNote.replyId,
+	}).then(res => {
+		conversation.value = res.reverse();
+	});
+}
+
+if (appearNote.reply && appearNote.reply.replyId && defaultStore.state.autoloadConversation) loadConversation();
+
+function animatedMFM() {
+	if (allowAnim.value) {
+		allowAnim.value = false;
+	} else {
+		os.confirm({
+			type: 'warning',
+			text: i18n.ts._animatedMFM._alert.text,
+			okText: i18n.ts._animatedMFM._alert.confirm,
+		}).then((res) => { if (!res.canceled) allowAnim.value = true; });
+	}
+}
+</script>
+
+<style lang="scss" module>
+.root {
+	position: relative;
+	transition: box-shadow 0.1s ease;
+	overflow: clip;
+	contain: content;
+}
+
+.footer {
+		position: relative;
+		z-index: 1;
+		margin-top: 0.4em;
+		width: max-content;
+		min-width: min-content;
+		max-width: fit-content;
+}
+
+.replyTo {
+	opacity: 0.7;
+	padding-bottom: 0;
+}
+
+.replyToMore {
+	opacity: 0.7;
+}
+
+.renote {
+	display: flex;
+	align-items: center;
+	padding: 16px 32px 8px 32px;
+	line-height: 28px;
+	white-space: pre;
+	color: var(--renote);
+}
+
+.renoteAvatar {
+	flex-shrink: 0;
+	display: inline-block;
+	width: 28px;
+	height: 28px;
+	margin: 0 8px 0 0;
+	border-radius: var(--radius-sm);
+}
+
+.renoteText {
+	overflow: hidden;
+	flex-shrink: 1;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+}
+
+.renoteName {
+	font-weight: bold;
+}
+
+.renoteInfo {
+	margin-left: auto;
+	font-size: 0.9em;
+}
+
+.renoteTime {
+	flex-shrink: 0;
+	color: inherit;
+}
+
+.renote + .note {
+	padding-top: 8px;
+}
+
+.note {
+	padding: 32px;
+	font-size: 1.2em;
+	overflow: hidden;
+
+	&:hover > .main > .footer > .button {
+		opacity: 1;
+	}
+}
+
+.noteHeader {
+	display: flex;
+	position: relative;
+	margin-bottom: 16px;
+	align-items: center;
+	z-index: 2;
+}
+
+.noteHeaderAvatar {
+	display: block;
+	flex-shrink: 0;
+	width: 58px;
+	height: 58px;
+}
+
+.noteHeaderBody {
+	flex: 1;
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	padding-left: 16px;
+	font-size: 0.95em;
+}
+
+.noteHeaderName {
+	font-weight: bold;
+	line-height: 1.3;
+}
+
+.isBot {
+	display: inline-block;
+	margin: 0 0.5em;
+	padding: 4px 6px;
+	font-size: 80%;
+	line-height: 1;
+	border: solid 0.5px var(--divider);
+	border-radius: var(--radius-xs);
+}
+
+.noteHeaderInfo {
+	float: right;
+	text-align: right;
+}
+
+.noteHeaderUsername {
+	margin-bottom: 2px;
+	line-height: 1.3;
+	word-wrap: anywhere;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+
+	&::-webkit-scrollbar {
+		display: none;
+	}
+}
+
+.playMFMButton {
+	margin-top: 5px;
+}
+
+.noteContent {
+	container-type: inline-size;
+	overflow-wrap: break-word;
+	z-index: 1;
+}
+
+.cw {
+	cursor: default;
+	display: block;
+	margin: 0;
+	padding: 0;
+	overflow-wrap: break-word;
+}
+
+.noteReplyTarget {
+	color: var(--accent);
+	margin-right: 0.5em;
+}
+
+.rn {
+	margin-left: 4px;
+	font-style: oblique;
+	color: var(--renote);
+}
+
+.translation {
+	border: solid 0.5px var(--divider);
+	border-radius: var(--radius);
+	padding: 12px;
+	margin-top: 8px;
+}
+
+.poll {
+	font-size: 80%;
+}
+
+.quote {
+	padding: 8px 0;
+}
+
+.quoteNote {
+	padding: 16px;
+	border: dashed 1px var(--renote);
+	border-radius: var(--radius-sm);
+	overflow: clip;
+}
+
+.channel {
+	opacity: 0.7;
+	font-size: 80%;
+}
+
+.noteFooterInfo {
+	margin: 16px 0;
+	opacity: 0.7;
+	font-size: 0.9em;
+}
+
+.noteFooterButton {
+	margin: 0;
+	padding: 8px;
+	opacity: 0.7;
+
+	&:not(:last-child) {
+		margin-right: 1.5em;
+	}
+
+	&:hover {
+		color: var(--fgHighlighted);
+	}
+}
+
+.noteFooterButtonCount {
+	display: inline;
+	margin: 0 0 0 8px;
+	opacity: 0.7;
+
+	&.reacted {
+		color: var(--accent);
+	}
+}
+
+.reply:not(:first-child) {
+	border-top: solid 0.5px var(--divider);
+}
+
+.tabs {
+	border-top: solid 0.5px var(--divider);
+	border-bottom: solid 0.5px var(--divider);
+	display: flex;
+}
+
+.tab {
+	flex: 1;
+	padding: 12px 8px;
+	border-top: solid 2px transparent;
+	border-bottom: solid 2px transparent;
+}
+
+.tabActive {
+	border-bottom: solid 2px var(--accent);
+}
+
+.tab_renotes {
+	padding: 16px;
+}
+
+.tab_reactions {
+	padding: 16px;
+}
+
+.reactionTabs {
+	display: flex;
+	gap: 8px;
+	flex-wrap: wrap;
+	margin-bottom: 8px;
+}
+
+.reactionTab {
+	padding: 4px 6px;
+	border: solid 1px var(--divider);
+	border-radius: var(--radius-sm);
+}
+
+.reactionTabActive {
+	border-color: var(--accent);
+}
+
+@container (max-width: 500px) {
+	.root {
+		font-size: 0.9em;
+	}
+}
+
+@container (max-width: 450px) {
+	.renote {
+		padding: 8px 16px 0 16px;
+	}
+
+	.note {
+		padding: 16px;
+	}
+
+	.noteHeaderAvatar {
+		width: 50px;
+		height: 50px;
+	}
+}
+
+@container (max-width: 350px) {
+	.noteFooterButton {
+		&:not(:last-child) {
+			margin-right: 0.1em;
+		}
+	}
+}
+
+@container (max-width: 300px) {
+	.root {
+		font-size: 0.825em;
+	}
+
+	.noteHeaderAvatar {
+		width: 50px;
+		height: 50px;
+	}
+
+	.noteFooterButton {
+		&:not(:last-child) {
+			margin-right: 0.1em;
+		}
+	}
+}
+
+.avatar {
+	flex-shrink: 0 !important;
+	display: block !important;
+	margin: 0 10px 0 0 !important;
+	width: 40px !important;
+	height: 40px !important;
+	border-radius: var(--radius-sm) !important;
+}
+
+.muted {
+	padding: 8px;
+	text-align: center;
+	opacity: 0.7;
+}
+
+.badgeRoles {
+	margin: 0 .5em 0 0;
+}
+
+.badgeRole {
+	height: 1.3em;
+	vertical-align: -20%;
+
+	& + .badgeRole {
+		margin-left: 0.2em;
+	}
+}
+</style>
diff --git a/packages/frontend/src/components/SkNoteHeader.vue b/packages/frontend/src/components/SkNoteHeader.vue
new file mode 100644
index 0000000000..d3ecdf17bb
--- /dev/null
+++ b/packages/frontend/src/components/SkNoteHeader.vue
@@ -0,0 +1,265 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<header v-if="!classic" :class="$style.root">
+	<div :class="$style.section">
+		<div style="display: flex;">
+			<div v-if="mock" :class="$style.name">
+				<MkUserName :user="note.user"/>
+			</div>
+			<MkA v-else v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)">
+				<MkUserName :user="note.user"/>
+			</MkA>
+			<div v-if="note.user.isBot" :class="$style.isBot">bot</div>
+			<div v-if="note.user.badgeRoles" :class="$style.badgeRoles">
+				<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
+			</div>
+		</div>
+		<div :class="$style.username"><MkAcct :user="note.user"/></div>
+	</div>
+	<div :class="$style.section">
+		<div :class="$style.info">
+			<div v-if="mock">
+				<MkTime :time="note.createdAt" colored/>
+			</div>
+			<MkA v-else :class="$style.time" :to="notePage(note)">
+				<MkTime :time="note.createdAt" colored/>
+			</MkA>
+			<span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]">
+				<i v-if="note.visibility === 'home'" class="ph-house ph-bold ph-lg"></i>
+				<i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
+				<i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
+			</span>
+			<span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em; cursor: pointer;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
+			<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
+			<span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span>
+		</div>
+		<div :class="$style.info"><SkInstanceTicker v-if="showTicker" style="cursor: pointer;" :instance="note.user.instance" @click.stop="showOnRemote()"/></div>
+	</div>
+</header>
+<header v-else :class="$style.classicRoot">
+	<div v-if="mock" :class="$style.name">
+		<MkUserName :user="note.user"/>
+	</div>
+	<MkA v-else v-user-preview="note.user.id" :class="$style.classicName" :to="userPage(note.user)">
+		<MkUserName :user="note.user"/>
+	</MkA>
+	<div v-if="note.user.isBot" :class="$style.isBot">bot</div>
+	<div :class="$style.classicUsername"><MkAcct :user="note.user"/></div>
+	<div v-if="note.user.badgeRoles" :class="$style.badgeRoles">
+		<img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/>
+	</div>
+	<SkInstanceTicker v-if="showTicker && !isMobile && defaultStore.state.showTickerOnReplies" style="cursor: pointer; max-height: 5px; top: 3px; position: relative; margin-top: 0px !important;" :instance="note.user.instance" @click.stop="showOnRemote()"/>
+	<div :class="$style.classicInfo">
+		<div v-if="mock">
+			<MkTime :time="note.createdAt" colored/>
+		</div>
+		<MkA v-else :to="notePage(note)">
+			<MkTime :time="note.createdAt" colored/>
+		</MkA>
+		<span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]">
+			<i v-if="note.visibility === 'home'" class="ph-house ph-bold ph-lg"></i>
+			<i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
+			<i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
+		</span>
+		<span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em; cursor: pointer;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
+		<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold ph-lg"></i></span>
+		<span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span>
+	</div>
+</header>
+</template>
+
+<script lang="ts" setup>
+import { inject, shallowRef, ref } from 'vue';
+import * as Misskey from 'misskey-js';
+import { i18n } from '@/i18n.js';
+import { notePage } from '@/filters/note.js';
+import { userPage } from '@/filters/user.js';
+import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js';
+import SkInstanceTicker from '@/components/SkInstanceTicker.vue';
+import { popupMenu } from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { useRouter } from '@/router.js';
+import { deviceKind } from '@/scripts/device-kind.js';
+
+const props = defineProps<{
+	note: Misskey.entities.Note;
+	classic?: boolean;
+}>();
+
+const menuVersionsButton = shallowRef<HTMLElement>();
+const router = useRouter();
+const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && props.note.user.instance);
+
+const MOBILE_THRESHOLD = 500;
+const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
+
+async function menuVersions(viaKeyboard = false): Promise<void> {
+	const { menu, cleanup } = await getNoteVersionsMenu({ note: props.note, menuVersionsButton });
+	popupMenu(menu, menuVersionsButton.value, {
+		viaKeyboard,
+	}).then(focus).finally(cleanup);
+}
+
+function showOnRemote() {
+	if (props.note.url ?? props.note.uri === undefined) router.push(notePage(props.note));
+	else window.open(props.note.url ?? props.note.uri);
+}
+
+const mock = inject<boolean>('mock', false);
+</script>
+
+<style lang="scss" module>
+.root {
+	display: flex;
+	cursor: auto; /* not clickToOpen-able */
+}
+
+.classicRoot {
+	display: flex;
+	align-items: baseline;
+	white-space: nowrap;
+	cursor: auto; /* not clickToOpen-able */
+}
+
+.section {
+		align-items: flex-start;
+		white-space: nowrap;
+		flex-direction: column;
+		overflow: hidden;
+
+		&:last-child {
+			display: flex;
+			align-items: flex-end;
+			margin-left: auto;
+			padding-left: 10px;
+			overflow: clip;
+		}
+}
+
+.name {
+	flex-shrink: 1;
+	display: block;
+	// note, these margin top values were done by hand may need futher checking if it actualy aligns pixel perfect
+	margin: 3px .5em 0 0;
+	padding: 0;
+	overflow: scroll;
+	overflow-wrap: anywhere;
+	font-size: 1em;
+	font-weight: bold;
+	text-decoration: none;
+	text-overflow: ellipsis;
+	max-width: 300px;
+
+		&::-webkit-scrollbar {
+			display: none;
+		}
+
+		&:hover {
+			color: var(--nameHover);
+			text-decoration: none;
+		}
+}
+
+.classicName {
+	flex-shrink: 1;
+	display: block;
+	margin: 0 .5em 0 0;
+	padding: 0;
+	overflow: hidden;
+	font-size: 1em;
+	font-weight: bold;
+	text-decoration: none;
+	text-overflow: ellipsis;
+
+	&:hover {
+		text-decoration: underline;
+	}
+}
+
+.isBot {
+	flex-shrink: 0;
+	align-self: center;
+	margin: 0 .5em 0 0;
+	padding: 1px 6px;
+	font-size: 80%;
+	border: solid 0.5px var(--divider);
+	border-radius: var(--radius-xs);
+}
+
+.username {
+	flex-shrink: 9999999;
+	// note these top margins were made to align with the instance ticker
+	margin: 4px .5em 0 0;
+	overflow: hidden;
+	text-overflow: ellipsis;
+	font-size: .95em;
+	max-width: 300px;
+
+	&::-webkit-scrollbar {
+		display: none;
+	}
+}
+
+.classicUsername {
+	flex-shrink: 9999999;
+	margin: 0 .5em 0 0;
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+.info {
+	&:first-child {
+		margin-top: 4px;
+		flex-shrink: 0;
+		margin-left: auto;
+		font-size: 0.9em;
+	}
+
+	&:not(:first-child) {
+		flex-shrink: 0;
+		margin-left: auto;
+		font-size: 0.9em;
+	}
+}
+
+.classicInfo {
+	flex-shrink: 0;
+	margin-left: auto;
+	font-size: 0.9em;
+}
+
+.time {
+	text-decoration: none;
+
+	&:hover {
+		text-decoration: none;
+	}
+}
+
+.badgeRoles {
+	margin: 0 .5em 0 0;
+}
+
+.badgeRole {
+	height: 1.3em;
+	vertical-align: -20%;
+
+	& + .badgeRole {
+		margin-left: 0.2em;
+	}
+}
+
+.danger {
+		color: var(--accent);
+	}
+
+	@container (max-width: 500px) {
+		.name, .username {
+			max-width: 200px;
+		}
+	}
+</style>
diff --git a/packages/frontend/src/components/SkNoteSimple.vue b/packages/frontend/src/components/SkNoteSimple.vue
new file mode 100644
index 0000000000..8ebd24b322
--- /dev/null
+++ b/packages/frontend/src/components/SkNoteSimple.vue
@@ -0,0 +1,113 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div :class="$style.root">
+	<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
+	<div :class="$style.main">
+		<MkNoteHeader :class="$style.header" :classic="true" :note="note" :mini="true"/>
+		<div>
+			<p v-if="note.cw != null" :class="$style.cw">
+				<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/>
+				<MkCwButton v-model="showContent" :note="note" v-on:click.stop/>
+			</p>
+			<div v-show="note.cw == null || showContent">
+				<MkSubNoteContent :hideFiles="hideFiles" :class="$style.text" :note="note"/>
+			</div>
+		</div>
+	</div>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { watch } from 'vue';
+import * as Misskey from 'misskey-js';
+import MkNoteHeader from '@/components/MkNoteHeader.vue';
+import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
+import MkCwButton from '@/components/MkCwButton.vue';
+import { $i } from '@/account.js';
+
+const props = defineProps<{
+	note: Misskey.entities.Note;
+	expandAllCws?: boolean;
+	hideFiles?: boolean;
+}>();
+
+let showContent = $ref(false);
+
+watch(() => props.expandAllCws, (expandAllCws) => {
+	if (expandAllCws !== showContent) showContent = expandAllCws;
+});
+</script>
+
+<style lang="scss" module>
+.root {
+	display: flex;
+	margin: 0;
+	padding: 0;
+	font-size: 0.95em;
+}
+
+.avatar {
+	flex-shrink: 0;
+	display: block;
+	margin: 0 10px 0 0;
+	width: 34px;
+	height: 34px;
+	border-radius: var(--radius-sm);
+	position: sticky !important;
+	top: calc(16px + var(--stickyTop, 0px));
+	left: 0;
+}
+
+.main {
+	flex: 1;
+	min-width: 0;
+}
+
+.header {
+	margin-bottom: 2px;
+	z-index: 2;
+}
+
+.cw {
+	display: block;
+	margin: 0;
+	padding: 0;
+	overflow-wrap: break-word;
+	overflow: hidden;
+}
+
+.text {
+	cursor: default;
+	margin: 0;
+	padding: 0;
+	overflow: hidden;
+}
+
+@container (min-width: 250px) {
+	.avatar {
+		margin: 0 10px 0 0;
+		width: 40px;
+		height: 40px;
+	}
+}
+
+@container (min-width: 350px) {
+	.avatar {
+		margin: 0 10px 0 0;
+		width: 44px;
+		height: 44px;
+	}
+}
+
+@container (min-width: 500px) {
+	.avatar {
+		margin: 0 12px 0 0;
+		width: 48px;
+		height: 48px;
+	}
+}
+</style>
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
new file mode 100644
index 0000000000..aab30e54dd
--- /dev/null
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -0,0 +1,545 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div v-if="!muted" ref="el" :class="[$style.root, { [$style.children]: depth > 1 }]">
+	<div v-if="!hideLine" :class="$style.line"></div>
+	<div :class="$style.main">
+		<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
+		<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
+		<div :class="$style.body">
+			<SkNoteHeader :class="$style.header" :note="note" :classic="true" :mini="true"/>
+			<div :class="$style.content">
+				<p v-if="note.cw != null" :class="$style.cw">
+					<Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'"/>
+					<MkCwButton v-model="showContent" :note="note"/>
+				</p>
+				<div v-show="note.cw == null || showContent">
+					<MkSubNoteContent :class="$style.text" :note="note" :translating="translating" :translation="translation"/>
+				</div>
+			</div>
+			<footer :class="$style.footer">
+				<MkReactionsViewer ref="reactionsViewer" :note="note"/>
+				<button class="_button" :class="$style.noteFooterButton" @click="reply()">
+					<i class="ph-arrow-u-up-left ph-bold ph-lg"></i>
+					<p v-if="note.repliesCount > 0" :class="$style.noteFooterButtonCount">{{ note.repliesCount }}</p>
+				</button>
+				<button
+					v-if="canRenote"
+					ref="renoteButton"
+					class="_button"
+					:class="$style.noteFooterButton"
+					:style="renoted ? 'color: var(--accent) !important;' : ''"
+					@mousedown="renoted ? undoRenote() : renote()"
+				>
+					<i class="ph-rocket-launch ph-bold ph-lg"></i>
+					<p v-if="note.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ note.renoteCount }}</p>
+				</button>
+				<button
+					v-if="canRenote"
+					ref="quoteButton"
+					class="_button"
+					:class="$style.noteFooterButton"
+					@mousedown="quote()"
+				>
+					<i class="ph-quotes ph-bold ph-lg"></i>
+				</button>
+				<button v-else class="_button" :class="$style.noteFooterButton" disabled>
+					<i class="ph-prohibit ph-bold ph-lg"></i>
+				</button>
+				<button v-if="note.myReaction == null && note.reactionAcceptance !== 'likeOnly'" ref="likeButton" :class="$style.noteFooterButton" class="_button" @mousedown="like()">
+					<i class="ph-heart ph-bold ph-lg"></i>
+				</button>
+				<button v-if="note.myReaction == null" ref="reactButton" :class="$style.noteFooterButton" class="_button" @mousedown="react()">
+					<i v-if="note.reactionAcceptance === 'likeOnly'" class="ph-heart ph-bold ph-lg"></i>
+					<i v-else class="ph-smiley ph-bold ph-lg"></i>
+				</button>
+				<button v-if="note.myReaction != null" ref="reactButton" class="_button" :class="[$style.noteFooterButton, $style.reacted]" @click="undoReact(note)">
+					<i class="ph-minus ph-bold ph-lg"></i>
+				</button>
+				<button ref="menuButton" class="_button" :class="$style.noteFooterButton" @mousedown="menu()">
+					<i class="ph-dots-three ph-bold ph-lg"></i>
+				</button>
+			</footer>
+		</div>
+	</div>
+	<template v-if="depth < 5">
+		<MkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="$style.reply" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws"/>
+	</template>
+	<div v-else :class="$style.more">
+		<MkA class="_link" :to="notePage(note)">{{ i18n.ts.continueThread }} <i class="ph-caret-double-right ph-bold ph-lg"></i></MkA>
+	</div>
+</div>
+<div v-else :class="$style.muted" @click="muted = false">
+	<I18n :src="i18n.ts.userSaysSomething" tag="small">
+		<template #name>
+			<MkA v-user-preview="note.userId" :to="userPage(note.user)">
+				<MkUserName :user="note.user"/>
+			</MkA>
+		</template>
+	</I18n>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref, shallowRef, watch } from 'vue';
+import * as Misskey from 'misskey-js';
+import SkNoteHeader from '@/components/SkNoteHeader.vue';
+import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
+import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
+import MkCwButton from '@/components/MkCwButton.vue';
+import { notePage } from '@/filters/note.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import { userPage } from "@/filters/user.js";
+import { checkWordMute } from "@/scripts/check-word-mute.js";
+import { defaultStore } from "@/store.js";
+import { pleaseLogin } from '@/scripts/please-login.js';
+import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
+import MkRippleEffect from '@/components/MkRippleEffect.vue';
+import { reactionPicker } from '@/scripts/reaction-picker.js';
+import { claimAchievement } from '@/scripts/achievements.js';
+import type { MenuItem } from '@/types/menu.js';
+import { getNoteMenu } from '@/scripts/get-note-menu.js';
+import { useNoteCapture } from '@/scripts/use-note-capture.js';
+
+const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
+const hideLine = computed(() => { return props.detail ? true : false; });
+
+const props = withDefaults(defineProps<{
+	note: Misskey.entities.Note;
+	detail?: boolean;
+	expandAllCws?: boolean;
+
+	// how many notes are in between this one and the note being viewed in detail
+	depth?: number;
+}>(), {
+	depth: 1,
+});
+
+const el = shallowRef<HTMLElement>();
+const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false);
+const translation = ref<any>(null);
+const translating = ref(false);
+const isDeleted = ref(false);
+const renoted = ref(false);
+const reactButton = shallowRef<HTMLElement>();
+const renoteButton = shallowRef<HTMLElement>();
+const quoteButton = shallowRef<HTMLElement>();
+const menuButton = shallowRef<HTMLElement>();
+const likeButton = shallowRef<HTMLElement>();
+
+let appearNote = $computed(() => isRenote ? props.note.renote as Misskey.entities.Note : props.note);
+const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
+
+const isRenote = (
+	props.note.renote != null &&
+	props.note.text == null &&
+	props.note.fileIds.length === 0 &&
+	props.note.poll == null
+);
+
+useNoteCapture({
+	rootEl: el,
+	note: $$(appearNote),
+	isDeletedRef: isDeleted,
+});
+
+if ($i) {
+	os.api("notes/renotes", {
+		noteId: appearNote.id,
+		userId: $i.id,
+		limit: 1,
+	}).then((res) => {
+		renoted.value = res.length > 0;
+	});
+}
+
+function focus() {
+	el.value.focus();
+}
+
+function reply(viaKeyboard = false): void {
+	pleaseLogin();
+	showMovedDialog();
+	os.post({
+		reply: props.note,
+		channel: props.note.channel,
+		animation: !viaKeyboard,
+	}, () => {
+		focus();
+	});
+}
+
+function react(viaKeyboard = false): void {
+	pleaseLogin();
+	showMovedDialog();
+	if (props.note.reactionAcceptance === 'likeOnly') {
+		os.api('notes/like', {
+			noteId: props.note.id,
+			override: defaultLike.value,
+		});
+		const el = reactButton.value as HTMLElement | null | undefined;
+		if (el) {
+			const rect = el.getBoundingClientRect();
+			const x = rect.left + (el.offsetWidth / 2);
+			const y = rect.top + (el.offsetHeight / 2);
+			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+		}
+	} else {
+		blur();
+		reactionPicker.show(reactButton.value, reaction => {
+			os.api('notes/reactions/create', {
+				noteId: props.note.id,
+				reaction: reaction,
+			});
+			if (props.note.text && props.note.text.length > 100 && (Date.now() - new Date(props.note.createdAt).getTime() < 1000 * 3)) {
+				claimAchievement('reactWithoutRead');
+			}
+		}, () => {
+			focus();
+		});
+	}
+}
+
+function like(): void {
+	pleaseLogin();
+	showMovedDialog();
+	os.api('notes/like', {
+		noteId: props.note.id,
+		override: defaultLike.value,
+	});
+	const el = reactButton.value as HTMLElement | null | undefined;
+	if (el) {
+		const rect = el.getBoundingClientRect();
+		const x = rect.left + (el.offsetWidth / 2);
+		const y = rect.top + (el.offsetHeight / 2);
+		os.popup(MkRippleEffect, { x, y }, {}, 'end');
+	}
+}
+
+function undoReact(note): void {
+	const oldReaction = note.myReaction;
+	if (!oldReaction) return;
+	os.api('notes/reactions/delete', {
+		noteId: note.id,
+	});
+}
+
+function undoRenote() : void {
+	if (!renoted.value) return;
+	os.api("notes/unrenote", {
+		noteId: appearNote.id,
+	});
+	os.toast(i18n.ts.rmboost);
+	renoted.value = false;
+
+	const el = renoteButton.value as HTMLElement | null | undefined;
+	if (el) {
+		const rect = el.getBoundingClientRect();
+		const x = rect.left + (el.offsetWidth / 2);
+		const y = rect.top + (el.offsetHeight / 2);
+		os.popup(MkRippleEffect, { x, y }, {}, 'end');
+	}
+}
+
+let showContent = $ref(false);
+
+watch(() => props.expandAllCws, (expandAllCws) => {
+	if (expandAllCws !== showContent) showContent = expandAllCws;
+});
+
+let replies: Misskey.entities.Note[] = $ref([]);
+
+function renote() {
+	pleaseLogin();
+	showMovedDialog();
+
+	if (appearNote.channel) {
+		const el = renoteButton.value as HTMLElement | null | undefined;
+		if (el) {
+			const rect = el.getBoundingClientRect();
+			const x = rect.left + (el.offsetWidth / 2);
+			const y = rect.top + (el.offsetHeight / 2);
+			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+		}
+
+		os.api('notes/create', {
+			renoteId: props.note.id,
+			channelId: props.note.channelId,
+		}).then(() => {
+			os.toast(i18n.ts.renoted);
+			renoted.value = true;
+		});
+	} else {
+		const el = renoteButton.value as HTMLElement | null | undefined;
+		if (el) {
+			const rect = el.getBoundingClientRect();
+			const x = rect.left + (el.offsetWidth / 2);
+			const y = rect.top + (el.offsetHeight / 2);
+			os.popup(MkRippleEffect, { x, y }, {}, 'end');
+		}
+
+		os.api('notes/create', {
+			renoteId: props.note.id,
+		}).then(() => {
+			os.toast(i18n.ts.renoted);
+			renoted.value = true;
+		});
+	}
+}
+
+function quote() {
+	pleaseLogin();
+	showMovedDialog();
+
+	if (appearNote.channel) {
+		os.post({
+			renote: appearNote,
+			channel: appearNote.channel,
+		}).then(() => {
+			os.api("notes/renotes", {
+				noteId: props.note.id,
+				userId: $i.id,
+				limit: 1,
+				quote: true,
+			}).then((res) => {
+				if (!(res.length > 0)) return;
+				const el = quoteButton.value as HTMLElement | null | undefined;
+				if (el && res.length > 0) {
+					const rect = el.getBoundingClientRect();
+					const x = rect.left + (el.offsetWidth / 2);
+					const y = rect.top + (el.offsetHeight / 2);
+					os.popup(MkRippleEffect, { x, y }, {}, 'end');
+				}
+
+				os.toast(i18n.ts.quoted);
+			});
+		});
+	} else {
+		os.post({
+			renote: appearNote,
+		}).then(() => {
+			os.api("notes/renotes", {
+				noteId: props.note.id,
+				userId: $i.id,
+				limit: 1,
+				quote: true,
+			}).then((res) => {
+				if (!(res.length > 0)) return;
+				const el = quoteButton.value as HTMLElement | null | undefined;
+				if (el && res.length > 0) {
+					const rect = el.getBoundingClientRect();
+					const x = rect.left + (el.offsetWidth / 2);
+					const y = rect.top + (el.offsetHeight / 2);
+					os.popup(MkRippleEffect, { x, y }, {}, 'end');
+				}
+
+				os.toast(i18n.ts.quoted);
+			});
+		});
+	}
+}
+
+function menu(viaKeyboard = false): void {
+	const { menu, cleanup } = getNoteMenu({ note: props.note, translating, translation, menuButton, isDeleted });
+	os.popupMenu(menu, menuButton.value, {
+		viaKeyboard,
+	}).then(focus).finally(cleanup);
+}
+
+if (props.detail) {
+	os.api('notes/children', {
+		noteId: props.note.id,
+		limit: 5,
+	}).then(res => {
+		replies = res;
+	});
+}
+</script>
+
+<style lang="scss" module>
+.root {
+	padding: 28px 32px;
+	font-size: 0.9em;
+	position: relative;
+
+	&.children {
+		padding: 10px 0 0 16px;
+		font-size: 1em;
+	}
+}
+
+.line {
+	position: absolute;
+	height: 100%;
+	left: 60px;
+	// using solid instead of dotted, stylelistic choice
+	border-left: 2.5px solid rgb(174, 174, 174);
+}
+
+.footer {
+	position: relative;
+	z-index: 1;
+	margin-top: 0.4em;
+	width: max-content;
+	min-width: max-content;
+}
+
+.main {
+	display: flex;
+}
+
+.colorBar {
+	position: absolute;
+	top: 8px;
+	left: 8px;
+	width: 5px;
+	height: calc(100% - 8px);
+	border-radius: var(--radius-ellipse);
+	pointer-events: none;
+}
+
+.avatar {
+	flex-shrink: 0;
+	display: block;
+	margin: 0 14px 0 0;
+	width: 58px;
+	height: 58px;
+	border-radius: var(--radius-sm);
+}
+
+.body {
+	flex: 1;
+	min-width: 0;
+}
+
+.content {
+	overflow: hidden;
+}
+
+.text {
+	margin: 0;
+	padding: 0;
+}
+
+.header {
+	margin-bottom: 2px;
+}
+
+.noteFooterButton {
+	margin: 0;
+	padding: 8px;
+	padding-top: 10px;
+	opacity: 0.7;
+
+	&:not(:last-child) {
+		margin-right: 1.5em;
+	}
+
+	&:hover {
+		color: var(--fgHighlighted);
+	}
+}
+
+.reply, .more {
+	border-left: solid 0.5px var(--divider);
+	margin-top: 10px;
+}
+
+.more {
+	padding: 10px 0 0 16px;
+}
+
+@container (max-width: 580px) {
+	.root {
+		padding: 28px 26px 0;
+	}
+
+	.line {
+		left: 50.5px;
+	}
+
+	.avatar {
+		width: 50px;
+		height: 50px;
+	}
+}
+
+@container (max-width: 500px) {
+	.root {
+		padding: 23px 25px;
+	}
+}
+
+@container (max-width: 400px) {
+	.noteFooterButton {
+		&:not(:last-child) {
+			margin-right: 0.7em;
+		}
+	}
+}
+
+.noteFooterButtonCount {
+	display: inline;
+	margin: 0 0 0 8px;
+	opacity: 0.7;
+
+	&.reacted {
+		color: var(--accent);
+	}
+}
+
+.cw {
+	display: block;
+	margin: 0;
+	padding: 0;
+	overflow-wrap: break-word;
+}
+
+.text {
+	margin: 0;
+	padding: 0;
+}
+
+.reply, .more {
+	border-left: solid 0.5px var(--divider);
+	margin-top: 10px;
+}
+
+.more {
+	padding: 10px 0 0 16px;
+}
+
+@container (max-width: 480px) {
+	.root {
+		padding: 22px 24px;
+
+		&.children {
+			padding: 10px 0 0 8px;
+		}
+	}
+}
+
+@container (max-width: 450px) {
+	.line {
+		left: 46px;
+	}
+
+	.avatar {
+		width: 46px;
+		height: 46px;
+	}
+}
+
+.muted {
+	text-align: center;
+	padding: 8px !important;
+	border: 1px solid var(--divider);
+	margin: 8px 8px 0 8px;
+	border-radius: var(--radius-sm);
+}
+</style>
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index c43b5f900f..9bafa17005 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -16,10 +16,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 					<div class="_margin">
 						<MkButton v-if="!showNext" :class="$style.loadNext" @click="showNext = true"><i class="ph-caret-up ph-bold ph-lg"></i></MkButton>
-						<div class="_margin _gaps_s">
+						<div v-if="defaultStore.state.noteDesign === 'misskey'" class="_margin _gaps_s">
 							<MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/>
 							<MkNoteDetailed :key="note.id" v-model:note="note" :class="$style.note" :expandAllCws="expandAllCws"/>
 						</div>
+						<div v-else-if="defaultStore.state.noteDesign === 'sharkey'" class="_margin _gaps_s">
+							<MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/>
+							<SkNoteDetailed :key="note.id" v-model:note="note" :class="$style.note" :expandAllCws="expandAllCws"/>
+						</div>
 						<div v-if="clips && clips.length > 0" class="_margin">
 							<div style="font-weight: bold; padding: 12px;">{{ i18n.ts.clip }}</div>
 							<div class="_gaps">
@@ -48,6 +52,7 @@ import { computed, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
 import MkNotes from '@/components/MkNotes.vue';
+import SkNoteDetailed from '@/components/SkNoteDetailed.vue';
 import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index c55728dc77..51be3b21b5 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -59,6 +59,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<option value="medium">{{ i18n.ts.medium }}</option>
 					<option value="large">{{ i18n.ts.large }}</option>
 				</MkRadios>
+				<MkRadios v-model="noteDesign">
+					<template #label>Note Design</template>
+					<option value="sharkey">Sharkey</option>
+					<option value="misskey">Misskey</option>
+			</MkRadios>
 			</div>
 
 			<MkSelect v-model="instanceTicker">
@@ -273,6 +278,7 @@ const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn'));
 const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline'));
 const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications'));
 const showTickerOnReplies = computed(defaultStore.makeGetterSetter('showTickerOnReplies'));
+const noteDesign = computed(defaultStore.makeGetterSetter('noteDesign'));
 
 watch(lang, () => {
 	miLocalStorage.setItem('lang', lang.value as string);
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 9443e8b210..e823e84eb6 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -254,6 +254,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: false,
 	},
+	noteDesign: {
+		where: 'device',
+		default: 'sharkey' as 'sharkey' | 'misskey',
+	},
 	enableInfiniteScroll: {
 		where: 'device',
 		default: true,

From 45e191674de6652cc314444e81bc408736c5d7f3 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Sat, 2 Dec 2023 13:27:31 +0100
Subject: [PATCH 097/435] fix: notifications not applying noteDesign option

---
 packages/frontend/src/components/MkNotifications.vue | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index 0c817bd64c..bfe668a165 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -14,10 +14,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</template>
 
 		<template #default="{ items: notifications }">
-			<MkDateSeparatedList v-slot="{ item: notification }" :class="$style.list" :items="notifications" :noGap="true">
+			<MkDateSeparatedList v-if="defaultStore.state.noteDesign === 'misskey'" v-slot="{ item: notification }" :class="$style.list" :items="notifications" :noGap="true">
 				<MkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id" :note="notification.note"/>
 				<XNotification v-else :key="notification.id" :notification="notification" :withTime="true" :full="true" class="_panel"/>
 			</MkDateSeparatedList>
+			<MkDateSeparatedList v-else-if="defaultStore.state.noteDesign === 'sharkey'" v-slot="{ item: notification }" :class="$style.list" :items="notifications" :noGap="true">
+				<SkNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :key="notification.id" :note="notification.note"/>
+				<XNotification v-else :key="notification.id" :notification="notification" :withTime="true" :full="true" class="_panel"/>
+			</MkDateSeparatedList>
 		</template>
 	</MkPagination>
 </MkPullToRefresh>
@@ -29,6 +33,7 @@ import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import XNotification from '@/components/MkNotification.vue';
 import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
 import MkNote from '@/components/MkNote.vue';
+import SkNote from '@/components/SkNote.vue';
 import { useStream } from '@/stream.js';
 import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';

From 183d24d63e09e3b97ceb5fcc0460419f5b12fdd9 Mon Sep 17 00:00:00 2001
From: Insert5StarName <anime@shourai.de>
Date: Sat, 2 Dec 2023 13:55:15 +0100
Subject: [PATCH 098/435] fix: replys only going one layer deep

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

diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index aab30e54dd..e11d7bc75d 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -66,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</div>
 	</div>
 	<template v-if="depth < 5">
-		<MkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="$style.reply" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws"/>
+		<SkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="$style.reply" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws"/>
 	</template>
 	<div v-else :class="$style.more">
 		<MkA class="_link" :to="notePage(note)">{{ i18n.ts.continueThread }} <i class="ph-caret-double-right ph-bold ph-lg"></i></MkA>

From 419bde43a6ab2a94b2dce9aa412c67cceb8c8672 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 2 Dec 2023 19:37:22 +0100
Subject: [PATCH 099/435] upd: make `Show changes` redirect to the latest
 release page

Closes #192
---
 packages/frontend/src/components/MkUpdated.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue
index 699d7af33e..b442672af1 100644
--- a/packages/frontend/src/components/MkUpdated.vue
+++ b/packages/frontend/src/components/MkUpdated.vue
@@ -27,7 +27,7 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
 
 const whatIsNew = () => {
 	modal.value.close();
-	window.open(`https://misskey-hub.net/docs/releases.html#_${version.replace(/\./g, '-')}`, '_blank');
+	window.open(`https://github.com/transfem-org/Sharkey/releases/tag/${version}`, '_blank');
 };
 
 onMounted(() => {

From 5db163bc67a928bb222af0472deaae73045f07cf Mon Sep 17 00:00:00 2001
From: Insert5StarName <anime@shourai.de>
Date: Sat, 2 Dec 2023 20:28:22 +0100
Subject: [PATCH 100/435] upd: rework threading ui 2/2

Co-authored-by: daph <d@waal.me>
Co-authored-by: Marie <marie@kaifa.ch>
---
 .../frontend/src/components/SkNoteSub.vue     | 97 ++++++++++++++++++-
 1 file changed, 92 insertions(+), 5 deletions(-)

diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index e11d7bc75d..629603738f 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -8,7 +8,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<div v-if="!hideLine" :class="$style.line"></div>
 	<div :class="$style.main">
 		<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
-		<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
+		<!-- new avatar container with line (post section) -->
+		<div :class="$style.avatarContainer">
+			<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
+			<template v-if="note.repliesCount > 0">
+				<div v-if="hideLine" :class="$style.threadLine"></div>
+			</template>
+		</div>
+		<!-- end new avatar container -->
 		<div :class="$style.body">
 			<SkNoteHeader :class="$style.header" :note="note" :classic="true" :mini="true"/>
 			<div :class="$style.content">
@@ -66,7 +73,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</div>
 	</div>
 	<template v-if="depth < 5">
-		<SkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="$style.reply" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws"/>
+		<SkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="[$style.reply, { [$style.single]: replies.length === 1 }]" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws"/>
 	</template>
 	<div v-else :class="$style.more">
 		<MkA class="_link" :to="notePage(note)">{{ i18n.ts.continueThread }} <i class="ph-caret-double-right ph-bold ph-lg"></i></MkA>
@@ -444,9 +451,9 @@ if (props.detail) {
 		color: var(--fgHighlighted);
 	}
 }
-
+// Responsible for Reply borders 448 and 508
 .reply, .more {
-	border-left: solid 0.5px var(--divider);
+	//border-left: solid 0.5px var(--divider);
 	margin-top: 10px;
 }
 
@@ -505,8 +512,9 @@ if (props.detail) {
 	padding: 0;
 }
 
+
 .reply, .more {
-	border-left: solid 0.5px var(--divider);
+	//border-left: solid 0.5px var(--divider);
 	margin-top: 10px;
 }
 
@@ -542,4 +550,83 @@ if (props.detail) {
 	margin: 8px 8px 0 8px;
 	border-radius: var(--radius-sm);
 }
+
+// avatar container with line
+.avatarContainer {
+	display: flex;
+	flex-direction: column;
+}
+
+.threadLine {
+	width: 0;
+	flex-grow: 1;
+	border-left: 2.5px solid rgb(174, 174, 174);
+	margin-left: 29px;
+}
+
+.reply {
+	margin-left: 29px;
+}
+
+.reply:not(:last-child) {
+	border-left: 2.5px solid rgb(174, 174, 174);
+
+	&::before {
+		left: -2px;
+	}
+}
+
+.reply::before {
+	position: absolute;
+	content: '';
+	left: 0px;
+	top: -10px;
+	height: 49px;
+	width: 15px;
+	border-left: 2.5px solid rgb(174, 174, 174);
+	border-bottom: 2.5px solid rgb(174, 174, 174);
+	border-bottom-left-radius: 15px;
+}
+
+.single {
+	margin-left: 0;
+	padding-left: 0 !important;
+
+	&::before {
+		left: 29px;
+		width: 0;
+		border-bottom: unset;
+	}
+}
+
+@container (max-width: 580px) {
+	.threadLine, .reply {
+		margin-left: 25px;
+	}
+	.reply::before {
+		height: 45px;
+	}
+	.single::before {
+		left: 25px;
+	}
+	.single {
+		margin-left: 0;
+	}
+}
+
+@container (max-width: 450px) {
+	.threadLine, .reply {
+		margin-left: 23px;
+	}
+	.reply::before {
+		height: 43px;
+	}
+	.single::before {
+		left: 23px;
+		width: 9px;
+	}
+	.single {
+		margin-left: 0;
+	}
+}
 </style>

From 5ccd61b1f8547c6d7dd846f57012c5d7010c4793 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sun, 3 Dec 2023 10:17:07 +0900
Subject: [PATCH 101/435] Revert "fix #12528 (#12536)" (#12548)

This reverts commit a5f0b5ec74940b0c53242dfc64c322139c91e362.
---
 packages/frontend/src/scripts/theme.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index bc97f971ef..b6383487c9 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -44,7 +44,7 @@ export const getBuiltinThemes = () => Promise.all(
 		'd-cherry',
 		'd-ice',
 		'd-u0',
-	].map(name => import(`${__dirname}/../themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
+	].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
 );
 
 export const getBuiltinThemesRef = () => {

From 4de4a2e14369acb79f070795e482a60700d97b12 Mon Sep 17 00:00:00 2001
From: shiosyakeyakini <blueskis382@gmail.com>
Date: Sun, 3 Dec 2023 10:18:28 +0900
Subject: [PATCH 102/435] =?UTF-8?q?fix:=20withChannelNotes=E3=81=A8withFil?=
 =?UTF-8?q?es=E3=82=92=E5=90=8C=E6=99=82=E3=81=AB=E6=8C=87=E5=AE=9A?=
 =?UTF-8?q?=E3=81=97=E3=81=9F=E3=81=A8=E3=81=8D=E3=81=AE=E8=80=83=E6=85=AE?=
 =?UTF-8?q?=20(#12550)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: sorairo <sorairo@shiosyakeyakini.info>
---
 packages/backend/src/server/api/endpoints/users/notes.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index 56983f7bc4..4a358b39cb 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -113,6 +113,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				redisTimelines,
 				useDbFallback: true,
 				noteFilter: note => {
+					if (ps.withFiles && note.fileIds.length === 0) {
+						return false;
+					}
 					if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false;
 
 					if (note.renoteId) {

From c68d87538a8486e5c236a142e42221c70b157861 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sun, 3 Dec 2023 10:19:37 +0900
Subject: [PATCH 103/435] =?UTF-8?q?=E3=83=AA=E3=82=B9=E3=83=88=E3=82=BF?=
 =?UTF-8?q?=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=A7=E3=83=9F?=
 =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=83=88=E3=81=8C=E8=B2=AB=E9=80=9A=E3=81=97?=
 =?UTF-8?q?=E3=81=A6=E3=81=97=E3=81=BE=E3=81=86=E5=95=8F=E9=A1=8C=E3=81=AB?=
 =?UTF-8?q?=E5=AF=BE=E5=87=A6=20(#12534)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* ユーザリストTL系の各種動作を修正・統一

* fix

* fix CHANGELOG.md

* テスト追加
---
 CHANGELOG.md                                  |   1 +
 .../backend/src/misc/is-instance-muted.ts     |   9 +-
 .../api/endpoints/notes/user-list-timeline.ts |   4 +
 .../src/server/api/stream/Connection.ts       |   2 +
 .../backend/src/server/api/stream/channel.ts  |   4 +
 .../server/api/stream/channels/user-list.ts   |   8 +-
 packages/backend/test/e2e/streaming.ts        | 108 +++++++++++++++++-
 7 files changed, 130 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d96a02325..07b7cc6b9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@
 - Fix: 招待コードが使い回せる問題を修正
 - Fix: 特定の条件下でチャンネルやユーザーのノート一覧に最新のノートが表示されなくなる問題を修正
 - Fix: 何もノートしていないユーザーのフィードにアクセスするとエラーになる問題を修正
+- Fix: リストタイムラインにてミュートが機能しないケースがある問題と、チャンネル投稿がストリーミングで流れてきてしまう問題を修正 #10443
 
 ## 2023.11.1
 
diff --git a/packages/backend/src/misc/is-instance-muted.ts b/packages/backend/src/misc/is-instance-muted.ts
index b231058a95..35fe11849d 100644
--- a/packages/backend/src/misc/is-instance-muted.ts
+++ b/packages/backend/src/misc/is-instance-muted.ts
@@ -3,12 +3,13 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+import { MiNote } from '@/models/Note.js';
 import type { Packed } from './json-schema.js';
 
-export function isInstanceMuted(note: Packed<'Note'>, mutedInstances: Set<string>): boolean {
-	if (mutedInstances.has(note.user.host ?? '')) return true;
-	if (mutedInstances.has(note.reply?.user.host ?? '')) return true;
-	if (mutedInstances.has(note.renote?.user.host ?? '')) return true;
+export function isInstanceMuted(note: Packed<'Note'> | MiNote, mutedInstances: Set<string>): boolean {
+	if (mutedInstances.has(note.user?.host ?? '')) return true;
+	if (mutedInstances.has(note.reply?.user?.host ?? '')) return true;
+	if (mutedInstances.has(note.renote?.user?.host ?? '')) return true;
 
 	return false;
 }
diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
index f39cac5c3e..f8f64738fe 100644
--- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -18,6 +18,7 @@ import { QueryService } from '@/core/QueryService.js';
 import { MiLocalUser } from '@/models/User.js';
 import { MetaService } from '@/core/MetaService.js';
 import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
+import { isInstanceMuted } from '@/misc/is-instance-muted.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -124,10 +125,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				userIdsWhoMeMuting,
 				userIdsWhoMeMutingRenotes,
 				userIdsWhoBlockingMe,
+				userMutedInstances,
 			] = await Promise.all([
 				this.cacheService.userMutingsCache.fetch(me.id),
 				this.cacheService.renoteMutingsCache.fetch(me.id),
 				this.cacheService.userBlockedCache.fetch(me.id),
+				this.cacheService.userProfileCache.fetch(me.id).then(p => new Set(p.mutedInstances)),
 			]);
 
 			const timeline = await this.fanoutTimelineEndpointService.timeline({
@@ -150,6 +153,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 							if (ps.withRenotes === false) return false;
 						}
 					}
+					if (isInstanceMuted(note, userMutedInstances)) return false;
 
 					return true;
 				},
diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts
index 2d8fec30b1..4180ccc56a 100644
--- a/packages/backend/src/server/api/stream/Connection.ts
+++ b/packages/backend/src/server/api/stream/Connection.ts
@@ -36,6 +36,7 @@ export default class Connection {
 	public userIdsWhoMeMuting: Set<string> = new Set();
 	public userIdsWhoBlockingMe: Set<string> = new Set();
 	public userIdsWhoMeMutingRenotes: Set<string> = new Set();
+	public userMutedInstances: Set<string> = new Set();
 	private fetchIntervalId: NodeJS.Timeout | null = null;
 
 	constructor(
@@ -69,6 +70,7 @@ export default class Connection {
 		this.userIdsWhoMeMuting = userIdsWhoMeMuting;
 		this.userIdsWhoBlockingMe = userIdsWhoBlockingMe;
 		this.userIdsWhoMeMutingRenotes = userIdsWhoMeMutingRenotes;
+		this.userMutedInstances = new Set(userProfile.mutedInstances);
 	}
 
 	@bindThis
diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts
index 3aa0d69c0b..46b0709773 100644
--- a/packages/backend/src/server/api/stream/channel.ts
+++ b/packages/backend/src/server/api/stream/channel.ts
@@ -41,6 +41,10 @@ export default abstract class Channel {
 		return this.connection.userIdsWhoBlockingMe;
 	}
 
+	protected get userMutedInstances() {
+		return this.connection.userMutedInstances;
+	}
+
 	protected get followingChannels() {
 		return this.connection.followingChannels;
 	}
diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts
index 4b6628df6f..fe293e2b4d 100644
--- a/packages/backend/src/server/api/stream/channels/user-list.ts
+++ b/packages/backend/src/server/api/stream/channels/user-list.ts
@@ -5,12 +5,12 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import type { MiUserListMembership, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
-import type { MiUser } from '@/models/User.js';
 import { isUserRelated } from '@/misc/is-user-related.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
+import { isInstanceMuted } from '@/misc/is-instance-muted.js';
 import Channel from '../channel.js';
 
 class UserListChannel extends Channel {
@@ -80,6 +80,9 @@ class UserListChannel extends Channel {
 	private async onNote(note: Packed<'Note'>) {
 		const isMe = this.user!.id === note.userId;
 
+		// チャンネル投稿は無視する
+		if (note.channelId) return;
+
 		if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
 
 		if (!Object.hasOwn(this.membershipsMap, note.userId)) return;
@@ -115,6 +118,9 @@ class UserListChannel extends Channel {
 			}
 		}
 
+		// 流れてきたNoteがミュートしているインスタンスに関わるものだったら無視する
+		if (isInstanceMuted(note, this.userMutedInstances)) return;
+
 		this.connection.cacheNote(note);
 
 		this.send('note', note);
diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
index f9f385e2b2..c4824f50ce 100644
--- a/packages/backend/test/e2e/streaming.ts
+++ b/packages/backend/test/e2e/streaming.ts
@@ -7,7 +7,7 @@ process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
 import { MiFollowing } from '@/models/Following.js';
-import { connectStream, signup, api, post, startServer, initTestDb, waitFire } from '../utils.js';
+import { signup, api, post, startServer, initTestDb, waitFire } from '../utils.js';
 import type { INestApplicationContext } from '@nestjs/common';
 import type * as misskey from 'misskey-js';
 
@@ -34,12 +34,16 @@ describe('Streaming', () => {
 		let ayano: misskey.entities.MeSignup;
 		let kyoko: misskey.entities.MeSignup;
 		let chitose: misskey.entities.MeSignup;
+		let kanako: misskey.entities.MeSignup;
 
 		// Remote users
 		let akari: misskey.entities.MeSignup;
 		let chinatsu: misskey.entities.MeSignup;
+		let takumi: misskey.entities.MeSignup;
 
 		let kyokoNote: any;
+		let kanakoNote: any;
+		let takumiNote: any;
 		let list: any;
 
 		beforeAll(async () => {
@@ -50,11 +54,15 @@ describe('Streaming', () => {
 			ayano = await signup({ username: 'ayano' });
 			kyoko = await signup({ username: 'kyoko' });
 			chitose = await signup({ username: 'chitose' });
+			kanako = await signup({ username: 'kanako' });
 
 			akari = await signup({ username: 'akari', host: 'example.com' });
 			chinatsu = await signup({ username: 'chinatsu', host: 'example.com' });
+			takumi = await signup({ username: 'takumi', host: 'example.com' });
 
 			kyokoNote = await post(kyoko, { text: 'foo' });
+			kanakoNote = await post(kanako, { text: 'hoge' });
+			takumiNote = await post(takumi, { text: 'piyo' });
 
 			// Follow: ayano => kyoko
 			await api('following/create', { userId: kyoko.id }, ayano);
@@ -62,6 +70,9 @@ describe('Streaming', () => {
 			// Follow: ayano => akari
 			await follow(ayano, akari);
 
+			// Mute: chitose => kanako
+			await api('mute/create', { userId: kanako.id }, chitose);
+
 			// List: chitose => ayano, kyoko
 			list = await api('users/lists/create', {
 				name: 'my list',
@@ -76,6 +87,11 @@ describe('Streaming', () => {
 				listId: list.id,
 				userId: kyoko.id,
 			}, chitose);
+
+			await api('users/lists/push', {
+				listId: list.id,
+				userId: takumi.id,
+			}, chitose);
 		}, 1000 * 60 * 2);
 
 		afterAll(async () => {
@@ -452,6 +468,96 @@ describe('Streaming', () => {
 
 				assert.strictEqual(fired, false);
 			});
+
+			// #10443
+			test('チャンネル投稿は流れない', async () => {
+				// リスインしている kyoko が 任意のチャンネルに投降した時の動きを見たい
+				const fired = await waitFire(
+					chitose, 'userList',
+					() => api('notes/create', { text: 'foo', channelId: 'dummy' }, kyoko),
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,
+					{ listId: list.id },
+				);
+
+				assert.strictEqual(fired, false);
+			});
+
+			// #10443
+			test('ミュートしているユーザへのリプライがリストTLに流れない', async () => {
+				// chitose が kanako をミュートしている状態で、リスインしている kyoko が kanako にリプライした時の動きを見たい
+				const fired = await waitFire(
+					chitose, 'userList',
+					() => api('notes/create', { text: 'foo', replyId: kanakoNote.id }, kyoko),
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,
+					{ listId: list.id },
+				);
+
+				assert.strictEqual(fired, false);
+			});
+
+			// #10443
+			test('ミュートしているユーザの投稿をリノートしたときリストTLに流れない', async () => {
+				// chitose が kanako をミュートしている状態で、リスインしている kyoko が kanako のノートをリノートした時の動きを見たい
+				const fired = await waitFire(
+					chitose, 'userList',
+					() => api('notes/create', { renoteId: kanakoNote.id }, kyoko),
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,
+					{ listId: list.id },
+				);
+
+				assert.strictEqual(fired, false);
+			});
+
+			// #10443
+			test('ミュートしているサーバのノートがリストTLに流れない', async () => {
+				await api('/i/update', {
+					mutedInstances: ['example.com'],
+				}, chitose);
+
+				// chitose が example.com をミュートしている状態で、リスインしている takumi が ノートした時の動きを見たい
+				const fired = await waitFire(
+					chitose, 'userList',
+					() => api('notes/create', { text: 'foo' }, takumi),
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,
+					{ listId: list.id },
+				);
+
+				assert.strictEqual(fired, false);
+			});
+
+			// #10443
+			test('ミュートしているサーバのノートに対するリプライがリストTLに流れない', async () => {
+				await api('/i/update', {
+					mutedInstances: ['example.com'],
+				}, chitose);
+
+				// chitose が example.com をミュートしている状態で、リスインしている kyoko が takumi のノートにリプライした時の動きを見たい
+				const fired = await waitFire(
+					chitose, 'userList',
+					() => api('notes/create', { text: 'foo', replyId: takumiNote.id }, kyoko),
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,
+					{ listId: list.id },
+				);
+
+				assert.strictEqual(fired, false);
+			});
+
+			// #10443
+			test('ミュートしているサーバのノートに対するリノートがリストTLに流れない', async () => {
+				await api('/i/update', {
+					mutedInstances: ['example.com'],
+				}, chitose);
+
+				// chitose が example.com をミュートしている状態で、リスインしている kyoko が takumi のノートをリノートした時の動きを見たい
+				const fired = await waitFire(
+					chitose, 'userList',
+					() => api('notes/create', { renoteId: takumiNote.id }, kyoko),
+					msg => msg.type === 'note' && msg.body.userId === kyoko.id,
+					{ listId: list.id },
+				);
+
+				assert.strictEqual(fired, false);
+			});
 		});
 
 		// XXX: QueryFailedError: duplicate key value violates unique constraint "IDX_347fec870eafea7b26c8a73bac"

From 2eb86e061935b68dc92626cb1ce7d8c1129d8d9d Mon Sep 17 00:00:00 2001
From: Nanaka Hiira <114819113+7ka-Hiira@users.noreply.github.com>
Date: Sun, 3 Dec 2023 10:28:35 +0900
Subject: [PATCH 104/435] =?UTF-8?q?fix(backend):=20/emoji=E3=81=AB?=
 =?UTF-8?q?=E3=81=8A=E3=81=91=E3=82=8B=E6=8B=A1=E5=BC=B5=E5=AD=90=E3=81=AE?=
 =?UTF-8?q?=E5=89=8A=E9=99=A4=E6=96=B9=E6=B3=95=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#12543)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
---
 packages/backend/src/server/ServerService.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index 17c2a93525..bb41ab0e42 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -119,8 +119,8 @@ export class ServerService implements OnApplicationShutdown {
 				return;
 			}
 
-			const name = path.split('@')[0].replace('.webp', '');
-			const host = path.split('@')[1]?.replace('.webp', '');
+			const name = path.split('@')[0].replace(/\.webp$/i, '');
+			const host = path.split('@')[1]?.replace(/\.webp$/i, '');
 
 			const emoji = await this.emojisRepository.findOneBy({
 				// `@.` is the spec of ReactionService.decodeReaction

From 5bf7813b2d6f6b34043d2dc4a9c200eea591a056 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?=
 <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Sun, 3 Dec 2023 10:58:42 +0900
Subject: [PATCH 105/435] =?UTF-8?q?enhance/feat(frontend):=20=E3=83=87?=
 =?UTF-8?q?=E3=83=BC=E3=82=BF=E3=82=BB=E3=83=BC=E3=83=90=E3=83=BC=E3=81=AE?=
 =?UTF-8?q?=E6=94=B9=E8=89=AF=E3=83=BB=E5=BC=B7=E5=8C=96=20(#12526)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance(frontend): データセーバーを個別で設定できるように

* Update Changelog

* fix design

* (fix) 設定が当たらない

* fix test(無理やり感)

* (fix) 設定がない状態ですべて有効・向操作が効かない

* fix

* tweak

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                  |  3 +
 locales/index.d.ts                            | 20 +++++++
 locales/ja-JP.yml                             | 16 ++++++
 .../frontend/src/components/MkCode.core.vue   |  2 +-
 packages/frontend/src/components/MkCode.vue   | 49 +++++++++++++---
 .../frontend/src/components/MkMediaImage.vue  |  8 +--
 .../frontend/src/components/MkMediaVideo.vue  |  6 +-
 .../frontend/src/components/MkUrlPreview.vue  |  2 +-
 .../src/components/global/MkAvatar.vue        |  2 +-
 .../frontend/src/pages/settings/general.vue   | 57 ++++++++++++++++++-
 .../pages/settings/preferences-backups.vue    |  2 +-
 packages/frontend/src/store.ts                | 13 +++--
 packages/frontend/test/init.ts                | 12 +++-
 13 files changed, 166 insertions(+), 26 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 07b7cc6b9b..99ef3a36ab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@
 
 ### Client
 - Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加
+- Feat: データセーバーでコードハイライトの読み込みを削減できるように
 - Enhance: 絵文字のオートコンプリート機能強化 #12364
 - Enhance: ユーザーのRawデータを表示するページが復活
 - Enhance: リアクション選択時に音を鳴らせるように
@@ -31,6 +32,8 @@
 - Enhance: Shareページで投稿を完了すると、親ウィンドウ(親フレーム)にpostMessageするように
 - Enhance: チャンネル、クリップ、ページ、Play、ギャラリーにURLのコピーボタンを設置 #11305
 - Enhance: ノートプレビューに「内容を隠す」が反映されるように
+- Enhance: データセーバーの適用範囲を個別で設定できるように
+	- 従来のデータセーバーの設定はリセットされます
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Enhance: 絵文字の詳細ページに記載される情報を追加
diff --git a/locales/index.d.ts b/locales/index.d.ts
index a8f54e2e18..846a6d503d 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1171,6 +1171,8 @@ export interface Locale {
     "signupPendingError": string;
     "cwNotationRequired": string;
     "doReaction": string;
+    "code": string;
+    "reloadRequiredToApplySettings": string;
     "_announcement": {
         "forExistingUsers": string;
         "forExistingUsersDescription": string;
@@ -2502,6 +2504,24 @@ export interface Locale {
             };
         };
     };
+    "_dataSaver": {
+        "_media": {
+            "title": string;
+            "description": string;
+        };
+        "_avatar": {
+            "title": string;
+            "description": string;
+        };
+        "_urlPreview": {
+            "title": string;
+            "description": string;
+        };
+        "_code": {
+            "title": string;
+            "description": string;
+        };
+    };
 }
 declare const locales: {
     [lang: string]: Locale;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 04fb1f947d..0d84440bc8 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1168,6 +1168,8 @@ useGroupedNotifications: "通知をグルーピングして表示する"
 signupPendingError: "メールアドレスの確認中に問題が発生しました。リンクの有効期限が切れている可能性があります。"
 cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述が必要です。"
 doReaction: "リアクションする"
+code: "コード"
+reloadRequiredToApplySettings: "設定の反映にはリロードが必要です。"
 
 _announcement:
   forExistingUsers: "既存ユーザーのみ"
@@ -2389,3 +2391,17 @@ _externalResourceInstaller:
     _themeInstallFailed:
       title: "テーマのインストールに失敗しました"
       description: "テーマのインストール中に問題が発生しました。もう一度お試しください。エラーの詳細はJavascriptコンソールをご覧ください。"
+
+_dataSaver:
+  _media:
+    title: "メディアの読み込み"
+    description: "画像・動画が自動で読み込まれるのを防止します。隠れている画像・動画はタップすると読み込まれます。"
+  _avatar:
+    title: "アイコン画像"
+    description: "アイコン画像のアニメーションが停止します。アニメーション画像は通常の画像よりファイルサイズが大きいことがあるので、データ通信量をさらに削減できます。"
+  _urlPreview:
+    title: "URLプレビューのサムネイル"
+    description: "URLプレビューのサムネイル画像が読み込まれなくなります。"
+  _code:
+    title: "コードハイライト"
+    description: "MFMなどでコードハイライト記法が使われている場合、タップするまで読み込まれなくなります。コードハイライトではハイライトする言語ごとにその定義ファイルを読み込む必要がありますが、それらが自動で読み込まれなくなるため、通信量の削減が見込めます。"
diff --git a/packages/frontend/src/components/MkCode.core.vue b/packages/frontend/src/components/MkCode.core.vue
index 4ec3540419..eee08b2f02 100644
--- a/packages/frontend/src/components/MkCode.core.vue
+++ b/packages/frontend/src/components/MkCode.core.vue
@@ -62,7 +62,7 @@ watch(() => props.lang, (to) => {
 	padding: 1em;
 	margin: .5em 0;
 	overflow: auto;
-	border-radius: .3em;
+	border-radius: 8px;
 
 	& pre,
 	& code {
diff --git a/packages/frontend/src/components/MkCode.vue b/packages/frontend/src/components/MkCode.vue
index b39e6ff23c..cb0eef0549 100644
--- a/packages/frontend/src/components/MkCode.vue
+++ b/packages/frontend/src/components/MkCode.vue
@@ -4,18 +4,26 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-	<Suspense>
-		<template #fallback>
-			<MkLoading v-if="!inline ?? true" />
-		</template>
-		<code v-if="inline" :class="$style.codeInlineRoot">{{ code }}</code>
-		<XCode v-else :code="code" :lang="lang"/>
-	</Suspense>
+<Suspense>
+	<template #fallback>
+		<MkLoading v-if="!inline ?? true"/>
+	</template>
+	<code v-if="inline" :class="$style.codeInlineRoot">{{ code }}</code>
+	<XCode v-else-if="show" :code="code" :lang="lang"/>
+	<button v-else :class="$style.codePlaceholderRoot" @click="show = true">
+		<div :class="$style.codePlaceholderContainer">
+			<div><i class="ti ti-code"></i> {{ i18n.ts.code }}</div>
+			<div>{{ i18n.ts.clickToShow }}</div>
+		</div>
+	</button>
+</Suspense>
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, ref } from 'vue';
 import MkLoading from '@/components/global/MkLoading.vue';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
 
 defineProps<{
 	code: string;
@@ -23,6 +31,8 @@ defineProps<{
 	inline?: boolean;
 }>();
 
+const show = ref(!defaultStore.state.dataSaver.code);
+
 const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue'));
 </script>
 
@@ -36,4 +46,27 @@ const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue'))
 	padding: .1em;
 	border-radius: .3em;
 }
+
+.codePlaceholderRoot {
+	display: block;
+	width: 100%;
+	background: none;
+	border: none;
+	outline: none;
+  font: inherit;
+  color: inherit;
+	cursor: pointer;
+
+	box-sizing: border-box;
+	border-radius: 8px;
+	padding: 24px;
+	margin-top: 4px;
+	color: #D4D4D4;
+	background: #1E1E1E;
+}
+
+.codePlaceholderContainer {
+	text-align: center;
+	font-size: 0.8em;
+}
 </style>
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index cc1c28a9e1..003c0979c7 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	>
 		<ImgWithBlurhash
 			:hash="image.blurhash"
-			:src="(defaultStore.state.enableDataSaverMode && hide) ? null : url"
+			:src="(defaultStore.state.dataSaver.media && hide) ? null : url"
 			:forceBlurhash="hide"
 			:cover="hide || cover"
 			:alt="image.comment || image.name"
@@ -32,8 +32,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<template v-if="hide">
 		<div :class="$style.hiddenText">
 			<div :class="$style.hiddenTextWrapper">
-				<b v-if="image.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.image}${image.size ? ' ' + bytes(image.size) : ''})` : '' }}</b>
-				<b v-else style="display: block;"><i class="ti ti-photo"></i> {{ defaultStore.state.enableDataSaverMode && image.size ? bytes(image.size) : i18n.ts.image }}</b>
+				<b v-if="image.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.dataSaver.media ? ` (${i18n.ts.image}${image.size ? ' ' + bytes(image.size) : ''})` : '' }}</b>
+				<b v-else style="display: block;"><i class="ti ti-photo"></i> {{ defaultStore.state.dataSaver.media && image.size ? bytes(image.size) : i18n.ts.image }}</b>
 				<span v-if="controls" style="display: block;">{{ i18n.ts.clickToShow }}</span>
 			</div>
 		</div>
@@ -94,7 +94,7 @@ function onclick() {
 
 // Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
 watch(() => props.image, () => {
-	hide = (defaultStore.state.nsfw === 'force' || defaultStore.state.enableDataSaverMode) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
+	hide = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
 }, {
 	deep: true,
 	immediate: true,
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index b6e8f1ff22..f9dba0b15a 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -7,8 +7,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <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>
-		<b v-else style="display: block;"><i class="ti ti-movie"></i> {{ defaultStore.state.enableDataSaverMode && video.size ? bytes(video.size) : i18n.ts.video }}</b>
+		<b v-if="video.isSensitive" style="display: block;"><i class="ti ti-alert-triangle"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.dataSaver.media ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b>
+		<b v-else style="display: block;"><i class="ti ti-movie"></i> {{ defaultStore.state.dataSaver.media && video.size ? bytes(video.size) : i18n.ts.video }}</b>
 		<span>{{ i18n.ts.clickToShow }}</span>
 	</div>
 </div>
@@ -43,7 +43,7 @@ const props = defineProps<{
 	video: Misskey.entities.DriveFile;
 }>();
 
-const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.enableDataSaverMode) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
+const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
 
 const videoEl = shallowRef<HTMLVideoElement>();
 
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index a460f3ea07..d3c486a98b 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 <div v-else>
 	<component :is="self ? 'MkA' : 'a'" :class="[$style.link, { [$style.compact]: compact }]" :[attr]="self ? url.substring(local.length) : url" rel="nofollow noopener" :target="target" :title="url">
-		<div v-if="thumbnail" :class="$style.thumbnail" :style="defaultStore.state.enableDataSaverMode ? '' : `background-image: url('${thumbnail}')`">
+		<div v-if="thumbnail" :class="$style.thumbnail" :style="defaultStore.state.dataSaver.urlPreview ? '' : `background-image: url('${thumbnail}')`">
 		</div>
 		<article :class="$style.body">
 			<header :class="$style.header">
diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index e238834872..51a454b2cc 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -83,7 +83,7 @@ const bound = $computed(() => props.link
 	? { to: userPage(props.user), target: props.target }
 	: {});
 
-const url = $computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.enableDataSaverMode)
+const url = $computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar)
 	? getStaticImageUrl(props.user.avatarUrl)
 	: props.user.avatarUrl);
 
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 313b5efc46..717021abd0 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -122,7 +122,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch>
 				<MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch>
 				<MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch>
-				<MkSwitch v-model="enableDataSaverMode">{{ i18n.ts.dataSaver }}</MkSwitch>
 			</div>
 			<div>
 				<MkRadios v-model="emojiStyle">
@@ -165,6 +164,37 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<template #label>{{ i18n.ts.numberOfPageCache }}</template>
 				<template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template>
 			</MkRange>
+
+			<MkFolder>
+				<template #label>{{ i18n.ts.dataSaver }}</template>
+
+				<div class="_gaps_m">
+					<MkInfo>{{ i18n.ts.reloadRequiredToApplySettings }}</MkInfo>
+
+					<div class="_buttons">
+						<MkButton inline @click="enableAllDataSaver">{{ i18n.ts.enableAll }}</MkButton>
+						<MkButton inline @click="disableAllDataSaver">{{ i18n.ts.disableAll }}</MkButton>
+					</div>
+					<div class="_gaps_m">
+						<MkSwitch v-model="dataSaver.media">
+							{{ i18n.ts._dataSaver._media.title }}
+							<template #caption>{{ i18n.ts._dataSaver._media.description }}</template>
+						</MkSwitch>
+						<MkSwitch v-model="dataSaver.avatar">
+							{{ i18n.ts._dataSaver._avatar.title }}
+							<template #caption>{{ i18n.ts._dataSaver._avatar.description }}</template>
+						</MkSwitch>
+						<MkSwitch v-model="dataSaver.urlPreview">
+							{{ i18n.ts._dataSaver._urlPreview.title }}
+							<template #caption>{{ i18n.ts._dataSaver._urlPreview.description }}</template>
+						</MkSwitch>
+						<MkSwitch v-model="dataSaver.code">
+							{{ i18n.ts._dataSaver._code.title }}
+							<template #caption>{{ i18n.ts._dataSaver._code.description }}</template>
+						</MkSwitch>
+					</div>
+				</div>
+			</MkFolder>
 		</div>
 	</FormSection>
 
@@ -198,6 +228,7 @@ import MkButton from '@/components/MkButton.vue';
 import FormSection from '@/components/form/section.vue';
 import FormLink from '@/components/form/link.vue';
 import MkLink from '@/components/MkLink.vue';
+import MkInfo from '@/components/MkInfo.vue';
 import { langs } from '@/config.js';
 import { defaultStore } from '@/store.js';
 import * as os from '@/os.js';
@@ -211,6 +242,7 @@ import { claimAchievement } from '@/scripts/achievements.js';
 const lang = ref(miLocalStorage.getItem('lang'));
 const fontSize = ref(miLocalStorage.getItem('fontSize'));
 const useSystemFont = ref(miLocalStorage.getItem('useSystemFont') != null);
+const dataSaver = ref(defaultStore.state.dataSaver);
 
 async function reloadAsk() {
 	const { canceled } = await os.confirm({
@@ -241,7 +273,6 @@ const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('dis
 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'));
 const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm'));
@@ -374,6 +405,28 @@ function testNotification(): void {
 	}, 300);
 }
 
+function enableAllDataSaver() {
+	const g = { ...defaultStore.state.dataSaver };
+
+	Object.keys(g).forEach((key) => { g[key] = true; });
+
+	dataSaver.value = g;
+}
+
+function disableAllDataSaver() {
+	const g = { ...defaultStore.state.dataSaver };
+
+	Object.keys(g).forEach((key) => { g[key] = false; });
+
+	dataSaver.value = g;
+}
+
+watch(dataSaver, (to) => {
+	defaultStore.set('dataSaver', to);
+}, {
+	deep: true,
+});
+
 const headerActions = $computed(() => []);
 
 const headerTabs = $computed(() => []);
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index 35435238fc..0362998855 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -71,7 +71,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
 	'advancedMfm',
 	'loadRawImages',
 	'imageNewTab',
-	'enableDataSaverMode',
+	'dataSaver',
 	'disableShowingAnimatedImages',
 	'emojiStyle',
 	'disableDrawer',
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 70d2cf402d..8459a5721a 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -223,10 +223,6 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: false,
 	},
-	enableDataSaverMode: {
-		where: 'device',
-		default: false,
-	},
 	disableShowingAnimatedImages: {
 		where: 'device',
 		default: window.matchMedia('(prefers-reduced-motion)').matches,
@@ -403,6 +399,15 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: true,
 	},
+	dataSaver: {
+		where: 'device',
+		default: {
+			media: false,
+			avatar: false,
+			urlPreview: false,
+			code: false,
+		} as Record<string, boolean>,
+	},
 
 	sound_masterVolume: {
 		where: 'device',
diff --git a/packages/frontend/test/init.ts b/packages/frontend/test/init.ts
index ab5e84b53c..dfc02378d5 100644
--- a/packages/frontend/test/init.ts
+++ b/packages/frontend/test/init.ts
@@ -21,7 +21,17 @@ vi.stubGlobal('WebSocket', class WebSocket extends EventTarget { static CLOSING
 vi.mock('@/store.js', () => {
 	return {
 		defaultStore: {
-			state: {},
+			state: {
+
+				// なんかtestがうまいこと動かないのでここに書く
+				dataSaver: {
+					media: false,
+					avatar: false,
+					urlPreview: false,
+					code: false,		
+				},
+
+			},
 		},
 	};
 });

From b4a83a22a1108354191bc822c19b5fa2b0a30a6e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9E=9C=E7=89=A9=E3=83=AA=E3=83=B3?= <nassii74@gmail.com>
Date: Sun, 3 Dec 2023 12:08:40 +0900
Subject: [PATCH 106/435] may be fix ruby justify on safari (#12551)

---
 packages/frontend/src/style.scss | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index 7bb443cece..274808b13f 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -132,6 +132,10 @@ hr {
 	background: var(--divider);
 }
 
+rt {
+	white-space: initial;
+}
+
 .ti {
 	width: 1.28em;
 	vertical-align: -12%;

From e17d741f4b14e3d675f2818fab3ec30c72a39586 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sun, 3 Dec 2023 12:45:18 +0900
Subject: [PATCH 107/435] =?UTF-8?q?enhance(misskey-js)=20misskey-js?=
 =?UTF-8?q?=E3=81=AE=E3=82=B9=E3=83=88=E3=83=AA=E3=83=BC=E3=83=9F=E3=83=B3?=
 =?UTF-8?q?=E3=82=B0API=E5=AE=9A=E7=BE=A9=E3=82=92=E3=83=90=E3=83=83?=
 =?UTF-8?q?=E3=82=AF=E3=82=A8=E3=83=B3=E3=83=89=E3=81=AB=E8=BF=BD=E5=BE=93?=
 =?UTF-8?q?=20(#12552)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (enhance) misskey-jsのストリーミングAPI定義をバックエンドに追従

* fix ci

* fix ci
---
 packages/backend/src/misc/json-schema.ts      |   2 +
 .../backend/src/models/json-schema/signin.ts  |  26 +++
 .../server/api/endpoints/i/signin-history.ts  |  11 +-
 packages/misskey-js/etc/misskey-js.api.md     | 180 ++++++++++++++++--
 packages/misskey-js/src/autogen/endpoint.ts   |   4 +-
 packages/misskey-js/src/autogen/entities.ts   |   4 +-
 packages/misskey-js/src/autogen/models.ts     |   5 +-
 packages/misskey-js/src/autogen/types.ts      |  42 ++--
 packages/misskey-js/src/entities.ts           |  56 +++++-
 packages/misskey-js/src/streaming.types.ts    | 140 ++++++++++++--
 10 files changed, 414 insertions(+), 56 deletions(-)
 create mode 100644 packages/backend/src/models/json-schema/signin.ts

diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index 80c1041c62..7e7fc447c3 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -36,6 +36,7 @@ import { packedGalleryPostSchema } from '@/models/json-schema/gallery-post.js';
 import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/json-schema/emoji.js';
 import { packedFlashSchema } from '@/models/json-schema/flash.js';
 import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
+import { packedSigninSchema } from '@/models/json-schema/signin.js';
 
 export const refs = {
 	UserLite: packedUserLiteSchema,
@@ -71,6 +72,7 @@ export const refs = {
 	EmojiSimple: packedEmojiSimpleSchema,
 	EmojiDetailed: packedEmojiDetailedSchema,
 	Flash: packedFlashSchema,
+	Signin: packedSigninSchema,
 };
 
 export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>;
diff --git a/packages/backend/src/models/json-schema/signin.ts b/packages/backend/src/models/json-schema/signin.ts
new file mode 100644
index 0000000000..d27d2490c5
--- /dev/null
+++ b/packages/backend/src/models/json-schema/signin.ts
@@ -0,0 +1,26 @@
+export const packedSigninSchema = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		createdAt: {
+			type: 'string',
+			optional: false, nullable: false,
+			format: 'date-time',
+		},
+		ip: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		headers: {
+			type: 'object',
+			optional: false, nullable: false,
+		},
+		success: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+	},
+} as const;
diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts
index 139bede7bc..f82e3f9b28 100644
--- a/packages/backend/src/server/api/endpoints/i/signin-history.ts
+++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts
@@ -12,8 +12,17 @@ import { DI } from '@/di-symbols.js';
 
 export const meta = {
 	requireCredential: true,
-
 	secure: true,
+
+	res: {
+		type: 'array',
+		optional: false, nullable: false,
+		items: {
+			type: 'object',
+			optional: false, nullable: false,
+			ref: 'Signin',
+		},
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 4e6e2adc02..e2e3349c08 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -292,6 +292,11 @@ type AdminUpdateUserNoteRequest = operations['admin/update-user-note']['requestB
 // @public (undocumented)
 type Announcement = components['schemas']['Announcement'];
 
+// @public (undocumented)
+type AnnouncementCreated = {
+    announcement: Announcement;
+};
+
 // @public (undocumented)
 type AnnouncementsRequest = operations['announcements']['requestBody']['content']['application/json'];
 
@@ -488,9 +493,7 @@ export type Channels = {
             unreadAntenna: (payload: Antenna) => void;
             readAllAnnouncements: () => void;
             myTokenRegenerated: () => void;
-            reversiNoInvites: () => void;
-            reversiInvited: (payload: FIXME) => void;
-            signin: (payload: FIXME) => void;
+            signin: (payload: Signin) => void;
             registryUpdated: (payload: {
                 scope?: string[];
                 key: string;
@@ -498,41 +501,116 @@ export type Channels = {
             }) => void;
             driveFileCreated: (payload: DriveFile) => void;
             readAntenna: (payload: Antenna) => void;
+            receiveFollowRequest: (payload: User) => void;
+            announcementCreated: (payload: AnnouncementCreated) => void;
         };
         receives: null;
     };
     homeTimeline: {
-        params: null;
+        params: {
+            withRenotes?: boolean;
+            withFiles?: boolean;
+        };
         events: {
             note: (payload: Note) => void;
         };
         receives: null;
     };
     localTimeline: {
-        params: null;
+        params: {
+            withRenotes?: boolean;
+            withReplies?: boolean;
+            withFiles?: boolean;
+        };
         events: {
             note: (payload: Note) => void;
         };
         receives: null;
     };
     hybridTimeline: {
-        params: null;
+        params: {
+            withRenotes?: boolean;
+            withReplies?: boolean;
+            withFiles?: boolean;
+        };
         events: {
             note: (payload: Note) => void;
         };
         receives: null;
     };
     globalTimeline: {
-        params: null;
+        params: {
+            withRenotes?: boolean;
+            withFiles?: boolean;
+        };
         events: {
             note: (payload: Note) => void;
         };
         receives: null;
     };
+    userList: {
+        params: {
+            listId: string;
+            withFiles?: boolean;
+        };
+        events: {
+            note: (payload: Note) => void;
+        };
+        receives: null;
+    };
+    hashtag: {
+        params: {
+            q?: string;
+        };
+        events: {
+            note: (payload: Note) => void;
+        };
+        receives: null;
+    };
+    roleTimeline: {
+        params: {
+            roleId: string;
+        };
+        events: {
+            note: (payload: Note) => void;
+        };
+        receives: null;
+    };
+    antenna: {
+        params: {
+            antennaId: string;
+        };
+        events: {
+            note: (payload: Note) => void;
+        };
+        receives: null;
+    };
+    channel: {
+        params: {
+            channelId: string;
+        };
+        events: {
+            note: (payload: Note) => void;
+        };
+        receives: null;
+    };
+    drive: {
+        params: null;
+        events: {
+            fileCreated: (payload: DriveFile) => void;
+            fileDeleted: (payload: DriveFile['id']) => void;
+            fileUpdated: (payload: DriveFile) => void;
+            folderCreated: (payload: DriveFolder) => void;
+            folderDeleted: (payload: DriveFolder['id']) => void;
+            folderUpdated: (payload: DriveFile) => void;
+        };
+        receives: null;
+    };
     serverStats: {
         params: null;
         events: {
-            stats: (payload: FIXME) => void;
+            stats: (payload: ServerStats) => void;
+            statsLog: (payload: ServerStatsLog) => void;
         };
         receives: {
             requestLog: {
@@ -544,7 +622,8 @@ export type Channels = {
     queueStats: {
         params: null;
         events: {
-            stats: (payload: FIXME) => void;
+            stats: (payload: QueueStats) => void;
+            statsLog: (payload: QueueStatsLog) => void;
         };
         receives: {
             requestLog: {
@@ -553,6 +632,18 @@ export type Channels = {
             };
         };
     };
+    admin: {
+        params: null;
+        events: {
+            newAbuseUserReport: {
+                id: string;
+                targetUserId: string;
+                reporterId: string;
+                comment: string;
+            };
+        };
+        receives: null;
+    };
 };
 
 // @public (undocumented)
@@ -846,6 +937,16 @@ type EmailAddressAvailableRequest = operations['email-address/available']['reque
 // @public (undocumented)
 type EmailAddressAvailableResponse = operations['email-address/available']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type EmojiAdded = {
+    emoji: EmojiDetailed;
+};
+
+// @public (undocumented)
+type EmojiDeleted = {
+    emojis: EmojiDetailed[];
+};
+
 // @public (undocumented)
 type EmojiDetailed = components['schemas']['EmojiDetailed'];
 
@@ -861,6 +962,11 @@ type EmojiSimple = components['schemas']['EmojiSimple'];
 // @public (undocumented)
 type EmojisResponse = operations['emojis']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type EmojiUpdated = {
+    emojis: EmojiDetailed[];
+};
+
 // @public (undocumented)
 type EmptyRequest = Record<string, unknown> | undefined;
 
@@ -902,6 +1008,14 @@ declare namespace entities {
         DateString,
         PageEvent,
         ModerationLog,
+        ServerStats,
+        ServerStatsLog,
+        QueueStats,
+        QueueStatsLog,
+        EmojiAdded,
+        EmojiUpdated,
+        EmojiDeleted,
+        AnnouncementCreated,
         EmptyRequest,
         EmptyResponse,
         AdminMetaResponse,
@@ -1404,7 +1518,8 @@ declare namespace entities {
         GalleryPost,
         EmojiSimple,
         EmojiDetailed,
-        Flash
+        Flash,
+        Signin
     }
 }
 export { entities }
@@ -2154,6 +2269,25 @@ type PromoReadRequest = operations['promo/read']['requestBody']['content']['appl
 // @public (undocumented)
 type QueueCount = components['schemas']['QueueCount'];
 
+// @public (undocumented)
+type QueueStats = {
+    deliver: {
+        activeSincePrevTick: number;
+        active: number;
+        waiting: number;
+        delayed: number;
+    };
+    inbox: {
+        activeSincePrevTick: number;
+        active: number;
+        waiting: number;
+        delayed: number;
+    };
+};
+
+// @public (undocumented)
+type QueueStatsLog = string[];
+
 // @public (undocumented)
 type RenoteMuteCreateRequest = operations['renote-mute/create']['requestBody']['content']['application/json'];
 
@@ -2190,6 +2324,29 @@ type RolesShowRequest = operations['roles/show']['requestBody']['content']['appl
 // @public (undocumented)
 type RolesUsersRequest = operations['roles/users']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type ServerStats = {
+    cpu: number;
+    mem: {
+        used: number;
+        active: number;
+    };
+    net: {
+        rx: number;
+        tx: number;
+    };
+    fs: {
+        r: number;
+        w: number;
+    };
+};
+
+// @public (undocumented)
+type ServerStatsLog = string[];
+
+// @public (undocumented)
+type Signin = components['schemas']['Signin'];
+
 // @public (undocumented)
 type StatsResponse = operations['stats']['responses']['200']['content']['application/json'];
 
@@ -2448,8 +2605,7 @@ type UsersUpdateMemoRequest = operations['users/update-memo']['requestBody']['co
 
 // Warnings were encountered during analysis:
 //
-// src/entities.ts:24:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
-// src/streaming.types.ts:31:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts
+// src/entities.ts:25:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts
 
 // (No @packageDocumentation comment for this package)
 
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 64739a65d0..b46e69f691 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -1,6 +1,6 @@
 /*
- * version: 2023.11.1
- * generatedAt: 2023-11-27T02:24:45.113Z
+ * version: 2023.12.0-beta.1
+ * generatedAt: 2023-12-03T02:04:45.058Z
  */
 
 import type {
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 133a30b4ca..a51ee037d0 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -1,6 +1,6 @@
 /*
- * version: 2023.11.1
- * generatedAt: 2023-11-27T02:24:45.111Z
+ * version: 2023.12.0-beta.1
+ * generatedAt: 2023-12-03T02:04:45.053Z
  */
 
 import { operations } from './types.js';
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index bc7ab1f3b9..c2b27b2c58 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -1,6 +1,6 @@
 /*
- * version: 2023.11.1
- * generatedAt: 2023-11-27T02:24:45.109Z
+ * version: 2023.12.0-beta.1
+ * generatedAt: 2023-12-03T02:04:45.051Z
  */
 
 import { components } from './types.js';
@@ -37,3 +37,4 @@ export type GalleryPost = components['schemas']['GalleryPost'];
 export type EmojiSimple = components['schemas']['EmojiSimple'];
 export type EmojiDetailed = components['schemas']['EmojiDetailed'];
 export type Flash = components['schemas']['Flash'];
+export type Signin = components['schemas']['Signin'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index c7f5c6c82d..44ed4dbaa8 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -2,8 +2,8 @@
 /* eslint @typescript-eslint/no-explicit-any: 0 */
 
 /*
- * version: 2023.11.1
- * generatedAt: 2023-11-27T02:24:44.994Z
+ * version: 2023.12.0-beta.1
+ * generatedAt: 2023-12-03T02:04:44.864Z
  */
 
 /**
@@ -3388,6 +3388,7 @@ export type components = {
       uri?: string;
       url?: string;
       reactionAndUserPairCache?: string[];
+      clippedCount?: number;
       myReaction?: Record<string, unknown> | null;
     };
     NoteReaction: {
@@ -3786,6 +3787,14 @@ export type components = {
       likedCount: number | null;
       isLiked?: boolean;
     };
+    Signin: {
+      id: string;
+      /** Format: date-time */
+      createdAt: string;
+      ip: string;
+      headers: Record<string, never>;
+      success: boolean;
+    };
   };
   responses: never;
   parameters: never;
@@ -6405,10 +6414,7 @@ export type operations = {
       /** @description OK (with results) */
       200: {
         content: {
-          'application/json': {
-              /** @example GR6S02ERUA5VR */
-              code: string;
-            }[];
+          'application/json': components['schemas']['InviteCode'][];
         };
       };
       /** @description Client error */
@@ -6471,7 +6477,7 @@ export type operations = {
       /** @description OK (with results) */
       200: {
         content: {
-          'application/json': Record<string, never>[];
+          'application/json': components['schemas']['InviteCode'][];
         };
       };
       /** @description Client error */
@@ -9600,6 +9606,8 @@ export type operations = {
           untilId?: string;
           sinceDate?: number;
           untilDate?: number;
+          /** @default false */
+          allowPartial?: boolean;
         };
       };
     };
@@ -15893,10 +15901,7 @@ export type operations = {
       /** @description OK (with results) */
       200: {
         content: {
-          'application/json': {
-            /** @example GR6S02ERUA5VR */
-            code: string;
-          };
+          'application/json': components['schemas']['InviteCode'];
         };
       };
       /** @description Client error */
@@ -17349,6 +17354,8 @@ export type operations = {
           untilId?: string;
           sinceDate?: number;
           untilDate?: number;
+          /** @default false */
+          allowPartial?: boolean;
           /** @default true */
           includeMyRenotes?: boolean;
           /** @default true */
@@ -17419,14 +17426,14 @@ export type operations = {
           withRenotes?: boolean;
           /** @default false */
           withReplies?: boolean;
-          /** @default false */
-          excludeNsfw?: boolean;
           /** @default 10 */
           limit?: number;
           /** Format: misskey:id */
           sinceId?: string;
           /** Format: misskey:id */
           untilId?: string;
+          /** @default false */
+          allowPartial?: boolean;
           sinceDate?: number;
           untilDate?: number;
         };
@@ -18317,6 +18324,8 @@ export type operations = {
           untilId?: string;
           sinceDate?: number;
           untilDate?: number;
+          /** @default false */
+          allowPartial?: boolean;
           /** @default true */
           includeMyRenotes?: boolean;
           /** @default true */
@@ -18502,6 +18511,8 @@ export type operations = {
           untilId?: string;
           sinceDate?: number;
           untilDate?: number;
+          /** @default false */
+          allowPartial?: boolean;
           /** @default true */
           includeMyRenotes?: boolean;
           /** @default true */
@@ -20799,6 +20810,7 @@ export type operations = {
           username?: string;
           /** @description The local host is represented with `null`. */
           host?: string | null;
+          birthday?: string | null;
         };
       };
     };
@@ -21704,9 +21716,9 @@ export type operations = {
           sinceDate?: number;
           untilDate?: number;
           /** @default false */
-          withFiles?: boolean;
+          allowPartial?: boolean;
           /** @default false */
-          excludeNsfw?: boolean;
+          withFiles?: boolean;
         };
       };
     };
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index b1d6b5c10f..99f433cc02 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -1,5 +1,6 @@
-import { ModerationLogPayloads, notificationTypes } from './consts.js';
-import { Page, User, UserDetailed } from './autogen/models';
+import { ModerationLogPayloads } from './consts.js';
+import { Announcement, EmojiDetailed, Page, User, UserDetailed } from './autogen/models';
+
 export * from './autogen/entities';
 export * from './autogen/models';
 
@@ -131,3 +132,54 @@ export type ModerationLog = {
 	type: 'unsetUserBanner';
 	info: ModerationLogPayloads['unsetUserBanner'];
 });
+
+export type ServerStats = {
+	cpu: number;
+	mem: {
+		used: number;
+		active: number;
+	};
+	net: {
+		rx: number;
+		tx: number;
+	};
+	fs: {
+		r: number;
+		w: number;
+	}
+};
+
+export type ServerStatsLog = string[];
+
+export type QueueStats = {
+	deliver: {
+		activeSincePrevTick: number;
+		active: number;
+		waiting: number;
+		delayed: number;
+	};
+	inbox: {
+		activeSincePrevTick: number;
+		active: number;
+		waiting: number;
+		delayed: number;
+	};
+};
+
+export type QueueStatsLog = string[];
+
+export type EmojiAdded = {
+	emoji: EmojiDetailed
+};
+
+export type EmojiUpdated = {
+	emojis: EmojiDetailed[]
+};
+
+export type EmojiDeleted = {
+	emojis: EmojiDetailed[]
+};
+
+export type AnnouncementCreated = {
+	announcement: Announcement;
+};
diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts
index 7981c280f6..6f575ce585 100644
--- a/packages/misskey-js/src/streaming.types.ts
+++ b/packages/misskey-js/src/streaming.types.ts
@@ -1,7 +1,23 @@
-import { Antenna, DriveFile, EmojiDetailed, MeDetailed, Note, User, Notification } from './autogen/models.js';
-import { PageEvent } from './entities.js';
-
-type FIXME = any;
+import {
+	Antenna,
+	DriveFile,
+	DriveFolder,
+	MeDetailed,
+	Note,
+	Notification,
+	Signin,
+	User,
+} from './autogen/models.js';
+import {
+	AnnouncementCreated,
+	EmojiAdded, EmojiDeleted,
+	EmojiUpdated,
+	PageEvent,
+	QueueStats,
+	QueueStatsLog,
+	ServerStats,
+	ServerStatsLog,
+} from './entities.js';
 
 export type Channels = {
 	main: {
@@ -27,9 +43,7 @@ export type Channels = {
 			unreadAntenna: (payload: Antenna) => void;
 			readAllAnnouncements: () => void;
 			myTokenRegenerated: () => void;
-			reversiNoInvites: () => void;
-			reversiInvited: (payload: FIXME) => void;
-			signin: (payload: FIXME) => void;
+			signin: (payload: Signin) => void;
 			registryUpdated: (payload: {
 				scope?: string[];
 				key: string;
@@ -37,41 +51,116 @@ export type Channels = {
 			}) => void;
 			driveFileCreated: (payload: DriveFile) => void;
 			readAntenna: (payload: Antenna) => void;
+			receiveFollowRequest: (payload: User) => void;
+			announcementCreated: (payload: AnnouncementCreated) => void;
 		};
 		receives: null;
 	};
 	homeTimeline: {
-		params: null;
+		params: {
+			withRenotes?: boolean;
+			withFiles?: boolean;
+		};
 		events: {
 			note: (payload: Note) => void;
 		};
 		receives: null;
 	};
 	localTimeline: {
-		params: null;
+		params: {
+			withRenotes?: boolean;
+			withReplies?: boolean;
+			withFiles?: boolean;
+		};
 		events: {
 			note: (payload: Note) => void;
 		};
 		receives: null;
 	};
 	hybridTimeline: {
-		params: null;
+		params: {
+			withRenotes?: boolean;
+			withReplies?: boolean;
+			withFiles?: boolean;
+		};
 		events: {
 			note: (payload: Note) => void;
 		};
 		receives: null;
 	};
 	globalTimeline: {
-		params: null;
+		params: {
+			withRenotes?: boolean;
+			withFiles?: boolean;
+		};
 		events: {
 			note: (payload: Note) => void;
 		};
 		receives: null;
 	};
+	userList: {
+		params: {
+			listId: string;
+			withFiles?: boolean;
+		};
+		events: {
+			note: (payload: Note) => void;
+		};
+		receives: null;
+	};
+	hashtag: {
+		params: {
+			q?: string;
+		};
+		events: {
+			note: (payload: Note) => void;
+		};
+		receives: null;
+	};
+	roleTimeline: {
+		params: {
+			roleId: string;
+		};
+		events: {
+			note: (payload: Note) => void;
+		};
+		receives: null;
+	};
+	antenna: {
+		params: {
+			antennaId: string;
+		};
+		events: {
+			note: (payload: Note) => void;
+		};
+		receives: null;
+	};
+	channel: {
+		params: {
+			channelId: string;
+		};
+		events: {
+			note: (payload: Note) => void;
+		};
+		receives: null;
+	};
+	drive: {
+		params: null;
+		events: {
+			fileCreated: (payload: DriveFile) => void;
+			fileDeleted: (payload: DriveFile['id']) => void;
+			fileUpdated: (payload: DriveFile) => void;
+			folderCreated: (payload: DriveFolder) => void;
+			folderDeleted: (payload: DriveFolder['id']) => void;
+			folderUpdated: (payload: DriveFile) => void;
+		};
+		receives: null;
+	};
 	serverStats: {
 		params: null;
 		events: {
-			stats: (payload: FIXME) => void;
+			stats: (payload: ServerStats) => void;
+			statsLog: (payload: ServerStatsLog) => void;
 		};
 		receives: {
 			requestLog: {
@@ -83,7 +172,8 @@ export type Channels = {
 	queueStats: {
 		params: null;
 		events: {
-			stats: (payload: FIXME) => void;
+			stats: (payload: QueueStats) => void;
+			statsLog: (payload: QueueStatsLog) => void;
 		};
 		receives: {
 			requestLog: {
@@ -92,30 +182,39 @@ export type Channels = {
 			};
 		};
 	};
+	admin: {
+		params: null;
+		events: {
+			newAbuseUserReport: {
+				id: string;
+				targetUserId: string;
+				reporterId: string;
+				comment: string;
+			}
+		};
+		receives: null;
+	}
 };
 
 export type NoteUpdatedEvent = {
-	id: Note['id'];
 	type: 'reacted';
 	body: {
 		reaction: string;
+		emoji: string | null;
 		userId: User['id'];
 	};
 } | {
-	id: Note['id'];
 	type: 'unreacted';
 	body: {
 		reaction: string;
 		userId: User['id'];
 	};
 } | {
-	id: Note['id'];
 	type: 'deleted';
 	body: {
 		deletedAt: string;
 	};
 } | {
-	id: Note['id'];
 	type: 'pollVoted';
 	body: {
 		choice: number;
@@ -125,7 +224,8 @@ export type NoteUpdatedEvent = {
 
 export type BroadcastEvents = {
 	noteUpdated: (payload: NoteUpdatedEvent) => void;
-	emojiAdded: (payload: {
-		emoji: EmojiDetailed;
-	}) => void;
+	emojiAdded: (payload: EmojiAdded) => void;
+	emojiUpdated: (payload: EmojiUpdated) => void;
+	emojiDeleted: (payload: EmojiDeleted) => void;
+	announcementCreated: (payload: AnnouncementCreated) => void;
 };

From 34223f3da41f1d006e950573952ee062256a0ed5 Mon Sep 17 00:00:00 2001
From: 6543 <6543@obermui.de>
Date: Sun, 3 Dec 2023 05:42:41 +0100
Subject: [PATCH 108/435] fix(backend): enhance nodeinfo by export instance
 admin via nodeAdmins key (#12503)

https://codeberg.org/thefederationinfo/nodeinfo_extension
---
 packages/backend/src/server/NodeinfoServerService.ts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts
index 79b0a57f2b..4d94fcdd54 100644
--- a/packages/backend/src/server/NodeinfoServerService.ts
+++ b/packages/backend/src/server/NodeinfoServerService.ts
@@ -96,6 +96,11 @@ export class NodeinfoServerService {
 				metadata: {
 					nodeName: meta.name,
 					nodeDescription: meta.description,
+					nodeAdmins: [{
+						name: meta.maintainerName,
+						email: meta.maintainerEmail,
+					}],
+					// deprecated
 					maintainer: {
 						name: meta.maintainerName,
 						email: meta.maintainerEmail,

From af15f8d09db6c1709950bf9d00ffb77613fbcf8a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Sun, 3 Dec 2023 14:38:42 +0900
Subject: [PATCH 109/435] fix(backend): reject malformed timestamp (#12554)

---
 packages/backend/src/core/IdService.ts        | 23 +++++++++++++++----
 .../src/core/activitypub/ApInboxService.ts    |  8 ++++++-
 .../core/activitypub/models/ApNoteService.ts  |  4 ++++
 packages/backend/src/misc/id/aid.ts           |  4 ++++
 packages/backend/src/misc/id/aidx.ts          |  4 ++++
 packages/backend/src/misc/id/meid.ts          |  4 ++++
 packages/backend/src/misc/id/meidg.ts         |  4 ++++
 packages/backend/src/misc/id/object-id.ts     |  4 ++++
 8 files changed, 49 insertions(+), 6 deletions(-)

diff --git a/packages/backend/src/core/IdService.ts b/packages/backend/src/core/IdService.ts
index c98b8ea6fc..43e72d2d7b 100644
--- a/packages/backend/src/core/IdService.ts
+++ b/packages/backend/src/core/IdService.ts
@@ -7,11 +7,11 @@ import { Inject, Injectable } from '@nestjs/common';
 import { ulid } from 'ulid';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
-import { genAid, parseAid } from '@/misc/id/aid.js';
-import { genAidx, parseAidx } from '@/misc/id/aidx.js';
-import { genMeid, parseMeid } from '@/misc/id/meid.js';
-import { genMeidg, parseMeidg } from '@/misc/id/meidg.js';
-import { genObjectId, parseObjectId } from '@/misc/id/object-id.js';
+import { genAid, isSafeAidT, parseAid } from '@/misc/id/aid.js';
+import { genAidx, isSafeAidxT, parseAidx } from '@/misc/id/aidx.js';
+import { genMeid, isSafeMeidT, parseMeid } from '@/misc/id/meid.js';
+import { genMeidg, isSafeMeidgT, parseMeidg } from '@/misc/id/meidg.js';
+import { genObjectId, isSafeObjectIdT, parseObjectId } from '@/misc/id/object-id.js';
 import { bindThis } from '@/decorators.js';
 import { parseUlid } from '@/misc/id/ulid.js';
 
@@ -26,6 +26,19 @@ export class IdService {
 		this.method = config.id.toLowerCase();
 	}
 
+	@bindThis
+	public isSafeT(t: number): boolean {
+		switch (this.method) {
+			case 'aid': return isSafeAidT(t);
+			case 'aidx': return isSafeAidxT(t);
+			case 'meid': return isSafeMeidT(t);
+			case 'meidg': return isSafeMeidgT(t);
+			case 'ulid': return t > 0;
+			case 'objectid': return isSafeObjectIdT(t);
+			default: throw new Error('unrecognized id generation method');
+		}
+	}
+
 	/**
 	 * 時間を元にIDを生成します(省略時は現在日時)
 	 * @param time 日時
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index 7aba140689..baaab67e48 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -306,9 +306,15 @@ export class ApInboxService {
 			this.logger.info(`Creating the (Re)Note: ${uri}`);
 
 			const activityAudience = await this.apAudienceService.parseAudience(actor, activity.to, activity.cc);
+			const createdAt = activity.published ? new Date(activity.published) : null;
+
+			if (createdAt && createdAt < this.idService.parse(renote.id).date) {
+				this.logger.warn('skip: malformed createdAt');
+				return;
+			}
 
 			await this.noteCreateService.create(actor, {
-				createdAt: activity.published ? new Date(activity.published) : null,
+				createdAt,
 				renote,
 				visibility: activityAudience.visibility,
 				visibleUsers: activityAudience.visibleUsers,
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index 1979cdda9c..05d5ca15db 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -92,6 +92,10 @@ export class ApNoteService {
 			return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`);
 		}
 
+		if (object.published && !this.idService.isSafeT(new Date(object.published).valueOf())) {
+			return new Error('invalid Note: published timestamp is malformed');
+		}
+
 		return null;
 	}
 
diff --git a/packages/backend/src/misc/id/aid.ts b/packages/backend/src/misc/id/aid.ts
index e7b59f262b..de03f6793f 100644
--- a/packages/backend/src/misc/id/aid.ts
+++ b/packages/backend/src/misc/id/aid.ts
@@ -34,3 +34,7 @@ export function parseAid(id: string): { date: Date; } {
 	const time = parseInt(id.slice(0, 8), 36) + TIME2000;
 	return { date: new Date(time) };
 }
+
+export function isSafeAidT(t: number): boolean {
+	return t > TIME2000;
+}
diff --git a/packages/backend/src/misc/id/aidx.ts b/packages/backend/src/misc/id/aidx.ts
index bed223225a..9f457f6f0a 100644
--- a/packages/backend/src/misc/id/aidx.ts
+++ b/packages/backend/src/misc/id/aidx.ts
@@ -41,3 +41,7 @@ export function parseAidx(id: string): { date: Date; } {
 	const time = parseInt(id.slice(0, TIME_LENGTH), 36) + TIME2000;
 	return { date: new Date(time) };
 }
+
+export function isSafeAidxT(t: number): boolean {
+	return t > TIME2000;
+}
diff --git a/packages/backend/src/misc/id/meid.ts b/packages/backend/src/misc/id/meid.ts
index 366738de05..7646282edb 100644
--- a/packages/backend/src/misc/id/meid.ts
+++ b/packages/backend/src/misc/id/meid.ts
@@ -38,3 +38,7 @@ export function parseMeid(id: string): { date: Date; } {
 		date: new Date(parseInt(id.slice(0, 12), 16) - 0x800000000000),
 	};
 }
+
+export function isSafeMeidT(t: number): boolean {
+	return t > 0;
+}
diff --git a/packages/backend/src/misc/id/meidg.ts b/packages/backend/src/misc/id/meidg.ts
index 426a46970b..f2a55443ef 100644
--- a/packages/backend/src/misc/id/meidg.ts
+++ b/packages/backend/src/misc/id/meidg.ts
@@ -38,3 +38,7 @@ export function parseMeidg(id: string): { date: Date; } {
 		date: new Date(parseInt(id.slice(1, 12), 16)),
 	};
 }
+
+export function isSafeMeidgT(t: number): boolean {
+	return t > 0;
+}
diff --git a/packages/backend/src/misc/id/object-id.ts b/packages/backend/src/misc/id/object-id.ts
index 49bd9591c0..f5c3619fdb 100644
--- a/packages/backend/src/misc/id/object-id.ts
+++ b/packages/backend/src/misc/id/object-id.ts
@@ -38,3 +38,7 @@ export function parseObjectId(id: string): { date: Date; } {
 		date: new Date(parseInt(id.slice(0, 8), 16) * 1000),
 	};
 }
+
+export function isSafeObjectIdT(t: number): boolean {
+	return t > 0;
+}

From 5e1d87240426e08858b7fc5ccad5ca235bd3c6e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sun, 3 Dec 2023 17:25:34 +0900
Subject: [PATCH 110/435] =?UTF-8?q?=E5=85=A5=E5=8A=9B=E3=83=95=E3=82=A9?=
 =?UTF-8?q?=E3=83=BC=E3=83=A0=E3=81=A7=E3=82=82=E3=83=AA=E3=82=A2=E3=82=AF?=
 =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E9=81=B8=E6=8A=9E=E6=99=82=E3=81=AB?=
 =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=99=E3=82=8B=E3=83=94=E3=83=83=E3=82=AB?=
 =?UTF-8?q?=E3=83=BC=E3=82=92=E4=BD=BF=E3=81=86=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=E3=81=97=E3=81=9F=E3=81=84=20(#12337)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 入力フォームでもリアクション選択時に使用するピッカーを使うようにしたい

* erase console.log

* fix CHANGELOG.md

* reaction-picker.ts を戻し、今回の対応を入れた emoji-picker.ts を新たに作成

* fix CHANGELOG.md

* tweak

---------

Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                  |  1 +
 packages/frontend/src/boot/main-boot.ts       |  2 +
 .../frontend/src/components/MkEmojiPicker.vue |  5 +-
 .../src/components/MkEmojiPickerDialog.vue    | 19 ++++---
 .../frontend/src/components/MkPostForm.vue    | 11 +++-
 packages/frontend/src/scripts/emoji-picker.ts | 57 +++++++++++++++++++
 6 files changed, 84 insertions(+), 11 deletions(-)
 create mode 100644 packages/frontend/src/scripts/emoji-picker.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 99ef3a36ab..694f330120 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,7 @@
 ### Client
 - Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加
 - Feat: データセーバーでコードハイライトの読み込みを削減できるように
+- Enhance: 投稿フォームの絵文字ピッカーをリアクション時に使用するものと同じのを使用するように #12336
 - Enhance: 絵文字のオートコンプリート機能強化 #12364
 - Enhance: ユーザーのRawデータを表示するページが復活
 - Enhance: リアクション選択時に音を鳴らせるように
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index 71236e4c53..88e2f83895 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -19,6 +19,7 @@ import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js
 import { mainRouter } from '@/router.js';
 import { initializeSw } from '@/scripts/initialize-sw.js';
 import { deckStore } from '@/ui/deck/deck-store.js';
+import { emojiPicker } from '@/scripts/emoji-picker.js';
 
 export async function mainBoot() {
 	const { isClientUpdated } = await common(() => createApp(
@@ -30,6 +31,7 @@ export async function mainBoot() {
 	));
 
 	reactionPicker.init();
+	emojiPicker.init();
 
 	if (isClientUpdated && $i) {
 		popup(defineAsyncComponent(() => import('@/components/MkUpdated.vue')), {}, {}, 'closed');
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index ecff2b5ace..b5e5a0260c 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</section>
 
 		<div v-if="tab === 'index'" class="group index">
-			<section v-if="showPinned">
+			<section v-if="showPinned && pinned.length > 0">
 				<div class="body">
 					<button
 						v-for="emoji in pinned"
@@ -137,7 +137,7 @@ const searchEl = shallowRef<HTMLInputElement>();
 const emojisEl = shallowRef<HTMLDivElement>();
 
 const {
-	reactions: pinned,
+	reactions: pinnedReactions,
 	reactionPickerSize,
 	reactionPickerWidth,
 	reactionPickerHeight,
@@ -145,6 +145,7 @@ const {
 	recentlyUsedEmojis,
 } = defaultStore.reactiveState;
 
+const pinned = computed(() => props.asReactionPicker ? pinnedReactions.value : []); // TODO: 非リアクションの絵文字ピッカー用のpinned絵文字を設定可能にする?
 const size = computed(() => props.asReactionPicker ? reactionPickerSize.value : 1);
 const width = computed(() => props.asReactionPicker ? reactionPickerWidth.value : 3);
 const height = computed(() => props.asReactionPicker ? reactionPickerHeight.value : 2);
diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue
index 9d3132c540..05b137e335 100644
--- a/packages/frontend/src/components/MkEmojiPickerDialog.vue
+++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue
@@ -31,20 +31,21 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { shallowRef } from 'vue';
 import MkModal from '@/components/MkModal.vue';
 import MkEmojiPicker from '@/components/MkEmojiPicker.vue';
 import { defaultStore } from '@/store.js';
 
-withDefaults(defineProps<{
+const props = withDefaults(defineProps<{
 	manualShowing?: boolean | null;
 	src?: HTMLElement;
 	showPinned?: boolean;
 	asReactionPicker?: boolean;
+  choseAndClose?: boolean;
 }>(), {
 	manualShowing: null,
 	showPinned: true,
 	asReactionPicker: false,
+	choseAndClose: true,
 });
 
 const emit = defineEmits<{
@@ -53,21 +54,23 @@ const emit = defineEmits<{
 	(ev: 'closed'): void;
 }>();
 
-const modal = shallowRef<InstanceType<typeof MkModal>>();
-const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>();
+const modal = $shallowRef<InstanceType<typeof MkModal>>();
+const picker = $shallowRef<InstanceType<typeof MkEmojiPicker>>();
 
 function chosen(emoji: any) {
 	emit('done', emoji);
-	modal.value?.close();
+	if (props.choseAndClose) {
+		modal?.close();
+	}
 }
 
 function opening() {
-	picker.value?.reset();
-	picker.value?.focus();
+	picker?.reset();
+	picker?.focus();
 
 	// 何故かちょっと待たないとフォーカスされない
 	setTimeout(() => {
-		picker.value?.focus();
+		picker?.focus();
 	}, 10);
 }
 </script>
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 3244f743ac..0445536ae5 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -124,6 +124,7 @@ import { deepClone } from '@/scripts/clone.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import { miLocalStorage } from '@/local-storage.js';
 import { claimAchievement } from '@/scripts/achievements.js';
+import { emojiPicker } from '@/scripts/emoji-picker.js';
 
 const modal = inject('modal');
 
@@ -845,7 +846,15 @@ function insertMention() {
 }
 
 async function insertEmoji(ev: MouseEvent) {
-	os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl);
+	emojiPicker.show(
+		ev.currentTarget ?? ev.target,
+		emoji => {
+			insertTextAtCursor(textareaEl, emoji);
+		},
+		() => {
+			focus();
+		},
+	);
 }
 
 function showActions(ev) {
diff --git a/packages/frontend/src/scripts/emoji-picker.ts b/packages/frontend/src/scripts/emoji-picker.ts
new file mode 100644
index 0000000000..d6d6bf1245
--- /dev/null
+++ b/packages/frontend/src/scripts/emoji-picker.ts
@@ -0,0 +1,57 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { defineAsyncComponent, Ref, ref } from 'vue';
+import { popup } from '@/os.js';
+
+/**
+ * 絵文字ピッカーを表示する。
+ * 類似の機能として{@link ReactionPicker}が存在しているが、この機能とは動きが異なる。
+ * 投稿フォームなどで絵文字を選択する時など、絵文字ピックアップ後でもダイアログが消えずに残り、
+ * 一度表示したダイアログを連続で使用できることが望ましいシーンでの利用が想定される。
+ */
+class EmojiPicker {
+	private src: Ref<HTMLElement | null> = ref(null);
+	private manualShowing = ref(false);
+	private onChosen?: (emoji: string) => void;
+	private onClosed?: () => void;
+
+	constructor() {
+		// nop
+	}
+
+	public async init() {
+		await popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), {
+			src: this.src,
+			asReactionPicker: false,
+			manualShowing: this.manualShowing,
+			choseAndClose: false,
+		}, {
+			done: emoji => {
+				if (this.onChosen) this.onChosen(emoji);
+			},
+			close: () => {
+				this.manualShowing.value = false;
+			},
+			closed: () => {
+				this.src.value = null;
+				if (this.onClosed) this.onClosed();
+			},
+		});
+	}
+
+	public show(
+		src: HTMLElement,
+		onChosen: EmojiPicker['onChosen'],
+		onClosed: EmojiPicker['onClosed'],
+	) {
+		this.src.value = src;
+		this.manualShowing.value = true;
+		this.onChosen = onChosen;
+		this.onClosed = onClosed;
+	}
+}
+
+export const emojiPicker = new EmojiPicker();

From 55c8ec80edb275f1477ab8e39274d7c8599b7a8d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sun, 3 Dec 2023 20:46:19 +0900
Subject: [PATCH 111/435] =?UTF-8?q?fix=20(backend):=20=E3=80=8C=E3=81=BF?=
 =?UTF-8?q?=E3=81=A4=E3=81=91=E3=82=8B=E3=80=8D=E3=81=AE=E3=81=AA=E3=81=8B?=
 =?UTF-8?q?=E3=81=AB=E3=83=9F=E3=83=A5=E3=83=BC=E3=83=88=E3=81=97=E3=81=9F?=
 =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=81=8C=E7=8F=BE=E3=82=8C=E3=81=A6?=
 =?UTF-8?q?=E3=81=97=E3=81=BE=E3=81=86=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#12559)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix (backend): 「みつける」のなかにミュートしたユーザが現れてしまう問題を修正

* fix
---
 CHANGELOG.md                                  |  1 +
 .../server/api/endpoints/notes/featured.ts    | 21 ++++++++++++++++---
 2 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 694f330120..453bdeff59 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -54,6 +54,7 @@
 - Fix: 特定の条件下でチャンネルやユーザーのノート一覧に最新のノートが表示されなくなる問題を修正
 - Fix: 何もノートしていないユーザーのフィードにアクセスするとエラーになる問題を修正
 - Fix: リストタイムラインにてミュートが機能しないケースがある問題と、チャンネル投稿がストリーミングで流れてきてしまう問題を修正 #10443
+- Fix: 「みつける」のなかにミュートしたユーザが現れてしまう問題を修正 #12383
 
 ## 2023.11.1
 
diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts
index bc6cbe242f..31b8d1ad2d 100644
--- a/packages/backend/src/server/api/endpoints/notes/featured.ts
+++ b/packages/backend/src/server/api/endpoints/notes/featured.ts
@@ -9,6 +9,8 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { DI } from '@/di-symbols.js';
 import { FeaturedService } from '@/core/FeaturedService.js';
+import { isUserRelated } from '@/misc/is-user-related.js';
+import { CacheService } from '@/core/CacheService.js';
 
 export const meta = {
 	tags: ['notes'],
@@ -47,6 +49,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		@Inject(DI.notesRepository)
 		private notesRepository: NotesRepository,
 
+		private cacheService: CacheService,
 		private noteEntityService: NoteEntityService,
 		private featuredService: FeaturedService,
 	) {
@@ -74,6 +77,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				return [];
 			}
 
+			const [
+				userIdsWhoMeMuting,
+				userIdsWhoBlockingMe,
+			] = me ? await Promise.all([
+				this.cacheService.userMutingsCache.fetch(me.id),
+				this.cacheService.userBlockedCache.fetch(me.id),
+			]) : [new Set<string>(), new Set<string>()];
+
 			const query = this.notesRepository.createQueryBuilder('note')
 				.where('note.id IN (:...noteIds)', { noteIds: noteIds })
 				.innerJoinAndSelect('note.user', 'user')
@@ -83,10 +94,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				.leftJoinAndSelect('renote.user', 'renoteUser')
 				.leftJoinAndSelect('note.channel', 'channel');
 
-			const notes = await query.getMany();
-			notes.sort((a, b) => a.id > b.id ? -1 : 1);
+			const notes = (await query.getMany()).filter(note => {
+				if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false;
+				if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
 
-			// TODO: ミュート等考慮
+				return true;
+			});
+
+			notes.sort((a, b) => a.id > b.id ? -1 : 1);
 
 			return await this.noteEntityService.packMany(notes, me);
 		});

From 3d9e16952c55c6603a3bbedaa439c14a91ef27ea Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 17:28:44 +0100
Subject: [PATCH 112/435] upd: apply different note design choice on pinned
 notes

---
 packages/frontend/src/pages/user/home.vue | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 03d6705f6b..5911bd0eae 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -122,9 +122,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</div>
 
 			<div class="contents _gaps">
-				<div v-if="user.pinnedNotes.length > 0" class="_gaps">
+				<div v-if="user.pinnedNotes.length > 0 && defaultStore.state.noteDesign === 'misskey'" class="_gaps">
 					<MkNote v-for="note in user.pinnedNotes" :key="note.id" class="note _panel" :note="note" :pinned="true"/>
 				</div>
+				<div v-else-if="user.pinnedNotes.length > 0 && defaultStore.state.noteDesign === 'sharkey'" class="_gaps">
+					<SkNote v-for="note in user.pinnedNotes" :key="note.id" class="note _panel" :note="note" :pinned="true"/>
+				</div>
 				<MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo>
 				<template v-if="narrow">
 					<XFiles :key="user.id" :user="user"/>
@@ -162,6 +165,7 @@ import { defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch
 import * as Misskey from 'misskey-js';
 import MkTab from '@/components/MkTab.vue';
 import MkNote from '@/components/MkNote.vue';
+import SkNote from '@/components/SkNote.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
 import MkAccountMoved from '@/components/MkAccountMoved.vue';
 import MkRemoteCaution from '@/components/MkRemoteCaution.vue';

From 9ea0ca5ab3215acad1a23a861d8e5a44e02201f9 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 17:31:08 +0100
Subject: [PATCH 113/435] fix: import defaultStore

---
 packages/frontend/src/pages/user/home.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 5911bd0eae..ce48805fb7 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -185,6 +185,7 @@ import { dateString } from '@/filters/date.js';
 import { confetti } from '@/scripts/confetti.js';
 import MkNotes from '@/components/MkNotes.vue';
 import { api } from '@/os.js';
+import {  } from '@/store.js';
 import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
 
 function calcAge(birthdate: string): number {

From fef56729d70d908c291c1570bdbb1ed2934d4dc2 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 17:31:35 +0100
Subject: [PATCH 114/435] fix: import missing

---
 packages/frontend/src/pages/user/home.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index ce48805fb7..2ed55b1215 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -185,7 +185,7 @@ import { dateString } from '@/filters/date.js';
 import { confetti } from '@/scripts/confetti.js';
 import MkNotes from '@/components/MkNotes.vue';
 import { api } from '@/os.js';
-import {  } from '@/store.js';
+import { defaultStore } from '@/store.js';
 import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
 
 function calcAge(birthdate: string): number {

From f639cc764c8270ae67e08b33ce52cf45efeaf74e Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 17:40:24 +0100
Subject: [PATCH 115/435] chore: lint

---
 packages/frontend/src/components/SkNoteSub.vue | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index 629603738f..bbad1680ac 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -512,7 +512,6 @@ if (props.detail) {
 	padding: 0;
 }
 
-
 .reply, .more {
 	//border-left: solid 0.5px var(--divider);
 	margin-top: 10px;

From ec5facd09ac448fac5488c80e2e49cab721cefdd Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 17:56:54 +0100
Subject: [PATCH 116/435] upd: increase cw limit

Closes transfem-org/Sharkey#193
---
 packages/backend/src/server/api/endpoints/notes/create.test.ts | 2 +-
 packages/backend/src/server/api/endpoints/notes/create.ts      | 2 +-
 packages/backend/src/server/api/endpoints/notes/edit.ts        | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/notes/create.test.ts b/packages/backend/src/server/api/endpoints/notes/create.test.ts
index 6086f99c92..0de5a14a93 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.test.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.test.ts
@@ -72,7 +72,7 @@ describe('api:notes/create', () => {
 					.toBe(INVALID);
 			});
 
-			test('over 100 characters cw', async () => {
+			test('over 500 characters cw', async () => {
 				expect(v({ text: 'Body', cw: await tooLong }))
 					.toBe(INVALID);
 			});
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 49287f4de9..14f67cdfcd 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -121,7 +121,7 @@ export const paramDef = {
 		visibleUserIds: { type: 'array', uniqueItems: true, items: {
 			type: 'string', format: 'misskey:id',
 		} },
-		cw: { type: 'string', nullable: true, minLength: 1, maxLength: 100 },
+		cw: { type: 'string', nullable: true, minLength: 1, maxLength: 500 },
 		localOnly: { type: 'boolean', default: false },
 		reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null },
 		noExtractMentions: { type: 'boolean', default: false },
diff --git a/packages/backend/src/server/api/endpoints/notes/edit.ts b/packages/backend/src/server/api/endpoints/notes/edit.ts
index 49fa4c3bd6..d5592c4726 100644
--- a/packages/backend/src/server/api/endpoints/notes/edit.ts
+++ b/packages/backend/src/server/api/endpoints/notes/edit.ts
@@ -139,7 +139,7 @@ export const paramDef = {
 				format: 'misskey:id',
 			},
 		},
-		cw: { type: 'string', nullable: true, minLength: 1, maxLength: 250 },
+		cw: { type: 'string', nullable: true, minLength: 1, maxLength: 500 },
 		localOnly: { type: 'boolean', default: false },
 		reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null },
 		noExtractMentions: { type: 'boolean', default: false },

From 1bb258b16a4c0227baa7bc68707a7acfb0f4a95d Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 19:09:15 +0100
Subject: [PATCH 117/435] add: back button in header

Closes transfem-org/Sharkey#196
---
 .../frontend/src/components/MkPageWindow.vue  |  1 +
 .../src/components/global/MkPageHeader.vue    | 19 ++++++++++++++++++-
 packages/frontend/src/pages/note.vue          |  2 +-
 packages/frontend/src/pages/page.vue          |  2 +-
 packages/frontend/src/pages/user/index.vue    |  2 +-
 5 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue
index b22cd683ff..163cba5e3c 100644
--- a/packages/frontend/src/components/MkPageWindow.vue
+++ b/packages/frontend/src/components/MkPageWindow.vue
@@ -99,6 +99,7 @@ provideMetadataReceiver((info) => {
 provide('shouldOmitHeaderTitle', true);
 provide('shouldHeaderThin', true);
 provide('forceSpacerMin', true);
+provide('shouldBackButton', false);
 
 const contextmenu = $computed(() => ([{
 	icon: 'ph-eject ph-bold ph-lg',
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 2134e9fad3..56fe920252 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -9,9 +9,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div v-if="!thin_ && narrow && props.displayMyAvatar && $i" class="_button" :class="$style.buttonsLeft" @click="openAccountMenu">
 			<MkAvatar :class="$style.avatar" :user="$i"/>
 		</div>
-		<div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft"/>
+		<div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft">
+			<button v-if="displayBackButton" class="_button" :class="$style.button" @click.stop="goBack()" @touchstart="preventDrag">
+				<i class="ph-caret-left ph-bold ph-lg"></i>
+			</button>
+		</div>
 
 		<template v-if="metadata">
+			<div v-if="displayBackButton && !narrow" :class="$style.buttonsLeft">
+				<button class="_button" :class="$style.button" @click.stop="goBack()" @touchstart="preventDrag">
+					<i class="ph-caret-left ph-bold ph-lg"></i>
+				</button>
+			</div>
 			<div v-if="!hideTitle" :class="$style.titleContainer" @click="top">
 				<div v-if="metadata.avatar" :class="$style.titleAvatarContainer">
 					<MkAvatar :class="$style.titleAvatar" :user="metadata.avatar" indicator/>
@@ -48,6 +57,7 @@ import { scrollToTop } from '@/scripts/scroll.js';
 import { globalEvents } from '@/events.js';
 import { injectPageMetadata } from '@/scripts/page-metadata.js';
 import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
+import { instance } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	tabs?: Tab[];
@@ -60,6 +70,7 @@ const props = withDefaults(defineProps<{
 	}[];
 	thin?: boolean;
 	displayMyAvatar?: boolean;
+	displayBackButton?: boolean;
 }>(), {
 	tabs: () => ([] as Tab[]),
 });
@@ -68,6 +79,8 @@ const emit = defineEmits<{
 	(ev: 'update:tab', key: string);
 }>();
 
+const displayBackButton = props.displayBackButton && history.state.key !== 'index' && history.length > 1 && inject('shouldBackButton', true);
+
 const metadata = injectPageMetadata();
 
 const hideTitle = inject('shouldOmitHeaderTitle', false);
@@ -102,6 +115,10 @@ function onTabClick(): void {
 	top();
 }
 
+function goBack(): void {
+	window.history.back();
+}
+
 const calcBg = () => {
 	const rawBg = 'var(--bg)';
 	const tinyBg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg);
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index 9bafa17005..14f9ff3816 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <MkStickyContainer>
-	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
+	<template #header><MkPageHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs"/></template>
 	<MkSpacer :contentMax="800">
 		<div>
 			<Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in">
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 1bb65d0cef..962d910405 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <MkStickyContainer>
-	<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
+	<template #header><MkPageHeader :actions="headerActions" :displayBackButton="true" :tabs="headerTabs"/></template>
 	<MkSpacer :contentMax="700">
 		<Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in">
 			<div v-if="page" :key="page.id" class="xcukqgmh">
diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue
index dd64cc2534..df07dd9786 100644
--- a/packages/frontend/src/pages/user/index.vue
+++ b/packages/frontend/src/pages/user/index.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <MkStickyContainer>
-	<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
+	<template #header><MkPageHeader v-model:tab="tab" :displayBackButton="true" :actions="headerActions" :tabs="headerTabs"/></template>
 	<div>
 		<div v-if="user">
 			<XHome v-if="tab === 'home'" :user="user"/>

From 00d798f92247d6434b508430a6c4677b2c91ea01 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 19:15:16 +0100
Subject: [PATCH 118/435] upd: change margin for desktop view on back button

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

diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 56fe920252..bb84128977 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -16,8 +16,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</div>
 
 		<template v-if="metadata">
-			<div v-if="displayBackButton && !narrow" :class="$style.buttonsLeft">
-				<button class="_button" :class="$style.button" @click.stop="goBack()" @touchstart="preventDrag">
+			<div v-if="displayBackButton && !narrow" style="margin: 0 -45px 0 0;" :class="$style.buttonsLeft">
+				<button class="_button" :class="$style.button" style="left: 5px;" @click.stop="goBack()" @touchstart="preventDrag">
 					<i class="ph-caret-left ph-bold ph-lg"></i>
 				</button>
 			</div>

From aa4c3dfffec41702b960716f3df7d19826f3df72 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 19:17:42 +0100
Subject: [PATCH 119/435] chore: remove unused import

---
 packages/frontend/src/components/global/MkPageHeader.vue | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index bb84128977..fd7aec5e5a 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -57,7 +57,6 @@ import { scrollToTop } from '@/scripts/scroll.js';
 import { globalEvents } from '@/events.js';
 import { injectPageMetadata } from '@/scripts/page-metadata.js';
 import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
-import { instance } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	tabs?: Tab[];

From 620be4a9e122a13d856ed96e7491c88377db546f Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 19:35:39 +0100
Subject: [PATCH 120/435] add: auto uncollapse CW on all notes option

Closes transfem-org/Sharkey#191
---
 packages/frontend/src/components/MkNote.vue         | 2 +-
 packages/frontend/src/components/MkNoteDetailed.vue | 2 +-
 packages/frontend/src/components/MkNoteSimple.vue   | 3 ++-
 packages/frontend/src/components/MkNoteSub.vue      | 2 +-
 packages/frontend/src/components/SkNote.vue         | 2 +-
 packages/frontend/src/components/SkNoteDetailed.vue | 2 +-
 packages/frontend/src/components/SkNoteSimple.vue   | 3 ++-
 packages/frontend/src/components/SkNoteSub.vue      | 2 +-
 packages/frontend/src/pages/settings/general.vue    | 4 +++-
 packages/frontend/src/store.ts                      | 4 ++++
 10 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 4da8f16df1..bfc03cff7b 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -271,7 +271,7 @@ const renoteUrl = appearNote.renote ? appearNote.renote.url : null;
 const renoteUri = appearNote.renote ? appearNote.renote.uri : null;
 
 const isMyRenote = $i && ($i.id === note.userId);
-const showContent = ref(false);
+const showContent = ref(defaultStore.state.uncollapseCW);
 const parsed = $computed(() => appearNote.text ? mfm.parse(appearNote.text) : null);
 const urls = $computed(() => parsed ? extractUrlFromMfm(parsed).filter(u => u !== renoteUrl && u !== renoteUri) : null);
 const animated = $computed(() => parsed ? checkAnimationFromMfm(parsed) : null);
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 30198d2a6a..a6baeb6e1f 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -306,7 +306,7 @@ const renoteUrl = appearNote.renote ? appearNote.renote.url : null;
 const renoteUri = appearNote.renote ? appearNote.renote.uri : null;
 
 const isMyRenote = $i && ($i.id === note.userId);
-const showContent = ref(false);
+const showContent = ref(defaultStore.state.uncollapseCW);
 const isDeleted = ref(false);
 const renoted = ref(false);
 const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue
index b1d4ed3f7e..bc0f82d44d 100644
--- a/packages/frontend/src/components/MkNoteSimple.vue
+++ b/packages/frontend/src/components/MkNoteSimple.vue
@@ -28,6 +28,7 @@ import MkNoteHeader from '@/components/MkNoteHeader.vue';
 import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
 import MkCwButton from '@/components/MkCwButton.vue';
 import { $i } from '@/account.js';
+import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
 	note: Misskey.entities.Note;
@@ -35,7 +36,7 @@ const props = defineProps<{
 	hideFiles?: boolean;
 }>();
 
-let showContent = $ref(false);
+let showContent = $ref(defaultStore.state.uncollapseCW);
 
 watch(() => props.expandAllCws, (expandAllCws) => {
 	if (expandAllCws !== showContent) showContent = expandAllCws;
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 3e33c7aa69..1e057abacf 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -244,7 +244,7 @@ function undoRenote() : void {
 	}
 }
 
-let showContent = $ref(false);
+let showContent = $ref(defaultStore.state.uncollapseCW);
 
 watch(() => props.expandAllCws, (expandAllCws) => {
 	if (expandAllCws !== showContent) showContent = expandAllCws;
diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue
index 924437def5..0a48173ac6 100644
--- a/packages/frontend/src/components/SkNote.vue
+++ b/packages/frontend/src/components/SkNote.vue
@@ -272,7 +272,7 @@ const renoteUrl = appearNote.renote ? appearNote.renote.url : null;
 const renoteUri = appearNote.renote ? appearNote.renote.uri : null;
 
 const isMyRenote = $i && ($i.id === note.userId);
-const showContent = ref(false);
+const showContent = ref(defaultStore.state.uncollapseCW);
 const parsed = $computed(() => appearNote.text ? mfm.parse(appearNote.text) : null);
 const urls = $computed(() => parsed ? extractUrlFromMfm(parsed).filter(u => u !== renoteUrl && u !== renoteUri) : null);
 const animated = $computed(() => parsed ? checkAnimationFromMfm(parsed) : null);
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index 91db2047e0..79d279aaed 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -314,7 +314,7 @@ const renoteUrl = appearNote.renote ? appearNote.renote.url : null;
 const renoteUri = appearNote.renote ? appearNote.renote.uri : null;
 
 const isMyRenote = $i && ($i.id === note.userId);
-const showContent = ref(false);
+const showContent = ref(defaultStore.state.uncollapseCW);
 const isDeleted = ref(false);
 const renoted = ref(false);
 const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
diff --git a/packages/frontend/src/components/SkNoteSimple.vue b/packages/frontend/src/components/SkNoteSimple.vue
index 8ebd24b322..05a19e291d 100644
--- a/packages/frontend/src/components/SkNoteSimple.vue
+++ b/packages/frontend/src/components/SkNoteSimple.vue
@@ -28,6 +28,7 @@ import MkNoteHeader from '@/components/MkNoteHeader.vue';
 import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
 import MkCwButton from '@/components/MkCwButton.vue';
 import { $i } from '@/account.js';
+import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
 	note: Misskey.entities.Note;
@@ -35,7 +36,7 @@ const props = defineProps<{
 	hideFiles?: boolean;
 }>();
 
-let showContent = $ref(false);
+let showContent = $ref(defaultStore.state.uncollapseCW);
 
 watch(() => props.expandAllCws, (expandAllCws) => {
 	if (expandAllCws !== showContent) showContent = expandAllCws;
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index bbad1680ac..90c550f6b1 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -253,7 +253,7 @@ function undoRenote() : void {
 	}
 }
 
-let showContent = $ref(false);
+let showContent = $ref(defaultStore.state.uncollapseCW);
 
 watch(() => props.expandAllCws, (expandAllCws) => {
 	if (expandAllCws !== showContent) showContent = expandAllCws;
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 51be3b21b5..5e9be51fa6 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -47,6 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch>
 				<MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch>
 				<MkSwitch v-model="collapseFiles">{{ i18n.ts.collapseFiles }}</MkSwitch>
+				<MkSwitch v-model="uncollapseCW">Uncollapse CWs on notes</MkSwitch>
 				<MkSwitch v-model="autoloadConversation">{{ i18n.ts.autoloadConversation }}</MkSwitch>
 				<MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch>
 				<MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
@@ -63,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<template #label>Note Design</template>
 					<option value="sharkey">Sharkey</option>
 					<option value="misskey">Misskey</option>
-			</MkRadios>
+				</MkRadios>
 			</div>
 
 			<MkSelect v-model="instanceTicker">
@@ -279,6 +280,7 @@ const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disable
 const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications'));
 const showTickerOnReplies = computed(defaultStore.makeGetterSetter('showTickerOnReplies'));
 const noteDesign = computed(defaultStore.makeGetterSetter('noteDesign'));
+const uncollapseCW = computed(defaultStore.makeGetterSetter('uncollapseCW'));
 
 watch(lang, () => {
 	miLocalStorage.setItem('lang', lang.value as string);
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index e823e84eb6..1863995bc1 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -74,6 +74,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'account',
 		default: false,
 	},
+	uncollapseCW: {
+		where: 'account',
+		default: false,
+	},
 	rememberNoteVisibility: {
 		where: 'account',
 		default: false,

From 0158b6fc1bbdd14b0aee8b399fedb21fe80af6ac Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 19:59:01 +0100
Subject: [PATCH 121/435] fix: Drive folder button not working

Closes #199
---
 packages/frontend/src/components/form/link.vue   | 13 +++++++++++--
 packages/frontend/src/pages/settings/general.vue |  2 ++
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/form/link.vue b/packages/frontend/src/components/form/link.vue
index 67fc87e370..94ec9a0ba4 100644
--- a/packages/frontend/src/components/form/link.vue
+++ b/packages/frontend/src/components/form/link.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<i class="ph-arrow-square-out ph-bold ph-lg"></i>
 		</span>
 	</a>
-	<MkA v-else :class="[$style.main, { [$style.active]: active }]" class="_button" :to="to" :behavior="behavior">
+	<MkA v-else-if="to" :class="[$style.main, { [$style.active]: active }]" class="_button" :to="to" :behavior="behavior">
 		<span :class="$style.icon"><slot name="icon"></slot></span>
 		<span :class="$style.text"><slot></slot></span>
 		<span :class="$style.suffix">
@@ -21,6 +21,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<i class="ph-caret-right ph-bold ph-lg"></i>
 		</span>
 	</MkA>
+	<a v-else-if="onClick" :class="[$style.main, { [$style.active]: active }]" class="_button" :behavior="behavior" @click="onClick">
+		<span :class="$style.icon"><slot name="icon"></slot></span>
+		<span :class="$style.text"><slot></slot></span>
+		<span :class="$style.suffix">
+			<span :class="$style.suffixText"><slot name="suffix"></slot></span>
+			<i class="ph-caret-right ph-bold ph-lg"></i>
+		</span>
+	</a>
 </div>
 </template>
 
@@ -28,9 +36,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 
 const props = defineProps<{
-	to: string;
+	to?: string;
 	active?: boolean;
 	external?: boolean;
+	onClick?: () => void;
 	behavior?: null | 'window' | 'browser';
 	inline?: boolean;
 }>();
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 5e9be51fa6..1acdf6f692 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -49,6 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSwitch v-model="collapseFiles">{{ i18n.ts.collapseFiles }}</MkSwitch>
 				<MkSwitch v-model="uncollapseCW">Uncollapse CWs on notes</MkSwitch>
 				<MkSwitch v-model="autoloadConversation">{{ i18n.ts.autoloadConversation }}</MkSwitch>
+				<MkSwitch v-model="expandLongNote">Always expand long notes</MkSwitch>
 				<MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch>
 				<MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
 				<MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
@@ -281,6 +282,7 @@ const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroup
 const showTickerOnReplies = computed(defaultStore.makeGetterSetter('showTickerOnReplies'));
 const noteDesign = computed(defaultStore.makeGetterSetter('noteDesign'));
 const uncollapseCW = computed(defaultStore.makeGetterSetter('uncollapseCW'));
+const expandLongNote = computed(defaultStore.makeGetterSetter('expandLongNote'));
 
 watch(lang, () => {
 	miLocalStorage.setItem('lang', lang.value as string);

From 304a91a4744e7a2c803b61cf6d9f542468e2fcea Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 20:01:05 +0100
Subject: [PATCH 122/435] upd: change formlink handling

---
 packages/frontend/src/components/form/link.vue | 18 +++++++++---------
 packages/frontend/src/pages/settings/drive.vue |  2 +-
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/packages/frontend/src/components/form/link.vue b/packages/frontend/src/components/form/link.vue
index 94ec9a0ba4..88602a007c 100644
--- a/packages/frontend/src/components/form/link.vue
+++ b/packages/frontend/src/components/form/link.vue
@@ -13,14 +13,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<i class="ph-arrow-square-out ph-bold ph-lg"></i>
 		</span>
 	</a>
-	<MkA v-else-if="to" :class="[$style.main, { [$style.active]: active }]" class="_button" :to="to" :behavior="behavior">
-		<span :class="$style.icon"><slot name="icon"></slot></span>
-		<span :class="$style.text"><slot></slot></span>
-		<span :class="$style.suffix">
-			<span :class="$style.suffixText"><slot name="suffix"></slot></span>
-			<i class="ph-caret-right ph-bold ph-lg"></i>
-		</span>
-	</MkA>
 	<a v-else-if="onClick" :class="[$style.main, { [$style.active]: active }]" class="_button" :behavior="behavior" @click="onClick">
 		<span :class="$style.icon"><slot name="icon"></slot></span>
 		<span :class="$style.text"><slot></slot></span>
@@ -29,6 +21,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<i class="ph-caret-right ph-bold ph-lg"></i>
 		</span>
 	</a>
+	<MkA v-else :class="[$style.main, { [$style.active]: active }]" class="_button" :to="to" :behavior="behavior">
+		<span :class="$style.icon"><slot name="icon"></slot></span>
+		<span :class="$style.text"><slot></slot></span>
+		<span :class="$style.suffix">
+			<span :class="$style.suffixText"><slot name="suffix"></slot></span>
+			<i class="ph-caret-right ph-bold ph-lg"></i>
+		</span>
+	</MkA>
 </div>
 </template>
 
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 
 const props = defineProps<{
-	to?: string;
+	to: string;
 	active?: boolean;
 	external?: boolean;
 	onClick?: () => void;
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index bff7d5350c..5b9bcce944 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 	<FormSection>
 		<div class="_gaps_m">
-			<FormLink @click="chooseUploadFolder()">
+			<FormLink to="" @click="chooseUploadFolder()">
 				{{ i18n.ts.uploadFolder }}
 				<template #suffix>{{ uploadFolder ? uploadFolder.name : '-' }}</template>
 				<template #suffixIcon><i class="ph-folder ph-bold ph-lg"></i></template>

From e5667913f709c26b0f43412d947e23c62e85ad16 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 20:08:21 +0100
Subject: [PATCH 123/435] add: expand all long notes option

---
 packages/frontend/src/components/MkNote.vue           | 2 +-
 packages/frontend/src/components/MkSubNoteContent.vue | 2 +-
 packages/frontend/src/components/SkNote.vue           | 2 +-
 packages/frontend/src/store.ts                        | 4 ++++
 4 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index bfc03cff7b..e17f4b355a 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -277,7 +277,7 @@ const urls = $computed(() => parsed ? extractUrlFromMfm(parsed).filter(u => u !=
 const animated = $computed(() => parsed ? checkAnimationFromMfm(parsed) : null);
 const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
 const isLong = shouldCollapsed(appearNote, urls ?? []);
-const collapsed = ref(appearNote.cw == null && isLong);
+const collapsed = defaultStore.state.expandLongNote && appearNote.cw == null ? false : ref(appearNote.cw == null && isLong);
 const isDeleted = ref(false);
 const renoted = ref(false);
 const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue
index 8a306d172f..abad074515 100644
--- a/packages/frontend/src/components/MkSubNoteContent.vue
+++ b/packages/frontend/src/components/MkSubNoteContent.vue
@@ -73,7 +73,7 @@ const parsed = $computed(() => props.note.text ? mfm.parse(props.note.text) : nu
 const animated = $computed(() => parsed ? checkAnimationFromMfm(parsed) : null);
 let allowAnim = $ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
 
-const isLong = shouldCollapsed(props.note, []);
+const isLong = defaultStore.state.expandLongNote && !props.hideFiles ? false : shouldCollapsed(props.note, []);
 
 function animatedMFM() {
 	if (allowAnim) {
diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue
index 0a48173ac6..349304db45 100644
--- a/packages/frontend/src/components/SkNote.vue
+++ b/packages/frontend/src/components/SkNote.vue
@@ -278,7 +278,7 @@ const urls = $computed(() => parsed ? extractUrlFromMfm(parsed).filter(u => u !=
 const animated = $computed(() => parsed ? checkAnimationFromMfm(parsed) : null);
 const allowAnim = ref(defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : false);
 const isLong = shouldCollapsed(appearNote, urls ?? []);
-const collapsed = ref(appearNote.cw == null && isLong);
+const collapsed = defaultStore.state.expandLongNote && appearNote.cw == null ? false : ref(appearNote.cw == null && isLong);
 const isDeleted = ref(false);
 const renoted = ref(false);
 const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 1863995bc1..eb16222968 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -78,6 +78,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'account',
 		default: false,
 	},
+	expandLongNote: {
+		where: 'device',
+		default: false,
+	},
 	rememberNoteVisibility: {
 		where: 'account',
 		default: false,

From 10b27ad972dda56ea104d67c541dd5638d6942a7 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Sun, 3 Dec 2023 20:43:25 +0100
Subject: [PATCH 124/435] fix: quotes showing in replies on a note

---
 packages/frontend/src/components/MkNoteSub.vue | 1 +
 packages/frontend/src/components/SkNoteSub.vue | 1 +
 2 files changed, 2 insertions(+)

diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 1e057abacf..5c08c3adc4 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -353,6 +353,7 @@ if (props.detail) {
 	os.api('notes/children', {
 		noteId: props.note.id,
 		limit: 5,
+		showQuotes: false,
 	}).then(res => {
 		replies = res;
 	});
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index 90c550f6b1..c537fc902b 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -362,6 +362,7 @@ if (props.detail) {
 	os.api('notes/children', {
 		noteId: props.note.id,
 		limit: 5,
+		showQuotes: false,
 	}).then(res => {
 		replies = res;
 	});

From fb75282ea8c71fd4dfc08c885fe099f2f1e2baba Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Mon, 4 Dec 2023 00:19:34 +0100
Subject: [PATCH 125/435] add: choose boost visibility when clicking boost

Closes transfem-org/Sharkey#195
---
 packages/frontend/src/components/MkNote.vue   | 51 +++++++++++++++---
 .../src/components/MkNoteDetailed.vue         | 53 +++++++++++++++----
 .../frontend/src/components/MkNoteSub.vue     | 42 ++++++++++++++-
 packages/frontend/src/components/SkNote.vue   | 51 +++++++++++++++---
 .../src/components/SkNoteDetailed.vue         | 51 +++++++++++++++---
 .../frontend/src/components/SkNoteSub.vue     | 42 ++++++++++++++-
 6 files changed, 253 insertions(+), 37 deletions(-)

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index e17f4b355a..b6d0d1af0a 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -114,7 +114,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					class="_button"
 					:style="renoted ? 'color: var(--accent) !important;' : ''"
 					v-on:click.stop
-					@mousedown="renoted ? undoRenote(appearNote) : renote()"
+					@mousedown="renoted ? undoRenote(appearNote) : boostVisibility()"
 				>
 					<i class="ph-rocket-launch ph-bold ph-lg"></i>
 					<p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ appearNote.renoteCount }}</p>
@@ -379,7 +379,43 @@ function smallerVisibility(a: Visibility | string, b: Visibility | string): Visi
 	return 'public';
 }
 
-function renote() {
+function boostVisibility() {
+	os.popupMenu([
+		{
+			type: 'button',
+			icon: 'ph-globe-hemisphere-west ph-bold ph-lg',
+			text: i18n.ts._visibility['public'],
+			action: () => {
+				renote('public');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-house ph-bold ph-lg',
+			text: i18n.ts._visibility['home'],
+			action: () => {
+				renote('home');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-lock ph-bold ph-lg',
+			text: i18n.ts._visibility['followers'],
+			action: () => {
+				renote('followers');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-planet ph-bold ph-lg',
+			text: i18n.ts._timelines.local,
+			action: () => {
+				renote('local');
+			},
+		}], renoteButton.value);
+}
+
+function renote(visibility: Visibility | 'local') {
 	pleaseLogin();
 	showMovedDialog();
 
@@ -411,18 +447,17 @@ function renote() {
 		}
 
 		const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
-		const localOnly = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
+		const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
 
-		let visibility = appearNote.visibility;
-		visibility = smallerVisibility(visibility, configuredVisibility);
+		let noteVisibility = visibility === 'local' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
 		if (appearNote.channel?.isSensitive) {
-			visibility = smallerVisibility(visibility, 'home');
+			noteVisibility = smallerVisibility(visibility === 'local' ? appearNote.visibility : visibility, 'home');
 		}
 
 		if (!props.mock) {
 			os.api('notes/create', {
-				localOnly,
-				visibility,
+				localOnly: visibility === 'local' ? true : localOnlySetting,
+				visibility: noteVisibility,
 				renoteId: appearNote.id,
 			}).then(() => {
 				os.toast(i18n.ts.renoted);
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index a6baeb6e1f..ec60620208 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -124,7 +124,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				class="_button"
 				:class="$style.noteFooterButton"
 				:style="renoted ? 'color: var(--accent) !important;' : ''"
-				@mousedown="renoted ? undoRenote() : renote()"
+				@mousedown="renoted ? undoRenote() : boostVisibility()"
 			>
 				<i class="ph-rocket-launch ph-bold ph-lg"></i>
 				<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ appearNote.renoteCount }}</p>
@@ -427,7 +427,43 @@ function smallerVisibility(a: Visibility | string, b: Visibility | string): Visi
 	return 'public';
 }
 
-function renote() {
+function boostVisibility() {
+	os.popupMenu([
+		{
+			type: 'button',
+			icon: 'ph-globe-hemisphere-west ph-bold ph-lg',
+			text: i18n.ts._visibility['public'],
+			action: () => {
+				renote('public');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-house ph-bold ph-lg',
+			text: i18n.ts._visibility['home'],
+			action: () => {
+				renote('home');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-lock ph-bold ph-lg',
+			text: i18n.ts._visibility['followers'],
+			action: () => {
+				renote('followers');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-planet ph-bold ph-lg',
+			text: i18n.ts._timelines.local,
+			action: () => {
+				renote('local');
+			},
+		}], renoteButton.value);
+}
+
+function renote(visibility: Visibility | 'local') {
 	pleaseLogin();
 	showMovedDialog();
 
@@ -457,17 +493,16 @@ function renote() {
 		}
 
 		const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
-		const localOnly = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
+		const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
 
-		let visibility = appearNote.visibility;
-		visibility = smallerVisibility(visibility, configuredVisibility);
+		let noteVisibility = visibility === 'local' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
 		if (appearNote.channel?.isSensitive) {
-			visibility = smallerVisibility(visibility, 'home');
+			noteVisibility = smallerVisibility(visibility === 'local' ? appearNote.visibility : visibility, 'home');
 		}
-		
+
 		os.api('notes/create', {
-			localOnly,
-			visibility,
+			localOnly: visibility === 'local' ? true : localOnlySetting,
+			visibility: noteVisibility,
 			renoteId: appearNote.id,
 		}).then(() => {
 			os.toast(i18n.ts.renoted);
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 5c08c3adc4..ce95444bc2 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					class="_button"
 					:class="$style.noteFooterButton"
 					:style="renoted ? 'color: var(--accent) !important;' : ''"
-					@mousedown="renoted ? undoRenote() : renote()"
+					@mousedown="renoted ? undoRenote() : boostVisibility()"
 				>
 					<i class="ph-rocket-launch ph-bold ph-lg"></i>
 					<p v-if="note.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ note.renoteCount }}</p>
@@ -252,7 +252,43 @@ watch(() => props.expandAllCws, (expandAllCws) => {
 
 let replies: Misskey.entities.Note[] = $ref([]);
 
-function renote() {
+function boostVisibility() {
+	os.popupMenu([
+		{
+			type: 'button',
+			icon: 'ph-globe-hemisphere-west ph-bold ph-lg',
+			text: i18n.ts._visibility['public'],
+			action: () => {
+				renote('public');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-house ph-bold ph-lg',
+			text: i18n.ts._visibility['home'],
+			action: () => {
+				renote('home');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-lock ph-bold ph-lg',
+			text: i18n.ts._visibility['followers'],
+			action: () => {
+				renote('followers');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-planet ph-bold ph-lg',
+			text: i18n.ts._timelines.local,
+			action: () => {
+				renote('local');
+			},
+		}], renoteButton.value);
+}
+
+function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'local') {
 	pleaseLogin();
 	showMovedDialog();
 
@@ -283,6 +319,8 @@ function renote() {
 
 		os.api('notes/create', {
 			renoteId: props.note.id,
+			localOnly: visibility === 'local' ? true : false,
+			visibility: visibility === 'local' ? props.note.visibility : visibility,
 		}).then(() => {
 			os.toast(i18n.ts.renoted);
 			renoted.value = true;
diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue
index 349304db45..5bad52040e 100644
--- a/packages/frontend/src/components/SkNote.vue
+++ b/packages/frontend/src/components/SkNote.vue
@@ -116,7 +116,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					class="_button"
 					:style="renoted ? 'color: var(--accent) !important;' : ''"
 					v-on:click.stop
-					@mousedown="renoted ? undoRenote(appearNote) : renote()"
+					@mousedown="renoted ? undoRenote(appearNote) : boostVisibility()"
 				>
 					<i class="ph-rocket-launch ph-bold ph-lg"></i>
 					<p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ appearNote.renoteCount }}</p>
@@ -380,7 +380,43 @@ function smallerVisibility(a: Visibility | string, b: Visibility | string): Visi
 	return 'public';
 }
 
-function renote() {
+function boostVisibility() {
+	os.popupMenu([
+		{
+			type: 'button',
+			icon: 'ph-globe-hemisphere-west ph-bold ph-lg',
+			text: i18n.ts._visibility['public'],
+			action: () => {
+				renote('public');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-house ph-bold ph-lg',
+			text: i18n.ts._visibility['home'],
+			action: () => {
+				renote('home');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-lock ph-bold ph-lg',
+			text: i18n.ts._visibility['followers'],
+			action: () => {
+				renote('followers');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-planet ph-bold ph-lg',
+			text: i18n.ts._timelines.local,
+			action: () => {
+				renote('local');
+			},
+		}], renoteButton.value);
+}
+
+function renote(visibility: Visibility | 'local') {
 	pleaseLogin();
 	showMovedDialog();
 
@@ -412,18 +448,17 @@ function renote() {
 		}
 
 		const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
-		const localOnly = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
+		const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
 
-		let visibility = appearNote.visibility;
-		visibility = smallerVisibility(visibility, configuredVisibility);
+		let noteVisibility = visibility === 'local' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
 		if (appearNote.channel?.isSensitive) {
-			visibility = smallerVisibility(visibility, 'home');
+			noteVisibility = smallerVisibility(visibility === 'local' ? appearNote.visibility : visibility, 'home');
 		}
 
 		if (!props.mock) {
 			os.api('notes/create', {
-				localOnly,
-				visibility,
+				localOnly: visibility === 'local' ? true : localOnlySetting,
+				visibility: noteVisibility,
 				renoteId: appearNote.id,
 			}).then(() => {
 				os.toast(i18n.ts.renoted);
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index 79d279aaed..85a6ed45ef 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -132,7 +132,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				class="_button"
 				:class="$style.noteFooterButton"
 				:style="renoted ? 'color: var(--accent) !important;' : ''"
-				@mousedown="renoted ? undoRenote() : renote()"
+				@mousedown="renoted ? undoRenote() : boostVisibility()"
 			>
 				<i class="ph-rocket-launch ph-bold ph-lg"></i>
 				<p v-if="appearNote.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ appearNote.renoteCount }}</p>
@@ -435,7 +435,43 @@ function smallerVisibility(a: Visibility | string, b: Visibility | string): Visi
 	return 'public';
 }
 
-function renote() {
+function boostVisibility() {
+	os.popupMenu([
+		{
+			type: 'button',
+			icon: 'ph-globe-hemisphere-west ph-bold ph-lg',
+			text: i18n.ts._visibility['public'],
+			action: () => {
+				renote('public');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-house ph-bold ph-lg',
+			text: i18n.ts._visibility['home'],
+			action: () => {
+				renote('home');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-lock ph-bold ph-lg',
+			text: i18n.ts._visibility['followers'],
+			action: () => {
+				renote('followers');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-planet ph-bold ph-lg',
+			text: i18n.ts._timelines.local,
+			action: () => {
+				renote('local');
+			},
+		}], renoteButton.value);
+}
+
+function renote(visibility: Visibility | 'local') {
 	pleaseLogin();
 	showMovedDialog();
 
@@ -465,17 +501,16 @@ function renote() {
 		}
 
 		const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
-		const localOnly = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
+		const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
 
-		let visibility = appearNote.visibility;
-		visibility = smallerVisibility(visibility, configuredVisibility);
+		let noteVisibility = visibility === 'local' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
 		if (appearNote.channel?.isSensitive) {
-			visibility = smallerVisibility(visibility, 'home');
+			noteVisibility = smallerVisibility(visibility === 'local' ? appearNote.visibility : visibility, 'home');
 		}
 
 		os.api('notes/create', {
-			localOnly,
-			visibility,
+			localOnly: visibility === 'local' ? true : localOnlySetting,
+			visibility: noteVisibility,
 			renoteId: appearNote.id,
 		}).then(() => {
 			os.toast(i18n.ts.renoted);
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index c537fc902b..cc44f42177 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					class="_button"
 					:class="$style.noteFooterButton"
 					:style="renoted ? 'color: var(--accent) !important;' : ''"
-					@mousedown="renoted ? undoRenote() : renote()"
+					@mousedown="renoted ? undoRenote() : boostVisibility()"
 				>
 					<i class="ph-rocket-launch ph-bold ph-lg"></i>
 					<p v-if="note.renoteCount > 0" :class="$style.noteFooterButtonCount">{{ note.renoteCount }}</p>
@@ -261,7 +261,43 @@ watch(() => props.expandAllCws, (expandAllCws) => {
 
 let replies: Misskey.entities.Note[] = $ref([]);
 
-function renote() {
+function boostVisibility() {
+	os.popupMenu([
+		{
+			type: 'button',
+			icon: 'ph-globe-hemisphere-west ph-bold ph-lg',
+			text: i18n.ts._visibility['public'],
+			action: () => {
+				renote('public');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-house ph-bold ph-lg',
+			text: i18n.ts._visibility['home'],
+			action: () => {
+				renote('home');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-lock ph-bold ph-lg',
+			text: i18n.ts._visibility['followers'],
+			action: () => {
+				renote('followers');
+			},
+		},
+		{
+			type: 'button',
+			icon: 'ph-planet ph-bold ph-lg',
+			text: i18n.ts._timelines.local,
+			action: () => {
+				renote('local');
+			},
+		}], renoteButton.value);
+}
+
+function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'local') {
 	pleaseLogin();
 	showMovedDialog();
 
@@ -292,6 +328,8 @@ function renote() {
 
 		os.api('notes/create', {
 			renoteId: props.note.id,
+			localOnly: visibility === 'local' ? true : false,
+			visibility: visibility === 'local' ? props.note.visibility : visibility,
 		}).then(() => {
 			os.toast(i18n.ts.renoted);
 			renoted.value = true;

From d586d1e6f80e29a5ad6f02a5c487804024f16b37 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Mon, 4 Dec 2023 00:26:47 +0100
Subject: [PATCH 126/435] upd: add additional check to visibility selector for
 boost

---
 packages/frontend/src/components/MkNote.vue         | 4 ++--
 packages/frontend/src/components/MkNoteDetailed.vue | 4 ++--
 packages/frontend/src/components/MkNoteSub.vue      | 2 +-
 packages/frontend/src/components/SkNote.vue         | 4 ++--
 packages/frontend/src/components/SkNoteDetailed.vue | 4 ++--
 packages/frontend/src/components/SkNoteSub.vue      | 2 +-
 6 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index b6d0d1af0a..dbbff650e0 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -449,9 +449,9 @@ function renote(visibility: Visibility | 'local') {
 		const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
 		const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
 
-		let noteVisibility = visibility === 'local' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
+		let noteVisibility = visibility === 'local' || visibility === 'specified' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
 		if (appearNote.channel?.isSensitive) {
-			noteVisibility = smallerVisibility(visibility === 'local' ? appearNote.visibility : visibility, 'home');
+			noteVisibility = smallerVisibility(visibility === 'local' || visibility === 'specified' ? appearNote.visibility : visibility, 'home');
 		}
 
 		if (!props.mock) {
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index ec60620208..34f782c269 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -495,9 +495,9 @@ function renote(visibility: Visibility | 'local') {
 		const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
 		const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
 
-		let noteVisibility = visibility === 'local' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
+		let noteVisibility = visibility === 'local' || visibility === 'specified' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
 		if (appearNote.channel?.isSensitive) {
-			noteVisibility = smallerVisibility(visibility === 'local' ? appearNote.visibility : visibility, 'home');
+			noteVisibility = smallerVisibility(visibility === 'local' || visibility === 'specified' ? appearNote.visibility : visibility, 'home');
 		}
 
 		os.api('notes/create', {
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index ce95444bc2..384a85e546 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -320,7 +320,7 @@ function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'loc
 		os.api('notes/create', {
 			renoteId: props.note.id,
 			localOnly: visibility === 'local' ? true : false,
-			visibility: visibility === 'local' ? props.note.visibility : visibility,
+			visibility: visibility === 'local' || visibility === 'specified' ? props.note.visibility : visibility,
 		}).then(() => {
 			os.toast(i18n.ts.renoted);
 			renoted.value = true;
diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue
index 5bad52040e..b01e78c436 100644
--- a/packages/frontend/src/components/SkNote.vue
+++ b/packages/frontend/src/components/SkNote.vue
@@ -450,9 +450,9 @@ function renote(visibility: Visibility | 'local') {
 		const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
 		const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
 
-		let noteVisibility = visibility === 'local' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
+		let noteVisibility = visibility === 'local' || visibility === 'specified' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
 		if (appearNote.channel?.isSensitive) {
-			noteVisibility = smallerVisibility(visibility === 'local' ? appearNote.visibility : visibility, 'home');
+			noteVisibility = smallerVisibility(visibility === 'local' || visibility === 'specified' ? appearNote.visibility : visibility, 'home');
 		}
 
 		if (!props.mock) {
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index 85a6ed45ef..0db4e0a30b 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -503,9 +503,9 @@ function renote(visibility: Visibility | 'local') {
 		const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
 		const localOnlySetting = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
 
-		let noteVisibility = visibility === 'local' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
+		let noteVisibility = visibility === 'local' || visibility === 'specified' ? smallerVisibility(appearNote.visibility, configuredVisibility) : smallerVisibility(visibility, configuredVisibility);
 		if (appearNote.channel?.isSensitive) {
-			noteVisibility = smallerVisibility(visibility === 'local' ? appearNote.visibility : visibility, 'home');
+			noteVisibility = smallerVisibility(visibility === 'local' || visibility === 'specified' ? appearNote.visibility : visibility, 'home');
 		}
 
 		os.api('notes/create', {
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index cc44f42177..64c71efd4e 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -329,7 +329,7 @@ function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'loc
 		os.api('notes/create', {
 			renoteId: props.note.id,
 			localOnly: visibility === 'local' ? true : false,
-			visibility: visibility === 'local' ? props.note.visibility : visibility,
+			visibility: visibility === 'local' || visibility === 'specified' ? props.note.visibility : visibility,
 		}).then(() => {
 			os.toast(i18n.ts.renoted);
 			renoted.value = true;

From 2f99c7e9dc2e5e3ca06c9672a6ab4887eb094310 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Mon, 4 Dec 2023 02:10:51 +0100
Subject: [PATCH 127/435] add: Bubble timeline

Closes  transfem-org/Sharkey#154
---
 locales/en-US.yml                             |   1 +
 locales/index.d.ts                            |   1 +
 locales/ja-JP.yml                             |   1 +
 .../1701647674000-BubbleInstances.js          |  11 ++
 packages/backend/src/core/RoleService.ts      |   3 +
 packages/backend/src/models/Meta.ts           |   5 +
 .../src/server/NodeinfoServerService.ts       |   1 +
 packages/backend/src/server/ServerModule.ts   |   2 +
 .../backend/src/server/api/EndpointsModule.ts |   4 +
 packages/backend/src/server/api/endpoints.ts  |   2 +
 .../src/server/api/endpoints/admin/meta.ts    |   8 ++
 .../server/api/endpoints/admin/update-meta.ts |   5 +
 .../api/endpoints/notes/bubble-timeline.ts    | 130 ++++++++++++++++++
 .../src/server/api/stream/ChannelsService.ts  |   3 +
 .../api/stream/channels/bubble-timeline.ts    | 124 +++++++++++++++++
 .../frontend/src/components/MkTimeline.vue    |  13 ++
 packages/frontend/src/const.ts                |   1 +
 .../frontend/src/pages/admin/moderation.vue   |  10 ++
 .../frontend/src/pages/admin/roles.editor.vue |  20 +++
 packages/frontend/src/pages/admin/roles.vue   |  12 ++
 packages/frontend/src/pages/timeline.vue      |   6 +
 packages/frontend/src/ui/deck/deck-store.ts   |   2 +-
 packages/frontend/src/ui/deck/tl-column.vue   |   9 +-
 .../frontend/src/widgets/WidgetTimeline.vue   |   8 +-
 .../megalodon/src/misskey/entities/meta.ts    |   1 +
 packages/misskey-js/src/entities.ts           |   1 +
 packages/misskey-js/src/streaming.types.ts    |   7 +
 27 files changed, 387 insertions(+), 4 deletions(-)
 create mode 100644 packages/backend/migration/1701647674000-BubbleInstances.js
 create mode 100644 packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts
 create mode 100644 packages/backend/src/server/api/stream/channels/bubble-timeline.ts

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 7f5239ae8d..6a8227511e 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -1598,6 +1598,7 @@ _role:
     high: "High"
   _options:
     gtlAvailable: "Can view the global timeline"
+    btlAvailable: "Can view the bubble timeline"
     ltlAvailable: "Can view the local timeline"
     canPublicNote: "Can send public notes"
     canImportNotes: "Can import notes"
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 9aeaeb9e56..98ab44dd41 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1698,6 +1698,7 @@ export interface Locale {
         };
         "_options": {
             "gtlAvailable": string;
+            "btlAvailable": string;
             "ltlAvailable": string;
             "canPublicNote": string;
             "canImportNotes": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 2f81a25cba..0af37e041f 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1607,6 +1607,7 @@ _role:
     high: "高"
   _options:
     gtlAvailable: "グローバルタイムラインの閲覧"
+    btlAvailable: "バブルのタイムラインを見ることができる"
     ltlAvailable: "ローカルタイムラインの閲覧"
     canPublicNote: "パブリック投稿の許可"
     canImportNotes: "ノートのインポートが可能"
diff --git a/packages/backend/migration/1701647674000-BubbleInstances.js b/packages/backend/migration/1701647674000-BubbleInstances.js
new file mode 100644
index 0000000000..9928b4c36e
--- /dev/null
+++ b/packages/backend/migration/1701647674000-BubbleInstances.js
@@ -0,0 +1,11 @@
+export class BubbleInstances1701647674000 {
+    name = 'BubbleInstances1701647674000'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "meta" ADD "bubbleInstances" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "bubbleInstances"`);
+    }
+}
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 4c5f883351..79c1ecc76f 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -26,6 +26,7 @@ import type { OnApplicationShutdown } from '@nestjs/common';
 export type RolePolicies = {
 	gtlAvailable: boolean;
 	ltlAvailable: boolean;
+	btlAvailable: boolean;
 	canPublicNote: boolean;
 	canInvite: boolean;
 	inviteLimit: number;
@@ -53,6 +54,7 @@ export type RolePolicies = {
 export const DEFAULT_POLICIES: RolePolicies = {
 	gtlAvailable: true,
 	ltlAvailable: true,
+	btlAvailable: false,
 	canPublicNote: true,
 	canInvite: false,
 	inviteLimit: 0,
@@ -303,6 +305,7 @@ export class RoleService implements OnApplicationShutdown {
 
 		return {
 			gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)),
+			btlAvailable: calc('btlAvailable', vs => vs.some(v => v === true)),
 			ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)),
 			canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)),
 			canInvite: calc('canInvite', vs => vs.some(v => v === true)),
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 97bec444d6..b70828f3dd 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -544,4 +544,9 @@ export class MiMeta {
 		nullable: true,
 	})
 	public defaultLike: string | null;
+
+	@Column('varchar', {
+		length: 256, array: true, default: '{}',
+	})
+	public bubbleInstances: string[];
 }
diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts
index 0b6a7dfe22..e308b5d3e4 100644
--- a/packages/backend/src/server/NodeinfoServerService.ts
+++ b/packages/backend/src/server/NodeinfoServerService.ts
@@ -109,6 +109,7 @@ export class NodeinfoServerService {
 					disableRegistration: meta.disableRegistration,
 					disableLocalTimeline: !basePolicies.ltlAvailable,
 					disableGlobalTimeline: !basePolicies.gtlAvailable,
+					disableBubbleTimeline: !basePolicies.btlAvailable,
 					emailRequiredForSignup: meta.emailRequiredForSignup,
 					enableHcaptcha: meta.enableHcaptcha,
 					enableRecaptcha: meta.enableRecaptcha,
diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts
index fc5eece01f..52070b5157 100644
--- a/packages/backend/src/server/ServerModule.ts
+++ b/packages/backend/src/server/ServerModule.ts
@@ -32,6 +32,7 @@ import { AntennaChannelService } from './api/stream/channels/antenna.js';
 import { ChannelChannelService } from './api/stream/channels/channel.js';
 import { DriveChannelService } from './api/stream/channels/drive.js';
 import { GlobalTimelineChannelService } from './api/stream/channels/global-timeline.js';
+import { BubbleTimelineChannelService } from './api/stream/channels/bubble-timeline.js';
 import { HashtagChannelService } from './api/stream/channels/hashtag.js';
 import { HomeTimelineChannelService } from './api/stream/channels/home-timeline.js';
 import { HybridTimelineChannelService } from './api/stream/channels/hybrid-timeline.js';
@@ -77,6 +78,7 @@ import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js';
 		ChannelChannelService,
 		DriveChannelService,
 		GlobalTimelineChannelService,
+		BubbleTimelineChannelService,
 		HashtagChannelService,
 		RoleTimelineChannelService,
 		HomeTimelineChannelService,
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index e7014c1333..2037856797 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -277,6 +277,7 @@ import * as ep___notes_favorites_create from './endpoints/notes/favorites/create
 import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js';
 import * as ep___notes_featured from './endpoints/notes/featured.js';
 import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js';
+import * as ep___notes_bubbleTimeline from './endpoints/notes/bubble-timeline.js';
 import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js';
 import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js';
 import * as ep___notes_mentions from './endpoints/notes/mentions.js';
@@ -648,6 +649,7 @@ const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create'
 const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default };
 const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default };
 const $notes_globalTimeline: Provider = { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default };
+const $notes_bubbleTimeline: Provider = { provide: 'ep:notes/bubble-timeline', useClass: ep___notes_bubbleTimeline.default };
 const $notes_hybridTimeline: Provider = { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default };
 const $notes_localTimeline: Provider = { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default };
 const $notes_mentions: Provider = { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default };
@@ -1023,6 +1025,7 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de
 		$notes_favorites_delete,
 		$notes_featured,
 		$notes_globalTimeline,
+		$notes_bubbleTimeline,
 		$notes_hybridTimeline,
 		$notes_localTimeline,
 		$notes_mentions,
@@ -1392,6 +1395,7 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de
 		$notes_favorites_delete,
 		$notes_featured,
 		$notes_globalTimeline,
+		$notes_bubbleTimeline,
 		$notes_hybridTimeline,
 		$notes_localTimeline,
 		$notes_mentions,
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index 0d32b79900..bf299d6ef4 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -277,6 +277,7 @@ import * as ep___notes_favorites_create from './endpoints/notes/favorites/create
 import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js';
 import * as ep___notes_featured from './endpoints/notes/featured.js';
 import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js';
+import * as ep___notes_bubbleTimeline from './endpoints/notes/bubble-timeline.js';
 import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js';
 import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js';
 import * as ep___notes_mentions from './endpoints/notes/mentions.js';
@@ -646,6 +647,7 @@ const eps = [
 	['notes/favorites/delete', ep___notes_favorites_delete],
 	['notes/featured', ep___notes_featured],
 	['notes/global-timeline', ep___notes_globalTimeline],
+	['notes/bubble-timeline', ep___notes_bubbleTimeline],
 	['notes/hybrid-timeline', ep___notes_hybridTimeline],
 	['notes/local-timeline', ep___notes_localTimeline],
 	['notes/mentions', ep___notes_mentions],
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index b1ba1633c9..f10accaeac 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -154,6 +154,13 @@ export const meta = {
 					type: 'string',
 				},
 			},
+			bubbleInstances: {
+				type: 'array',
+				optional: false, nullable: false,
+				items: {
+					type: 'string',
+				},
+			},
 			hcaptchaSecretKey: {
 				type: 'string',
 				optional: false, nullable: true,
@@ -402,6 +409,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				silencedHosts: instance.silencedHosts,
 				sensitiveWords: instance.sensitiveWords,
 				preservedUsernames: instance.preservedUsernames,
+				bubbleInstances: instance.bubbleInstances,
 				hcaptchaSecretKey: instance.hcaptchaSecretKey,
 				recaptchaSecretKey: instance.recaptchaSecretKey,
 				turnstileSecretKey: instance.turnstileSecretKey,
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 e1a1f3acb3..47deeffe0c 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -123,6 +123,7 @@ export const paramDef = {
 		enableIdenticonGeneration: { type: 'boolean' },
 		serverRules: { type: 'array', items: { type: 'string' } },
 		preservedUsernames: { type: 'array', items: { type: 'string' } },
+		bubbleInstances: { type: 'array', items: { type: 'string' } },
 		manifestJsonOverride: { type: 'string' },
 		enableFanoutTimeline: { type: 'boolean' },
 		enableFanoutTimelineDbFallback: { type: 'boolean' },
@@ -482,6 +483,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				set.preservedUsernames = ps.preservedUsernames;
 			}
 
+			if (ps.bubbleInstances !== undefined) {
+				set.bubbleInstances = ps.bubbleInstances;
+			}
+
 			if (ps.manifestJsonOverride !== undefined) {
 				set.manifestJsonOverride = ps.manifestJsonOverride;
 			}
diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts
new file mode 100644
index 0000000000..0652c82a9d
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts
@@ -0,0 +1,130 @@
+import { Inject, Injectable } from '@nestjs/common';
+import { Brackets } from 'typeorm';
+import type { NotesRepository } from '@/models/_.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { QueryService } from '@/core/QueryService.js';
+import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
+import ActiveUsersChart from '@/core/chart/charts/active-users.js';
+import { DI } from '@/di-symbols.js';
+import { RoleService } from '@/core/RoleService.js';
+import { ApiError } from '../../error.js';
+import { CacheService } from '@/core/CacheService.js';
+import { MetaService } from '@/core/MetaService.js';
+
+export const meta = {
+	tags: ['notes'],
+
+	res: {
+		type: 'array',
+		optional: false, nullable: false,
+		items: {
+			type: 'object',
+			optional: false, nullable: false,
+			ref: 'Note',
+		},
+	},
+
+	errors: {
+		btlDisabled: {
+			message: 'Bubble timeline has been disabled.',
+			code: 'BTL_DISABLED',
+			id: '0332fc13-6ab2-4427-ae80-a9fadffd1a6c',
+		},
+	},
+} as const;
+
+export const paramDef = {
+	type: 'object',
+	properties: {
+		withFiles: { type: 'boolean', default: false },
+		withBots: { type: 'boolean', default: true },
+		withRenotes: { type: 'boolean', default: true },
+		limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
+		sinceId: { type: 'string', format: 'misskey:id' },
+		untilId: { type: 'string', format: 'misskey:id' },
+		sinceDate: { type: 'integer' },
+		untilDate: { type: 'integer' },
+	},
+	required: [],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+	constructor(
+		@Inject(DI.notesRepository)
+		private notesRepository: NotesRepository,
+
+		private noteEntityService: NoteEntityService,
+		private queryService: QueryService,
+		private roleService: RoleService,
+		private activeUsersChart: ActiveUsersChart,
+		private cacheService: CacheService,
+		private metaService: MetaService,
+	) {
+		super(meta, paramDef, async (ps, me) => {
+			const policies = await this.roleService.getUserPolicies(me ? me.id : null);
+			const instance = await this.metaService.fetch();
+			if (!policies.btlAvailable) {
+				throw new ApiError(meta.errors.btlDisabled);
+			}
+
+			const [
+				followings,
+			] = me ? await Promise.all([
+				this.cacheService.userFollowingsCache.fetch(me.id),
+			]) : [undefined];
+
+			//#region Construct query
+			const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'),
+				ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
+				.andWhere('note.visibility = \'public\'')
+				.andWhere('note.channelId IS NULL')
+				.andWhere(
+					`(note.userHost = ANY ('{"${instance.bubbleInstances.join('","')}"}'))`,
+				)		
+				.innerJoinAndSelect('note.user', 'user')
+				.leftJoinAndSelect('note.reply', 'reply')
+				.leftJoinAndSelect('note.renote', 'renote')
+				.leftJoinAndSelect('reply.user', 'replyUser')
+				.leftJoinAndSelect('renote.user', 'renoteUser');
+
+			if (me) {
+				this.queryService.generateMutedUserQuery(query, me);
+				this.queryService.generateBlockedUserQuery(query, me);
+				this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
+			}
+
+			if (ps.withFiles) {
+				query.andWhere('note.fileIds != \'{}\'');
+			}
+
+			if (!ps.withBots) query.andWhere('user.isBot = FALSE');
+			
+			if (ps.withRenotes === false) {
+				query.andWhere(new Brackets(qb => {
+					qb.where('note.renoteId IS NULL');
+					qb.orWhere(new Brackets(qb => {
+						qb.where('note.text IS NOT NULL');
+						qb.orWhere('note.fileIds != \'{}\'');
+					}));
+				}));
+			}
+			//#endregion
+
+			let timeline = await query.limit(ps.limit).getMany();
+
+			timeline = timeline.filter(note => {
+				if (note.user?.isSilenced && me && followings && note.userId !== me.id && !followings[note.userId]) return false;
+				return true;
+			});
+
+			process.nextTick(() => {
+				if (me) {
+					this.activeUsersChart.read(me);
+				}
+			});
+
+			return await this.noteEntityService.packMany(timeline, me);
+		});
+	}
+}
diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts
index 8fd106c10c..f9f2f15aff 100644
--- a/packages/backend/src/server/api/stream/ChannelsService.ts
+++ b/packages/backend/src/server/api/stream/ChannelsService.ts
@@ -8,6 +8,7 @@ import { bindThis } from '@/decorators.js';
 import { HybridTimelineChannelService } from './channels/hybrid-timeline.js';
 import { LocalTimelineChannelService } from './channels/local-timeline.js';
 import { HomeTimelineChannelService } from './channels/home-timeline.js';
+import { BubbleTimelineChannelService } from './channels/bubble-timeline.js';
 import { GlobalTimelineChannelService } from './channels/global-timeline.js';
 import { MainChannelService } from './channels/main.js';
 import { ChannelChannelService } from './channels/channel.js';
@@ -28,6 +29,7 @@ export class ChannelsService {
 		private localTimelineChannelService: LocalTimelineChannelService,
 		private hybridTimelineChannelService: HybridTimelineChannelService,
 		private globalTimelineChannelService: GlobalTimelineChannelService,
+		private bubbleTimelineChannelService: BubbleTimelineChannelService,
 		private userListChannelService: UserListChannelService,
 		private hashtagChannelService: HashtagChannelService,
 		private roleTimelineChannelService: RoleTimelineChannelService,
@@ -48,6 +50,7 @@ export class ChannelsService {
 			case 'localTimeline': return this.localTimelineChannelService;
 			case 'hybridTimeline': return this.hybridTimelineChannelService;
 			case 'globalTimeline': return this.globalTimelineChannelService;
+			case 'bubbleTimeline': return this.bubbleTimelineChannelService;
 			case 'userList': return this.userListChannelService;
 			case 'hashtag': return this.hashtagChannelService;
 			case 'roleTimeline': return this.roleTimelineChannelService;
diff --git a/packages/backend/src/server/api/stream/channels/bubble-timeline.ts b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts
new file mode 100644
index 0000000000..74d5c3ea4e
--- /dev/null
+++ b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts
@@ -0,0 +1,124 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { checkWordMute } from '@/misc/check-word-mute.js';
+import { isInstanceMuted } from '@/misc/is-instance-muted.js';
+import { isUserRelated } from '@/misc/is-user-related.js';
+import type { Packed } from '@/misc/json-schema.js';
+import { MetaService } from '@/core/MetaService.js';
+import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
+import { bindThis } from '@/decorators.js';
+import { RoleService } from '@/core/RoleService.js';
+import type { MiMeta } from '@/models/Meta.js';
+import Channel from '../channel.js';
+
+class BubbleTimelineChannel extends Channel {
+	public readonly chName = 'bubbleTimeline';
+	public static shouldShare = false;
+	public static requireCredential = false;
+	private withRenotes: boolean;
+	private withFiles: boolean;
+	private withBots: boolean;
+	private instance: MiMeta;
+
+	constructor(
+		private metaService: MetaService,
+		private roleService: RoleService,
+		private noteEntityService: NoteEntityService,
+
+		id: string,
+		connection: Channel['connection'],
+	) {
+		super(id, connection);
+		//this.onNote = this.onNote.bind(this);
+	}
+
+	@bindThis
+	public async init(params: any) {
+		const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
+		if (!policies.btlAvailable) return;
+
+		this.withRenotes = params.withRenotes ?? true;
+		this.withFiles = params.withFiles ?? false;
+		this.withBots = params.withBots ?? true;
+		this.instance = await this.metaService.fetch();
+
+		// Subscribe events
+		this.subscriber.on('notesStream', this.onNote);
+	}
+
+	@bindThis
+	private async onNote(note: Packed<'Note'>) {
+		if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return;
+		if (!this.withBots && note.user.isBot) return;
+		
+		if (!(note.user.host != null && this.instance.bubbleInstances.includes(note.user.host) && note.visibility === 'public' )) return;
+
+		if (note.channelId != null) return;
+
+		// 関係ない返信は除外
+		if (note.reply && !this.following[note.userId]?.withReplies) {
+			const reply = note.reply;
+			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
+			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;
+		}
+
+		if (note.user.isSilenced && !this.following[note.userId] && note.userId !== this.user!.id) return;
+
+		if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
+
+		// Ignore notes from instances the user has muted
+		if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
+
+		// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
+		if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
+		// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
+		if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
+
+		if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
+
+		if (this.user && note.renoteId && !note.text) {
+			if (note.renote && Object.keys(note.renote.reactions).length > 0) {
+				const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id);
+				note.renote.myReaction = myRenoteReaction;
+			}
+		}
+
+		this.connection.cacheNote(note);
+
+		this.send('note', note);
+	}
+
+	@bindThis
+	public dispose() {
+		// Unsubscribe events
+		this.subscriber.off('notesStream', this.onNote);
+	}
+}
+
+@Injectable()
+export class BubbleTimelineChannelService {
+	public readonly shouldShare = BubbleTimelineChannel.shouldShare;
+	public readonly requireCredential = BubbleTimelineChannel.requireCredential;
+
+	constructor(
+		private metaService: MetaService,
+		private roleService: RoleService,
+		private noteEntityService: NoteEntityService,
+	) {
+	}
+
+	@bindThis
+	public create(id: string, connection: Channel['connection']): BubbleTimelineChannel {
+		return new BubbleTimelineChannel(
+			this.metaService,
+			this.roleService,
+			this.noteEntityService,
+			id,
+			connection,
+		);
+	}
+}
diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue
index 466c534259..85096dc583 100644
--- a/packages/frontend/src/components/MkTimeline.vue
+++ b/packages/frontend/src/components/MkTimeline.vue
@@ -117,6 +117,12 @@ function connectChannel() {
 			withFiles: props.onlyFiles ? true : undefined,
 			withBots: props.withBots,
 		});
+	} else if (props.src === 'bubble') {
+		connection = stream.useChannel('bubbleTimeline', {
+			withRenotes: props.withRenotes,
+			withFiles: props.onlyFiles ? true : undefined,
+			withBots: props.withBots,
+		});
 	} else if (props.src === 'global') {
 		connection = stream.useChannel('globalTimeline', {
 			withRenotes: props.withRenotes,
@@ -188,6 +194,13 @@ function updatePaginationQuery() {
 			withFiles: props.onlyFiles ? true : undefined,
 			withBots: props.withBots,
 		};
+	} else if (props.src === 'bubble') {
+		endpoint = 'notes/bubble-timeline';
+		query = {
+			withRenotes: props.withRenotes,
+			withFiles: props.onlyFiles ? true : undefined,
+			withBots: props.withBots,
+		};
 	} else if (props.src === 'global') {
 		endpoint = 'notes/global-timeline';
 		query = {
diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts
index 03be17de38..2f8d57c7f6 100644
--- a/packages/frontend/src/const.ts
+++ b/packages/frontend/src/const.ts
@@ -111,6 +111,7 @@ export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const;
 export const ROLE_POLICIES = [
 	'gtlAvailable',
 	'ltlAvailable',
+	'btlAvailable',
 	'canPublicNote',
 	'canImportNotes',
 	'canInvite',
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index fff4629766..cacb3254a8 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -34,6 +34,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<template #label>{{ i18n.ts.privacyPolicyUrl }}</template>
 					</MkInput>
 
+					<MkTextarea v-if="bubbleTimelineEnabled" v-model="bubbleTimeline">
+						<template #label>Bubble timeline</template>
+						<template #caption>Choose which instances should be displayed in the bubble.</template>
+					</MkTextarea>
+
 					<MkTextarea v-model="preservedUsernames">
 						<template #label>{{ i18n.ts.preservedUsernames }}</template>
 						<template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
@@ -76,8 +81,10 @@ import FormLink from '@/components/form/link.vue';
 let enableRegistration: boolean = $ref(false);
 let emailRequiredForSignup: boolean = $ref(false);
 let approvalRequiredForSignup: boolean = $ref(false);
+let bubbleTimelineEnabled: boolean = $ref(false);
 let sensitiveWords: string = $ref('');
 let preservedUsernames: string = $ref('');
+let bubbleTimeline: string = $ref('');
 let tosUrl: string | null = $ref(null);
 let privacyPolicyUrl: string | null = $ref(null);
 
@@ -90,6 +97,8 @@ async function init() {
 	preservedUsernames = meta.preservedUsernames.join('\n');
 	tosUrl = meta.tosUrl;
 	privacyPolicyUrl = meta.privacyPolicyUrl;
+	bubbleTimeline = meta.bubbleInstances.join('\n');
+	bubbleTimelineEnabled = meta.policies.btlAvailable;
 }
 
 function save() {
@@ -101,6 +110,7 @@ function save() {
 		privacyPolicyUrl,
 		sensitiveWords: sensitiveWords.split('\n'),
 		preservedUsernames: preservedUsernames.split('\n'),
+		bubbleInstances: bubbleTimeline.split('\n'),
 	}).then(() => {
 		fetchInstance();
 	});
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index 8c656e917b..efdf1ff4f8 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -120,6 +120,26 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 			</MkFolder>
 
+			<MkFolder v-if="matchQuery([i18n.ts._role._options.btlAvailable, 'btlAvailable'])">
+				<template #label>{{ i18n.ts._role._options.btlAvailable }}</template>
+				<template #suffix>
+					<span v-if="role.policies.btlAvailable.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+					<span v-else>{{ role.policies.btlAvailable.value ? i18n.ts.yes : i18n.ts.no }}</span>
+					<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.btlAvailable)"></i></span>
+				</template>
+				<div class="_gaps">
+					<MkSwitch v-model="role.policies.btlAvailable.useDefault" :readonly="readonly">
+						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
+					</MkSwitch>
+					<MkSwitch v-model="role.policies.btlAvailable.value" :disabled="role.policies.btlAvailable.useDefault" :readonly="readonly">
+						<template #label>{{ i18n.ts.enable }}</template>
+					</MkSwitch>
+					<MkRange v-model="role.policies.btlAvailable.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+						<template #label>{{ i18n.ts._role.priority }}</template>
+					</MkRange>
+				</div>
+			</MkFolder>
+
 			<MkFolder v-if="matchQuery([i18n.ts._role._options.ltlAvailable, 'ltlAvailable'])">
 				<template #label>{{ i18n.ts._role._options.ltlAvailable }}</template>
 				<template #suffix>
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index a5e4369b38..7fedb87d41 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -32,6 +32,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 							</MkSwitch>
 						</MkFolder>
 
+						<MkFolder v-if="matchQuery([i18n.ts._role._options.btlAvailable, 'btlAvailable'])">
+							<template #label>{{ i18n.ts._role._options.btlAvailable }}</template>
+							<template #suffix>{{ policies.btlAvailable ? i18n.ts.yes : i18n.ts.no }}</template>
+							<div class="_gaps_s">
+								<MkInfo :warn="true">After enabling this option navigate to the Moderation section to configure which instances should be shown.</MkInfo>
+								<MkSwitch v-model="policies.btlAvailable">
+									<template #label>{{ i18n.ts.enable }}</template>
+								</MkSwitch>
+							</div>
+						</MkFolder>
+
 						<MkFolder v-if="matchQuery([i18n.ts._role._options.ltlAvailable, 'ltlAvailable'])">
 							<template #label>{{ i18n.ts._role._options.ltlAvailable }}</template>
 							<template #suffix>{{ policies.ltlAvailable ? i18n.ts.yes : i18n.ts.no }}</template>
@@ -232,6 +243,7 @@ import MkFolder from '@/components/MkFolder.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkRange from '@/components/MkRange.vue';
+import MkInfo from '@/components/MkInfo.vue';
 import MkRolePreview from '@/components/MkRolePreview.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 4abffb72da..2f63ec9a38 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -54,6 +54,7 @@ provide('shouldOmitHeaderTitle', true);
 
 const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable);
 const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable);
+const isBubbleTimelineAvailable = ($i == null && instance.policies.btlAvailable) || ($i != null && $i.policies.btlAvailable);
 const keymap = {
 	't': focus,
 };
@@ -207,6 +208,11 @@ const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLis
 	title: i18n.ts._timelines.social,
 	icon: 'ph-rocket-launch ph-bold ph-lg',
 	iconOnly: true,
+}] : []), ...(isBubbleTimelineAvailable ? [{
+	key: 'bubble',
+	title: 'Bubble',
+	icon: 'ph-drop ph-bold ph-lg',
+	iconOnly: true,
 }] : []), ...(isGlobalTimelineAvailable ? [{
 	key: 'global',
 	title: i18n.ts._timelines.global,
diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts
index 49fdf4d314..e68b7bba8c 100644
--- a/packages/frontend/src/ui/deck/deck-store.ts
+++ b/packages/frontend/src/ui/deck/deck-store.ts
@@ -29,7 +29,7 @@ export type Column = {
 	channelId?: string;
 	roleId?: string;
 	excludeTypes?: typeof notificationTypes[number][];
-	tl?: 'home' | 'local' | 'social' | 'global';
+	tl?: 'home' | 'local' | 'social' | 'global' | 'bubble';
 	withRenotes?: boolean;
 	withReplies?: boolean;
 	onlyFiles?: boolean;
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index 1b04bcf0f3..b94adfb711 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -9,11 +9,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<i v-if="column.tl === 'home'" class="ph-house ph-bold ph-lg"></i>
 		<i v-else-if="column.tl === 'local'" class="ph-planet ph-bold ph-lg"></i>
 		<i v-else-if="column.tl === 'social'" class="ph-rocket-launch ph-bold ph-lg"></i>
+		<i v-else-if="column.tl === 'bubble'" class="ph-thumb-up ph-bold ph-lg"></i>
 		<i v-else-if="column.tl === 'global'" class="ph-globe-hemisphere-west ph-bold ph-lg"></i>
 		<span style="margin-left: 8px;">{{ column.name }}</span>
 	</template>
 
-	<div v-if="(((column.tl === 'local' || column.tl === 'social') && !isLocalTimelineAvailable) || (column.tl === 'global' && !isGlobalTimelineAvailable))" :class="$style.disabled">
+	<div v-if="(((column.tl === 'local' || column.tl === 'social') && !isLocalTimelineAvailable) || (column.tl === 'bubble' && !isBubbleTimelineAvailable)) || (column.tl === 'global' && !isGlobalTimelineAvailable))" :class="$style.disabled">
 		<p :class="$style.disabledTitle">
 			<i class="ph-minus-circle ph-bold ph-lg"></i>
 			{{ i18n.ts._disabledTimeline.title }}
@@ -52,6 +53,7 @@ let timeline = $shallowRef<InstanceType<typeof MkTimeline>>();
 
 const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
 const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable));
+const isBubbleTimelineAvailable = ($i == null && instance.policies.btlAvailable) || ($i != null && $i.policies.btlAvailable);
 const withRenotes = $ref(props.column.withRenotes ?? true);
 const withReplies = $ref(props.column.withReplies ?? false);
 const onlyFiles = $ref(props.column.onlyFiles ?? false);
@@ -80,7 +82,8 @@ onMounted(() => {
 	} else if ($i) {
 		disabled = (
 			(!((instance.policies.ltlAvailable) || ($i.policies.ltlAvailable)) && ['local', 'social'].includes(props.column.tl)) ||
-			(!((instance.policies.gtlAvailable) || ($i.policies.gtlAvailable)) && ['global'].includes(props.column.tl)));
+			(!((instance.policies.gtlAvailable) || ($i.policies.gtlAvailable)) && ['global'].includes(props.column.tl)) ||
+			(!((instance.policies.btlAvailable) || ($i.policies.btlAvailable)) && ['bubble'].includes(props.column.tl)));
 	}
 });
 
@@ -93,6 +96,8 @@ async function setType() {
 			value: 'local' as const, text: i18n.ts._timelines.local,
 		}, {
 			value: 'social' as const, text: i18n.ts._timelines.social,
+		}, {
+			value: 'bubble' as const, text: 'Bubble',
 		}, {
 			value: 'global' as const, text: i18n.ts._timelines.global,
 		}],
diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue
index 52b7068209..0ebffa105e 100644
--- a/packages/frontend/src/widgets/WidgetTimeline.vue
+++ b/packages/frontend/src/widgets/WidgetTimeline.vue
@@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<i v-if="widgetProps.src === 'home'" class="ph-house ph-bold ph-lg"></i>
 		<i v-else-if="widgetProps.src === 'local'" class="ph-planet ph-bold ph-lg"></i>
 		<i v-else-if="widgetProps.src === 'social'" class="ph-rocket-launch ph-bold ph-lg"></i>
+		<i v-else-if="widgetProps.src === 'bubble'" class="ph-drop ph-bold ph-lg"></i>
 		<i v-else-if="widgetProps.src === 'global'" class="ph-globe-hemisphere-west ph-bold ph-lg"></i>
 		<i v-else-if="widgetProps.src === 'list'" class="ph-list ph-bold ph-lg"></i>
 		<i v-else-if="widgetProps.src === 'antenna'" class="ph-flying-saucer ph-bold ph-lg"></i>
@@ -20,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</button>
 	</template>
 
-	<div v-if="(((widgetProps.src === 'local' || widgetProps.src === 'social') && !isLocalTimelineAvailable) || (widgetProps.src === 'global' && !isGlobalTimelineAvailable))" :class="$style.disabled">
+	<div v-if="(((widgetProps.src === 'local' || widgetProps.src === 'social') && !isLocalTimelineAvailable) || (widgetProps.src === 'bubble' && !isBubbleTimelineAvailable) || (widgetProps.src === 'global' && !isGlobalTimelineAvailable))" :class="$style.disabled">
 		<p :class="$style.disabledTitle">
 			<i class="ph-minus ph-bold ph-lg"></i>
 			{{ i18n.ts._disabledTimeline.title }}
@@ -47,6 +48,7 @@ import { instance } from '@/instance.js';
 const name = 'timeline';
 const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
 const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable));
+const isBubbleTimelineAvailable = (($i == null && instance.policies.btlAvailable) || ($i != null && $i.policies.btlAvailable));
 
 const widgetPropsDef = {
 	showHeader: {
@@ -126,6 +128,10 @@ const choose = async (ev) => {
 		text: i18n.ts._timelines.social,
 		icon: 'ph-rocket-launch ph-bold ph-lg',
 		action: () => { setSrc('social'); },
+	}, {
+		text: 'Bubble',
+		icon: 'ph-drop ph-bold ph-lg',
+		action: () => { setSrc('bubble'); },
 	}, {
 		text: i18n.ts._timelines.global,
 		icon: 'ph-globe-hemisphere-west ph-bold ph-lg',
diff --git a/packages/megalodon/src/misskey/entities/meta.ts b/packages/megalodon/src/misskey/entities/meta.ts
index b9b74b91d8..73a0104bdd 100644
--- a/packages/megalodon/src/misskey/entities/meta.ts
+++ b/packages/megalodon/src/misskey/entities/meta.ts
@@ -16,6 +16,7 @@ namespace MisskeyEntity {
     emojis: Array<Emoji>
     policies: {
       gtlAvailable: boolean
+      btlAvailable: boolean
       ltlAvailable: boolean
       canPublicNote: boolean
       canImportNotes: boolean
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 10b9dd5eb4..05960a5719 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -361,6 +361,7 @@ export type LiteInstanceMetadata = {
 	privacyPolicyUrl: string | null;
 	disableRegistration: boolean;
 	disableLocalTimeline: boolean;
+	disableBubbleTimeline: boolean;
 	disableGlobalTimeline: boolean;
 	driveCapacityPerLocalUserMb: number;
 	driveCapacityPerRemoteUserMb: number;
diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts
index 124770bf1d..a6688e3866 100644
--- a/packages/misskey-js/src/streaming.types.ts
+++ b/packages/misskey-js/src/streaming.types.ts
@@ -70,6 +70,13 @@ export type Channels = {
 		};
 		receives: null;
 	};
+	bubbleTimeline: {
+		params: null;
+		events: {
+			note: (payload: Note) => void;
+		};
+		receives: null;
+	};
 	messaging: {
 		params: {
 			otherparty?: User['id'] | null;

From ca06e452992ef0589fb90f4aa5ea706070042847 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Mon, 4 Dec 2023 02:15:52 +0100
Subject: [PATCH 128/435] fix: build error

---
 packages/frontend/src/ui/deck/tl-column.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index b94adfb711..5237010f50 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<span style="margin-left: 8px;">{{ column.name }}</span>
 	</template>
 
-	<div v-if="(((column.tl === 'local' || column.tl === 'social') && !isLocalTimelineAvailable) || (column.tl === 'bubble' && !isBubbleTimelineAvailable)) || (column.tl === 'global' && !isGlobalTimelineAvailable))" :class="$style.disabled">
+	<div v-if="(((column.tl === 'local' || column.tl === 'social') && !isLocalTimelineAvailable) || (column.tl === 'bubble' && !isBubbleTimelineAvailable) || (column.tl === 'global' && !isGlobalTimelineAvailable))" :class="$style.disabled">
 		<p :class="$style.disabledTitle">
 			<i class="ph-minus-circle ph-bold ph-lg"></i>
 			{{ i18n.ts._disabledTimeline.title }}

From b2c4973cda7202f6b2c4f5f150dcdbb54d9c9b06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Mon, 4 Dec 2023 12:05:35 +0900
Subject: [PATCH 129/435] fix dev build (#12566)

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

diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index b6383487c9..21ef85fe7a 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -44,7 +44,7 @@ export const getBuiltinThemes = () => Promise.all(
 		'd-cherry',
 		'd-ice',
 		'd-u0',
-	].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
+	].map(name => import(`@/themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
 );
 
 export const getBuiltinThemesRef = () => {

From 18109fcef760dee8171364fd0382375c4047b8e7 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 4 Dec 2023 14:38:21 +0900
Subject: [PATCH 130/435] Filter User / Instance Mutes in
 FanoutTimelineEndpointService (#12565)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: unnecessary logging in FanoutTimelineEndpointService

* chore: TimelineOptions

* chore: add FanoutTimelineName type

* chore: forbid specifying both withReplies and withFiles since it's not implemented correctly

* chore: filter mutes, replies, renotes, files in FanoutTimelineEndpointService

* revert unintended changes

* use isReply in NoteCreateService

* fix: excludePureRenotes is not implemented

* fix: replies to me is excluded from local timeline

* chore(frontend): forbid enabling both withReplies and withFiles

* docs(changelog): インスタンスミュートが効かない問題の修正について言及
---
 CHANGELOG.md                                  |   3 +
 .../src/core/FanoutTimelineEndpointService.ts | 103 +++++++++++++-----
 .../backend/src/core/FanoutTimelineService.ts |  36 +++++-
 .../backend/src/core/NoteCreateService.ts     |   7 +-
 packages/backend/src/misc/is-reply.ts         |  10 ++
 .../server/api/endpoints/channels/timeline.ts |  16 +--
 .../api/endpoints/notes/hybrid-timeline.ts    |  40 ++-----
 .../api/endpoints/notes/local-timeline.ts     |  40 ++-----
 .../server/api/endpoints/notes/timeline.ts    |  20 +---
 .../api/endpoints/notes/user-list-timeline.ts |  36 +-----
 .../src/server/api/endpoints/users/notes.ts   |  34 +++---
 packages/frontend/src/components/MkMenu.vue   |   2 +-
 packages/frontend/src/pages/timeline.vue      |   4 +-
 packages/frontend/src/types/menu.ts           |   2 +-
 packages/frontend/src/ui/deck/tl-column.vue   |   2 +
 15 files changed, 176 insertions(+), 179 deletions(-)
 create mode 100644 packages/backend/src/misc/is-reply.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 453bdeff59..51eb5400ba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -55,6 +55,9 @@
 - Fix: 何もノートしていないユーザーのフィードにアクセスするとエラーになる問題を修正
 - Fix: リストタイムラインにてミュートが機能しないケースがある問題と、チャンネル投稿がストリーミングで流れてきてしまう問題を修正 #10443
 - Fix: 「みつける」のなかにミュートしたユーザが現れてしまう問題を修正 #12383
+- Fix: Social/Local/Home Timelineにてインスタンスミュートが効かない問題
+- Fix: ユーザのノート一覧にてインスタンスミュートが効かない問題
+- Fix: チャンネルのノート一覧にてインスタンスミュートが効かない問題
 
 ## 2023.11.1
 
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index 157fcbe877..6775f0051a 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -11,7 +11,29 @@ import type { MiNote } from '@/models/Note.js';
 import { Packed } from '@/misc/json-schema.js';
 import type { NotesRepository } from '@/models/_.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
-import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
+import { FanoutTimelineName, FanoutTimelineService } from '@/core/FanoutTimelineService.js';
+import { isUserRelated } from '@/misc/is-user-related.js';
+import { isPureRenote } from '@/misc/is-pure-renote.js';
+import { CacheService } from '@/core/CacheService.js';
+import { isReply } from '@/misc/is-reply.js';
+import { isInstanceMuted } from '@/misc/is-instance-muted.js';
+
+type TimelineOptions = {
+	untilId: string | null,
+	sinceId: string | null,
+	limit: number,
+	allowPartial: boolean,
+	me?: { id: MiUser['id'] } | undefined | null,
+	useDbFallback: boolean,
+	redisTimelines: FanoutTimelineName[],
+	noteFilter?: (note: MiNote) => boolean,
+	alwaysIncludeMyNotes?: boolean;
+	ignoreAuthorFromMute?: boolean;
+	excludeNoFiles?: boolean;
+	excludeReplies?: boolean;
+	excludePureRenotes: boolean;
+	dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise<MiNote[]>,
+};
 
 @Injectable()
 export class FanoutTimelineEndpointService {
@@ -20,37 +42,18 @@ export class FanoutTimelineEndpointService {
 		private notesRepository: NotesRepository,
 
 		private noteEntityService: NoteEntityService,
+		private cacheService: CacheService,
 		private fanoutTimelineService: FanoutTimelineService,
 	) {
 	}
 
 	@bindThis
-	async timeline(ps: {
-		untilId: string | null,
-		sinceId: string | null,
-		limit: number,
-		allowPartial: boolean,
-		me?: { id: MiUser['id'] } | undefined | null,
-		useDbFallback: boolean,
-		redisTimelines: string[],
-		noteFilter: (note: MiNote) => boolean,
-		dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise<MiNote[]>,
-	}): Promise<Packed<'Note'>[]> {
+	async timeline(ps: TimelineOptions): Promise<Packed<'Note'>[]> {
 		return await this.noteEntityService.packMany(await this.getMiNotes(ps), ps.me);
 	}
 
 	@bindThis
-	private async getMiNotes(ps: {
-		untilId: string | null,
-		sinceId: string | null,
-		limit: number,
-		allowPartial: boolean,
-		me?: { id: MiUser['id'] } | undefined | null,
-		useDbFallback: boolean,
-		redisTimelines: string[],
-		noteFilter: (note: MiNote) => boolean,
-		dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise<MiNote[]>,
-	}): Promise<MiNote[]> {
+	private async getMiNotes(ps: TimelineOptions): Promise<MiNote[]> {
 		let noteIds: string[];
 		let shouldFallbackToDb = false;
 
@@ -67,10 +70,57 @@ export class FanoutTimelineEndpointService {
 		shouldFallbackToDb = shouldFallbackToDb || (noteIds.length === 0);
 
 		if (!shouldFallbackToDb) {
+			let filter = ps.noteFilter ?? (_note => true);
+
+			if (ps.alwaysIncludeMyNotes && ps.me) {
+				const me = ps.me;
+				const parentFilter = filter;
+				filter = (note) => note.userId === me.id || parentFilter(note);
+			}
+
+			if (ps.excludeNoFiles) {
+				const parentFilter = filter;
+				filter = (note) => note.fileIds.length !== 0 && parentFilter(note);
+			}
+
+			if (ps.excludeReplies) {
+				const parentFilter = filter;
+				filter = (note) => !isReply(note, ps.me?.id) && parentFilter(note);
+			}
+
+			if (ps.excludePureRenotes) {
+				const parentFilter = filter;
+				filter = (note) => !isPureRenote(note) && parentFilter(note);
+			}
+
+			if (ps.me) {
+				const me = ps.me;
+				const [
+					userIdsWhoMeMuting,
+					userIdsWhoMeMutingRenotes,
+					userIdsWhoBlockingMe,
+					userMutedInstances,
+				] = await Promise.all([
+					this.cacheService.userMutingsCache.fetch(ps.me.id),
+					this.cacheService.renoteMutingsCache.fetch(ps.me.id),
+					this.cacheService.userBlockedCache.fetch(ps.me.id),
+					this.cacheService.userProfileCache.fetch(me.id).then(p => new Set(p.mutedInstances)),
+				]);
+
+				const parentFilter = filter;
+				filter = (note) => {
+					if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromMute)) return false;
+					if (isUserRelated(note, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) return false;
+					if (isPureRenote(note) && isUserRelated(note, userIdsWhoMeMutingRenotes, ps.ignoreAuthorFromMute)) return false;
+					if (isInstanceMuted(note, userMutedInstances)) return false;
+
+					return parentFilter(note);
+				};
+			}
+
 			const redisTimeline: MiNote[] = [];
 			let readFromRedis = 0;
 			let lastSuccessfulRate = 1; // rateをキャッシュする?
-			let trialCount = 1;
 
 			while ((redisResultIds.length - readFromRedis) !== 0) {
 				const remainingToRead = ps.limit - redisTimeline.length;
@@ -81,12 +131,10 @@ export class FanoutTimelineEndpointService {
 
 				readFromRedis += noteIds.length;
 
-				const gotFromDb = await this.getAndFilterFromDb(noteIds, ps.noteFilter);
+				const gotFromDb = await this.getAndFilterFromDb(noteIds, filter);
 				redisTimeline.push(...gotFromDb);
 				lastSuccessfulRate = gotFromDb.length / noteIds.length;
 
-				console.log(`fanoutTimelineTrial#${trialCount++}: req: ${ps.limit}, tried: ${noteIds.length}, got: ${gotFromDb.length}, rate: ${lastSuccessfulRate}, total: ${redisTimeline.length}, fromRedis: ${redisResultIds.length}`);
-
 				if (ps.allowPartial ? redisTimeline.length !== 0 : redisTimeline.length >= ps.limit) {
 					// 十分Redisからとれた
 					return redisTimeline.slice(0, ps.limit);
@@ -97,7 +145,6 @@ export class FanoutTimelineEndpointService {
 			const remainingToRead = ps.limit - redisTimeline.length;
 			const gotFromDb = await ps.dbFallback(noteIds[noteIds.length - 1], ps.sinceId, remainingToRead);
 			redisTimeline.push(...gotFromDb);
-			console.log(`fanoutTimelineTrial#db: req: ${ps.limit}, tried: ${remainingToRead}, got: ${gotFromDb.length}, since: ${noteIds[noteIds.length - 1]}, until: ${ps.untilId}, total: ${redisTimeline.length}`);
 			return redisTimeline;
 		}
 
diff --git a/packages/backend/src/core/FanoutTimelineService.ts b/packages/backend/src/core/FanoutTimelineService.ts
index 6a1b0aa879..654a035a5f 100644
--- a/packages/backend/src/core/FanoutTimelineService.ts
+++ b/packages/backend/src/core/FanoutTimelineService.ts
@@ -9,6 +9,34 @@ import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
 import { IdService } from '@/core/IdService.js';
 
+export type FanoutTimelineName =
+	// home timeline
+	| `homeTimeline:${string}`
+	| `homeTimelineWithFiles:${string}` // only notes with files are included
+	// local timeline
+	| `localTimeline` // replies are not included
+	| `localTimelineWithFiles` // only non-reply notes with files are included
+	| `localTimelineWithReplies` // only replies are included
+
+	// antenna
+	| `antennaTimeline:${string}`
+
+	// user timeline
+	| `userTimeline:${string}` // replies are not included
+	| `userTimelineWithFiles:${string}` // only non-reply notes with files are included
+	| `userTimelineWithReplies:${string}` // only replies are included
+	| `userTimelineWithChannel:${string}` // only channel notes are included, replies are included
+
+	// user list timelines
+	| `userListTimeline:${string}`
+	| `userListTimelineWithFiles:${string}` // only notes with files are included
+
+	// channel timelines
+	| `channelTimeline:${string}` // replies are included
+
+	// role timelines
+	| `roleTimeline:${string}` // any notes are included
+
 @Injectable()
 export class FanoutTimelineService {
 	constructor(
@@ -20,7 +48,7 @@ export class FanoutTimelineService {
 	}
 
 	@bindThis
-	public push(tl: string, id: string, maxlen: number, pipeline: Redis.ChainableCommander) {
+	public push(tl: FanoutTimelineName, id: string, maxlen: number, pipeline: Redis.ChainableCommander) {
 		// リモートから遅れて届いた(もしくは後から追加された)投稿日時が古い投稿が追加されるとページネーション時に問題を引き起こすため、
 		// 3分以内に投稿されたものでない場合、Redisにある最古のIDより新しい場合のみ追加する
 		if (this.idService.parse(id).date.getTime() > Date.now() - 1000 * 60 * 3) {
@@ -41,7 +69,7 @@ export class FanoutTimelineService {
 	}
 
 	@bindThis
-	public get(name: string, untilId?: string | null, sinceId?: string | null) {
+	public get(name: FanoutTimelineName, untilId?: string | null, sinceId?: string | null) {
 		if (untilId && sinceId) {
 			return this.redisForTimelines.lrange('list:' + name, 0, -1)
 				.then(ids => ids.filter(id => id < untilId && id > sinceId).sort((a, b) => a > b ? -1 : 1));
@@ -58,7 +86,7 @@ export class FanoutTimelineService {
 	}
 
 	@bindThis
-	public getMulti(name: string[], untilId?: string | null, sinceId?: string | null): Promise<string[][]> {
+	public getMulti(name: FanoutTimelineName[], untilId?: string | null, sinceId?: string | null): Promise<string[][]> {
 		const pipeline = this.redisForTimelines.pipeline();
 		for (const n of name) {
 			pipeline.lrange('list:' + n, 0, -1);
@@ -79,7 +107,7 @@ export class FanoutTimelineService {
 	}
 
 	@bindThis
-	public purge(name: string) {
+	public purge(name: FanoutTimelineName) {
 		return this.redisForTimelines.del('list:' + name);
 	}
 }
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index fd87edc28e..0110ebaf5e 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -57,6 +57,7 @@ import { FeaturedService } from '@/core/FeaturedService.js';
 import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { UserBlockingService } from '@/core/UserBlockingService.js';
+import { isReply } from '@/misc/is-reply.js';
 
 type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
 
@@ -891,7 +892,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 				if (note.visibility === 'specified' && !note.visibleUserIds.some(v => v === following.followerId)) continue;
 
 				// 「自分自身への返信 or そのフォロワーへの返信」のどちらでもない場合
-				if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === following.followerId)) {
+				if (isReply(note, following.followerId)) {
 					if (!following.withReplies) continue;
 				}
 
@@ -909,7 +910,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 				) continue;
 
 				// 「自分自身への返信 or そのリストの作成者への返信」のどちらでもない場合
-				if (note.replyId && !(note.replyUserId === note.userId || note.replyUserId === userListMembership.userListUserId)) {
+				if (isReply(note, userListMembership.userListUserId)) {
 					if (!userListMembership.withReplies) continue;
 				}
 
@@ -927,7 +928,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 			}
 
 			// 自分自身以外への返信
-			if (note.replyId && note.replyUserId !== note.userId) {
+			if (isReply(note)) {
 				this.fanoutTimelineService.push(`userTimelineWithReplies:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
 
 				if (note.visibility === 'public' && note.userHost == null) {
diff --git a/packages/backend/src/misc/is-reply.ts b/packages/backend/src/misc/is-reply.ts
new file mode 100644
index 0000000000..964c2aa153
--- /dev/null
+++ b/packages/backend/src/misc/is-reply.ts
@@ -0,0 +1,10 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { MiUser } from '@/models/User.js';
+
+export function isReply(note: any, viewerId?: MiUser['id'] | undefined | null): boolean {
+	return note.replyId && note.replyUserId !== note.userId && note.replyUserId !== viewerId;
+}
diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts
index 9ef494d6d8..006228ceee 100644
--- a/packages/backend/src/server/api/endpoints/channels/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts
@@ -4,15 +4,13 @@
  */
 
 import { Inject, Injectable } from '@nestjs/common';
-import * as Redis from 'ioredis';
 import { Endpoint } from '@/server/api/endpoint-base.js';
-import type { ChannelsRepository, MiNote, NotesRepository } from '@/models/_.js';
+import type { ChannelsRepository, NotesRepository } from '@/models/_.js';
 import { QueryService } from '@/core/QueryService.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import ActiveUsersChart from '@/core/chart/charts/active-users.js';
 import { DI } from '@/di-symbols.js';
 import { IdService } from '@/core/IdService.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
 import { CacheService } from '@/core/CacheService.js';
 import { MetaService } from '@/core/MetaService.js';
 import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
@@ -94,12 +92,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				return await this.noteEntityService.packMany(await this.getFromDb({ untilId, sinceId, limit: ps.limit, channelId: channel.id }, me), me);
 			}
 
-			const [
-				userIdsWhoMeMuting,
-			] = me ? await Promise.all([
-				this.cacheService.userMutingsCache.fetch(me.id),
-			]) : [new Set<string>()];
-
 			return await this.fanoutTimelineEndpointService.timeline({
 				untilId,
 				sinceId,
@@ -108,11 +100,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				me,
 				useDbFallback: true,
 				redisTimelines: [`channelTimeline:${channel.id}`],
-				noteFilter: note => {
-					if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
-
-					return true;
-				},
+				excludePureRenotes: false,
 				dbFallback: async (untilId, sinceId, limit) => {
 					return await this.getFromDb({ untilId, sinceId, limit, channelId: channel.id }, me);
 				},
diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index deb9e014c4..effcbaf2ee 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -12,9 +12,8 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { DI } from '@/di-symbols.js';
 import { RoleService } from '@/core/RoleService.js';
 import { IdService } from '@/core/IdService.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
 import { CacheService } from '@/core/CacheService.js';
-import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
+import { FanoutTimelineName } from '@/core/FanoutTimelineService.js';
 import { QueryService } from '@/core/QueryService.js';
 import { UserFollowingService } from '@/core/UserFollowingService.js';
 import { MetaService } from '@/core/MetaService.js';
@@ -43,6 +42,12 @@ export const meta = {
 			code: 'STL_DISABLED',
 			id: '620763f4-f621-4533-ab33-0577a1a3c342',
 		},
+
+		bothWithRepliesAndWithFiles: {
+			message: 'Specifying both withReplies and withFiles is not supported',
+			code: 'BOTH_WITH_REPLIES_AND_WITH_FILES',
+			id: 'dfaa3eb7-8002-4cb7-bcc4-1095df46656f'
+		},
 	},
 } as const;
 
@@ -93,6 +98,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.stlDisabled);
 			}
 
+			if (ps.withReplies && ps.withFiles) throw new ApiError(meta.errors.bothWithRepliesAndWithFiles);
+
 			const serverSettings = await this.metaService.fetch();
 
 			if (!serverSettings.enableFanoutTimeline) {
@@ -114,17 +121,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				return await this.noteEntityService.packMany(timeline, me);
 			}
 
-			const [
-				userIdsWhoMeMuting,
-				userIdsWhoMeMutingRenotes,
-				userIdsWhoBlockingMe,
-			] = await Promise.all([
-				this.cacheService.userMutingsCache.fetch(me.id),
-				this.cacheService.renoteMutingsCache.fetch(me.id),
-				this.cacheService.userBlockedCache.fetch(me.id),
-			]);
-
-			let timelineConfig: string[];
+			let timelineConfig: FanoutTimelineName[];
 
 			if (ps.withFiles) {
 				timelineConfig = [
@@ -152,21 +149,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				me,
 				redisTimelines: timelineConfig,
 				useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
-				noteFilter: (note) => {
-					if (note.userId === me.id) {
-						return true;
-					}
-					if (isUserRelated(note, userIdsWhoBlockingMe)) return false;
-					if (isUserRelated(note, userIdsWhoMeMuting)) return false;
-					if (note.renoteId) {
-						if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
-							if (isUserRelated(note, userIdsWhoMeMutingRenotes)) return false;
-							if (ps.withRenotes === false) return false;
-						}
-					}
-
-					return true;
-				},
+				alwaysIncludeMyNotes: true,
+				excludePureRenotes: !ps.withRenotes,
 				dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
 					untilId,
 					sinceId,
diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index 97b05016ec..e8ba39bbf0 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -5,7 +5,7 @@
 
 import { Brackets } from 'typeorm';
 import { Inject, Injectable } from '@nestjs/common';
-import type { MiNote, NotesRepository } from '@/models/_.js';
+import type { NotesRepository } from '@/models/_.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import ActiveUsersChart from '@/core/chart/charts/active-users.js';
@@ -13,7 +13,6 @@ import { DI } from '@/di-symbols.js';
 import { RoleService } from '@/core/RoleService.js';
 import { IdService } from '@/core/IdService.js';
 import { CacheService } from '@/core/CacheService.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
 import { QueryService } from '@/core/QueryService.js';
 import { MetaService } from '@/core/MetaService.js';
 import { MiLocalUser } from '@/models/User.js';
@@ -39,6 +38,12 @@ export const meta = {
 			code: 'LTL_DISABLED',
 			id: '45a6eb02-7695-4393-b023-dd3be9aaaefd',
 		},
+
+		bothWithRepliesAndWithFiles: {
+			message: 'Specifying both withReplies and withFiles is not supported',
+			code: 'BOTH_WITH_REPLIES_AND_WITH_FILES',
+			id: 'dd9c8400-1cb5-4eef-8a31-200c5f933793'
+		},
 	},
 } as const;
 
@@ -82,6 +87,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.ltlDisabled);
 			}
 
+			if (ps.withReplies && ps.withFiles) throw new ApiError(meta.errors.bothWithRepliesAndWithFiles);
+
 			const serverSettings = await this.metaService.fetch();
 
 			if (!serverSettings.enableFanoutTimeline) {
@@ -102,16 +109,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				return await this.noteEntityService.packMany(timeline, me);
 			}
 
-			const [
-				userIdsWhoMeMuting,
-				userIdsWhoMeMutingRenotes,
-				userIdsWhoBlockingMe,
-			] = me ? await Promise.all([
-				this.cacheService.userMutingsCache.fetch(me.id),
-				this.cacheService.renoteMutingsCache.fetch(me.id),
-				this.cacheService.userBlockedCache.fetch(me.id),
-			]) : [new Set<string>(), new Set<string>(), new Set<string>()];
-
 			const timeline = await this.fanoutTimelineEndpointService.timeline({
 				untilId,
 				sinceId,
@@ -120,22 +117,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				me,
 				useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
 				redisTimelines: ps.withFiles ? ['localTimelineWithFiles'] : ['localTimeline', 'localTimelineWithReplies'],
-				noteFilter: note => {
-					if (me && (note.userId === me.id)) {
-						return true;
-					}
-					if (!ps.withReplies && note.replyId && note.replyUserId !== note.userId && (me == null || note.replyUserId !== me.id)) return false;
-					if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false;
-					if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
-					if (note.renoteId) {
-						if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
-							if (me && isUserRelated(note, userIdsWhoMeMutingRenotes)) return false;
-							if (ps.withRenotes === false) return false;
-						}
-					}
-
-					return true;
-				},
+				alwaysIncludeMyNotes: true,
+				excludeReplies: !ps.withReplies,
+				excludePureRenotes: !ps.withRenotes,
 				dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
 					untilId,
 					sinceId,
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index 74d0a6e0c0..790bcbe151 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -13,7 +13,6 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { DI } from '@/di-symbols.js';
 import { IdService } from '@/core/IdService.js';
 import { CacheService } from '@/core/CacheService.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
 import { UserFollowingService } from '@/core/UserFollowingService.js';
 import { MiLocalUser } from '@/models/User.js';
 import { MetaService } from '@/core/MetaService.js';
@@ -98,14 +97,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const [
 				followings,
-				userIdsWhoMeMuting,
-				userIdsWhoMeMutingRenotes,
-				userIdsWhoBlockingMe,
 			] = await Promise.all([
 				this.cacheService.userFollowingsCache.fetch(me.id),
-				this.cacheService.userMutingsCache.fetch(me.id),
-				this.cacheService.renoteMutingsCache.fetch(me.id),
-				this.cacheService.userBlockedCache.fetch(me.id),
 			]);
 
 			const timeline = this.fanoutTimelineEndpointService.timeline({
@@ -116,18 +109,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				me,
 				useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
 				redisTimelines: ps.withFiles ? [`homeTimelineWithFiles:${me.id}`] : [`homeTimeline:${me.id}`],
+				alwaysIncludeMyNotes: true,
+				excludePureRenotes: !ps.withRenotes,
 				noteFilter: note => {
-					if (note.userId === me.id) {
-						return true;
-					}
-					if (isUserRelated(note, userIdsWhoBlockingMe)) return false;
-					if (isUserRelated(note, userIdsWhoMeMuting)) return false;
-					if (note.renoteId) {
-						if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
-							if (isUserRelated(note, userIdsWhoMeMutingRenotes)) return false;
-							if (ps.withRenotes === false) return false;
-						}
-					}
 					if (note.reply && note.reply.visibility === 'followers') {
 						if (!Object.hasOwn(followings, note.reply.userId)) return false;
 					}
diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
index f8f64738fe..10d3a7a697 100644
--- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -5,20 +5,17 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { Brackets } from 'typeorm';
-import type { MiNote, MiUserList, NotesRepository, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
+import type { MiUserList, NotesRepository, UserListMembershipsRepository, UserListsRepository } from '@/models/_.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import ActiveUsersChart from '@/core/chart/charts/active-users.js';
 import { DI } from '@/di-symbols.js';
 import { CacheService } from '@/core/CacheService.js';
 import { IdService } from '@/core/IdService.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
-import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
 import { QueryService } from '@/core/QueryService.js';
 import { MiLocalUser } from '@/models/User.js';
 import { MetaService } from '@/core/MetaService.js';
 import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
-import { isInstanceMuted } from '@/misc/is-instance-muted.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -84,7 +81,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private activeUsersChart: ActiveUsersChart,
 		private cacheService: CacheService,
 		private idService: IdService,
-		private fanoutTimelineService: FanoutTimelineService,
 		private fanoutTimelineEndpointService: FanoutTimelineEndpointService,
 		private queryService: QueryService,
 		private metaService: MetaService,
@@ -121,18 +117,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				await this.noteEntityService.packMany(timeline, me);
 			}
 
-			const [
-				userIdsWhoMeMuting,
-				userIdsWhoMeMutingRenotes,
-				userIdsWhoBlockingMe,
-				userMutedInstances,
-			] = await Promise.all([
-				this.cacheService.userMutingsCache.fetch(me.id),
-				this.cacheService.renoteMutingsCache.fetch(me.id),
-				this.cacheService.userBlockedCache.fetch(me.id),
-				this.cacheService.userProfileCache.fetch(me.id).then(p => new Set(p.mutedInstances)),
-			]);
-
 			const timeline = await this.fanoutTimelineEndpointService.timeline({
 				untilId,
 				sinceId,
@@ -141,22 +125,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				me,
 				useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
 				redisTimelines: ps.withFiles ? [`userListTimelineWithFiles:${list.id}`] : [`userListTimeline:${list.id}`],
-				noteFilter: note => {
-					if (note.userId === me.id) {
-						return true;
-					}
-					if (isUserRelated(note, userIdsWhoBlockingMe)) return false;
-					if (isUserRelated(note, userIdsWhoMeMuting)) return false;
-					if (note.renoteId) {
-						if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
-							if (isUserRelated(note, userIdsWhoMeMutingRenotes)) return false;
-							if (ps.withRenotes === false) return false;
-						}
-					}
-					if (isInstanceMuted(note, userMutedInstances)) return false;
-
-					return true;
-				},
+				alwaysIncludeMyNotes: true,
+				excludePureRenotes: !ps.withRenotes,
 				dbFallback: async (untilId, sinceId, limit) => await this.getFromDb(list, {
 					untilId,
 					sinceId,
diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index 4a358b39cb..b32128a8aa 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -11,11 +11,12 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { DI } from '@/di-symbols.js';
 import { CacheService } from '@/core/CacheService.js';
 import { IdService } from '@/core/IdService.js';
-import { isUserRelated } from '@/misc/is-user-related.js';
 import { QueryService } from '@/core/QueryService.js';
 import { MetaService } from '@/core/MetaService.js';
 import { MiLocalUser } from '@/models/User.js';
 import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
+import { FanoutTimelineName } from '@/core/FanoutTimelineService.js';
+import { ApiError } from '@/server/api/error.js';
 
 export const meta = {
 	tags: ['users', 'notes'],
@@ -36,6 +37,12 @@ export const meta = {
 			code: 'NO_SUCH_USER',
 			id: '27e494ba-2ac2-48e8-893b-10d4d8c2387b',
 		},
+
+		bothWithRepliesAndWithFiles: {
+			message: 'Specifying both withReplies and withFiles is not supported',
+			code: 'BOTH_WITH_REPLIES_AND_WITH_FILES',
+			id: '91c8cb9f-36ed-46e7-9ca2-7df96ed6e222',
+		},
 	},
 } as const;
 
@@ -77,6 +84,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const serverSettings = await this.metaService.fetch();
 
+			if (ps.withReplies && ps.withFiles) throw new ApiError(meta.errors.bothWithRepliesAndWithFiles);
+
 			if (!serverSettings.enableFanoutTimeline) {
 				const timeline = await this.getFromDb({
 					untilId,
@@ -91,13 +100,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				return await this.noteEntityService.packMany(timeline, me);
 			}
 
-			const [
-				userIdsWhoMeMuting,
-			] = me ? await Promise.all([
-				this.cacheService.userMutingsCache.fetch(me.id),
-			]) : [new Set<string>()];
-
-			const redisTimelines = [ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`];
+			const redisTimelines: FanoutTimelineName[] = [ps.withFiles ? `userTimelineWithFiles:${ps.userId}` : `userTimeline:${ps.userId}`];
 
 			if (ps.withReplies) redisTimelines.push(`userTimelineWithReplies:${ps.userId}`);
 			if (ps.withChannelNotes) redisTimelines.push(`userTimelineWithChannel:${ps.userId}`);
@@ -112,18 +115,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				me,
 				redisTimelines,
 				useDbFallback: true,
+				ignoreAuthorFromMute: true,
+				excludeReplies: ps.withChannelNotes && !ps.withReplies, // userTimelineWithChannel may include replies
+				excludeNoFiles: ps.withChannelNotes && ps.withFiles, // userTimelineWithChannel may include notes without files
+				excludePureRenotes: !ps.withRenotes,
 				noteFilter: note => {
-					if (ps.withFiles && note.fileIds.length === 0) {
-						return false;
-					}
-					if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false;
-
-					if (note.renoteId) {
-						if (note.text == null && note.fileIds.length === 0 && !note.hasPoll) {
-							if (ps.withRenotes === false) return false;
-						}
-					}
-
 					if (note.channel?.isSensitive && !isSelf) return false;
 					if (note.visibility === 'specified' && (!me || (me.id !== note.userId && !note.visibleUserIds.some(v => v === me.id)))) return false;
 					if (note.visibility === 'followers' && !isFollowing && !isSelf) return false;
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 9457bf385f..4fafd35f72 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -202,7 +202,7 @@ function focusDown() {
 }
 
 function switchItem(item: MenuSwitch & { ref: any }) {
-	if (item.disabled) return;
+	if (typeof item.disabled === 'boolean' ? item.disabled : item.disabled.value) return;
 	item.ref = !item.ref;
 }
 
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index cfe270aefb..b390d1931d 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -157,17 +157,17 @@ const headerActions = $computed(() => {
 				os.popupMenu([{
 					type: 'switch',
 					text: i18n.ts.showRenotes,
-					icon: 'ti ti-repeat',
 					ref: $$(withRenotes),
 				}, src === 'local' || src === 'social' ? {
 					type: 'switch',
 					text: i18n.ts.showRepliesToOthersInTimeline,
 					ref: $$(withReplies),
+					disabled: $$(onlyFiles),
 				} : undefined, {
 					type: 'switch',
 					text: i18n.ts.fileAttachedOnly,
-					icon: 'ti ti-photo',
 					ref: $$(onlyFiles),
+					disabled: src === 'local' || src === 'social' ? $$(withReplies) : false,
 				}], ev.currentTarget ?? ev.target);
 			},
 		},
diff --git a/packages/frontend/src/types/menu.ts b/packages/frontend/src/types/menu.ts
index 66061fcd70..fbe627176b 100644
--- a/packages/frontend/src/types/menu.ts
+++ b/packages/frontend/src/types/menu.ts
@@ -14,7 +14,7 @@ export type MenuLabel = { type: 'label', text: string };
 export type MenuLink = { type: 'link', to: string, text: string, icon?: string, indicate?: boolean, avatar?: Misskey.entities.User };
 export type MenuA = { type: 'a', href: string, target?: string, download?: string, text: string, icon?: string, indicate?: boolean };
 export type MenuUser = { type: 'user', user: Misskey.entities.User, active?: boolean, indicate?: boolean, action: MenuAction };
-export type MenuSwitch = { type: 'switch', ref: Ref<boolean>, text: string, disabled?: boolean };
+export type MenuSwitch = { type: 'switch', ref: Ref<boolean>, text: string, disabled?: boolean | Ref<boolean> };
 export type MenuButton = { type?: 'button', text: string, icon?: string, indicate?: boolean, danger?: boolean, active?: boolean, avatar?: Misskey.entities.User; action: MenuAction };
 export type MenuParent = { type: 'parent', text: string, icon?: string, children: MenuItem[] | (() => Promise<MenuItem[]> | MenuItem[]) };
 
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index 9f24ea31ed..41582bbfe3 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -120,10 +120,12 @@ const menu = [{
 	type: 'switch',
 	text: i18n.ts.showRepliesToOthersInTimeline,
 	ref: $$(withReplies),
+	disabled: $$(onlyFiles),
 } : undefined, {
 	type: 'switch',
 	text: i18n.ts.fileAttachedOnly,
 	ref: $$(onlyFiles),
+	disabled: props.column.tl === 'local' || props.column.tl === 'social' ? $$(withReplies) : false,
 }];
 </script>
 

From 33034b0e02860513d277e8f8a08f96a84592b3e3 Mon Sep 17 00:00:00 2001
From: yupix <yupi0982@outlook.jp>
Date: Mon, 4 Dec 2023 16:53:31 +0900
Subject: [PATCH 131/435] =?UTF-8?q?feat:=20=E3=83=A6=E3=83=BC=E3=82=B6?=
 =?UTF-8?q?=E3=83=BC=E3=82=B9=E3=82=AD=E3=83=BC=E3=83=9E=E3=81=AE=E6=94=B9?=
 =?UTF-8?q?=E5=96=84=20(#12568)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore: notifyにenumを設定

* feat: securityKeysListの型を明確に

* feat: notificationRecieveConfigにpropertiesを定義

* chore: misskey.jsのmodelを更新

* fix: as constをつけ忘れている
---
 .../backend/src/models/json-schema/user.ts    | 43 +++++++++++++
 packages/misskey-js/src/autogen/endpoint.ts   |  2 +-
 packages/misskey-js/src/autogen/entities.ts   |  2 +-
 packages/misskey-js/src/autogen/models.ts     |  2 +-
 packages/misskey-js/src/autogen/types.ts      | 63 +++++++++++++++++--
 5 files changed, 105 insertions(+), 7 deletions(-)

diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index a2ec203e96..2621e7e6c0 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -3,6 +3,18 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
+const notificationRecieveConfig = {
+	type: 'object',
+	nullable: false, optional: true,
+	properties: {
+		type: {
+			type: 'string',
+			nullable: false, optional: false,
+			enum: ['all', 'following', 'follower', 'mutualFollow', 'list', 'never'],
+		},
+	},
+} as const;
+
 export const packedUserLiteSchema = {
 	type: 'object',
 	properties: {
@@ -398,6 +410,7 @@ export const packedUserDetailedNotMeOnlySchema = {
 		notify: {
 			type: 'string',
 			nullable: false, optional: true,
+			enum: ['normal', 'none'],
 		},
 		withReplies: {
 			type: 'boolean',
@@ -553,6 +566,19 @@ export const packedMeDetailedOnlySchema = {
 		notificationRecieveConfig: {
 			type: 'object',
 			nullable: false, optional: false,
+			properties: {
+				app: notificationRecieveConfig,
+				quote: notificationRecieveConfig,
+				reply: notificationRecieveConfig,
+				follow: notificationRecieveConfig,
+				renote: notificationRecieveConfig,
+				mention: notificationRecieveConfig,
+				reaction: notificationRecieveConfig,
+				pollEnded: notificationRecieveConfig,
+				achievementEarned: notificationRecieveConfig,
+				receiveFollowRequest: notificationRecieveConfig,
+				followRequestAccepted: notificationRecieveConfig,
+			},
 		},
 		emailNotificationTypes: {
 			type: 'array',
@@ -697,6 +723,23 @@ export const packedMeDetailedOnlySchema = {
 			items: {
 				type: 'object',
 				nullable: false, optional: false,
+				properties: {
+					id: {
+						type: 'string',
+						nullable: false, optional: false,
+						format: 'id',
+						example: 'xxxxxxxxxx',
+					},
+					name: {
+						type: 'string',
+						nullable: false, optional: false,
+					},
+					lastUsed: {
+						type: 'string',
+						nullable: false, optional: false,
+						format: 'date-time',
+					},
+				},
 			},
 		},
 		//#endregion
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index b46e69f691..cf571fc689 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-03T02:04:45.058Z
+ * generatedAt: 2023-12-04T07:13:58.541Z
  */
 
 import type {
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index a51ee037d0..bbfa14913f 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-03T02:04:45.053Z
+ * generatedAt: 2023-12-04T07:13:58.538Z
  */
 
 import { operations } from './types.js';
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index c2b27b2c58..566b6dfb97 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-03T02:04:45.051Z
+ * generatedAt: 2023-12-04T07:13:58.535Z
  */
 
 import { components } from './types.js';
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 44ed4dbaa8..b734b36e49 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -3,7 +3,7 @@
 
 /*
  * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-03T02:04:44.864Z
+ * generatedAt: 2023-12-04T07:13:58.362Z
  */
 
 /**
@@ -3218,7 +3218,8 @@ export type components = {
       isBlocked?: boolean;
       isMuted?: boolean;
       isRenoteMuted?: boolean;
-      notify?: string;
+      /** @enum {string} */
+      notify?: 'normal' | 'none';
       withReplies?: boolean;
     };
     MeDetailedOnly: {
@@ -3253,7 +3254,52 @@ export type components = {
       mutedWords: string[][];
       hardMutedWords: string[][];
       mutedInstances: string[] | null;
-      notificationRecieveConfig: Record<string, never>;
+      notificationRecieveConfig: {
+        app?: {
+          /** @enum {string} */
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
+        };
+        quote?: {
+          /** @enum {string} */
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
+        };
+        reply?: {
+          /** @enum {string} */
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
+        };
+        follow?: {
+          /** @enum {string} */
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
+        };
+        renote?: {
+          /** @enum {string} */
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
+        };
+        mention?: {
+          /** @enum {string} */
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
+        };
+        reaction?: {
+          /** @enum {string} */
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
+        };
+        pollEnded?: {
+          /** @enum {string} */
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
+        };
+        achievementEarned?: {
+          /** @enum {string} */
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
+        };
+        receiveFollowRequest?: {
+          /** @enum {string} */
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
+        };
+        followRequestAccepted?: {
+          /** @enum {string} */
+          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
+        };
+      };
       emailNotificationTypes: string[];
       achievements: {
           name: string;
@@ -3287,7 +3333,16 @@ export type components = {
       };
       email?: string | null;
       emailVerified?: boolean | null;
-      securityKeysList?: Record<string, never>[];
+      securityKeysList?: {
+          /**
+           * Format: id
+           * @example xxxxxxxxxx
+           */
+          id: string;
+          name: string;
+          /** Format: date-time */
+          lastUsed: string;
+        }[];
     };
     UserDetailedNotMe: components['schemas']['UserLite'] & components['schemas']['UserDetailedNotMeOnly'];
     MeDetailed: components['schemas']['UserLite'] & components['schemas']['UserDetailedNotMeOnly'] & components['schemas']['MeDetailedOnly'];

From bb38e62ae696873990d0252c66200c361878e9f8 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 4 Dec 2023 17:56:48 +0900
Subject: [PATCH 132/435] =?UTF-8?q?chore:=20=E8=87=AA=E5=88=86=E3=81=B8?=
 =?UTF-8?q?=E3=81=AE=E3=83=AA=E3=83=97=E3=83=A9=E3=82=A4=E3=81=AE=E3=81=BF?=
 =?UTF-8?q?=E8=B5=B0=E6=9F=BB=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=20(#12570)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/src/core/FanoutTimelineService.ts       | 1 +
 packages/backend/src/core/NoteCreateService.ts           | 3 +++
 .../src/server/api/endpoints/notes/local-timeline.ts     | 9 ++++++---
 3 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/core/FanoutTimelineService.ts b/packages/backend/src/core/FanoutTimelineService.ts
index 654a035a5f..9b2678fbcd 100644
--- a/packages/backend/src/core/FanoutTimelineService.ts
+++ b/packages/backend/src/core/FanoutTimelineService.ts
@@ -17,6 +17,7 @@ export type FanoutTimelineName =
 	| `localTimeline` // replies are not included
 	| `localTimelineWithFiles` // only non-reply notes with files are included
 	| `localTimelineWithReplies` // only replies are included
+	| `localTimelineWithReplyTo:${string}` // Only replies to specific local user are included. Parameter is reply user id.
 
 	// antenna
 	| `antennaTimeline:${string}`
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 0110ebaf5e..45dfbb87aa 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -933,6 +933,9 @@ export class NoteCreateService implements OnApplicationShutdown {
 
 				if (note.visibility === 'public' && note.userHost == null) {
 					this.fanoutTimelineService.push('localTimelineWithReplies', note.id, 300, r);
+					if (note.replyUserHost == null) {
+						this.fanoutTimelineService.push(`localTimelineWithReplyTo:${note.replyUserId}`, note.id, 300 / 10, r);
+					}
 				}
 			} else {
 				this.fanoutTimelineService.push(`userTimeline:${user.id}`, note.id, note.userHost == null ? meta.perLocalUserUserTimelineCacheMax : meta.perRemoteUserUserTimelineCacheMax, r);
diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
index e8ba39bbf0..3fd4dc83fb 100644
--- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts
@@ -42,7 +42,7 @@ export const meta = {
 		bothWithRepliesAndWithFiles: {
 			message: 'Specifying both withReplies and withFiles is not supported',
 			code: 'BOTH_WITH_REPLIES_AND_WITH_FILES',
-			id: 'dd9c8400-1cb5-4eef-8a31-200c5f933793'
+			id: 'dd9c8400-1cb5-4eef-8a31-200c5f933793',
 		},
 	},
 } as const;
@@ -116,9 +116,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				allowPartial: ps.allowPartial,
 				me,
 				useDbFallback: serverSettings.enableFanoutTimelineDbFallback,
-				redisTimelines: ps.withFiles ? ['localTimelineWithFiles'] : ['localTimeline', 'localTimelineWithReplies'],
+				redisTimelines:
+					ps.withFiles ? ['localTimelineWithFiles']
+					: ps.withReplies ? ['localTimeline', 'localTimelineWithReplies']
+					: me ? ['localTimeline', `localTimelineWithReplyTo:${me.id}`]
+					: ['localTimeline'],
 				alwaysIncludeMyNotes: true,
-				excludeReplies: !ps.withReplies,
 				excludePureRenotes: !ps.withRenotes,
 				dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({
 					untilId,

From e90ad095516bfd6c363596c9262f4269359d48c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Mon, 4 Dec 2023 18:12:14 +0900
Subject: [PATCH 133/435] =?UTF-8?q?fix=20(frontend):=20=E7=B5=B5=E6=96=87?=
 =?UTF-8?q?=E5=AD=97=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC=E7=B5=8C=E7=94=B1?=
 =?UTF-8?q?=E3=81=A7=E6=8A=95=E7=A8=BF=E6=AC=84=E3=81=AB=E7=B5=B5=E6=96=87?=
 =?UTF-8?q?=E5=AD=97=E3=82=92=E5=85=A5=E3=82=8C=E3=81=9F=E9=9A=9B=E3=80=81?=
 =?UTF-8?q?=E3=82=BD=E3=83=95=E3=83=88=E3=82=A6=E3=82=A7=E3=82=A2=E3=82=AD?=
 =?UTF-8?q?=E3=83=BC=E3=83=9C=E3=83=BC=E3=83=89=E3=81=8C=E7=AB=8B=E3=81=A1?=
 =?UTF-8?q?=E4=B8=8A=E3=81=8C=E3=82=89=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B=20(#12561)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkPostForm.vue | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 0445536ae5..13b615d104 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo>
 	<input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown">
 	<div :class="[$style.textOuter, { [$style.withCw]: useCw }]">
-		<textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
+		<textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
 		<div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div>
 	</div>
 	<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags">
@@ -98,7 +98,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide } from 'vue';
+import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, ref } from 'vue';
 import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import insertTextAtCursor from 'insert-text-at-cursor';
@@ -195,6 +195,7 @@ let hasNotSpecifiedMentions = $ref(false);
 let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
 let imeText = $ref('');
 let showingOptions = $ref(false);
+const textAreaReadOnly = ref(false);
 
 const draftKey = $computed((): string => {
 	let key = props.channel ? `channel:${props.channel.id}` : '';
@@ -846,12 +847,15 @@ function insertMention() {
 }
 
 async function insertEmoji(ev: MouseEvent) {
+	textAreaReadOnly.value = true;
+
 	emojiPicker.show(
 		ev.currentTarget ?? ev.target,
 		emoji => {
 			insertTextAtCursor(textareaEl, emoji);
 		},
 		() => {
+			textAreaReadOnly.value = false;
 			focus();
 		},
 	);

From 9c90ff7d063f97c4097cfc6f9dba5d02126476a6 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 4 Dec 2023 19:40:46 +0900
Subject: [PATCH 134/435] update deps

---
 package.json                     |    8 +-
 packages/backend/package.json    |   24 +-
 packages/frontend/package.json   |   60 +-
 packages/misskey-js/package.json |   10 +-
 packages/sw/package.json         |    4 +-
 pnpm-lock.yaml                   | 3752 ++++++++++++++----------------
 6 files changed, 1819 insertions(+), 2039 deletions(-)

diff --git a/package.json b/package.json
index 9fa094e049..aa67b006db 100644
--- a/package.json
+++ b/package.json
@@ -48,16 +48,16 @@
 		"execa": "8.0.1",
 		"cssnano": "6.0.1",
 		"js-yaml": "4.1.0",
-		"postcss": "8.4.31",
+		"postcss": "8.4.32",
 		"terser": "5.24.0",
 		"typescript": "5.3.2"
 	},
 	"devDependencies": {
-		"@typescript-eslint/eslint-plugin": "6.12.0",
-		"@typescript-eslint/parser": "6.12.0",
+		"@typescript-eslint/eslint-plugin": "6.13.1",
+		"@typescript-eslint/parser": "6.13.1",
 		"cross-env": "7.0.3",
 		"cypress": "13.6.0",
-		"eslint": "8.54.0",
+		"eslint": "8.55.0",
 		"start-server-and-test": "2.0.3",
 		"ncp": "2.0.0"
 	},
diff --git a/packages/backend/package.json b/packages/backend/package.json
index b17ce904f6..fa20458093 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -60,9 +60,9 @@
 	"dependencies": {
 		"@aws-sdk/client-s3": "3.412.0",
 		"@aws-sdk/lib-storage": "3.412.0",
-		"@bull-board/api": "5.9.2",
-		"@bull-board/fastify": "5.9.2",
-		"@bull-board/ui": "5.9.2",
+		"@bull-board/api": "5.10.1",
+		"@bull-board/fastify": "5.10.1",
+		"@bull-board/ui": "5.10.1",
 		"@discordapp/twemoji": "14.1.2",
 		"@fastify/accepts": "4.2.0",
 		"@fastify/cookie": "9.2.0",
@@ -80,7 +80,7 @@
 		"@sinonjs/fake-timers": "11.2.2",
 		"@smithy/node-http-handler": "2.1.10",
 		"@swc/cli": "0.1.63",
-		"@swc/core": "1.3.99",
+		"@swc/core": "1.3.100",
 		"accepts": "1.3.8",
 		"ajv": "8.12.0",
 		"archiver": "6.0.1",
@@ -88,7 +88,7 @@
 		"bcryptjs": "2.4.3",
 		"blurhash": "2.0.5",
 		"body-parser": "1.20.2",
-		"bullmq": "4.14.2",
+		"bullmq": "4.14.4",
 		"cacheable-lookup": "7.0.0",
 		"cbor": "9.0.1",
 		"chalk": "5.3.0",
@@ -114,7 +114,7 @@
 		"ipaddr.js": "2.1.0",
 		"is-svg": "5.0.0",
 		"js-yaml": "4.1.0",
-		"jsdom": "23.0.0",
+		"jsdom": "23.0.1",
 		"json5": "2.2.3",
 		"jsonld": "8.3.1",
 		"jsrsasign": "10.9.0",
@@ -124,7 +124,7 @@
 		"mime-types": "2.1.35",
 		"misskey-js": "workspace:*",
 		"ms": "3.0.0-canary.1",
-		"nanoid": "5.0.3",
+		"nanoid": "5.0.4",
 		"nested-property": "4.0.0",
 		"node-fetch": "3.3.2",
 		"nodemailer": "6.9.7",
@@ -159,7 +159,7 @@
 		"strict-event-emitter-types": "2.0.0",
 		"stringz": "2.1.0",
 		"summaly": "github:misskey-dev/summaly",
-		"systeminformation": "5.21.18",
+		"systeminformation": "5.21.20",
 		"tinycolor2": "1.6.0",
 		"tmp": "0.2.1",
 		"tsc-alias": "1.8.8",
@@ -193,7 +193,7 @@
 		"@types/jsrsasign": "10.5.12",
 		"@types/mime-types": "2.1.4",
 		"@types/ms": "0.7.34",
-		"@types/node": "20.10.0",
+		"@types/node": "20.10.3",
 		"@types/node-fetch": "3.0.3",
 		"@types/nodemailer": "6.4.14",
 		"@types/oauth": "0.9.4",
@@ -216,11 +216,11 @@
 		"@types/vary": "1.1.3",
 		"@types/web-push": "3.6.3",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "6.12.0",
-		"@typescript-eslint/parser": "6.12.0",
+		"@typescript-eslint/eslint-plugin": "6.13.1",
+		"@typescript-eslint/parser": "6.13.1",
 		"aws-sdk-client-mock": "3.0.0",
 		"cross-env": "7.0.3",
-		"eslint": "8.54.0",
+		"eslint": "8.55.0",
 		"eslint-plugin-import": "2.29.0",
 		"execa": "8.0.1",
 		"jest": "29.7.0",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index fab12df575..4f3b74649a 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -24,7 +24,7 @@
 		"@rollup/pluginutils": "5.0.5",
 		"@syuilo/aiscript": "0.16.0",
 		"@tabler/icons-webfont": "2.37.0",
-		"@vitejs/plugin-vue": "4.5.0",
+		"@vitejs/plugin-vue": "4.5.1",
 		"@vue-macros/reactivity-transform": "0.4.0",
 		"@vue/compiler-sfc": "3.3.9",
 		"astring": "1.8.6",
@@ -46,7 +46,7 @@
 		"escape-regexp": "0.0.1",
 		"estree-walker": "3.0.3",
 		"eventemitter3": "5.0.1",
-		"gsap": "3.12.2",
+		"gsap": "3.12.3",
 		"idb-keyval": "6.2.1",
 		"insert-text-at-cursor": "0.3.0",
 		"is-file-animated": "1.0.2",
@@ -54,16 +54,16 @@
 		"matter-js": "0.19.0",
 		"mfm-js": "0.23.3",
 		"misskey-js": "workspace:*",
-		"photoswipe": "5.4.2",
+		"photoswipe": "5.4.3",
 		"punycode": "2.3.1",
 		"querystring": "0.2.1",
-		"rollup": "4.6.0",
+		"rollup": "4.6.1",
 		"sanitize-html": "2.11.0",
 		"shiki": "^0.14.5",
 		"sass": "1.69.5",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
-		"three": "0.158.0",
+		"three": "0.159.0",
 		"throttle-debounce": "5.0.0",
 		"tinycolor2": "1.6.0",
 		"tsc-alias": "1.8.8",
@@ -73,35 +73,35 @@
 		"uuid": "9.0.1",
 		"v-code-diff": "1.7.2",
 		"vanilla-tilt": "1.8.1",
-		"vite": "5.0.2",
+		"vite": "5.0.5",
 		"vue": "3.3.9",
 		"vuedraggable": "next"
 	},
 	"devDependencies": {
-		"@storybook/addon-actions": "7.5.3",
-		"@storybook/addon-essentials": "7.5.3",
-		"@storybook/addon-interactions": "7.5.3",
-		"@storybook/addon-links": "7.5.3",
-		"@storybook/addon-storysource": "7.5.3",
-		"@storybook/addons": "7.5.3",
-		"@storybook/blocks": "7.5.3",
-		"@storybook/core-events": "7.5.3",
+		"@storybook/addon-actions": "7.6.3",
+		"@storybook/addon-essentials": "7.6.3",
+		"@storybook/addon-interactions": "7.6.3",
+		"@storybook/addon-links": "7.6.3",
+		"@storybook/addon-storysource": "7.6.3",
+		"@storybook/addons": "7.6.3",
+		"@storybook/blocks": "7.6.3",
+		"@storybook/core-events": "7.6.3",
 		"@storybook/jest": "0.2.3",
-		"@storybook/manager-api": "7.5.3",
-		"@storybook/preview-api": "7.5.3",
-		"@storybook/react": "7.5.3",
-		"@storybook/react-vite": "7.5.3",
+		"@storybook/manager-api": "7.6.3",
+		"@storybook/preview-api": "7.6.3",
+		"@storybook/react": "7.6.3",
+		"@storybook/react-vite": "7.6.3",
 		"@storybook/testing-library": "0.2.2",
-		"@storybook/theming": "7.5.3",
-		"@storybook/types": "7.5.3",
-		"@storybook/vue3": "7.5.3",
-		"@storybook/vue3-vite": "7.5.3",
+		"@storybook/theming": "7.6.3",
+		"@storybook/types": "7.6.3",
+		"@storybook/vue3": "7.6.3",
+		"@storybook/vue3-vite": "7.6.3",
 		"@testing-library/vue": "8.0.1",
 		"@types/escape-regexp": "0.0.3",
 		"@types/estree": "1.0.5",
 		"@types/matter-js": "0.19.5",
 		"@types/micromatch": "4.0.6",
-		"@types/node": "20.10.0",
+		"@types/node": "20.10.3",
 		"@types/punycode": "2.1.3",
 		"@types/sanitize-html": "2.9.5",
 		"@types/throttle-debounce": "5.0.2",
@@ -109,33 +109,33 @@
 		"@types/uuid": "9.0.7",
 		"@types/websocket": "1.0.10",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "6.12.0",
-		"@typescript-eslint/parser": "6.12.0",
+		"@typescript-eslint/eslint-plugin": "6.13.1",
+		"@typescript-eslint/parser": "6.13.1",
 		"@vitest/coverage-v8": "0.34.6",
 		"@vue/runtime-core": "3.3.9",
 		"acorn": "8.11.2",
 		"cross-env": "7.0.3",
 		"cypress": "13.6.0",
-		"eslint": "8.54.0",
+		"eslint": "8.55.0",
 		"eslint-plugin-import": "2.29.0",
-		"eslint-plugin-vue": "9.18.1",
+		"eslint-plugin-vue": "9.19.2",
 		"fast-glob": "3.3.2",
 		"happy-dom": "10.0.3",
 		"micromatch": "4.0.5",
 		"msw": "1.3.2",
 		"msw-storybook-addon": "1.10.0",
-		"nodemon": "3.0.1",
+		"nodemon": "3.0.2",
 		"prettier": "3.1.0",
 		"react": "18.2.0",
 		"react-dom": "18.2.0",
 		"start-server-and-test": "2.0.3",
-		"storybook": "7.5.3",
+		"storybook": "7.6.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.6",
 		"vitest-fetch-mock": "0.2.2",
 		"vue-eslint-parser": "9.3.2",
-		"vue-tsc": "1.8.22"
+		"vue-tsc": "1.8.24"
 	}
 }
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 1d21923132..e447383a4f 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -24,10 +24,10 @@
 		"@microsoft/api-extractor": "7.38.3",
 		"@swc/jest": "0.2.29",
 		"@types/jest": "29.5.10",
-		"@types/node": "20.10.0",
-		"@typescript-eslint/eslint-plugin": "6.12.0",
-		"@typescript-eslint/parser": "6.12.0",
-		"eslint": "8.54.0",
+		"@types/node": "20.10.3",
+		"@typescript-eslint/eslint-plugin": "6.13.1",
+		"@typescript-eslint/parser": "6.13.1",
+		"eslint": "8.55.0",
 		"jest": "29.7.0",
 		"jest-fetch-mock": "3.0.3",
 		"jest-websocket-mock": "2.5.0",
@@ -41,7 +41,7 @@
 	],
 	"dependencies": {
 		"@swc/cli": "0.1.63",
-		"@swc/core": "1.3.99",
+		"@swc/core": "1.3.100",
 		"eventemitter3": "5.0.1",
 		"reconnecting-websocket": "4.4.0"
 	}
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 8ece5baf21..64d1def267 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -14,9 +14,9 @@
 		"misskey-js": "workspace:*"
 	},
 	"devDependencies": {
-		"@typescript-eslint/parser": "6.12.0",
+		"@typescript-eslint/parser": "6.13.1",
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
-		"eslint": "8.54.0",
+		"eslint": "8.55.0",
 		"eslint-plugin-import": "2.29.0",
 		"typescript": "5.3.2"
 	},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8831c43036..ea1ea6cd18 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,7 +14,7 @@ importers:
     dependencies:
       cssnano:
         specifier: 6.0.1
-        version: 6.0.1(postcss@8.4.31)
+        version: 6.0.1(postcss@8.4.32)
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -22,8 +22,8 @@ importers:
         specifier: 4.1.0
         version: 4.1.0
       postcss:
-        specifier: 8.4.31
-        version: 8.4.31
+        specifier: 8.4.32
+        version: 8.4.32
       terser:
         specifier: 5.24.0
         version: 5.24.0
@@ -36,11 +36,11 @@ importers:
         version: 4.4.0
     devDependencies:
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.12.0
-        version: 6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2)
+        specifier: 6.13.1
+        version: 6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
-        specifier: 6.12.0
-        version: 6.12.0(eslint@8.54.0)(typescript@5.3.2)
+        specifier: 6.13.1
+        version: 6.13.1(eslint@8.55.0)(typescript@5.3.2)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
@@ -48,8 +48,8 @@ importers:
         specifier: 13.6.0
         version: 13.6.0
       eslint:
-        specifier: 8.54.0
-        version: 8.54.0
+        specifier: 8.55.0
+        version: 8.55.0
       ncp:
         specifier: 2.0.0
         version: 2.0.0
@@ -66,14 +66,14 @@ importers:
         specifier: 3.412.0
         version: 3.412.0(@aws-sdk/client-s3@3.412.0)
       '@bull-board/api':
-        specifier: 5.9.2
-        version: 5.9.2(@bull-board/ui@5.9.2)
+        specifier: 5.10.1
+        version: 5.10.1(@bull-board/ui@5.10.1)
       '@bull-board/fastify':
-        specifier: 5.9.2
-        version: 5.9.2
+        specifier: 5.10.1
+        version: 5.10.1
       '@bull-board/ui':
-        specifier: 5.9.2
-        version: 5.9.2
+        specifier: 5.10.1
+        version: 5.10.1
       '@discordapp/twemoji':
         specifier: 14.1.2
         version: 14.1.2
@@ -124,10 +124,10 @@ importers:
         version: 2.1.10
       '@swc/cli':
         specifier: 0.1.63
-        version: 0.1.63(@swc/core@1.3.99)(chokidar@3.5.3)
+        version: 0.1.63(@swc/core@1.3.100)(chokidar@3.5.3)
       '@swc/core':
-        specifier: 1.3.99
-        version: 1.3.99
+        specifier: 1.3.100
+        version: 1.3.100
       accepts:
         specifier: 1.3.8
         version: 1.3.8
@@ -150,8 +150,8 @@ importers:
         specifier: 1.20.2
         version: 1.20.2
       bullmq:
-        specifier: 4.14.2
-        version: 4.14.2
+        specifier: 4.14.4
+        version: 4.14.4
       cacheable-lookup:
         specifier: 7.0.0
         version: 7.0.0
@@ -228,8 +228,8 @@ importers:
         specifier: 4.1.0
         version: 4.1.0
       jsdom:
-        specifier: 23.0.0
-        version: 23.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+        specifier: 23.0.1
+        version: 23.0.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       json5:
         specifier: 2.2.3
         version: 2.2.3
@@ -258,8 +258,8 @@ importers:
         specifier: 3.0.0-canary.1
         version: 3.0.0-canary.1
       nanoid:
-        specifier: 5.0.3
-        version: 5.0.3
+        specifier: 5.0.4
+        version: 5.0.4
       nested-property:
         specifier: 4.0.0
         version: 4.0.0
@@ -363,8 +363,8 @@ importers:
         specifier: github:misskey-dev/summaly
         version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8
       systeminformation:
-        specifier: 5.21.18
-        version: 5.21.18
+        specifier: 5.21.20
+        version: 5.21.20
       tinycolor2:
         specifier: 1.6.0
         version: 1.6.0
@@ -498,7 +498,7 @@ importers:
         version: 8.3.4
       '@swc/jest':
         specifier: 0.2.29
-        version: 0.2.29(@swc/core@1.3.99)
+        version: 0.2.29(@swc/core@1.3.100)
       '@types/accepts':
         specifier: 1.3.7
         version: 1.3.7
@@ -548,8 +548,8 @@ importers:
         specifier: 0.7.34
         version: 0.7.34
       '@types/node':
-        specifier: 20.10.0
-        version: 20.10.0
+        specifier: 20.10.3
+        version: 20.10.3
       '@types/node-fetch':
         specifier: 3.0.3
         version: 3.0.3
@@ -617,11 +617,11 @@ importers:
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.12.0
-        version: 6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2)
+        specifier: 6.13.1
+        version: 6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
-        specifier: 6.12.0
-        version: 6.12.0(eslint@8.54.0)(typescript@5.3.2)
+        specifier: 6.13.1
+        version: 6.13.1(eslint@8.55.0)(typescript@5.3.2)
       aws-sdk-client-mock:
         specifier: 3.0.0
         version: 3.0.0
@@ -629,17 +629,17 @@ importers:
         specifier: 7.0.3
         version: 7.0.3
       eslint:
-        specifier: 8.54.0
-        version: 8.54.0
+        specifier: 8.55.0
+        version: 8.55.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)
+        version: 2.29.0(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)
       execa:
         specifier: 8.0.1
         version: 8.0.1
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.10.0)
+        version: 29.7.0(@types/node@20.10.3)
       jest-mock:
         specifier: 29.7.0
         version: 29.7.0
@@ -657,16 +657,16 @@ importers:
         version: 2.1.1
       '@rollup/plugin-alias':
         specifier: 5.1.0
-        version: 5.1.0(rollup@4.6.0)
+        version: 5.1.0(rollup@4.6.1)
       '@rollup/plugin-json':
         specifier: 6.0.1
-        version: 6.0.1(rollup@4.6.0)
+        version: 6.0.1(rollup@4.6.1)
       '@rollup/plugin-replace':
         specifier: 5.0.5
-        version: 5.0.5(rollup@4.6.0)
+        version: 5.0.5(rollup@4.6.1)
       '@rollup/pluginutils':
         specifier: 5.0.5
-        version: 5.0.5(rollup@4.6.0)
+        version: 5.0.5(rollup@4.6.1)
       '@syuilo/aiscript':
         specifier: 0.16.0
         version: 0.16.0
@@ -674,11 +674,11 @@ importers:
         specifier: 2.37.0
         version: 2.37.0
       '@vitejs/plugin-vue':
-        specifier: 4.5.0
-        version: 4.5.0(vite@5.0.2)(vue@3.3.9)
+        specifier: 4.5.1
+        version: 4.5.1(vite@5.0.5)(vue@3.3.9)
       '@vue-macros/reactivity-transform':
         specifier: 0.4.0
-        version: 0.4.0(rollup@4.6.0)(vue@3.3.9)
+        version: 0.4.0(rollup@4.6.1)(vue@3.3.9)
       '@vue/compiler-sfc':
         specifier: 3.3.9
         version: 3.3.9
@@ -740,8 +740,8 @@ importers:
         specifier: 5.0.1
         version: 5.0.1
       gsap:
-        specifier: 3.12.2
-        version: 3.12.2
+        specifier: 3.12.3
+        version: 3.12.3
       idb-keyval:
         specifier: 6.2.1
         version: 6.2.1
@@ -764,8 +764,8 @@ importers:
         specifier: workspace:*
         version: link:../misskey-js
       photoswipe:
-        specifier: 5.4.2
-        version: 5.4.2
+        specifier: 5.4.3
+        version: 5.4.3
       punycode:
         specifier: 2.3.1
         version: 2.3.1
@@ -773,8 +773,8 @@ importers:
         specifier: 0.2.1
         version: 0.2.1
       rollup:
-        specifier: 4.6.0
-        version: 4.6.0
+        specifier: 4.6.1
+        version: 4.6.1
       sanitize-html:
         specifier: 2.11.0
         version: 2.11.0
@@ -791,8 +791,8 @@ importers:
         specifier: 3.1.0
         version: 3.1.0
       three:
-        specifier: 0.158.0
-        version: 0.158.0
+        specifier: 0.159.0
+        version: 0.159.0
       throttle-debounce:
         specifier: 5.0.0
         version: 5.0.0
@@ -821,8 +821,8 @@ importers:
         specifier: 1.8.1
         version: 1.8.1
       vite:
-        specifier: 5.0.2
-        version: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+        specifier: 5.0.5
+        version: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
       vue:
         specifier: 3.3.9
         version: 3.3.9(typescript@5.3.2)
@@ -831,59 +831,59 @@ importers:
         version: 4.1.0(vue@3.3.9)
     devDependencies:
       '@storybook/addon-actions':
-        specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.3
+        version: 7.6.3
       '@storybook/addon-essentials':
-        specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.3
+        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)
       '@storybook/addon-interactions':
-        specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.3
+        version: 7.6.3
       '@storybook/addon-links':
-        specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.3
+        version: 7.6.3(react@18.2.0)
       '@storybook/addon-storysource':
-        specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.3
+        version: 7.6.3
       '@storybook/addons':
-        specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.3
+        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)
       '@storybook/blocks':
-        specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.3
+        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)
       '@storybook/core-events':
-        specifier: 7.5.3
-        version: 7.5.3
+        specifier: 7.6.3
+        version: 7.6.3
       '@storybook/jest':
         specifier: 0.2.3
         version: 0.2.3(vitest@0.34.6)
       '@storybook/manager-api':
-        specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.3
+        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)
       '@storybook/preview-api':
-        specifier: 7.5.3
-        version: 7.5.3
+        specifier: 7.6.3
+        version: 7.6.3
       '@storybook/react':
-        specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)
+        specifier: 7.6.3
+        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)
       '@storybook/react-vite':
-        specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.6.0)(typescript@5.3.2)(vite@5.0.2)
+        specifier: 7.6.3
+        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.6.1)(typescript@5.3.2)(vite@5.0.5)
       '@storybook/testing-library':
         specifier: 0.2.2
         version: 0.2.2
       '@storybook/theming':
-        specifier: 7.5.3
-        version: 7.5.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.3
+        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)
       '@storybook/types':
-        specifier: 7.5.3
-        version: 7.5.3
+        specifier: 7.6.3
+        version: 7.6.3
       '@storybook/vue3':
-        specifier: 7.5.3
-        version: 7.5.3(@vue/compiler-core@3.3.9)(vue@3.3.9)
+        specifier: 7.6.3
+        version: 7.6.3(@vue/compiler-core@3.3.9)(vue@3.3.9)
       '@storybook/vue3-vite':
-        specifier: 7.5.3
-        version: 7.5.3(@vue/compiler-core@3.3.9)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9)
+        specifier: 7.6.3
+        version: 7.6.3(@vue/compiler-core@3.3.9)(typescript@5.3.2)(vite@5.0.5)(vue@3.3.9)
       '@testing-library/vue':
         specifier: 8.0.1
         version: 8.0.1(@vue/compiler-sfc@3.3.9)(vue@3.3.9)
@@ -900,8 +900,8 @@ importers:
         specifier: 4.0.6
         version: 4.0.6
       '@types/node':
-        specifier: 20.10.0
-        version: 20.10.0
+        specifier: 20.10.3
+        version: 20.10.3
       '@types/punycode':
         specifier: 2.1.3
         version: 2.1.3
@@ -924,11 +924,11 @@ importers:
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.12.0
-        version: 6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2)
+        specifier: 6.13.1
+        version: 6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
-        specifier: 6.12.0
-        version: 6.12.0(eslint@8.54.0)(typescript@5.3.2)
+        specifier: 6.13.1
+        version: 6.13.1(eslint@8.55.0)(typescript@5.3.2)
       '@vitest/coverage-v8':
         specifier: 0.34.6
         version: 0.34.6(vitest@0.34.6)
@@ -945,14 +945,14 @@ importers:
         specifier: 13.6.0
         version: 13.6.0
       eslint:
-        specifier: 8.54.0
-        version: 8.54.0
+        specifier: 8.55.0
+        version: 8.55.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)
+        version: 2.29.0(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)
       eslint-plugin-vue:
-        specifier: 9.18.1
-        version: 9.18.1(eslint@8.54.0)
+        specifier: 9.19.2
+        version: 9.19.2(eslint@8.55.0)
       fast-glob:
         specifier: 3.3.2
         version: 3.3.2
@@ -969,8 +969,8 @@ importers:
         specifier: 1.10.0
         version: 1.10.0(msw@1.3.2)
       nodemon:
-        specifier: 3.0.1
-        version: 3.0.1
+        specifier: 3.0.2
+        version: 3.0.2
       prettier:
         specifier: 3.1.0
         version: 3.1.0
@@ -984,11 +984,11 @@ importers:
         specifier: 2.0.3
         version: 2.0.3
       storybook:
-        specifier: 7.5.3
-        version: 7.5.3
+        specifier: 7.6.3
+        version: 7.6.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.5.3)(@storybook/components@7.5.3)(@storybook/core-events@7.5.3)(@storybook/manager-api@7.5.3)(@storybook/preview-api@7.5.3)(@storybook/theming@7.5.3)(@storybook/types@7.5.3)(react-dom@18.2.0)(react@18.2.0)
+        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.3)(@storybook/components@7.5.3)(@storybook/core-events@7.6.3)(@storybook/manager-api@7.6.3)(@storybook/preview-api@7.6.3)(@storybook/theming@7.6.3)(@storybook/types@7.6.3)(react-dom@18.2.0)(react@18.2.0)
       summaly:
         specifier: github:misskey-dev/summaly
         version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8
@@ -1003,19 +1003,19 @@ importers:
         version: 0.2.2(vitest@0.34.6)
       vue-eslint-parser:
         specifier: 9.3.2
-        version: 9.3.2(eslint@8.54.0)
+        version: 9.3.2(eslint@8.55.0)
       vue-tsc:
-        specifier: 1.8.22
-        version: 1.8.22(typescript@5.3.2)
+        specifier: 1.8.24
+        version: 1.8.24(typescript@5.3.2)
 
   packages/misskey-js:
     dependencies:
       '@swc/cli':
         specifier: 0.1.63
-        version: 0.1.63(@swc/core@1.3.99)(chokidar@3.5.3)
+        version: 0.1.63(@swc/core@1.3.100)(chokidar@3.5.3)
       '@swc/core':
-        specifier: 1.3.99
-        version: 1.3.99
+        specifier: 1.3.100
+        version: 1.3.100
       eventemitter3:
         specifier: 5.0.1
         version: 5.0.1
@@ -1025,28 +1025,28 @@ importers:
     devDependencies:
       '@microsoft/api-extractor':
         specifier: 7.38.3
-        version: 7.38.3(@types/node@20.10.0)
+        version: 7.38.3(@types/node@20.10.3)
       '@swc/jest':
         specifier: 0.2.29
-        version: 0.2.29(@swc/core@1.3.99)
+        version: 0.2.29(@swc/core@1.3.100)
       '@types/jest':
         specifier: 29.5.10
         version: 29.5.10
       '@types/node':
-        specifier: 20.10.0
-        version: 20.10.0
+        specifier: 20.10.3
+        version: 20.10.3
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.12.0
-        version: 6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2)
+        specifier: 6.13.1
+        version: 6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2)
       '@typescript-eslint/parser':
-        specifier: 6.12.0
-        version: 6.12.0(eslint@8.54.0)(typescript@5.3.2)
+        specifier: 6.13.1
+        version: 6.13.1(eslint@8.55.0)(typescript@5.3.2)
       eslint:
-        specifier: 8.54.0
-        version: 8.54.0
+        specifier: 8.55.0
+        version: 8.55.0
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.10.0)
+        version: 29.7.0(@types/node@20.10.3)
       jest-fetch-mock:
         specifier: 3.0.3
         version: 3.0.3
@@ -1112,17 +1112,17 @@ importers:
         version: link:../misskey-js
     devDependencies:
       '@typescript-eslint/parser':
-        specifier: 6.12.0
-        version: 6.12.0(eslint@8.54.0)(typescript@5.3.2)
+        specifier: 6.13.1
+        version: 6.13.1(eslint@8.55.0)(typescript@5.3.2)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: /@types/serviceworker@0.0.67
       eslint:
-        specifier: 8.54.0
-        version: 8.54.0
+        specifier: 8.55.0
+        version: 8.55.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)
+        version: 2.29.0(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)
       typescript:
         specifier: 5.3.2
         version: 5.3.2
@@ -1781,11 +1781,24 @@ packages:
       '@babel/highlight': 7.22.5
     dev: true
 
+  /@babel/code-frame@7.23.5:
+    resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/highlight': 7.23.4
+      chalk: 2.4.2
+    dev: true
+
   /@babel/compat-data@7.22.9:
     resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==}
     engines: {node: '>=6.9.0'}
     dev: true
 
+  /@babel/compat-data@7.23.5:
+    resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
   /@babel/core@7.22.11:
     resolution: {integrity: sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==}
     engines: {node: '>=6.9.0'}
@@ -1801,7 +1814,30 @@ packages:
       '@babel/traverse': 7.22.11
       '@babel/types': 7.22.17
       convert-source-map: 1.9.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
+      gensync: 1.0.0-beta.2
+      json5: 2.2.3
+      semver: 6.3.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/core@7.23.5:
+    resolution: {integrity: sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@ampproject/remapping': 2.2.1
+      '@babel/code-frame': 7.23.5
+      '@babel/generator': 7.23.5
+      '@babel/helper-compilation-targets': 7.22.15
+      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
+      '@babel/helpers': 7.23.5
+      '@babel/parser': 7.23.5
+      '@babel/template': 7.22.15
+      '@babel/traverse': 7.23.5
+      '@babel/types': 7.23.5
+      convert-source-map: 2.0.0
+      debug: 4.3.4(supports-color@5.5.0)
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -1819,18 +1855,28 @@ packages:
       jsesc: 2.5.2
     dev: true
 
+  /@babel/generator@7.23.5:
+    resolution: {integrity: sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.23.5
+      '@jridgewell/gen-mapping': 0.3.2
+      '@jridgewell/trace-mapping': 0.3.18
+      jsesc: 2.5.2
+    dev: true
+
   /@babel/helper-annotate-as-pure@7.22.5:
     resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
     dev: true
 
-  /@babel/helper-builder-binary-assignment-operator-visitor@7.22.5:
-    resolution: {integrity: sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==}
+  /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15:
+    resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
     dev: true
 
   /@babel/helper-compilation-targets@7.22.10:
@@ -1844,51 +1890,97 @@ packages:
       semver: 6.3.1
     dev: true
 
-  /@babel/helper-create-class-features-plugin@7.22.9(@babel/core@7.22.11):
+  /@babel/helper-compilation-targets@7.22.15:
+    resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/compat-data': 7.22.9
+      '@babel/helper-validator-option': 7.23.5
+      browserslist: 4.21.9
+      lru-cache: 5.1.1
+      semver: 6.3.1
+    dev: true
+
+  /@babel/helper-create-class-features-plugin@7.22.9(@babel/core@7.23.5):
     resolution: {integrity: sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-annotate-as-pure': 7.22.5
       '@babel/helper-environment-visitor': 7.22.5
       '@babel/helper-function-name': 7.22.5
       '@babel/helper-member-expression-to-functions': 7.22.5
       '@babel/helper-optimise-call-expression': 7.22.5
-      '@babel/helper-replace-supers': 7.22.9(@babel/core@7.22.11)
+      '@babel/helper-replace-supers': 7.22.9(@babel/core@7.23.5)
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
       '@babel/helper-split-export-declaration': 7.22.6
       semver: 6.3.1
     dev: true
 
-  /@babel/helper-create-regexp-features-plugin@7.22.9(@babel/core@7.22.11):
-    resolution: {integrity: sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==}
+  /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.23.5):
+    resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
+      '@babel/helper-annotate-as-pure': 7.22.5
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-function-name': 7.23.0
+      '@babel/helper-member-expression-to-functions': 7.23.0
+      '@babel/helper-optimise-call-expression': 7.22.5
+      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5)
+      '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
+      '@babel/helper-split-export-declaration': 7.22.6
+      semver: 6.3.1
+    dev: true
+
+  /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.5):
+    resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.23.5
       '@babel/helper-annotate-as-pure': 7.22.5
       regexpu-core: 5.3.2
       semver: 6.3.1
     dev: true
 
-  /@babel/helper-define-polyfill-provider@0.4.1(@babel/core@7.22.11):
-    resolution: {integrity: sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A==}
+  /@babel/helper-create-regexp-features-plugin@7.22.9(@babel/core@7.23.5):
+    resolution: {integrity: sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==}
+    engines: {node: '>=6.9.0'}
     peerDependencies:
-      '@babel/core': ^7.4.0-0
+      '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-compilation-targets': 7.22.10
+      '@babel/core': 7.23.5
+      '@babel/helper-annotate-as-pure': 7.22.5
+      regexpu-core: 5.3.2
+      semver: 6.3.1
+    dev: true
+
+  /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==}
+    peerDependencies:
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-compilation-targets': 7.22.15
       '@babel/helper-plugin-utils': 7.22.5
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       lodash.debounce: 4.0.8
       resolve: 1.22.8
     transitivePeerDependencies:
       - supports-color
     dev: true
 
+  /@babel/helper-environment-visitor@7.22.20:
+    resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
   /@babel/helper-environment-visitor@7.22.5:
     resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==}
     engines: {node: '>=6.9.0'}
@@ -1899,21 +1991,43 @@ packages:
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/template': 7.22.5
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
+    dev: true
+
+  /@babel/helper-function-name@7.23.0:
+    resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/template': 7.22.15
+      '@babel/types': 7.23.5
     dev: true
 
   /@babel/helper-hoist-variables@7.22.5:
     resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
     dev: true
 
   /@babel/helper-member-expression-to-functions@7.22.5:
     resolution: {integrity: sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
+    dev: true
+
+  /@babel/helper-member-expression-to-functions@7.23.0:
+    resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.23.3
+    dev: true
+
+  /@babel/helper-module-imports@7.22.15:
+    resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.23.5
     dev: true
 
   /@babel/helper-module-imports@7.22.5:
@@ -1937,11 +2051,25 @@ packages:
       '@babel/helper-validator-identifier': 7.22.15
     dev: true
 
+  /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-module-imports': 7.22.15
+      '@babel/helper-simple-access': 7.22.5
+      '@babel/helper-split-export-declaration': 7.22.6
+      '@babel/helper-validator-identifier': 7.22.20
+    dev: true
+
   /@babel/helper-optimise-call-expression@7.22.5:
     resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
     dev: true
 
   /@babel/helper-plugin-utils@7.22.5:
@@ -1949,25 +2077,37 @@ packages:
     engines: {node: '>=6.9.0'}
     dev: true
 
-  /@babel/helper-remap-async-to-generator@7.22.9(@babel/core@7.22.11):
-    resolution: {integrity: sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==}
+  /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.5):
+    resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-environment-visitor': 7.22.5
-      '@babel/helper-wrap-function': 7.22.9
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-wrap-function': 7.22.20
     dev: true
 
-  /@babel/helper-replace-supers@7.22.9(@babel/core@7.22.11):
+  /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.5):
+    resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-member-expression-to-functions': 7.23.0
+      '@babel/helper-optimise-call-expression': 7.22.5
+    dev: true
+
+  /@babel/helper-replace-supers@7.22.9(@babel/core@7.23.5):
     resolution: {integrity: sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-environment-visitor': 7.22.5
       '@babel/helper-member-expression-to-functions': 7.22.5
       '@babel/helper-optimise-call-expression': 7.22.5
@@ -1984,7 +2124,7 @@ packages:
     resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
     dev: true
 
   /@babel/helper-split-export-declaration@7.22.6:
@@ -1998,6 +2138,11 @@ packages:
     resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==}
     engines: {node: '>=6.9.0'}
 
+  /@babel/helper-string-parser@7.23.4:
+    resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
   /@babel/helper-validator-identifier@7.22.15:
     resolution: {integrity: sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==}
     engines: {node: '>=6.9.0'}
@@ -2005,7 +2150,6 @@ packages:
   /@babel/helper-validator-identifier@7.22.20:
     resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
     engines: {node: '>=6.9.0'}
-    dev: false
 
   /@babel/helper-validator-identifier@7.22.5:
     resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
@@ -2017,13 +2161,18 @@ packages:
     engines: {node: '>=6.9.0'}
     dev: true
 
-  /@babel/helper-wrap-function@7.22.9:
-    resolution: {integrity: sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==}
+  /@babel/helper-validator-option@7.23.5:
+    resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helper-wrap-function@7.22.20:
+    resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==}
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/helper-function-name': 7.22.5
-      '@babel/template': 7.22.5
-      '@babel/types': 7.22.17
+      '@babel/template': 7.22.15
+      '@babel/types': 7.23.3
     dev: true
 
   /@babel/helpers@7.22.11:
@@ -2037,6 +2186,17 @@ packages:
       - supports-color
     dev: true
 
+  /@babel/helpers@7.23.5:
+    resolution: {integrity: sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/template': 7.22.15
+      '@babel/traverse': 7.23.5
+      '@babel/types': 7.23.5
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
   /@babel/highlight@7.22.13:
     resolution: {integrity: sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==}
     engines: {node: '>=6.9.0'}
@@ -2055,6 +2215,15 @@ packages:
       js-tokens: 4.0.0
     dev: true
 
+  /@babel/highlight@7.23.4:
+    resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-validator-identifier': 7.22.20
+      chalk: 2.4.2
+      js-tokens: 4.0.0
+    dev: true
+
   /@babel/parser@7.23.0:
     resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==}
     engines: {node: '>=6.0.0'}
@@ -2069,84 +2238,54 @@ packages:
     dependencies:
       '@babel/types': 7.22.17
 
-  /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==}
+  /@babel/parser@7.23.5:
+    resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+    dependencies:
+      '@babel/types': 7.23.5
+    dev: true
+
+  /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==}
+  /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.13.0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
-      '@babel/plugin-transform-optional-chaining': 7.22.6(@babel/core@7.22.11)
+      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.22.11):
-    resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==}
+  /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-XaJak1qcityzrX0/IU5nKHb34VaibwP3saKqG6a/tppelgllOH13LUann4ZCIBcVOeE6H18K4Vx9QKkVww3z/w==}
     engines: {node: '>=6.9.0'}
-    deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.
     peerDependencies:
-      '@babel/core': ^7.0.0-0
+      '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.22.11):
-    resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==}
-    engines: {node: '>=6.9.0'}
-    deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.11)
-    dev: true
-
-  /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.22.11):
-    resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==}
-    engines: {node: '>=6.9.0'}
-    deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.11)
-    dev: true
-
-  /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.22.11):
+  /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.5):
     resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-    dev: true
-
-  /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.22.11):
-    resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==}
-    engines: {node: '>=4'}
-    deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.11)
-      '@babel/helper-plugin-utils': 7.22.5
+      '@babel/core': 7.23.5
     dev: true
 
   /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.22.11):
@@ -2158,6 +2297,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.22.11):
     resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
     peerDependencies:
@@ -2176,61 +2324,70 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.22.11):
+  /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.5):
+    resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
+  /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.5):
     resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.22.11):
+  /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.22.11):
+  /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-flow@7.18.6(@babel/core@7.22.11):
-    resolution: {integrity: sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A==}
+  /@babel/plugin-syntax-flow@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-import-assertions@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==}
+  /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-import-attributes@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==}
+  /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
@@ -2243,6 +2400,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.22.11):
     resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
     peerDependencies:
@@ -2252,6 +2418,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.22.11):
     resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==}
     engines: {node: '>=6.9.0'}
@@ -2262,6 +2437,16 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.22.11):
     resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
     peerDependencies:
@@ -2271,6 +2456,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.22.11):
     resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
     peerDependencies:
@@ -2280,6 +2474,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.22.11):
     resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
     peerDependencies:
@@ -2289,6 +2492,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.22.11):
     resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
     peerDependencies:
@@ -2298,6 +2510,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.22.11):
     resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
     peerDependencies:
@@ -2307,6 +2528,15 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.22.11):
     resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
     peerDependencies:
@@ -2316,13 +2546,22 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.22.11):
+  /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
+  /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.5):
     resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
@@ -2336,6 +2575,16 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
+  /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.5):
+    resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
   /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.22.11):
     resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==}
     engines: {node: '>=6.9.0'}
@@ -2346,442 +2595,474 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.22.11):
+  /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
+  /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.5):
     resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-arrow-functions@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==}
+  /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-async-generator-functions@7.22.7(@babel/core@7.22.11):
-    resolution: {integrity: sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==}
+  /@babel/plugin-transform-async-generator-functions@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-efdkfPhHYTtn0G6n2ddrESE91fgXxjlqLsnUtPWnJs4a4mZIbUaK7ffqKIIUKXSHwcDvaCVX6GXkaJJFqtX7jw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-environment-visitor': 7.22.5
+      '@babel/core': 7.23.5
+      '@babel/helper-environment-visitor': 7.22.20
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-remap-async-to-generator': 7.22.9(@babel/core@7.22.11)
-      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.11)
+      '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.5)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-async-to-generator@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==}
+  /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-module-imports': 7.22.5
+      '@babel/core': 7.23.5
+      '@babel/helper-module-imports': 7.22.15
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-remap-async-to-generator': 7.22.9(@babel/core@7.22.11)
+      '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-block-scoped-functions@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==}
+  /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-block-scoping@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==}
+  /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-class-properties@7.22.5(@babel/core@7.22.11):
+  /@babel/plugin-transform-class-properties@7.22.5(@babel/core@7.23.5):
     resolution: {integrity: sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-class-static-block@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==}
+  /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5)
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
+  /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.12.0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.22.11)
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-classes@7.22.6(@babel/core@7.22.11):
-    resolution: {integrity: sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==}
+  /@babel/plugin-transform-classes@7.23.5(@babel/core@7.23.5):
+    resolution: {integrity: sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-compilation-targets': 7.22.10
-      '@babel/helper-environment-visitor': 7.22.5
-      '@babel/helper-function-name': 7.22.5
+      '@babel/helper-compilation-targets': 7.22.15
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-function-name': 7.23.0
       '@babel/helper-optimise-call-expression': 7.22.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-replace-supers': 7.22.9(@babel/core@7.22.11)
+      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5)
       '@babel/helper-split-export-declaration': 7.22.6
       globals: 11.12.0
     dev: true
 
-  /@babel/plugin-transform-computed-properties@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==}
+  /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/template': 7.22.5
+      '@babel/template': 7.22.15
     dev: true
 
-  /@babel/plugin-transform-destructuring@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==}
+  /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-dotall-regex@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==}
+  /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-duplicate-keys@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==}
+  /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-dynamic-import@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==}
+  /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.22.11)
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-exponentiation-operator@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==}
+  /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.5
+      '@babel/core': 7.23.5
+      '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-export-namespace-from@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==}
+  /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.11)
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-flow-strip-types@7.21.0(@babel/core@7.22.11):
-    resolution: {integrity: sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==}
+  /@babel/plugin-transform-flow-strip-types@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-flow': 7.18.6(@babel/core@7.22.11)
+      '@babel/plugin-syntax-flow': 7.23.3(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-for-of@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==}
+  /@babel/plugin-transform-for-of@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-function-name@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==}
+  /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-compilation-targets': 7.22.10
-      '@babel/helper-function-name': 7.22.5
+      '@babel/core': 7.23.5
+      '@babel/helper-compilation-targets': 7.22.15
+      '@babel/helper-function-name': 7.23.0
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-json-strings@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==}
+  /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.11)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-literals@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==}
+  /@babel/plugin-transform-literals@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-logical-assignment-operators@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==}
+  /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.11)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-member-expression-literals@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==}
+  /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-modules-amd@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==}
+  /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-modules-commonjs@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==}
+  /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-simple-access': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-modules-systemjs@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==}
+  /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-hoist-variables': 7.22.5
-      '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.11)
+      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-validator-identifier': 7.22.15
+      '@babel/helper-validator-identifier': 7.22.20
     dev: true
 
-  /@babel/plugin-transform-modules-umd@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==}
+  /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.22.11):
+  /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.5):
     resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-new-target@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==}
+  /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-nullish-coalescing-operator@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==}
+  /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.11)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-numeric-separator@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==}
+  /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.11)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-object-rest-spread@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==}
+  /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/compat-data': 7.22.9
-      '@babel/core': 7.22.11
-      '@babel/helper-compilation-targets': 7.22.10
+      '@babel/compat-data': 7.23.5
+      '@babel/core': 7.23.5
+      '@babel/helper-compilation-targets': 7.22.15
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.11)
-      '@babel/plugin-transform-parameters': 7.22.5(@babel/core@7.22.11)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-object-super@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==}
+  /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-replace-supers': 7.22.9(@babel/core@7.22.11)
+      '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-optional-catch-binding@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==}
+  /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.11)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-optional-chaining@7.22.6(@babel/core@7.22.11):
-    resolution: {integrity: sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==}
+  /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.11)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-parameters@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==}
+  /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-private-methods@7.22.5(@babel/core@7.22.11):
+  /@babel/plugin-transform-private-methods@7.22.5(@babel/core@7.23.5):
     resolution: {integrity: sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-private-property-in-object@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==}
+  /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
+      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5)
+      '@babel/helper-plugin-utils': 7.22.5
+    dev: true
+
+  /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.23.5):
+    resolution: {integrity: sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.23.5
       '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.22.11)
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-property-literals@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==}
+  /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
@@ -2805,269 +3086,269 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-regenerator@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==}
+  /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      regenerator-transform: 0.15.1
+      regenerator-transform: 0.15.2
     dev: true
 
-  /@babel/plugin-transform-reserved-words@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==}
+  /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-shorthand-properties@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==}
+  /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-spread@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==}
+  /@babel/plugin-transform-spread@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
       '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-sticky-regex@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==}
+  /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-template-literals@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==}
+  /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-typeof-symbol@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==}
+  /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-typescript@7.21.3(@babel/core@7.22.11):
-    resolution: {integrity: sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==}
+  /@babel/plugin-transform-typescript@7.23.5(@babel/core@7.23.5):
+    resolution: {integrity: sha512-2fMkXEJkrmwgu2Bsv1Saxgj30IXZdJ+84lQcKKI7sm719oXs0BBw2ZENKdJdR1PjWndgLCEBNXJOri0fk7RYQA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/helper-create-class-features-plugin': 7.23.5(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.22.11)
+      '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.5)
     dev: true
 
-  /@babel/plugin-transform-unicode-escapes@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==}
+  /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-unicode-property-regex@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==}
+  /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-unicode-regex@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==}
+  /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-unicode-sets-regex@7.22.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==}
+  /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/preset-env@7.22.9(@babel/core@7.22.11):
-    resolution: {integrity: sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==}
+  /@babel/preset-env@7.23.5(@babel/core@7.23.5):
+    resolution: {integrity: sha512-0d/uxVD6tFGWXGDSfyMD1p2otoaKmu6+GD+NfAx0tMaH+dxORnp7T9TaVQ6mKyya7iBtCIVxHjWT7MuzzM9z+A==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/compat-data': 7.22.9
-      '@babel/core': 7.22.11
-      '@babel/helper-compilation-targets': 7.22.10
+      '@babel/compat-data': 7.23.5
+      '@babel/core': 7.23.5
+      '@babel/helper-compilation-targets': 7.22.15
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-validator-option': 7.22.5
-      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.22.11)
-      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.22.11)
-      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.22.11)
-      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.22.11)
-      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.22.11)
-      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.22.11)
-      '@babel/plugin-syntax-import-assertions': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-syntax-import-attributes': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.22.11)
-      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.22.11)
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.22.11)
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.22.11)
-      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.22.11)
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.22.11)
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.22.11)
-      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.22.11)
-      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.22.11)
-      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.11)
-      '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.22.11)
-      '@babel/plugin-transform-arrow-functions': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-async-generator-functions': 7.22.7(@babel/core@7.22.11)
-      '@babel/plugin-transform-async-to-generator': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-block-scoped-functions': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-block-scoping': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-class-properties': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-class-static-block': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-classes': 7.22.6(@babel/core@7.22.11)
-      '@babel/plugin-transform-computed-properties': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-destructuring': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-dotall-regex': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-duplicate-keys': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-dynamic-import': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-exponentiation-operator': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-export-namespace-from': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-for-of': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-function-name': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-json-strings': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-literals': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-logical-assignment-operators': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-member-expression-literals': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-modules-amd': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-modules-commonjs': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-modules-systemjs': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-modules-umd': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-new-target': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-nullish-coalescing-operator': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-numeric-separator': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-object-rest-spread': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-object-super': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-optional-catch-binding': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-optional-chaining': 7.22.6(@babel/core@7.22.11)
-      '@babel/plugin-transform-parameters': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-private-methods': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-private-property-in-object': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-property-literals': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-regenerator': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-reserved-words': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-shorthand-properties': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-spread': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-sticky-regex': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-template-literals': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-typeof-symbol': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-unicode-escapes': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-unicode-property-regex': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-unicode-regex': 7.22.5(@babel/core@7.22.11)
-      '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.22.11)
-      '@babel/preset-modules': 0.1.5(@babel/core@7.22.11)
-      '@babel/types': 7.22.17
-      babel-plugin-polyfill-corejs2: 0.4.4(@babel/core@7.22.11)
-      babel-plugin-polyfill-corejs3: 0.8.2(@babel/core@7.22.11)
-      babel-plugin-polyfill-regenerator: 0.5.1(@babel/core@7.22.11)
+      '@babel/helper-validator-option': 7.23.5
+      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.5)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.5)
+      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.5)
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.5)
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.5)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.5)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.5)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5)
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.5)
+      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.5)
+      '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.5)
+      '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-async-generator-functions': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-classes': 7.23.5(@babel/core@7.23.5)
+      '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-for-of': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.5)
+      '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.23.5)
+      '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.5)
+      babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.5)
+      babel-plugin-polyfill-corejs3: 0.8.6(@babel/core@7.23.5)
+      babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.5)
       core-js-compat: 3.31.1
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@babel/preset-flow@7.18.6(@babel/core@7.22.11):
-    resolution: {integrity: sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ==}
+  /@babel/preset-flow@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-7yn6hl8RIv+KNk6iIrGZ+D06VhVY35wLVf23Cz/mMu1zOr7u4MMP4j0nZ9tLf8+4ZFpnib8cFYgB/oYg9hfswA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-validator-option': 7.22.5
-      '@babel/plugin-transform-flow-strip-types': 7.21.0(@babel/core@7.22.11)
+      '@babel/helper-validator-option': 7.23.5
+      '@babel/plugin-transform-flow-strip-types': 7.23.3(@babel/core@7.23.5)
     dev: true
 
-  /@babel/preset-modules@0.1.5(@babel/core@7.22.11):
-    resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==}
+  /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.5):
+    resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==}
     peerDependencies:
-      '@babel/core': ^7.0.0-0
+      '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.22.11)
-      '@babel/plugin-transform-dotall-regex': 7.22.5(@babel/core@7.22.11)
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
       esutils: 2.0.3
     dev: true
 
-  /@babel/preset-typescript@7.21.0(@babel/core@7.22.11):
-    resolution: {integrity: sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==}
+  /@babel/preset-typescript@7.23.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/helper-validator-option': 7.22.5
-      '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.22.11)
+      '@babel/helper-validator-option': 7.23.5
+      '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-typescript': 7.23.5(@babel/core@7.23.5)
     dev: true
 
-  /@babel/register@7.21.0(@babel/core@7.22.11):
-    resolution: {integrity: sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw==}
+  /@babel/register@7.22.15(@babel/core@7.23.5):
+    resolution: {integrity: sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       clone-deep: 4.0.1
       find-cache-dir: 2.1.0
       make-dir: 2.1.0
@@ -3091,6 +3372,15 @@ packages:
     dependencies:
       regenerator-runtime: 0.14.0
 
+  /@babel/template@7.22.15:
+    resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.23.5
+      '@babel/parser': 7.23.5
+      '@babel/types': 7.23.5
+    dev: true
+
   /@babel/template@7.22.5:
     resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==}
     engines: {node: '>=6.9.0'}
@@ -3111,8 +3401,26 @@ packages:
       '@babel/helper-hoist-variables': 7.22.5
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/parser': 7.23.3
-      '@babel/types': 7.22.17
-      debug: 4.3.4(supports-color@8.1.1)
+      '@babel/types': 7.23.3
+      debug: 4.3.4(supports-color@5.5.0)
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/traverse@7.23.5:
+    resolution: {integrity: sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.23.5
+      '@babel/generator': 7.23.5
+      '@babel/helper-environment-visitor': 7.22.20
+      '@babel/helper-function-name': 7.23.0
+      '@babel/helper-hoist-variables': 7.22.5
+      '@babel/helper-split-export-declaration': 7.22.6
+      '@babel/parser': 7.23.5
+      '@babel/types': 7.23.5
+      debug: 4.3.4(supports-color@5.5.0)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -3142,7 +3450,15 @@ packages:
       '@babel/helper-string-parser': 7.22.5
       '@babel/helper-validator-identifier': 7.22.20
       to-fast-properties: 2.0.0
-    dev: false
+
+  /@babel/types@7.23.5:
+    resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-string-parser': 7.23.4
+      '@babel/helper-validator-identifier': 7.22.20
+      to-fast-properties: 2.0.0
+    dev: true
 
   /@base2/pretty-print-object@1.0.1:
     resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==}
@@ -3152,29 +3468,29 @@ packages:
     resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
     dev: true
 
-  /@bull-board/api@5.9.2(@bull-board/ui@5.9.2):
-    resolution: {integrity: sha512-x89+CsOZHgQnnUFmC0NRgBJNJtgvQSmrpvAb4SA/tX2ftJ9PSxobLhAx/mvL9Ekdi6U1FZD2uA2JH2nsVVzwCg==}
+  /@bull-board/api@5.10.1(@bull-board/ui@5.10.1):
+    resolution: {integrity: sha512-IBsMtB1xBz+bhIYt3aAXHu9LghTjJ8zKOxJdn7K+z0ZpD0hBF4xWSan5dEhQumk69qO9xq+2EX4wgwrc1mPUxw==}
     peerDependencies:
-      '@bull-board/ui': 5.9.2
+      '@bull-board/ui': 5.10.1
     dependencies:
-      '@bull-board/ui': 5.9.2
+      '@bull-board/ui': 5.10.1
       redis-info: 3.1.0
     dev: false
 
-  /@bull-board/fastify@5.9.2:
-    resolution: {integrity: sha512-K5jR3H/lTQqehxznDMFyqD6xejxlPXuerVXGGcFMmRrz1LFDsbbZYe4y4YE+sGN/+HmaQQjjWWQDRtYQmAlOBQ==}
+  /@bull-board/fastify@5.10.1:
+    resolution: {integrity: sha512-Wj6y4T1BXlY3EVjYnRoRDN1K/NxZlQmyzCjOkeQujkivaXgKQZUhEYVgamg5nMwt6gD4/ve1eQok1eXX3luY5Q==}
     dependencies:
-      '@bull-board/api': 5.9.2(@bull-board/ui@5.9.2)
-      '@bull-board/ui': 5.9.2
+      '@bull-board/api': 5.10.1(@bull-board/ui@5.10.1)
+      '@bull-board/ui': 5.10.1
       '@fastify/static': 6.12.0
       '@fastify/view': 8.2.0
       ejs: 3.1.9
     dev: false
 
-  /@bull-board/ui@5.9.2:
-    resolution: {integrity: sha512-wNlY8l+x4rhwoiGIpZNk/FrCrNzKcBeLqMcZTRC3DIBkp91njMkx+MJeG6EYlENcsN5EQk3I6IwUgvTXcS315g==}
+  /@bull-board/ui@5.10.1:
+    resolution: {integrity: sha512-8jpNh5XM9mzBd/2PTXqqIzF5S6fau+GyAjiN8LCgpW+ralQZ9QtJ8B00srp/2x1cQro9BVV3g9dXt1nWmkz6yw==}
     dependencies:
-      '@bull-board/api': 5.9.2(@bull-board/ui@5.9.2)
+      '@bull-board/api': 5.10.1(@bull-board/ui@5.10.1)
     dev: false
 
   /@canvas/image-data@1.0.0:
@@ -3390,15 +3706,6 @@ packages:
       react: 18.2.0
     dev: true
 
-  /@esbuild/android-arm64@0.18.17:
-    resolution: {integrity: sha512-9np+YYdNDed5+Jgr1TdWBsozZ85U1Oa3xW0c7TWqH0y2aGghXtZsuT8nYRbzOMcl0bXZXjOGbksoTtVOlWrRZg==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/android-arm64@0.18.20:
     resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
     engines: {node: '>=12'}
@@ -3416,15 +3723,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/android-arm@0.18.17:
-    resolution: {integrity: sha512-wHsmJG/dnL3OkpAcwbgoBTTMHVi4Uyou3F5mf58ZtmUyIKfcdA7TROav/6tCzET4A3QW2Q2FC+eFneMU+iyOxg==}
-    engines: {node: '>=12'}
-    cpu: [arm]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/android-arm@0.18.20:
     resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
     engines: {node: '>=12'}
@@ -3442,15 +3740,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/android-x64@0.18.17:
-    resolution: {integrity: sha512-O+FeWB/+xya0aLg23hHEM2E3hbfwZzjqumKMSIqcHbNvDa+dza2D0yLuymRBQQnC34CWrsJUXyH2MG5VnLd6uw==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [android]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/android-x64@0.18.20:
     resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
     engines: {node: '>=12'}
@@ -3468,15 +3757,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/darwin-arm64@0.18.17:
-    resolution: {integrity: sha512-M9uJ9VSB1oli2BE/dJs3zVr9kcCBBsE883prage1NWz6pBS++1oNn/7soPNS3+1DGj0FrkSvnED4Bmlu1VAE9g==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/darwin-arm64@0.18.20:
     resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
     engines: {node: '>=12'}
@@ -3494,15 +3774,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/darwin-x64@0.18.17:
-    resolution: {integrity: sha512-XDre+J5YeIJDMfp3n0279DFNrGCXlxOuGsWIkRb1NThMZ0BsrWXoTg23Jer7fEXQ9Ye5QjrvXpxnhzl3bHtk0g==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/darwin-x64@0.18.20:
     resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
     engines: {node: '>=12'}
@@ -3520,15 +3791,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/freebsd-arm64@0.18.17:
-    resolution: {integrity: sha512-cjTzGa3QlNfERa0+ptykyxs5A6FEUQQF0MuilYXYBGdBxD3vxJcKnzDlhDCa1VAJCmAxed6mYhA2KaJIbtiNuQ==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [freebsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/freebsd-arm64@0.18.20:
     resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
     engines: {node: '>=12'}
@@ -3546,15 +3808,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/freebsd-x64@0.18.17:
-    resolution: {integrity: sha512-sOxEvR8d7V7Kw8QqzxWc7bFfnWnGdaFBut1dRUYtu+EIRXefBc/eIsiUiShnW0hM3FmQ5Zf27suDuHsKgZ5QrA==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [freebsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/freebsd-x64@0.18.20:
     resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
     engines: {node: '>=12'}
@@ -3572,15 +3825,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/linux-arm64@0.18.17:
-    resolution: {integrity: sha512-c9w3tE7qA3CYWjT+M3BMbwMt+0JYOp3vCMKgVBrCl1nwjAlOMYzEo+gG7QaZ9AtqZFj5MbUc885wuBBmu6aADQ==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/linux-arm64@0.18.20:
     resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
     engines: {node: '>=12'}
@@ -3598,15 +3842,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/linux-arm@0.18.17:
-    resolution: {integrity: sha512-2d3Lw6wkwgSLC2fIvXKoMNGVaeY8qdN0IC3rfuVxJp89CRfA3e3VqWifGDfuakPmp90+ZirmTfye1n4ncjv2lg==}
-    engines: {node: '>=12'}
-    cpu: [arm]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/linux-arm@0.18.20:
     resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
     engines: {node: '>=12'}
@@ -3624,15 +3859,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/linux-ia32@0.18.17:
-    resolution: {integrity: sha512-1DS9F966pn5pPnqXYz16dQqWIB0dmDfAQZd6jSSpiT9eX1NzKh07J6VKR3AoXXXEk6CqZMojiVDSZi1SlmKVdg==}
-    engines: {node: '>=12'}
-    cpu: [ia32]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/linux-ia32@0.18.20:
     resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
     engines: {node: '>=12'}
@@ -3650,15 +3876,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/linux-loong64@0.18.17:
-    resolution: {integrity: sha512-EvLsxCk6ZF0fpCB6w6eOI2Fc8KW5N6sHlIovNe8uOFObL2O+Mr0bflPHyHwLT6rwMg9r77WOAWb2FqCQrVnwFg==}
-    engines: {node: '>=12'}
-    cpu: [loong64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/linux-loong64@0.18.20:
     resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
     engines: {node: '>=12'}
@@ -3676,15 +3893,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/linux-mips64el@0.18.17:
-    resolution: {integrity: sha512-e0bIdHA5p6l+lwqTE36NAW5hHtw2tNRmHlGBygZC14QObsA3bD4C6sXLJjvnDIjSKhW1/0S3eDy+QmX/uZWEYQ==}
-    engines: {node: '>=12'}
-    cpu: [mips64el]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/linux-mips64el@0.18.20:
     resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
     engines: {node: '>=12'}
@@ -3702,15 +3910,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/linux-ppc64@0.18.17:
-    resolution: {integrity: sha512-BAAilJ0M5O2uMxHYGjFKn4nJKF6fNCdP1E0o5t5fvMYYzeIqy2JdAP88Az5LHt9qBoUa4tDaRpfWt21ep5/WqQ==}
-    engines: {node: '>=12'}
-    cpu: [ppc64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/linux-ppc64@0.18.20:
     resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
     engines: {node: '>=12'}
@@ -3728,15 +3927,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/linux-riscv64@0.18.17:
-    resolution: {integrity: sha512-Wh/HW2MPnC3b8BqRSIme/9Zhab36PPH+3zam5pqGRH4pE+4xTrVLx2+XdGp6fVS3L2x+DrsIcsbMleex8fbE6g==}
-    engines: {node: '>=12'}
-    cpu: [riscv64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/linux-riscv64@0.18.20:
     resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
     engines: {node: '>=12'}
@@ -3754,15 +3944,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/linux-s390x@0.18.17:
-    resolution: {integrity: sha512-j/34jAl3ul3PNcK3pfI0NSlBANduT2UO5kZ7FCaK33XFv3chDhICLY8wJJWIhiQ+YNdQ9dxqQctRg2bvrMlYgg==}
-    engines: {node: '>=12'}
-    cpu: [s390x]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/linux-s390x@0.18.20:
     resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
     engines: {node: '>=12'}
@@ -3780,15 +3961,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/linux-x64@0.18.17:
-    resolution: {integrity: sha512-QM50vJ/y+8I60qEmFxMoxIx4de03pGo2HwxdBeFd4nMh364X6TIBZ6VQ5UQmPbQWUVWHWws5MmJXlHAXvJEmpQ==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [linux]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/linux-x64@0.18.20:
     resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
     engines: {node: '>=12'}
@@ -3806,15 +3978,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/netbsd-x64@0.18.17:
-    resolution: {integrity: sha512-/jGlhWR7Sj9JPZHzXyyMZ1RFMkNPjC6QIAan0sDOtIo2TYk3tZn5UDrkE0XgsTQCxWTTOcMPf9p6Rh2hXtl5TQ==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [netbsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/netbsd-x64@0.18.20:
     resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
     engines: {node: '>=12'}
@@ -3832,15 +3995,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/openbsd-x64@0.18.17:
-    resolution: {integrity: sha512-rSEeYaGgyGGf4qZM2NonMhMOP/5EHp4u9ehFiBrg7stH6BYEEjlkVREuDEcQ0LfIl53OXLxNbfuIj7mr5m29TA==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [openbsd]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/openbsd-x64@0.18.20:
     resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
     engines: {node: '>=12'}
@@ -3858,15 +4012,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/sunos-x64@0.18.17:
-    resolution: {integrity: sha512-Y7ZBbkLqlSgn4+zot4KUNYst0bFoO68tRgI6mY2FIM+b7ZbyNVtNbDP5y8qlu4/knZZ73fgJDlXID+ohY5zt5g==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [sunos]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/sunos-x64@0.18.20:
     resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
     engines: {node: '>=12'}
@@ -3884,15 +4029,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/win32-arm64@0.18.17:
-    resolution: {integrity: sha512-bwPmTJsEQcbZk26oYpc4c/8PvTY3J5/QK8jM19DVlEsAB41M39aWovWoHtNm78sd6ip6prilxeHosPADXtEJFw==}
-    engines: {node: '>=12'}
-    cpu: [arm64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/win32-arm64@0.18.20:
     resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
     engines: {node: '>=12'}
@@ -3910,15 +4046,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/win32-ia32@0.18.17:
-    resolution: {integrity: sha512-H/XaPtPKli2MhW+3CQueo6Ni3Avggi6hP/YvgkEe1aSaxw+AeO8MFjq8DlgfTd9Iz4Yih3QCZI6YLMoyccnPRg==}
-    engines: {node: '>=12'}
-    cpu: [ia32]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/win32-ia32@0.18.20:
     resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
     engines: {node: '>=12'}
@@ -3936,15 +4063,6 @@ packages:
     requiresBuild: true
     optional: true
 
-  /@esbuild/win32-x64@0.18.17:
-    resolution: {integrity: sha512-fGEb8f2BSA3CW7riJVurug65ACLuQAzKq0SSqkY2b2yHHH0MzDfbLyKIGzHwOI/gkHcxM/leuSW6D5w/LMNitA==}
-    engines: {node: '>=12'}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    dev: true
-    optional: true
-
   /@esbuild/win32-x64@0.18.20:
     resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
     engines: {node: '>=12'}
@@ -3972,13 +4090,13 @@ packages:
       eslint-visitor-keys: 3.4.3
     dev: true
 
-  /@eslint-community/eslint-utils@4.4.0(eslint@8.54.0):
+  /@eslint-community/eslint-utils@4.4.0(eslint@8.55.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.54.0
+      eslint: 8.55.0
       eslint-visitor-keys: 3.4.3
     dev: true
 
@@ -3992,7 +4110,24 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dependencies:
       ajv: 6.12.6
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
+      espree: 9.6.1
+      globals: 13.19.0
+      ignore: 5.2.4
+      import-fresh: 3.3.0
+      js-yaml: 4.1.0
+      minimatch: 3.1.2
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@eslint/eslintrc@2.1.4:
+    resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      ajv: 6.12.6
+      debug: 4.3.4(supports-color@5.5.0)
       espree: 9.6.1
       globals: 13.19.0
       ignore: 5.2.4
@@ -4009,8 +4144,8 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
-  /@eslint/js@8.54.0:
-    resolution: {integrity: sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==}
+  /@eslint/js@8.55.0:
+    resolution: {integrity: sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
@@ -4258,7 +4393,7 @@ packages:
     engines: {node: '>=10.10.0'}
     dependencies:
       '@humanwhocodes/object-schema': 2.0.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
@@ -4309,7 +4444,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
@@ -4330,14 +4465,14 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       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.10.0)
+      jest-config: 29.7.0(@types/node@20.10.3)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -4372,7 +4507,7 @@ packages:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       jest-mock: 29.7.0
     dev: true
 
@@ -4399,7 +4534,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -4432,7 +4567,7 @@ packages:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.18
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       chalk: 4.1.2
       collect-v8-coverage: 1.0.1
       exit: 0.1.2
@@ -4526,7 +4661,7 @@ packages:
     dependencies:
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       '@types/yargs': 16.0.5
       chalk: 4.1.2
     dev: true
@@ -4538,12 +4673,12 @@ packages:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       '@types/yargs': 17.0.19
       chalk: 4.1.2
     dev: true
 
-  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.2)(vite@5.0.2):
+  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.2)(vite@5.0.5):
     resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==}
     peerDependencies:
       typescript: '>= 4.3.x'
@@ -4557,7 +4692,7 @@ packages:
       magic-string: 0.27.0
       react-docgen-typescript: 2.2.2(typescript@5.3.2)
       typescript: 5.3.2
-      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
     dev: true
 
   /@jridgewell/gen-mapping@0.3.2:
@@ -4646,24 +4781,24 @@ packages:
       react: 18.2.0
     dev: true
 
-  /@microsoft/api-extractor-model@7.28.2(@types/node@20.10.0):
+  /@microsoft/api-extractor-model@7.28.2(@types/node@20.10.3):
     resolution: {integrity: sha512-vkojrM2fo3q4n4oPh4uUZdjJ2DxQ2+RnDQL/xhTWSRUNPF6P4QyrvY357HBxbnltKcYu+nNNolVqc6TIGQ73Ig==}
     dependencies:
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.61.0(@types/node@20.10.0)
+      '@rushstack/node-core-library': 3.61.0(@types/node@20.10.3)
     transitivePeerDependencies:
       - '@types/node'
     dev: true
 
-  /@microsoft/api-extractor@7.38.3(@types/node@20.10.0):
+  /@microsoft/api-extractor@7.38.3(@types/node@20.10.3):
     resolution: {integrity: sha512-xt9iYyC5f39281j77JTA9C3ISJpW1XWkCcnw+2vM78CPnro6KhPfwQdPDfwS5JCPNuq0grm8cMdPUOPvrchDWw==}
     hasBin: true
     dependencies:
-      '@microsoft/api-extractor-model': 7.28.2(@types/node@20.10.0)
+      '@microsoft/api-extractor-model': 7.28.2(@types/node@20.10.3)
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.61.0(@types/node@20.10.0)
+      '@rushstack/node-core-library': 3.61.0(@types/node@20.10.3)
       '@rushstack/rig-package': 0.5.1
       '@rushstack/ts-command-line': 4.17.1
       colors: 1.2.5
@@ -4766,7 +4901,7 @@ packages:
       '@open-draft/until': 1.0.3
       '@types/debug': 4.1.7
       '@xmldom/xmldom': 0.8.6
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       headers-polyfill: 3.2.5
       outvariant: 1.4.0
       strict-event-emitter: 0.2.8
@@ -4852,11 +4987,6 @@ packages:
       tslib: 2.6.2
     dev: false
 
-  /@nicolo-ribaudo/semver-v6@6.3.3:
-    resolution: {integrity: sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==}
-    hasBin: true
-    dev: true
-
   /@nodelib/fs.scandir@2.1.5:
     resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
     engines: {node: '>= 8'}
@@ -5511,7 +5641,7 @@ packages:
       '@babel/runtime': 7.23.2
     dev: true
 
-  /@rollup/plugin-alias@5.1.0(rollup@4.6.0):
+  /@rollup/plugin-alias@5.1.0(rollup@4.6.1):
     resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5520,11 +5650,11 @@ packages:
       rollup:
         optional: true
     dependencies:
-      rollup: 4.6.0
+      rollup: 4.6.1
       slash: 4.0.0
     dev: false
 
-  /@rollup/plugin-json@6.0.1(rollup@4.6.0):
+  /@rollup/plugin-json@6.0.1(rollup@4.6.1):
     resolution: {integrity: sha512-RgVfl5hWMkxN1h/uZj8FVESvPuBJ/uf6ly6GTj0GONnkfoBN5KC0MSz+PN2OLDgYXMhtG0mWpTrkiOjoxAIevw==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5533,11 +5663,11 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.0.5(rollup@4.6.0)
-      rollup: 4.6.0
+      '@rollup/pluginutils': 5.0.5(rollup@4.6.1)
+      rollup: 4.6.1
     dev: false
 
-  /@rollup/plugin-replace@5.0.5(rollup@4.6.0):
+  /@rollup/plugin-replace@5.0.5(rollup@4.6.1):
     resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5546,12 +5676,12 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.0.5(rollup@4.6.0)
+      '@rollup/pluginutils': 5.0.5(rollup@4.6.1)
       magic-string: 0.30.5
-      rollup: 4.6.0
+      rollup: 4.6.1
     dev: false
 
-  /@rollup/pluginutils@5.0.5(rollup@4.6.0):
+  /@rollup/pluginutils@5.0.5(rollup@4.6.1):
     resolution: {integrity: sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5563,93 +5693,93 @@ packages:
       '@types/estree': 1.0.5
       estree-walker: 2.0.2
       picomatch: 2.3.1
-      rollup: 4.6.0
+      rollup: 4.6.1
 
-  /@rollup/rollup-android-arm-eabi@4.6.0:
-    resolution: {integrity: sha512-keHkkWAe7OtdALGoutLY3utvthkGF+Y17ws9LYT8pxMBYXaCoH/8dXS2uzo6e8+sEhY7y/zi5RFo22Dy2lFpDw==}
+  /@rollup/rollup-android-arm-eabi@4.6.1:
+    resolution: {integrity: sha512-0WQ0ouLejaUCRsL93GD4uft3rOmB8qoQMU05Kb8CmMtMBe7XUDLAltxVZI1q6byNqEtU7N1ZX1Vw5lIpgulLQA==}
     cpu: [arm]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-android-arm64@4.6.0:
-    resolution: {integrity: sha512-y3Kt+34smKQNWilicPbBz/MXEY7QwDzMFNgwEWeYiOhUt9MTWKjHqe3EVkXwT2fR7izOvHpDWZ0o2IyD9SWX7A==}
+  /@rollup/rollup-android-arm64@4.6.1:
+    resolution: {integrity: sha512-1TKm25Rn20vr5aTGGZqo6E4mzPicCUD79k17EgTLAsXc1zysyi4xXKACfUbwyANEPAEIxkzwue6JZ+stYzWUTA==}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-arm64@4.6.0:
-    resolution: {integrity: sha512-oLzzxcUIHltHxOCmaXl+pkIlU+uhSxef5HfntW7RsLh1eHm+vJzjD9Oo4oUKso4YuP4PpbFJNlZjJuOrxo8dPg==}
+  /@rollup/rollup-darwin-arm64@4.6.1:
+    resolution: {integrity: sha512-cEXJQY/ZqMACb+nxzDeX9IPLAg7S94xouJJCNVE5BJM8JUEP4HeTF+ti3cmxWeSJo+5D+o8Tc0UAWUkfENdeyw==}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-x64@4.6.0:
-    resolution: {integrity: sha512-+ANnmjkcOBaV25n0+M0Bere3roeVAnwlKW65qagtuAfIxXF9YxUneRyAn/RDcIdRa7QrjRNJL3jR7T43ObGe8Q==}
+  /@rollup/rollup-darwin-x64@4.6.1:
+    resolution: {integrity: sha512-LoSU9Xu56isrkV2jLldcKspJ7sSXmZWkAxg7sW/RfF7GS4F5/v4EiqKSMCFbZtDu2Nc1gxxFdQdKwkKS4rwxNg==}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm-gnueabihf@4.6.0:
-    resolution: {integrity: sha512-tBTSIkjSVUyrekddpkAqKOosnj1Fc0ZY0rJL2bIEWPKqlEQk0paORL9pUIlt7lcGJi3LzMIlUGXvtNi1Z6MOCQ==}
+  /@rollup/rollup-linux-arm-gnueabihf@4.6.1:
+    resolution: {integrity: sha512-EfI3hzYAy5vFNDqpXsNxXcgRDcFHUWSx5nnRSCKwXuQlI5J9dD84g2Usw81n3FLBNsGCegKGwwTVsSKK9cooSQ==}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-gnu@4.6.0:
-    resolution: {integrity: sha512-Ed8uJI3kM11de9S0j67wAV07JUNhbAqIrDYhQBrQW42jGopgheyk/cdcshgGO4fW5Wjq97COCY/BHogdGvKVNQ==}
+  /@rollup/rollup-linux-arm64-gnu@4.6.1:
+    resolution: {integrity: sha512-9lhc4UZstsegbNLhH0Zu6TqvDfmhGzuCWtcTFXY10VjLLUe4Mr0Ye2L3rrtHaDd/J5+tFMEuo5LTCSCMXWfUKw==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-musl@4.6.0:
-    resolution: {integrity: sha512-mZoNQ/qK4D7SSY8v6kEsAAyDgznzLLuSFCA3aBHZTmf3HP/dW4tNLTtWh9+LfyO0Z1aUn+ecpT7IQ3WtIg3ViQ==}
+  /@rollup/rollup-linux-arm64-musl@4.6.1:
+    resolution: {integrity: sha512-FfoOK1yP5ksX3wwZ4Zk1NgyGHZyuRhf99j64I5oEmirV8EFT7+OhUZEnP+x17lcP/QHJNWGsoJwrz4PJ9fBEXw==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-gnu@4.6.0:
-    resolution: {integrity: sha512-rouezFHpwCqdEXsqAfNsTgSWO0FoZ5hKv5p+TGO5KFhyN/dvYXNMqMolOb8BkyKcPqjYRBeT+Z6V3aM26rPaYg==}
+  /@rollup/rollup-linux-x64-gnu@4.6.1:
+    resolution: {integrity: sha512-DNGZvZDO5YF7jN5fX8ZqmGLjZEXIJRdJEdTFMhiyXqyXubBa0WVLDWSNlQ5JR2PNgDbEV1VQowhVRUh+74D+RA==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-musl@4.6.0:
-    resolution: {integrity: sha512-Bbm+fyn3S6u51urfj3YnqBXg5vI2jQPncRRELaucmhBVyZkbWClQ1fEsRmdnCPpQOQfkpg9gZArvtMVkOMsh1w==}
+  /@rollup/rollup-linux-x64-musl@4.6.1:
+    resolution: {integrity: sha512-RkJVNVRM+piYy87HrKmhbexCHg3A6Z6MU0W9GHnJwBQNBeyhCJG9KDce4SAMdicQnpURggSvtbGo9xAWOfSvIQ==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-arm64-msvc@4.6.0:
-    resolution: {integrity: sha512-+MRMcyx9L2kTrTUzYmR61+XVsliMG4odFb5UmqtiT8xOfEicfYAGEuF/D1Pww1+uZkYhBqAHpvju7VN+GnC3ng==}
+  /@rollup/rollup-win32-arm64-msvc@4.6.1:
+    resolution: {integrity: sha512-v2FVT6xfnnmTe3W9bJXl6r5KwJglMK/iRlkKiIFfO6ysKs0rDgz7Cwwf3tjldxQUrHL9INT/1r4VA0n9L/F1vQ==}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-ia32-msvc@4.6.0:
-    resolution: {integrity: sha512-rxfeE6K6s/Xl2HGeK6cO8SiQq3k/3BYpw7cfhW5Bk2euXNEpuzi2cc7llxx1si1QgwfjNtdRNTGqdBzGlFZGFw==}
+  /@rollup/rollup-win32-ia32-msvc@4.6.1:
+    resolution: {integrity: sha512-YEeOjxRyEjqcWphH9dyLbzgkF8wZSKAKUkldRY6dgNR5oKs2LZazqGB41cWJ4Iqqcy9/zqYgmzBkRoVz3Q9MLw==}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-x64-msvc@4.6.0:
-    resolution: {integrity: sha512-QqmCsydHS172Y0Kc13bkMXvipbJSvzeglBncJG3LsYJSiPlxYACz7MmJBs4A8l1oU+jfhYEIC/+AUSlvjmiX/g==}
+  /@rollup/rollup-win32-x64-msvc@4.6.1:
+    resolution: {integrity: sha512-0zfTlFAIhgz8V2G8STq8toAjsYYA6eci1hnXuyOTUFnymrtJwnS6uGKiv3v5UrPZkBlamLvrLV2iiaeqCKzb0A==}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rushstack/node-core-library@3.61.0(@types/node@20.10.0):
+  /@rushstack/node-core-library@3.61.0(@types/node@20.10.3):
     resolution: {integrity: sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==}
     peerDependencies:
       '@types/node': '*'
@@ -5657,7 +5787,7 @@ packages:
       '@types/node':
         optional: true
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       colors: 1.2.5
       fs-extra: 7.0.1
       import-lazy: 4.0.0
@@ -6244,121 +6374,61 @@ packages:
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
     dev: false
 
-  /@storybook/addon-actions@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-v3yL6Eq/jCiXfA24JjRdbEQUuorms6tmrywaKcd1tAy4Ftgof0KHB4tTcTyiajrI5bh6PVJoRBkE8IDqmNAHkA==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
+  /@storybook/addon-actions@7.6.3:
+    resolution: {integrity: sha512-f4HXteYE8IJXztAK+ab5heSjXWNWvyIAU63T3Fqe3zmqONwCerUKY54Op+RkAZc/R6aALTxvGRKAH2ff8g2vjQ==}
     dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.5.3
+      '@storybook/core-events': 7.6.3
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.3
+      '@types/uuid': 9.0.7
       dequal: 2.0.3
-      lodash: 4.17.21
       polished: 4.2.2
-      prop-types: 15.8.1
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-      react-inspector: 6.0.1(react@18.2.0)
-      telejson: 7.2.0
-      ts-dedent: 2.2.0
       uuid: 9.0.1
-    transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
     dev: true
 
-  /@storybook/addon-backgrounds@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-UCOVd4UNIL5FRiwi9nyiWFocn/7ewwS6bIWnq66AaHg/sv92YwsPmgQJn0DMBGDOvUAWpiHdVsZNOTX6nvw4gA==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
+  /@storybook/addon-backgrounds@7.6.3:
+    resolution: {integrity: sha512-ZZFNf8FBYBsuXvXdVk3sBgxJTn6s0HznuEE9OmAA7tMsLEDlUiWS9LEvjX2jX5K0kWivHTkJDTXV0NcLL1vWAg==}
     dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.5.3
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.3
       memoizerific: 1.11.3
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
       ts-dedent: 2.2.0
-    transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
     dev: true
 
-  /@storybook/addon-controls@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-KEuU4X5Xr6cJI9xrzOUVGEmUf1iHPfK7cj0GACKv0GElsdIsQryv+OZ7gRnvmNax/e2hm2t9cJcFxB24/p6rVg==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
+  /@storybook/addon-controls@7.6.3(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-xsM3z+CY1YOPqrcCldQLoon947fbd/o3gSO7hM3NwKiw/2WikExPO3VM4R2oi4W4PvnhkSOIO+ZDRuSs1yFmOg==}
     dependencies:
-      '@storybook/blocks': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/client-logger': 7.5.3
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-common': 7.5.3
-      '@storybook/core-events': 7.5.3
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/node-logger': 7.5.3
-      '@storybook/preview-api': 7.5.3
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.3
+      '@storybook/blocks': 7.6.3(react-dom@18.2.0)(react@18.2.0)
       lodash: 4.17.21
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - '@types/react'
       - '@types/react-dom'
       - encoding
+      - react
+      - react-dom
       - supports-color
     dev: true
 
-  /@storybook/addon-docs@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-JVQ6iCXKESij/SbE4Wq47dkSSgBRulvA8SUf8NWL5m9qpiHrg0lPSERHfoTLiB5uC/JwF0OKIlhxoWl+zCmtYg==}
+  /@storybook/addon-docs@7.6.3(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-2Ts+3EFg9ehkQdbjBWnCH1SE0BdyCLN6hO2N030tGxi0Vko9t9O7NLj5qdBwxLcEzb/XzL4zWukzfU17pktQwA==}
     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.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/client-logger': 7.5.3
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/csf-plugin': 7.5.3
-      '@storybook/csf-tools': 7.5.3
+      '@storybook/blocks': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/client-logger': 7.6.3
+      '@storybook/components': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/csf-plugin': 7.6.3
+      '@storybook/csf-tools': 7.6.3
       '@storybook/global': 5.0.0
       '@storybook/mdx2-csf': 1.0.0
-      '@storybook/node-logger': 7.5.3
-      '@storybook/postinstall': 7.5.3
-      '@storybook/preview-api': 7.5.3
-      '@storybook/react-dom-shim': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.3
+      '@storybook/node-logger': 7.6.3
+      '@storybook/postinstall': 7.6.3
+      '@storybook/preview-api': 7.6.3
+      '@storybook/react-dom-shim': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/theming': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.3
       fs-extra: 11.1.1
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -6372,25 +6442,25 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-essentials@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-PYj6swEI4nEzIbOTyHJB8u3K8ABYKoaW8XB5emMwsnrzB/TN7auHVhze2bQ/+ax5wyPKZpArPjxbWlSHtSws+A==}
+  /@storybook/addon-essentials@7.6.3(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-bpbt5O0wcB83VLZg8QMXut+8g+7EF4iuevpwiynN9mbpQFvG49c6SE6T2eFJKTvVb4zszyfcNA0Opne2G83wZw==}
     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.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-backgrounds': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-controls': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-docs': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-highlight': 7.5.3
-      '@storybook/addon-measure': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-outline': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-toolbars': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-viewport': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-common': 7.5.3
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/node-logger': 7.5.3
-      '@storybook/preview-api': 7.5.3
+      '@storybook/addon-actions': 7.6.3
+      '@storybook/addon-backgrounds': 7.6.3
+      '@storybook/addon-controls': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-docs': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-highlight': 7.6.3
+      '@storybook/addon-measure': 7.6.3
+      '@storybook/addon-outline': 7.6.3
+      '@storybook/addon-toolbars': 7.6.3
+      '@storybook/addon-viewport': 7.6.3
+      '@storybook/core-common': 7.6.3
+      '@storybook/manager-api': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/node-logger': 7.6.3
+      '@storybook/preview-api': 7.6.3
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       ts-dedent: 2.2.0
@@ -6401,233 +6471,95 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-highlight@7.5.3:
-    resolution: {integrity: sha512-jb+aNRhj+tFK7EqqTlNCjGkTrkWqWHGdD1ubgnj29v8XhRuCR9YboPS+306KYwBEkuF4kNCHZofLiEBPf6nCJg==}
+  /@storybook/addon-highlight@7.6.3:
+    resolution: {integrity: sha512-Z9AJ05XCTzFZPAxQSkQf9/Hazf5/QQI0jYSsvKqt7Vk+03q5727oD9KcIY5IHPYqQqN9fHExQh1eyqY8AnS8mg==}
     dependencies:
-      '@storybook/core-events': 7.5.3
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.5.3
     dev: true
 
-  /@storybook/addon-interactions@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-gD3cU8sYSM/mdbA9ooYIb4c689JkDsJbZ17vfYJ5RjNkSmqKehybdpZOfkj27sVIyFtmscSi75t+pzK4Pv4rZw==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
+  /@storybook/addon-interactions@7.6.3:
+    resolution: {integrity: sha512-Gm2UJvQC8xs9KIbVZQegTLT3VBsEZIRsXy3htNqWjSdoJZK5M4/YJ3jB247CA/Jc+Mkj7d5SlJe+bCGEzjKTbw==}
     dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-common': 7.5.3
-      '@storybook/core-events': 7.5.3
       '@storybook/global': 5.0.0
-      '@storybook/instrumenter': 7.5.3
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.3
+      '@storybook/types': 7.6.3
       jest-mock: 27.5.1
       polished: 4.2.2
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-      ts-dedent: 2.2.0
-    transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
-      - encoding
-      - supports-color
-    dev: true
-
-  /@storybook/addon-links@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-NcigW0HX8AllZ/KJ4u1KMiK30QvjqtC+zApI6Yc3tTaa6+BldbLv06fEgHgMY0yC8R+Ly9mUN7S1HiU7LQ7Qxg==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
-    dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/core-events': 7.5.3
-      '@storybook/csf': 0.1.0
-      '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/router': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.3
-      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.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-fun9BqUTGXgcMpcbX9wUowGDkjCL8oKasZbjp/MvGM3vPTM6HQdwzHTLJGPBnmJ1xK92NhwFRs0BrQX6uF1yrg==}
+  /@storybook/addon-links@7.6.3(react@18.2.0):
+    resolution: {integrity: sha512-dUIf6Y0nckxZfVQvQSqcthaycRxy69dCJLo3aORrOPL8NvGz3v1bK0AUded5wv8vnOVxfSx/Zqu7MyFr9xyjOA==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
     peerDependenciesMeta:
       react:
         optional: true
-      react-dom:
-        optional: true
     dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.5.3
+      '@storybook/csf': 0.1.2
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/types': 7.5.3
       react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
+      ts-dedent: 2.2.0
+    dev: true
+
+  /@storybook/addon-measure@7.6.3:
+    resolution: {integrity: sha512-DqxADof04ktA5GSA8XnckYGdVYyC4oN8vfKSGcPzpcKrJ2uVr0BXbcyJAEcJAshEJimmpA6nH5TxabXDFBZgPQ==}
+    dependencies:
+      '@storybook/global': 5.0.0
       tiny-invariant: 1.3.1
-    transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
     dev: true
 
-  /@storybook/addon-outline@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-c9vCi1SCGrtWr8qaOu/1GNWlrlrpl2lg4F9r+xtYf/KopenI3jSMz0YeTfmepZGAl+6Yc2Ywhm60jgpQ6SKciA==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
+  /@storybook/addon-outline@7.6.3:
+    resolution: {integrity: sha512-M7d2tcqBBl+mPBUS6Nrwis50QYSCcmT/uKamud7CnlIWsMH/5GZFfAzGSLY5ETfiGsSFYssOwrXLOV4y0enu2g==}
     dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.5.3
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/types': 7.5.3
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
       ts-dedent: 2.2.0
-    transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
     dev: true
 
-  /@storybook/addon-storysource@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-3t/ypTGyPSxRwOJkAtYw+HKZWUzIayx1xZTU6iip7gTMWz56mhZrsBBcNFXOnQkRBAsQHi1Nhr3Ef1JRguiMMQ==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
+  /@storybook/addon-storysource@7.6.3:
+    resolution: {integrity: sha512-iiuOMdCSxl4NHRkZmpvEqKvs3NLM+tv0vUDeWS9BACmDEsSgqlo8UQChueplm9GWyYLxTarMkGk9yxanGcLS3w==}
     dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/router': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/source-loader': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
       estraverse: 5.3.0
-      prop-types: 15.8.1
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-      react-syntax-highlighter: 15.5.0(react@18.2.0)
       tiny-invariant: 1.3.1
-    transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
     dev: true
 
-  /@storybook/addon-toolbars@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-KdLr4sGMJzhtjNTNE2ocfu58yOHHUyZ/cI3BTp7a0gq9YbUpHmC3XTNr26/yOYYrdjkiMD26XusJUjXe+/V2xw==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
-    dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-    transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
+  /@storybook/addon-toolbars@7.6.3:
+    resolution: {integrity: sha512-8GpwOt0J5yLrJhTr9/h0a/LTDjt49FhdvdxiVWLlLMrjIXSIc7j193ZgoHfnlwVhJS5zojcjB+HmRw/E+AneoA==}
     dev: true
 
-  /@storybook/addon-viewport@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-gT2XX0NNBrzSs1nrxadl6LnvcwgN7z2R0LzTK8/hxvx4D0EnXrV3feXLzjewr8ZYjzfEeSpO+W+bQTVNm3fNsg==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
-    peerDependenciesMeta:
-      react:
-        optional: true
-      react-dom:
-        optional: true
+  /@storybook/addon-viewport@7.6.3:
+    resolution: {integrity: sha512-I9FQxHi4W7RUyZut4NziYa+nkBCpD1k2YpEDE5IwSC3lqQpDzFZN89eNWQtZ38tIU4c90jL3L1k69IHvANGHsA==}
     dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.5.3
-      '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
       memoizerific: 1.11.3
-      prop-types: 15.8.1
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
+    dev: true
+
+  /@storybook/addons@7.6.3(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-UuqMOcr+x+4ogtn889wGgVAFxswHjN8ybD6ZTuRatLXA3YC2aywKGL1Xz0bmrTfv5WTlNxOPuwoTIhIH/P073w==}
+    dependencies:
+      '@storybook/manager-api': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.6.3
+      '@storybook/types': 7.6.3
     transitivePeerDependencies:
-      - '@types/react'
-      - '@types/react-dom'
+      - react
+      - react-dom
     dev: true
 
-  /@storybook/addons@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-1JDndMZ/Pju4YJ4aXegeF0O6BVT19c+Gu7WOlsD0aHbmAsPK5qH9QvcpR04nby6VrVZYtBOEJhGsWtAytzLVZw==}
+  /@storybook/blocks@7.6.3(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-EyjyNNCZMcV9UnBSujwduiq+F1VLVX/f16fTTPqqZOHigyfrG5LoEYC6dwOC4yO/xfWY+h3qJ51yiugMxVl0Vg==}
     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.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/types': 7.5.3
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-    dev: true
-
-  /@storybook/blocks@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-Z8yF820v78clQWkwG5OA5qugbQn7rtutq9XCsd03NDB+IEfDaTFQAZG8gs62ZX2ZaXAJsqJSr/mL9oURzXto2A==}
-    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.5.3
-      '@storybook/client-logger': 7.5.3
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.5.3
-      '@storybook/csf': 0.1.0
-      '@storybook/docs-tools': 7.5.3
+      '@storybook/channels': 7.6.3
+      '@storybook/client-logger': 7.6.3
+      '@storybook/components': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 7.6.3
+      '@storybook/csf': 0.1.2
+      '@storybook/docs-tools': 7.6.3
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.3
+      '@storybook/manager-api': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.6.3
+      '@storybook/theming': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.3
       '@types/lodash': 4.14.191
       color-convert: 2.0.1
       dequal: 2.0.3
@@ -6649,19 +6581,19 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-manager@7.5.3:
-    resolution: {integrity: sha512-uf4Vyj8ofHaq94m065SMvFKak1XrrxgI83VZAxc2QjiPcbRwcVOZd+wcKFdZydqqA6FlBDdJrU+k9INA4Qkfcw==}
+  /@storybook/builder-manager@7.6.3:
+    resolution: {integrity: sha512-eLMjRudhiRsg7kgbmPcCkuVf2ut753fbiVR7REtqIYwq5vu8UeNOzt1vA6HgfsUj77/7+1zG8/zeyBv/5nY5mw==}
     dependencies:
       '@fal-works/esbuild-plugin-global-externals': 2.1.2
-      '@storybook/core-common': 7.5.3
-      '@storybook/manager': 7.5.3
-      '@storybook/node-logger': 7.5.3
+      '@storybook/core-common': 7.6.3
+      '@storybook/manager': 7.6.3
+      '@storybook/node-logger': 7.6.3
       '@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)
+      '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.18.20)
       browser-assert: 1.2.1
       ejs: 3.1.9
-      esbuild: 0.18.17
+      esbuild: 0.18.20
       esbuild-plugin-alias: 0.2.1
       express: 4.18.2
       find-cache-dir: 3.3.2
@@ -6673,8 +6605,8 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-vite@7.5.3(typescript@5.3.2)(vite@5.0.2):
-    resolution: {integrity: sha512-c104V3O75OCVnfZj0Jr70V09g0KSbPGvQK2Zh31omXGvakG8XrhWolYxkmjOcForJmAqsXnKs/nw3F75Gp853g==}
+  /@storybook/builder-vite@7.6.3(typescript@5.3.2)(vite@5.0.5):
+    resolution: {integrity: sha512-r/G/6wdwgbhMiMZ8Z+Js8VLjIo7a0DG5SxJorTHSWNi0+jyM+3Qlg3Xj96I8yL4gfTIKWVScHqHprhjRb2E64g==}
     peerDependencies:
       '@preact/preset-vite': '*'
       typescript: '>= 4.3.x'
@@ -6688,14 +6620,14 @@ packages:
       vite-plugin-glimmerx:
         optional: true
     dependencies:
-      '@storybook/channels': 7.5.3
-      '@storybook/client-logger': 7.5.3
-      '@storybook/core-common': 7.5.3
-      '@storybook/csf-plugin': 7.5.3
-      '@storybook/node-logger': 7.5.3
-      '@storybook/preview': 7.5.3
-      '@storybook/preview-api': 7.5.3
-      '@storybook/types': 7.5.3
+      '@storybook/channels': 7.6.3
+      '@storybook/client-logger': 7.6.3
+      '@storybook/core-common': 7.6.3
+      '@storybook/csf-plugin': 7.6.3
+      '@storybook/node-logger': 7.6.3
+      '@storybook/preview': 7.6.3
+      '@storybook/preview-api': 7.6.3
+      '@storybook/types': 7.6.3
       '@types/find-cache-dir': 3.2.1
       browser-assert: 1.2.1
       es-module-lexer: 0.9.3
@@ -6705,7 +6637,7 @@ packages:
       magic-string: 0.30.5
       rollup: 3.29.4
       typescript: 5.3.2
-      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -6722,22 +6654,33 @@ packages:
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/cli@7.5.3:
-    resolution: {integrity: sha512-XysHSnknZTAcTbQ0bQsbfv5J8ifHpOBsmXjk1HCA05E9WGGrn9JrQRCfpDUQJ6O6UWq0bpMqzP8gFLWXFE7hug==}
+  /@storybook/channels@7.6.3:
+    resolution: {integrity: sha512-o9J0TBbFon16tUlU5V6kJgzAlsloJcS1cTHWqh3VWczohbRm+X1PLNUihJ7Q8kBWXAuuJkgBu7RQH7Ib46WyYg==}
+    dependencies:
+      '@storybook/client-logger': 7.6.3
+      '@storybook/core-events': 7.6.3
+      '@storybook/global': 5.0.0
+      qs: 6.11.1
+      telejson: 7.2.0
+      tiny-invariant: 1.3.1
+    dev: true
+
+  /@storybook/cli@7.6.3:
+    resolution: {integrity: sha512-OuYnzZlAtpGm4rDgI4ZWkNbAkddutlJh6KmoU9oQAlZP0zmETyJN8REUWjj5T9Z1AS2iXjCMGlFVd4TC8nKocw==}
     hasBin: true
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/preset-env': 7.22.9(@babel/core@7.22.11)
-      '@babel/types': 7.22.17
+      '@babel/core': 7.23.5
+      '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
+      '@babel/types': 7.23.3
       '@ndelangen/get-tarball': 3.0.7
-      '@storybook/codemod': 7.5.3
-      '@storybook/core-common': 7.5.3
-      '@storybook/core-events': 7.5.3
-      '@storybook/core-server': 7.5.3
-      '@storybook/csf-tools': 7.5.3
-      '@storybook/node-logger': 7.5.3
-      '@storybook/telemetry': 7.5.3
-      '@storybook/types': 7.5.3
+      '@storybook/codemod': 7.6.3
+      '@storybook/core-common': 7.6.3
+      '@storybook/core-events': 7.6.3
+      '@storybook/core-server': 7.6.3
+      '@storybook/csf-tools': 7.6.3
+      '@storybook/node-logger': 7.6.3
+      '@storybook/telemetry': 7.6.3
+      '@storybook/types': 7.6.3
       '@types/semver': 7.5.6
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
@@ -6754,7 +6697,7 @@ packages:
       get-port: 5.1.1
       giget: 1.1.2
       globby: 11.1.0
-      jscodeshift: 0.14.0(@babel/preset-env@7.22.9)
+      jscodeshift: 0.15.1(@babel/preset-env@7.23.5)
       leven: 3.1.0
       ora: 5.4.1
       prettier: 2.8.8
@@ -6780,20 +6723,26 @@ packages:
       '@storybook/global': 5.0.0
     dev: true
 
-  /@storybook/codemod@7.5.3:
-    resolution: {integrity: sha512-gzycFdqnF4drUjfzMTrLNHqi2jkw1lDeACUzQdug5uWxynZKAvMTHAgU0q9wvoYRR9Xhq8PhfKtXtYCCj2Er4Q==}
+  /@storybook/client-logger@7.6.3:
+    resolution: {integrity: sha512-BpsCnefrBFdxD6ukMjAblm1D6zB4U5HR1I85VWw6LOqZrfzA6l/1uBxItz0XG96HTjngbvAabWf5k7ZFCx5UCg==}
     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.5.3
-      '@storybook/node-logger': 7.5.3
-      '@storybook/types': 7.5.3
+      '@storybook/global': 5.0.0
+    dev: true
+
+  /@storybook/codemod@7.6.3:
+    resolution: {integrity: sha512-A1i8+WQfNg3frVcwSyu8E/cDkCu88Sw7JiGNnq9iW2e2oWMr2awpCDgXp8WfTK+HiDb2X1Pq5y/GmUlh3qr77Q==}
+    dependencies:
+      '@babel/core': 7.23.5
+      '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
+      '@babel/types': 7.23.3
+      '@storybook/csf': 0.1.2
+      '@storybook/csf-tools': 7.6.3
+      '@storybook/node-logger': 7.6.3
+      '@storybook/types': 7.6.3
       '@types/cross-spawn': 6.0.2
       cross-spawn: 7.0.3
       globby: 11.1.0
-      jscodeshift: 0.14.0(@babel/preset-env@7.22.9)
+      jscodeshift: 0.15.1(@babel/preset-env@7.23.5)
       lodash: 4.17.21
       prettier: 2.8.8
       recast: 0.23.1
@@ -6824,31 +6773,54 @@ packages:
       - '@types/react-dom'
     dev: true
 
-  /@storybook/core-client@7.5.3:
-    resolution: {integrity: sha512-sIviDytbhos02TVXxU8XLymzty7IAtLs5e16hv49JSdBp47iBajRaNBmBj/l+sgTH+3M+R6gP8yGFMsZSCnU2g==}
+  /@storybook/components@7.6.3(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-UNV0WoUo+W0huOLvoEMuqRN/VB4p0CNswrXN1mi/oGWvAFJ8idu63lSuV4uQ/LKxAZ6v3Kpdd+oK/o+OeOoL6w==}
+    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.5.3
-      '@storybook/preview-api': 7.5.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.6.3
+      '@storybook/csf': 0.1.2
+      '@storybook/global': 5.0.0
+      '@storybook/theming': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.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/core-common@7.5.3:
-    resolution: {integrity: sha512-WGMwjtVUxUzFwQz7Mgs0gLuNebIGNV55dCdZgurx2/y6QOkJ2v8D0b3iL+xKMV4B5Nwoc2DsM418Y+Hy3UQd+w==}
+  /@storybook/core-client@7.6.3:
+    resolution: {integrity: sha512-RM0Svlajddl8PP4Vq7LK8T22sFefNcTDgo82iRPZzGz0oH8LT0oXGFanj2Nkn0jruOBFClkiJ7EcwrbGJZHELg==}
     dependencies:
-      '@storybook/core-events': 7.5.3
-      '@storybook/node-logger': 7.5.3
-      '@storybook/types': 7.5.3
+      '@storybook/client-logger': 7.6.3
+      '@storybook/preview-api': 7.6.3
+    dev: true
+
+  /@storybook/core-common@7.6.3:
+    resolution: {integrity: sha512-/ZE4BEyGwBHCQCOo681GyBKF4IqCiwVV/ZJCHTMTHFCPLJT2r+Qwv4tnI7xt1kwflOlbBlG6B6CvAqTjjVw/Ew==}
+    dependencies:
+      '@storybook/core-events': 7.6.3
+      '@storybook/node-logger': 7.6.3
+      '@storybook/types': 7.6.3
       '@types/find-cache-dir': 3.2.1
       '@types/node': 18.17.15
       '@types/node-fetch': 2.6.4
       '@types/pretty-hrtime': 1.0.1
       chalk: 4.1.2
-      esbuild: 0.18.17
-      esbuild-register: 3.5.0(esbuild@0.18.17)
+      esbuild: 0.18.20
+      esbuild-register: 3.5.0(esbuild@0.18.20)
       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
+      glob: 10.3.10
       handlebars: 4.7.7
       lazy-universal-dotenv: 4.0.0
       node-fetch: 2.7.0
@@ -6868,24 +6840,30 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/core-server@7.5.3:
-    resolution: {integrity: sha512-Gmq1w7ulN/VIeTDboNcb6GNM+S8T0SqhJUqeoHzn0vLGnzxeuYRJ0V3ZJhGZiJfSmCNqYAjC8QUBf6uU1gLipw==}
+  /@storybook/core-events@7.6.3:
+    resolution: {integrity: sha512-Vu3JX1mjtR8AX84lyqWsi2s2lhD997jKRWVznI3wx+UpTk8t7TTMLFk2rGYJRjaornhrqwvLYpnmtxRSxW9BOQ==}
+    dependencies:
+      ts-dedent: 2.2.0
+    dev: true
+
+  /@storybook/core-server@7.6.3:
+    resolution: {integrity: sha512-IsM24MmiFmtZeyqoijiExpIPkJNBaWQg9ttkkHS6iYwf3yFNBpYVbvuX2OpT7FDdiF3uTl0R8IvfnJR58tHD7w==}
     dependencies:
       '@aw-web-design/x-default-browser': 1.4.126
       '@discoveryjs/json-ext': 0.5.7
-      '@storybook/builder-manager': 7.5.3
-      '@storybook/channels': 7.5.3
-      '@storybook/core-common': 7.5.3
-      '@storybook/core-events': 7.5.3
-      '@storybook/csf': 0.1.0
-      '@storybook/csf-tools': 7.5.3
+      '@storybook/builder-manager': 7.6.3
+      '@storybook/channels': 7.6.3
+      '@storybook/core-common': 7.6.3
+      '@storybook/core-events': 7.6.3
+      '@storybook/csf': 0.1.2
+      '@storybook/csf-tools': 7.6.3
       '@storybook/docs-mdx': 0.1.0
       '@storybook/global': 5.0.0
-      '@storybook/manager': 7.5.3
-      '@storybook/node-logger': 7.5.3
-      '@storybook/preview-api': 7.5.3
-      '@storybook/telemetry': 7.5.3
-      '@storybook/types': 7.5.3
+      '@storybook/manager': 7.6.3
+      '@storybook/node-logger': 7.6.3
+      '@storybook/preview-api': 7.6.3
+      '@storybook/telemetry': 7.6.3
+      '@storybook/types': 7.6.3
       '@types/detect-port': 1.3.2
       '@types/node': 18.17.15
       '@types/pretty-hrtime': 1.0.1
@@ -6919,24 +6897,24 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/csf-plugin@7.5.3:
-    resolution: {integrity: sha512-yQ3S/IOT08Y7XTnlc3SPkrJKZ6Xld6liAlHn+ddjge4oZa0hUqwYLb+piXUhFMfL6Ij65cj4hu3vMbw89azIhg==}
+  /@storybook/csf-plugin@7.6.3:
+    resolution: {integrity: sha512-8bMYPsWw2tv+fqZ5H436l4x1KLSB6gIcm6snsjyF916yCHG6WcWm+EI6+wNUoySEtrQY2AiwFJqE37wI5OUJFg==}
     dependencies:
-      '@storybook/csf-tools': 7.5.3
-      unplugin: 1.4.0
+      '@storybook/csf-tools': 7.6.3
+      unplugin: 1.5.1
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/csf-tools@7.5.3:
-    resolution: {integrity: sha512-676C3ISn7FQJKjb3DBWXhjGN2OQEv4s71dx+5D0TlmswDCOOGS8dYFjP8wVx51+mAIE8CROAw7vLHLtVKU7SwQ==}
+  /@storybook/csf-tools@7.6.3:
+    resolution: {integrity: sha512-Zi3pg2pg88/mvBKewkfWhFUR1J4uYpHI5fSjOE+J/FeZObX/DIE7r+wJxZ0UBGyrk0Wy7Jajlb2uSP56Y0i19w==}
     dependencies:
-      '@babel/generator': 7.22.10
-      '@babel/parser': 7.23.0
-      '@babel/traverse': 7.22.11
-      '@babel/types': 7.22.17
-      '@storybook/csf': 0.1.0
-      '@storybook/types': 7.5.3
+      '@babel/generator': 7.23.5
+      '@babel/parser': 7.23.3
+      '@babel/traverse': 7.23.5
+      '@babel/types': 7.23.3
+      '@storybook/csf': 0.1.2
+      '@storybook/types': 7.6.3
       fs-extra: 11.1.1
       recast: 0.23.1
       ts-dedent: 2.2.0
@@ -6950,17 +6928,24 @@ packages:
       type-fest: 2.19.0
     dev: true
 
+  /@storybook/csf@0.1.2:
+    resolution: {integrity: sha512-ePrvE/pS1vsKR9Xr+o+YwdqNgHUyXvg+1Xjx0h9LrVx7Zq4zNe06pd63F5EvzTbCbJsHj7GHr9tkiaqm7U8WRA==}
+    dependencies:
+      type-fest: 2.19.0
+    dev: true
+
   /@storybook/docs-mdx@0.1.0:
     resolution: {integrity: sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==}
     dev: true
 
-  /@storybook/docs-tools@7.5.3:
-    resolution: {integrity: sha512-f20EUQlwamcSPrOFn42fj9gpkZIDNCZkC3N19yGzLYiE4UMyaYQgRl18oLvqd3M6aBm6UW6SCoIIgeaOViBSqg==}
+  /@storybook/docs-tools@7.6.3:
+    resolution: {integrity: sha512-6MtirRCQIkBeQ3bksPignZgUuFmjWqcFleTEN6vrNEfbCzMlMvuBGfm9tl4sS3n8ATWmKGj87DcJepPOT3FB4A==}
     dependencies:
-      '@storybook/core-common': 7.5.3
-      '@storybook/preview-api': 7.5.3
-      '@storybook/types': 7.5.3
+      '@storybook/core-common': 7.6.3
+      '@storybook/preview-api': 7.6.3
+      '@storybook/types': 7.6.3
       '@types/doctrine': 0.0.3
+      assert: 2.1.0
       doctrine: 3.0.0
       lodash: 4.17.21
     transitivePeerDependencies:
@@ -6978,16 +6963,6 @@ packages:
     resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==}
     dev: true
 
-  /@storybook/instrumenter@7.5.3:
-    resolution: {integrity: sha512-p6b+/6ohTCKxWn00bXT8KBqVjXUOxeILnJtLlG83USLQCpI+XVkpmK57HYuydqEwy/1XjG+4S4ntPk9VVz3u7w==}
-    dependencies:
-      '@storybook/channels': 7.5.3
-      '@storybook/client-logger': 7.5.3
-      '@storybook/core-events': 7.5.3
-      '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.5.3
-    dev: true
-
   /@storybook/jest@0.2.3(vitest@0.34.6):
     resolution: {integrity: sha512-ov5izrmbAFObzKeh9AOC5MlmFxAcf0o5i6YFGae9sDx6DGh6alXsRM+chIbucVkUwVHVlSzdfbLDEFGY/ShaYw==}
     dependencies:
@@ -7001,56 +6976,54 @@ packages:
       - vitest
     dev: true
 
-  /@storybook/manager-api@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-d8mVLr/5BEG4bAS2ZeqYTy/aX4jPEpZHdcLaWoB4mAM+PAL9wcWsirUyApKtDVYLITJf/hd8bb2Dm2ok6E45gA==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+  /@storybook/manager-api@7.6.3(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-soDH7GZuukkhYRGzlw4jhCm5EzjfkuIAtb37/DFplqxuVbvlyJEVzkMUM2KQO7kq0/8GlWPiZ5mn56wagYyhKQ==}
     dependencies:
-      '@storybook/channels': 7.5.3
-      '@storybook/client-logger': 7.5.3
-      '@storybook/core-events': 7.5.3
-      '@storybook/csf': 0.1.0
+      '@storybook/channels': 7.6.3
+      '@storybook/client-logger': 7.6.3
+      '@storybook/core-events': 7.6.3
+      '@storybook/csf': 0.1.2
       '@storybook/global': 5.0.0
-      '@storybook/router': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.3
+      '@storybook/router': 7.6.3
+      '@storybook/theming': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.3
       dequal: 2.0.3
       lodash: 4.17.21
       memoizerific: 1.11.3
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
       semver: 7.5.4
       store2: 2.14.2
       telejson: 7.2.0
       ts-dedent: 2.2.0
+    transitivePeerDependencies:
+      - react
+      - react-dom
     dev: true
 
-  /@storybook/manager@7.5.3:
-    resolution: {integrity: sha512-3ZZrHYcXWAQXpDQZBvKyScGgQaAaBc63i+KC2mXqzTdXuJhVDUiylvqLRprBnrEprgePQLFrxGC2JSHUwH7dqg==}
+  /@storybook/manager@7.6.3:
+    resolution: {integrity: sha512-6eMaogHANCSVV2zLPt4Q7fp8RT+AdlOe6IR0583AuqpepcFzj33iGNYABk2rmXAlkD0WzoLcC4H5mouU0fduLA==}
     dev: true
 
   /@storybook/mdx2-csf@1.0.0:
     resolution: {integrity: sha512-dBAnEL4HfxxJmv7LdEYUoZlQbWj9APZNIbOaq0tgF8XkxiIbzqvgB0jhL/9UOrysSDbQWBiCRTu2wOVxedGfmw==}
     dev: true
 
-  /@storybook/node-logger@7.5.3:
-    resolution: {integrity: sha512-7ZZDw/q3hakBj1FngsBjaHNIBguYAWojp7R1fFTvwkeunCi21EUzZjRBcqp10kB6BP3/NLX32bIQknsCWD76rQ==}
+  /@storybook/node-logger@7.6.3:
+    resolution: {integrity: sha512-7yL0CMHuh1DhpUAoKCU0a53DvxBpkUom9SX5RaC1G2A9BK/B3XcHtDPAC0uyUwNCKLJMZo9QtmJspvxWjR0LtA==}
     dev: true
 
-  /@storybook/postinstall@7.5.3:
-    resolution: {integrity: sha512-r+H3xGMu2A9yOSsygc3bDFhku8wpOZF3SqO19B7eAML12viHwUtYfyGL74svw4TMcKukyQ+KPn5QsSG+4bjZMg==}
+  /@storybook/postinstall@7.6.3:
+    resolution: {integrity: sha512-WpgdpJpY6rionluxjFZLbKiSDjvQJ5cPgufjvBRuXTsnVOsH3JNRWnPdkQkJLT9uTUMoNcyBMxbjYkK3vU6wSg==}
     dev: true
 
-  /@storybook/preview-api@7.5.3:
-    resolution: {integrity: sha512-LNmEf7oBRnZ1wG3bQ+P+TO29+NN5pSDJiAA6FabZBrtIVm+psc2lxBCDQvFYyAFzQSlt60toGKNW8+RfFNdR5Q==}
+  /@storybook/preview-api@7.6.3:
+    resolution: {integrity: sha512-uPaK7yLE1P++F+IOb/1j9pgdCwfMYZrUPHogF/Mf9r4cfEjDCcIeKgGMcsbU1KnkzNQQGPh8JRzRr/iYnLjswg==}
     dependencies:
-      '@storybook/channels': 7.5.3
-      '@storybook/client-logger': 7.5.3
-      '@storybook/core-events': 7.5.3
-      '@storybook/csf': 0.1.0
+      '@storybook/channels': 7.6.3
+      '@storybook/client-logger': 7.6.3
+      '@storybook/core-events': 7.6.3
+      '@storybook/csf': 0.1.2
       '@storybook/global': 5.0.0
-      '@storybook/types': 7.5.3
+      '@storybook/types': 7.6.3
       '@types/qs': 6.9.7
       dequal: 2.0.3
       lodash: 4.17.21
@@ -7061,12 +7034,12 @@ packages:
       util-deprecate: 1.0.2
     dev: true
 
-  /@storybook/preview@7.5.3:
-    resolution: {integrity: sha512-Hf90NlLaSrdMZXPOHDCMPjTywVrQKK0e5CtzqWx/ZQz91JDINxJD+sGj2wZU+wuBtQcTtlsXc9OewlJ+9ETwIw==}
+  /@storybook/preview@7.6.3:
+    resolution: {integrity: sha512-obSmKN8arWSHuLbCDM1H0lTVRMvAP/l7vOi6TQtFi6TxBz9MRCJA3Ugc0PZrbDADVZP+cp0ZJA0JQtAm+SqNAA==}
     dev: true
 
-  /@storybook/react-dom-shim@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-9aNcKdhoP36jMrcXgfzE9jVg/SpqPpWnUJM70upYoZXytG2wQSPtawLHHyC6kycvTzwncyfF3rwUnOFBB8zmig==}
+  /@storybook/react-dom-shim@7.6.3(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-UtaEaTQB27aBsAmn5IfAYkX2xl4wWWXkoAO/jUtx86FQ/r85FG0zxh/rac6IgzjYUqzjJtjIeLdeciG/48hMMA==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -7075,24 +7048,24 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/react-vite@7.5.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.6.0)(typescript@5.3.2)(vite@5.0.2):
-    resolution: {integrity: sha512-ArPyHgiPbT5YvcyK4xK/DfqBOpn4R4/EP3kfIGhx8QKJyOtxPEYFdkLIZ5xu3KnPX7/z7GT+4a6Rb+8sk9gliA==}
+  /@storybook/react-vite@7.6.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.6.1)(typescript@5.3.2)(vite@5.0.5):
+    resolution: {integrity: sha512-sPrNJbnThmxsSeNj6vyG9pCCnnYzyiS+f7DVy2qeQrXvEuCYiQc503bavE3BKLxqjZQ3SkbhPsiEHcaw3I9x7A==}
     engines: {node: '>=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 || ^5.0.0
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.2)(vite@5.0.2)
-      '@rollup/pluginutils': 5.0.5(rollup@4.6.0)
-      '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@5.0.2)
-      '@storybook/react': 7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)
-      '@vitejs/plugin-react': 3.1.0(vite@5.0.2)
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.2)(vite@5.0.5)
+      '@rollup/pluginutils': 5.0.5(rollup@4.6.1)
+      '@storybook/builder-vite': 7.6.3(typescript@5.3.2)(vite@5.0.5)
+      '@storybook/react': 7.6.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)
+      '@vitejs/plugin-react': 3.1.0(vite@5.0.5)
       magic-string: 0.30.5
       react: 18.2.0
-      react-docgen: 6.0.4
+      react-docgen: 7.0.1
       react-dom: 18.2.0(react@18.2.0)
-      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - encoding
@@ -7102,8 +7075,8 @@ packages:
       - vite-plugin-glimmerx
     dev: true
 
-  /@storybook/react@7.5.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-dZILdM36xMFDjdmmy421G5X+sOIncB2qF3IPTooniG1i1Z6v/dVNo57ovdID9lDTNa+AWr2fLB9hANiISMqmjQ==}
+  /@storybook/react@7.6.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-W+530cC0BAU+yBc7NzSXYWR3e8Lo5qMsmFJjWYK7zGW/YZGhSG3mjhF9pDzNM+cMtHvUS6qf5PJPQM8jePpPhg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -7113,13 +7086,13 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/core-client': 7.5.3
-      '@storybook/docs-tools': 7.5.3
+      '@storybook/client-logger': 7.6.3
+      '@storybook/core-client': 7.6.3
+      '@storybook/docs-tools': 7.6.3
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.5.3
-      '@storybook/react-dom-shim': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.3
+      '@storybook/preview-api': 7.6.3
+      '@storybook/react-dom-shim': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.3
       '@types/escodegen': 0.0.6
       '@types/estree': 0.0.51
       '@types/node': 18.17.15
@@ -7142,40 +7115,20 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/router@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-/iNYCFore7R5n6eFHbBYoB0P2/sybTVpA+uXTNUd3UEt7Ro6CEslTaFTEiH2RVQwOkceBp/NpyWon74xZuXhMg==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+  /@storybook/router@7.6.3:
+    resolution: {integrity: sha512-NZfhJqsXYca9mZCL/LGx6FmZDbrxX2S4ImW7Tqdtcc/sSlZ0BpCDkNUTesCA287cmoKMhXZRh/+bU+C2h2a+bw==}
     dependencies:
-      '@storybook/client-logger': 7.5.3
+      '@storybook/client-logger': 7.6.3
       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.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-cPOi/hxdblTPw2AvwlR0VaGmELXyLGGE8KQDGcOnZtU6PpyyCwV4ZJiJz1qjiHdjfHlJMJG33JiyHfvsngT8jQ==}
-    peerDependencies:
-      react: ^16.8.0 || ^17.0.0 || ^18.0.0
-      react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+  /@storybook/telemetry@7.6.3:
+    resolution: {integrity: sha512-NDCZWhVIUI3M6Lq4M/HPOvZqDXqANDNbI3kyHr4pFGoVaCUXuDPokL9wR+CZcMvATkJ1gHrfLPBdcRq6Biw3Iw==}
     dependencies:
-      '@storybook/csf': 0.1.0
-      '@storybook/types': 7.5.3
-      estraverse: 5.3.0
-      lodash: 4.17.21
-      prettier: 2.8.8
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-    dev: true
-
-  /@storybook/telemetry@7.5.3:
-    resolution: {integrity: sha512-X6alII3o0jCb5xALuw+qcWmvyrbhlkmPeNZ6ZQXknOfB4DkwponFdWN5y6W7yGvr01xa5QBepJRV79isl97d8g==}
-    dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/core-common': 7.5.3
-      '@storybook/csf-tools': 7.5.3
+      '@storybook/client-logger': 7.6.3
+      '@storybook/core-common': 7.6.3
+      '@storybook/csf-tools': 7.6.3
       chalk: 4.1.2
       detect-package-manager: 2.0.1
       fetch-retry: 5.0.4
@@ -7208,6 +7161,20 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
+  /@storybook/theming@7.6.3(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-9ToNU2LM6a2kVBjOXitXEeEOuMurVLhn+uaZO1dJjv8NGnJVYiLwNPwrLsImiUD8/XXNuil972aanBR6+Aj9jw==}
+    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.6.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/types@7.5.3:
     resolution: {integrity: sha512-iu5W0Kdd6nysN5CPkY4GRl+0BpxRTdSfBIJak7mb6xCIHSB5t1tw4BOuqMQ5EgpikRY3MWJ4gY647QkWBX3MNQ==}
     dependencies:
@@ -7217,22 +7184,27 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.5.3(@vue/compiler-core@3.3.9)(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)(vite@5.0.2)(vue@3.3.9):
-    resolution: {integrity: sha512-gkNwDDn2AKthAtaoPrHb0+2gi33UluxpfSq/M5COoMEVFphj6y/jyDa+OEYlceXgnD8g2xvX4/yv2TbTNDzmcQ==}
+  /@storybook/types@7.6.3:
+    resolution: {integrity: sha512-vj9Jzg5eR52l8O9512QywbQpNdo67Z6BQWR8QoZRcG+/Bhzt08YI8IZMPQLFMKzcmWDPK0blQ4GfyKDYplMjPA==}
+    dependencies:
+      '@storybook/channels': 7.6.3
+      '@types/babel__core': 7.20.0
+      '@types/express': 4.17.17
+      file-system-cache: 2.3.0
+    dev: true
+
+  /@storybook/vue3-vite@7.6.3(@vue/compiler-core@3.3.9)(typescript@5.3.2)(vite@5.0.5)(vue@3.3.9):
+    resolution: {integrity: sha512-7NupDZn7FNm8SJSfTWOj1mrfyjGNII7bpnlvt1iH88L8UECmIh/gbDoBNi1XcGmPK09nrx3RnFdsxN5PVpeLPQ==}
     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 || ^5.0.0
     dependencies:
-      '@storybook/builder-vite': 7.5.3(typescript@5.3.2)(vite@5.0.2)
-      '@storybook/core-server': 7.5.3
-      '@storybook/vue3': 7.5.3(@vue/compiler-core@3.3.9)(vue@3.3.9)
-      '@vitejs/plugin-vue': 4.5.0(vite@5.0.2)(vue@3.3.9)
+      '@storybook/builder-vite': 7.6.3(typescript@5.3.2)(vite@5.0.5)
+      '@storybook/core-server': 7.6.3
+      '@storybook/vue3': 7.6.3(@vue/compiler-core@3.3.9)(vue@3.3.9)
+      '@vitejs/plugin-vue': 4.5.1(vite@5.0.5)(vue@3.3.9)
       magic-string: 0.30.5
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
       vue-docgen-api: 4.64.1(vue@3.3.9)
     transitivePeerDependencies:
       - '@preact/preset-vite'
@@ -7246,42 +7218,42 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.5.3(@vue/compiler-core@3.3.9)(vue@3.3.9):
-    resolution: {integrity: sha512-JaxtOl3UD9YhPrOqHuKtpqHMnFril3sBUxx/no2yM/mZYmNpAVd/C6PFM839WCay1mAywPuUoebJvmwWxWijkw==}
+  /@storybook/vue3@7.6.3(@vue/compiler-core@3.3.9)(vue@3.3.9):
+    resolution: {integrity: sha512-XoJOMcRngxOblXGFiaz5oqsCbTy+TVoWrOodjizYfJcMXIceKZrPz+bNI4x6HL5koXrx1HWy5VN187QGqy+RJg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
       '@vue/compiler-core': ^3.0.0
       vue: ^3.0.0
     dependencies:
-      '@storybook/core-client': 7.5.3
-      '@storybook/docs-tools': 7.5.3
+      '@storybook/core-client': 7.6.3
+      '@storybook/docs-tools': 7.6.3
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.5.3
-      '@storybook/types': 7.5.3
+      '@storybook/preview-api': 7.6.3
+      '@storybook/types': 7.6.3
       '@vue/compiler-core': 3.3.9
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.3.9(typescript@5.3.2)
-      vue-component-type-helpers: 1.8.22
+      vue-component-type-helpers: 1.8.24
     transitivePeerDependencies:
       - encoding
       - supports-color
     dev: true
 
-  /@swc/cli@0.1.63(@swc/core@1.3.99)(chokidar@3.5.3):
+  /@swc/cli@0.1.63(@swc/core@1.3.100)(chokidar@3.5.3):
     resolution: {integrity: sha512-EM9oxxHzmmsprYRbGqsS2M4M/Gr5Gkcl0ROYYIdlUyTkhOiX822EQiRCpPCwdutdnzH2GyaTN7wc6i0Y+CKd3A==}
     engines: {node: '>= 12.13'}
     hasBin: true
     peerDependencies:
       '@swc/core': ^1.2.66
-      chokidar: ^3.5.1
+      chokidar: 3.5.3
     peerDependenciesMeta:
       chokidar:
         optional: true
     dependencies:
       '@mole-inc/bin-wrapper': 8.0.1
-      '@swc/core': 1.3.99
+      '@swc/core': 1.3.100
       chokidar: 3.5.3
       commander: 7.2.0
       fast-glob: 3.3.2
@@ -7301,6 +7273,14 @@ packages:
     dev: false
     optional: true
 
+  /@swc/core-darwin-arm64@1.3.100:
+    resolution: {integrity: sha512-XVWFsKe6ei+SsDbwmsuRkYck1SXRpO60Hioa4hoLwR8fxbA9eVp6enZtMxzVVMBi8ej5seZ4HZQeAWepbukiBw==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    optional: true
+
   /@swc/core-darwin-arm64@1.3.56:
     resolution: {integrity: sha512-DZcu7BzDaLEdWHabz9DRTP0yEBLqkrWmskFcD5BX0lGAvoIvE4duMnAqi5F2B3X7630QioHRCYFoRw2WkeE3Cw==}
     engines: {node: '>=10'}
@@ -7310,10 +7290,10 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-darwin-arm64@1.3.99:
-    resolution: {integrity: sha512-Qj7Jct68q3ZKeuJrjPx7k8SxzWN6PqLh+VFxzA+KwLDpQDPzOlKRZwkIMzuFjLhITO4RHgSnXoDk/Syz0ZeN+Q==}
+  /@swc/core-darwin-x64@1.3.100:
+    resolution: {integrity: sha512-KF/MXrnH1nakm1wbt4XV8FS7kvqD9TGmVxeJ0U4bbvxXMvzeYUurzg3AJUTXYmXDhH/VXOYJE5N5RkwZZPs5iA==}
     engines: {node: '>=10'}
-    cpu: [arm64]
+    cpu: [x64]
     os: [darwin]
     requiresBuild: true
     optional: true
@@ -7327,14 +7307,6 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-darwin-x64@1.3.99:
-    resolution: {integrity: sha512-wR7m9QVJjgiBu1PSOHy7s66uJPa45Kf9bZExXUL+JAa9OQxt5y+XVzr+n+F045VXQOwdGWplgPnWjgbUUHEVyw==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [darwin]
-    requiresBuild: true
-    optional: true
-
   /@swc/core-freebsd-x64@1.3.11:
     resolution: {integrity: sha512-02uqYktPp6WmZfZ2Crc/yIVOcgANtjo8ciHcT7yLHvz7v+S7gx1I2tyNGUFtTX5hcR2IFNGrL8Yj4DvpTABFHg==}
     engines: {node: '>=10'}
@@ -7355,6 +7327,14 @@ packages:
     dev: false
     optional: true
 
+  /@swc/core-linux-arm64-gnu@1.3.100:
+    resolution: {integrity: sha512-p8hikNnAEJrw5vHCtKiFT4hdlQxk1V7vqPmvUDgL/qe2menQDK/i12tbz7/3BEQ4UqUPnvwpmVn2d19RdEMNxw==}
+    engines: {node: '>=10'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
   /@swc/core-linux-arm64-gnu@1.3.56:
     resolution: {integrity: sha512-GzsUy/4egJ4cMlxbM+Ub7AMi5CKAc+pxBxrh8MUPQbyStW8jGgnQsJouTnGy0LHawtdEnsCOl6PcO6OgvktXuQ==}
     engines: {node: '>=10'}
@@ -7364,8 +7344,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-arm64-gnu@1.3.99:
-    resolution: {integrity: sha512-gcGv1l5t0DScEONmw5OhdVmEI/o49HCe9Ik38zzH0NtDkc+PDYaCcXU5rvfZP2qJFaAAr8cua8iJcOunOSLmnA==}
+  /@swc/core-linux-arm64-musl@1.3.100:
+    resolution: {integrity: sha512-BWx/0EeY89WC4q3AaIaBSGfQxkYxIlS3mX19dwy2FWJs/O+fMvF9oLk/CyJPOZzbp+1DjGeeoGFuDYpiNO91JA==}
     engines: {node: '>=10'}
     cpu: [arm64]
     os: [linux]
@@ -7381,10 +7361,10 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-arm64-musl@1.3.99:
-    resolution: {integrity: sha512-XL1/eUsTO8BiKsWq9i3iWh7H99iPO61+9HYiWVKhSavknfj4Plbn+XyajDpxsauln5o8t+BRGitymtnAWJM4UQ==}
+  /@swc/core-linux-x64-gnu@1.3.100:
+    resolution: {integrity: sha512-XUdGu3dxAkjsahLYnm8WijPfKebo+jHgHphDxaW0ovI6sTdmEGFDew7QzKZRlbYL2jRkUuuKuDGvD6lO5frmhA==}
     engines: {node: '>=10'}
-    cpu: [arm64]
+    cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
@@ -7398,8 +7378,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-x64-gnu@1.3.99:
-    resolution: {integrity: sha512-fGrXYE6DbTfGNIGQmBefYxSk3rp/1lgbD0nVg4rl4mfFRQPi7CgGhrrqSuqZ/ezXInUIgoCyvYGWFSwjLXt/Qg==}
+  /@swc/core-linux-x64-musl@1.3.100:
+    resolution: {integrity: sha512-PhoXKf+f0OaNW/GCuXjJ0/KfK9EJX7z2gko+7nVnEA0p3aaPtbP6cq1Ubbl6CMoPL+Ci3gZ7nYumDqXNc3CtLQ==}
     engines: {node: '>=10'}
     cpu: [x64]
     os: [linux]
@@ -7415,11 +7395,11 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-linux-x64-musl@1.3.99:
-    resolution: {integrity: sha512-kvgZp/mqf3IJ806gUOL6gN6VU15+DfzM1Zv4Udn8GqgXiUAvbQehrtruid4Snn5pZTLj4PEpSCBbxgxK1jbssA==}
+  /@swc/core-win32-arm64-msvc@1.3.100:
+    resolution: {integrity: sha512-PwLADZN6F9cXn4Jw52FeP/MCLVHm8vwouZZSOoOScDtihjY495SSjdPnlosMaRSR4wJQssGwiD/4MbpgQPqbAw==}
     engines: {node: '>=10'}
-    cpu: [x64]
-    os: [linux]
+    cpu: [arm64]
+    os: [win32]
     requiresBuild: true
     optional: true
 
@@ -7432,10 +7412,10 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-arm64-msvc@1.3.99:
-    resolution: {integrity: sha512-yt8RtZ4W/QgFF+JUemOUQAkVW58cCST7mbfKFZ1v16w3pl3NcWd9OrtppFIXpbjU1rrUX2zp2R7HZZzZ2Zk/aQ==}
+  /@swc/core-win32-ia32-msvc@1.3.100:
+    resolution: {integrity: sha512-0f6nicKSLlDKlyPRl2JEmkpBV4aeDfRQg6n8mPqgL7bliZIcDahG0ej+HxgNjZfS3e0yjDxsNRa6sAqWU2Z60A==}
     engines: {node: '>=10'}
-    cpu: [arm64]
+    cpu: [ia32]
     os: [win32]
     requiresBuild: true
     optional: true
@@ -7449,10 +7429,10 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-ia32-msvc@1.3.99:
-    resolution: {integrity: sha512-62p5fWnOJR/rlbmbUIpQEVRconICy5KDScWVuJg1v3GPLBrmacjphyHiJC1mp6dYvvoEWCk/77c/jcQwlXrDXw==}
+  /@swc/core-win32-x64-msvc@1.3.100:
+    resolution: {integrity: sha512-b7J0rPoMkRTa3XyUGt8PwCaIBuYWsL2DqbirrQKRESzgCvif5iNpqaM6kjIjI/5y5q1Ycv564CB51YDpiS8EtQ==}
     engines: {node: '>=10'}
-    cpu: [ia32]
+    cpu: [x64]
     os: [win32]
     requiresBuild: true
     optional: true
@@ -7466,16 +7446,8 @@ packages:
     dev: false
     optional: true
 
-  /@swc/core-win32-x64-msvc@1.3.99:
-    resolution: {integrity: sha512-PdppWhkoS45VGdMBxvClVgF1hVjqamtvYd82Gab1i4IV45OSym2KinoDCKE1b6j3LwBLOn2J9fvChGSgGfDCHQ==}
-    engines: {node: '>=10'}
-    cpu: [x64]
-    os: [win32]
-    requiresBuild: true
-    optional: true
-
-  /@swc/core@1.3.99:
-    resolution: {integrity: sha512-8O996RfuPC4ieb4zbYMfbyCU9k4gSOpyCNnr7qBQ+o7IEmh8JCV6B8wwu+fT/Om/6Lp34KJe1IpJ/24axKS6TQ==}
+  /@swc/core@1.3.100:
+    resolution: {integrity: sha512-7dKgTyxJjlrMwFZYb1auj3Xq0D8ZBe+5oeIgfMlRU05doXZypYJe0LAk0yjj3WdbwYzpF+T1PLxwTWizI0pckw==}
     engines: {node: '>=10'}
     requiresBuild: true
     peerDependencies:
@@ -7487,27 +7459,27 @@ packages:
       '@swc/counter': 0.1.1
       '@swc/types': 0.1.5
     optionalDependencies:
-      '@swc/core-darwin-arm64': 1.3.99
-      '@swc/core-darwin-x64': 1.3.99
-      '@swc/core-linux-arm64-gnu': 1.3.99
-      '@swc/core-linux-arm64-musl': 1.3.99
-      '@swc/core-linux-x64-gnu': 1.3.99
-      '@swc/core-linux-x64-musl': 1.3.99
-      '@swc/core-win32-arm64-msvc': 1.3.99
-      '@swc/core-win32-ia32-msvc': 1.3.99
-      '@swc/core-win32-x64-msvc': 1.3.99
+      '@swc/core-darwin-arm64': 1.3.100
+      '@swc/core-darwin-x64': 1.3.100
+      '@swc/core-linux-arm64-gnu': 1.3.100
+      '@swc/core-linux-arm64-musl': 1.3.100
+      '@swc/core-linux-x64-gnu': 1.3.100
+      '@swc/core-linux-x64-musl': 1.3.100
+      '@swc/core-win32-arm64-msvc': 1.3.100
+      '@swc/core-win32-ia32-msvc': 1.3.100
+      '@swc/core-win32-x64-msvc': 1.3.100
 
   /@swc/counter@0.1.1:
     resolution: {integrity: sha512-xVRaR4u9hcYjFvcSg71Lz5Bo4//CyjAAfMxa7UsaDSYxAshflUkVJWiyVWrfxC59z2kP1IzI4/1BEpnhI9o3Mw==}
 
-  /@swc/jest@0.2.29(@swc/core@1.3.99):
+  /@swc/jest@0.2.29(@swc/core@1.3.100):
     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.99
+      '@swc/core': 1.3.100
       jsonc-parser: 3.2.0
     dev: true
 
@@ -7770,7 +7742,7 @@ packages:
   /@types/accepts@1.3.7:
     resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/archiver@6.0.2:
@@ -7790,8 +7762,8 @@ packages:
   /@types/babel__core@7.20.0:
     resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==}
     dependencies:
-      '@babel/parser': 7.23.0
-      '@babel/types': 7.22.17
+      '@babel/parser': 7.23.3
+      '@babel/types': 7.23.3
       '@types/babel__generator': 7.6.4
       '@types/babel__template': 7.4.1
       '@types/babel__traverse': 7.20.0
@@ -7800,20 +7772,20 @@ packages:
   /@types/babel__generator@7.6.4:
     resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==}
     dependencies:
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
     dev: true
 
   /@types/babel__template@7.4.1:
     resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==}
     dependencies:
-      '@babel/parser': 7.23.0
-      '@babel/types': 7.22.17
+      '@babel/parser': 7.23.3
+      '@babel/types': 7.23.3
     dev: true
 
   /@types/babel__traverse@7.20.0:
     resolution: {integrity: sha512-TBOjqAGf0hmaqRwpii5LLkJLg7c6OMm4nHLmpsUxwk9bBHtoTC6dAHdVWdGv4TBxj2CZOZY8Xfq8WmfoVi7n4Q==}
     dependencies:
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
     dev: true
 
   /@types/bcryptjs@2.4.6:
@@ -7824,7 +7796,7 @@ packages:
     resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
     dependencies:
       '@types/connect': 3.4.35
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/braces@3.0.1:
@@ -7836,7 +7808,7 @@ packages:
     dependencies:
       '@types/http-cache-semantics': 4.0.1
       '@types/keyv': 3.1.4
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       '@types/responselike': 1.0.0
     dev: false
 
@@ -7869,7 +7841,7 @@ packages:
   /@types/connect@3.4.35:
     resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/content-disposition@0.5.8:
@@ -7883,7 +7855,7 @@ packages:
   /@types/cross-spawn@6.0.2:
     resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/debug@4.1.7:
@@ -7904,8 +7876,8 @@ packages:
     resolution: {integrity: sha512-w5jZ0ee+HaPOaX25X2/2oGR/7rgAQSYII7X7pp0m9KgBfMP7uKfMfTvcpl5Dj+eDBbpxKGiqE+flqDr6XTd2RA==}
     dev: true
 
-  /@types/doctrine@0.0.6:
-    resolution: {integrity: sha512-KlEqPtaNBHBJ2/fVA4yLdD0Tc8zw34pKU4K5SHBIEwtLJ8xxumIC1xeG+4S+/9qhVj2MqC7O3Ld8WvDG4HqlgA==}
+  /@types/doctrine@0.0.9:
+    resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==}
     dev: true
 
   /@types/ejs@3.1.2:
@@ -7941,7 +7913,7 @@ packages:
   /@types/express-serve-static-core@4.17.33:
     resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       '@types/qs': 6.9.7
       '@types/range-parser': 1.2.4
     dev: true
@@ -7962,26 +7934,20 @@ packages:
   /@types/fluent-ffmpeg@2.1.24:
     resolution: {integrity: sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/glob@7.2.0:
     resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/graceful-fs@4.1.6:
     resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
     dependencies:
-      '@types/node': 20.10.0
-    dev: true
-
-  /@types/hast@2.3.4:
-    resolution: {integrity: sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==}
-    dependencies:
-      '@types/unist': 2.0.6
+      '@types/node': 20.10.3
     dev: true
 
   /@types/http-cache-semantics@4.0.1:
@@ -7990,7 +7956,7 @@ packages:
   /@types/http-link-header@1.0.5:
     resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/istanbul-lib-coverage@2.0.4:
@@ -8034,7 +8000,7 @@ packages:
   /@types/jsdom@21.1.6:
     resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       '@types/tough-cookie': 4.0.2
       parse5: 7.1.2
     dev: true
@@ -8058,7 +8024,7 @@ packages:
   /@types/keyv@3.1.4:
     resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: false
 
   /@types/lodash@4.14.191:
@@ -8107,7 +8073,7 @@ packages:
   /@types/node-fetch@2.6.4:
     resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       form-data: 3.0.1
 
   /@types/node-fetch@3.0.3:
@@ -8120,8 +8086,8 @@ packages:
     resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==}
     dev: true
 
-  /@types/node@20.10.0:
-    resolution: {integrity: sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==}
+  /@types/node@20.10.3:
+    resolution: {integrity: sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==}
     dependencies:
       undici-types: 5.26.5
 
@@ -8134,7 +8100,7 @@ packages:
   /@types/nodemailer@6.4.14:
     resolution: {integrity: sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/normalize-package-data@2.4.1:
@@ -8151,13 +8117,13 @@ packages:
     resolution: {integrity: sha512-Ali0fUUn+zgr4Yy/pCTFbuiaiJpq7l7OQwFnxYVchNbNGIx0c4Wkcdje6WO89I91RAaYF+gVc1pOaizA4YKZmA==}
     dependencies:
       '@types/express': 4.17.17
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/oauth@0.9.4:
     resolution: {integrity: sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/offscreencanvas@2019.3.0:
@@ -8173,7 +8139,7 @@ packages:
   /@types/pg@8.10.9:
     resolution: {integrity: sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       pg-protocol: 1.6.0
       pg-types: 4.0.1
     dev: true
@@ -8197,7 +8163,7 @@ packages:
   /@types/qrcode@1.5.5:
     resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/qs@6.9.7:
@@ -8227,7 +8193,7 @@ packages:
   /@types/readdir-glob@1.1.1:
     resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/rename@1.0.7:
@@ -8241,7 +8207,7 @@ packages:
   /@types/responselike@1.0.0:
     resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: false
 
   /@types/sanitize-html@2.9.5:
@@ -8267,7 +8233,7 @@ packages:
     resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
     dependencies:
       '@types/mime': 3.0.1
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/serviceworker@0.0.67:
@@ -8277,7 +8243,7 @@ packages:
   /@types/set-cookie-parser@2.4.3:
     resolution: {integrity: sha512-7QhnH7bi+6KAhBB+Auejz1uV9DHiopZqu7LfR/5gZZTkejJV5nYeZZpgfFoE0N8aDsXuiYpfKyfyMatCwQhyTQ==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/sharp@0.32.0:
@@ -8340,13 +8306,13 @@ packages:
   /@types/vary@1.1.3:
     resolution: {integrity: sha512-XJT8/ZQCL7NUut9QDLf6l24JfAEl7bnNdgxfj50cHIpEPRJLHHDDFOAq6i+GsEmeFfH7NamhBE4c4Thtb2egWg==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/web-push@3.6.3:
     resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/webgl-ext@0.0.30:
@@ -8357,13 +8323,13 @@ packages:
   /@types/websocket@1.0.10:
     resolution: {integrity: sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/ws@8.5.10:
     resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /@types/yargs-parser@21.0.0:
@@ -8386,7 +8352,7 @@ packages:
     resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
     requiresBuild: true
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
     dev: true
     optional: true
 
@@ -8407,7 +8373,7 @@ packages:
       '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
       graphemer: 1.4.0
       ignore: 5.2.4
@@ -8419,8 +8385,8 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/eslint-plugin@6.12.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-XOpZ3IyJUIV1b15M7HVOpgQxPPF7lGXgsfcEIu3yDxFPaf/xZKt7s9QO/pbk7vpWQyVulpJbu4E5LwpZiQo4kA==}
+  /@typescript-eslint/eslint-plugin@6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
@@ -8431,13 +8397,13 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
-      '@typescript-eslint/scope-manager': 6.12.0
-      '@typescript-eslint/type-utils': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
-      '@typescript-eslint/utils': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
-      '@typescript-eslint/visitor-keys': 6.12.0
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.54.0
+      '@typescript-eslint/parser': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+      '@typescript-eslint/scope-manager': 6.13.1
+      '@typescript-eslint/type-utils': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+      '@typescript-eslint/utils': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+      '@typescript-eslint/visitor-keys': 6.13.1
+      debug: 4.3.4(supports-color@5.5.0)
+      eslint: 8.55.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -8462,15 +8428,15 @@ packages:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
       typescript: 5.3.2
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.12.0(eslint@8.54.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-s8/jNFPKPNRmXEnNXfuo1gemBdVmpQsK1pcu+QIvuNJuhFzGrpD7WjOcvDc/+uEdfzSYpNu7U/+MmbScjoQ6vg==}
+  /@typescript-eslint/parser@6.13.1(eslint@8.55.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
@@ -8479,12 +8445,12 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/scope-manager': 6.12.0
-      '@typescript-eslint/types': 6.12.0
-      '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.3.2)
-      '@typescript-eslint/visitor-keys': 6.12.0
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.54.0
+      '@typescript-eslint/scope-manager': 6.13.1
+      '@typescript-eslint/types': 6.13.1
+      '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.2)
+      '@typescript-eslint/visitor-keys': 6.13.1
+      debug: 4.3.4(supports-color@5.5.0)
+      eslint: 8.55.0
       typescript: 5.3.2
     transitivePeerDependencies:
       - supports-color
@@ -8498,12 +8464,12 @@ packages:
       '@typescript-eslint/visitor-keys': 6.11.0
     dev: true
 
-  /@typescript-eslint/scope-manager@6.12.0:
-    resolution: {integrity: sha512-5gUvjg+XdSj8pcetdL9eXJzQNTl3RD7LgUiYTl8Aabdi8hFkaGSYnaS6BLc0BGNaDH+tVzVwmKtWvu0jLgWVbw==}
+  /@typescript-eslint/scope-manager@6.13.1:
+    resolution: {integrity: sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dependencies:
-      '@typescript-eslint/types': 6.12.0
-      '@typescript-eslint/visitor-keys': 6.12.0
+      '@typescript-eslint/types': 6.13.1
+      '@typescript-eslint/visitor-keys': 6.13.1
     dev: true
 
   /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.2):
@@ -8518,7 +8484,7 @@ packages:
     dependencies:
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
       '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
       ts-api-utils: 1.0.1(typescript@5.3.2)
       typescript: 5.3.2
@@ -8526,8 +8492,8 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/type-utils@6.12.0(eslint@8.54.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-WWmRXxhm1X8Wlquj+MhsAG4dU/Blvf1xDgGaYCzfvStP2NwPQh6KBvCDbiOEvaE0filhranjIlK/2fSTVwtBng==}
+  /@typescript-eslint/type-utils@6.13.1(eslint@8.55.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
@@ -8536,10 +8502,10 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.3.2)
-      '@typescript-eslint/utils': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
-      debug: 4.3.4(supports-color@8.1.1)
-      eslint: 8.54.0
+      '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.2)
+      '@typescript-eslint/utils': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+      debug: 4.3.4(supports-color@5.5.0)
+      eslint: 8.55.0
       ts-api-utils: 1.0.1(typescript@5.3.2)
       typescript: 5.3.2
     transitivePeerDependencies:
@@ -8551,8 +8517,8 @@ packages:
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
-  /@typescript-eslint/types@6.12.0:
-    resolution: {integrity: sha512-MA16p/+WxM5JG/F3RTpRIcuOghWO30//VEOvzubM8zuOOBYXsP+IfjoCXXiIfy2Ta8FRh9+IO9QLlaFQUU+10Q==}
+  /@typescript-eslint/types@6.13.1:
+    resolution: {integrity: sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
@@ -8567,7 +8533,7 @@ packages:
     dependencies:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
@@ -8577,8 +8543,8 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/typescript-estree@6.12.0(typescript@5.3.2):
-    resolution: {integrity: sha512-vw9E2P9+3UUWzhgjyyVczLWxZ3GuQNT7QpnIY3o5OMeLO/c8oHljGc8ZpryBMIyympiAAaKgw9e5Hl9dCWFOYw==}
+  /@typescript-eslint/typescript-estree@6.13.1(typescript@5.3.2):
+    resolution: {integrity: sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       typescript: '*'
@@ -8586,9 +8552,9 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/types': 6.12.0
-      '@typescript-eslint/visitor-keys': 6.12.0
-      debug: 4.3.4(supports-color@8.1.1)
+      '@typescript-eslint/types': 6.13.1
+      '@typescript-eslint/visitor-keys': 6.13.1
+      debug: 4.3.4(supports-color@5.5.0)
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
@@ -8617,19 +8583,19 @@ packages:
       - typescript
     dev: true
 
-  /@typescript-eslint/utils@6.12.0(eslint@8.54.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-LywPm8h3tGEbgfyjYnu3dauZ0U7R60m+miXgKcZS8c7QALO9uWJdvNoP+duKTk2XMWc7/Q3d/QiCuLN9X6SWyQ==}
+  /@typescript-eslint/utils@6.13.1(eslint@8.55.0)(typescript@5.3.2):
+    resolution: {integrity: sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==}
     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.54.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.6
-      '@typescript-eslint/scope-manager': 6.12.0
-      '@typescript-eslint/types': 6.12.0
-      '@typescript-eslint/typescript-estree': 6.12.0(typescript@5.3.2)
-      eslint: 8.54.0
+      '@typescript-eslint/scope-manager': 6.13.1
+      '@typescript-eslint/types': 6.13.1
+      '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.2)
+      eslint: 8.55.0
       semver: 7.5.4
     transitivePeerDependencies:
       - supports-color
@@ -8644,11 +8610,11 @@ packages:
       eslint-visitor-keys: 3.4.3
     dev: true
 
-  /@typescript-eslint/visitor-keys@6.12.0:
-    resolution: {integrity: sha512-rg3BizTZHF1k3ipn8gfrzDXXSFKyOEB5zxYXInQ6z0hUvmQlhaZQzK+YmHmNViMA9HzW5Q9+bPPt90bU6GQwyw==}
+  /@typescript-eslint/visitor-keys@6.13.1:
+    resolution: {integrity: sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dependencies:
-      '@typescript-eslint/types': 6.12.0
+      '@typescript-eslint/types': 6.13.1
       eslint-visitor-keys: 3.4.3
     dev: true
 
@@ -8656,7 +8622,7 @@ packages:
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
     dev: true
 
-  /@vitejs/plugin-react@3.1.0(vite@5.0.2):
+  /@vitejs/plugin-react@3.1.0(vite@5.0.5):
     resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
@@ -8667,19 +8633,19 @@ 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: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@vitejs/plugin-vue@4.5.0(vite@5.0.2)(vue@3.3.9):
-    resolution: {integrity: sha512-a2WSpP8X8HTEww/U00bU4mX1QpLINNuz/2KMNpLsdu3BzOpak3AGI1CJYBTXcc4SPhaD0eNRUp7IyQK405L5dQ==}
+  /@vitejs/plugin-vue@4.5.1(vite@5.0.5)(vue@3.3.9):
+    resolution: {integrity: sha512-DaUzYFr+2UGDG7VSSdShKa9sIWYBa1LL8KC0MNOf2H5LjcTPjob0x8LbkqXWmAtbANJCkpiQTj66UVcQkN2s3g==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
       vite: ^4.0.0 || ^5.0.0
       vue: ^3.2.25
     dependencies:
-      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
       vue: 3.3.9(typescript@5.3.2)
 
   /@vitest/coverage-v8@0.34.6(vitest@0.34.6):
@@ -8741,26 +8707,26 @@ packages:
       pretty-format: 29.7.0
     dev: true
 
-  /@volar/language-core@1.10.7:
-    resolution: {integrity: sha512-6+WI7HGqWCsKJ/bms4V45WP7eDeoGxDtLjYPrHB7QkIWVkRLIeGPzzBoonZz9kERM+Kld3W89Y+IlICejVAKhA==}
+  /@volar/language-core@1.11.1:
+    resolution: {integrity: sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==}
     dependencies:
-      '@volar/source-map': 1.10.7
+      '@volar/source-map': 1.11.1
     dev: true
 
-  /@volar/source-map@1.10.7:
-    resolution: {integrity: sha512-anA254XO0lmmeu0p/kvgPOCkrVpqNIHWMvEkPX70PSk4ntg0iBzN/f0Kip6deXvibl6v14Q3Z8RihWrZwdZEEQ==}
+  /@volar/source-map@1.11.1:
+    resolution: {integrity: sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==}
     dependencies:
       muggle-string: 0.3.1
     dev: true
 
-  /@volar/typescript@1.10.7:
-    resolution: {integrity: sha512-2hvA3vjXVUn1vOpsP/nWLnE5DUmY6YKQhvDRoZVfBrnWwIo0ySxdTUP4XieXGGgSk43xJaeU1zqQS/3Wfm7QgA==}
+  /@volar/typescript@1.11.1:
+    resolution: {integrity: sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==}
     dependencies:
-      '@volar/language-core': 1.10.7
+      '@volar/language-core': 1.11.1
       path-browserify: 1.0.1
     dev: true
 
-  /@vue-macros/common@1.9.0(rollup@4.6.0)(vue@3.3.9):
+  /@vue-macros/common@1.9.0(rollup@4.6.1)(vue@3.3.9):
     resolution: {integrity: sha512-LbfRHDkceuokkLlVuQW9Wq3ZLmRs6KIDPzCjUvvL14HB4GslWdtvBB1suFfNs6VMvh9Zj30cEKF/EAP7QBCZ6Q==}
     engines: {node: '>=16.14.0'}
     peerDependencies:
@@ -8770,9 +8736,9 @@ packages:
         optional: true
     dependencies:
       '@babel/types': 7.23.3
-      '@rollup/pluginutils': 5.0.5(rollup@4.6.0)
+      '@rollup/pluginutils': 5.0.5(rollup@4.6.1)
       '@vue/compiler-sfc': 3.3.9
-      ast-kit: 0.11.2(rollup@4.6.0)
+      ast-kit: 0.11.2(rollup@4.6.1)
       local-pkg: 0.5.0
       magic-string-ast: 0.3.0
       vue: 3.3.9(typescript@5.3.2)
@@ -8780,14 +8746,14 @@ packages:
       - rollup
     dev: false
 
-  /@vue-macros/reactivity-transform@0.4.0(rollup@4.6.0)(vue@3.3.9):
+  /@vue-macros/reactivity-transform@0.4.0(rollup@4.6.1)(vue@3.3.9):
     resolution: {integrity: sha512-3DG+FWkIZe5xZJhIdxyieIYcDKJGC3aUab1JWtEOkS8Q21rLpu6VKUjV6TmB5LNyLSGVp+7de/87Ptd6C6RHOA==}
     engines: {node: '>=16.14.0'}
     peerDependencies:
       vue: ^2.7.0 || ^3.2.25
     dependencies:
       '@babel/parser': 7.23.3
-      '@vue-macros/common': 1.9.0(rollup@4.6.0)(vue@3.3.9)
+      '@vue-macros/common': 1.9.0(rollup@4.6.1)(vue@3.3.9)
       '@vue/compiler-core': 3.3.8
       '@vue/shared': 3.3.8
       magic-string: 0.30.5
@@ -8797,24 +8763,6 @@ packages:
       - rollup
     dev: false
 
-  /@vue/compiler-core@3.3.6:
-    resolution: {integrity: sha512-2JNjemwaNwf+MkkatATVZi7oAH1Hx0B04DdPH3ZoZ8vKC1xZVP7nl4HIsk8XYd3r+/52sqqoz9TWzYc3yE9dqA==}
-    dependencies:
-      '@babel/parser': 7.23.0
-      '@vue/shared': 3.3.6
-      estree-walker: 2.0.2
-      source-map-js: 1.0.2
-    dev: true
-
-  /@vue/compiler-core@3.3.7:
-    resolution: {integrity: sha512-pACdY6YnTNVLXsB86YD8OF9ihwpolzhhtdLVHhBL6do/ykr6kKXNYABRtNMGrsQXpEXXyAdwvWWkuTbs4MFtPQ==}
-    dependencies:
-      '@babel/parser': 7.23.0
-      '@vue/shared': 3.3.7
-      estree-walker: 2.0.2
-      source-map-js: 1.0.2
-    dev: true
-
   /@vue/compiler-core@3.3.8:
     resolution: {integrity: sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==}
     dependencies:
@@ -8832,20 +8780,6 @@ packages:
       estree-walker: 2.0.2
       source-map-js: 1.0.2
 
-  /@vue/compiler-dom@3.3.6:
-    resolution: {integrity: sha512-1MxXcJYMHiTPexjLAJUkNs/Tw2eDf2tY3a0rL+LfuWyiKN2s6jvSwywH3PWD8bKICjfebX3GWx2Os8jkRDq3Ng==}
-    dependencies:
-      '@vue/compiler-core': 3.3.6
-      '@vue/shared': 3.3.6
-    dev: true
-
-  /@vue/compiler-dom@3.3.7:
-    resolution: {integrity: sha512-0LwkyJjnUPssXv/d1vNJ0PKfBlDoQs7n81CbO6Q0zdL7H1EzqYRrTVXDqdBVqro0aJjo/FOa1qBAPVI4PGSHBw==}
-    dependencies:
-      '@vue/compiler-core': 3.3.7
-      '@vue/shared': 3.3.7
-    dev: true
-
   /@vue/compiler-dom@3.3.9:
     resolution: {integrity: sha512-nfWubTtLXuT4iBeDSZ5J3m218MjOy42Vp2pmKVuBKo2/BLcrFUX8nCSr/bKRFiJ32R8qbdnnnBgRn9AdU5v0Sg==}
     dependencies:
@@ -8863,7 +8797,7 @@ packages:
       '@vue/shared': 3.3.9
       estree-walker: 2.0.2
       magic-string: 0.30.5
-      postcss: 8.4.31
+      postcss: 8.4.32
       source-map-js: 1.0.2
 
   /@vue/compiler-ssr@3.3.9:
@@ -8872,21 +8806,22 @@ packages:
       '@vue/compiler-dom': 3.3.9
       '@vue/shared': 3.3.9
 
-  /@vue/language-core@1.8.22(typescript@5.3.2):
-    resolution: {integrity: sha512-bsMoJzCrXZqGsxawtUea1cLjUT9dZnDsy5TuZ+l1fxRMzUGQUG9+Ypq4w//CqpWmrx7nIAJpw2JVF/t258miRw==}
+  /@vue/language-core@1.8.24(typescript@5.3.2):
+    resolution: {integrity: sha512-2ClHvij0WlsDWryPzXJCSpPc6rusZFNoVtRZGgGGkKCmKuIREDDKmH8j+1tYyxPYyH0qL6pZ6+IHD8KIm5nWAw==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@volar/language-core': 1.10.7
-      '@volar/source-map': 1.10.7
-      '@vue/compiler-dom': 3.3.6
-      '@vue/shared': 3.3.6
+      '@volar/language-core': 1.11.1
+      '@volar/source-map': 1.11.1
+      '@vue/compiler-dom': 3.3.9
+      '@vue/shared': 3.3.9
       computeds: 0.0.1
       minimatch: 9.0.3
       muggle-string: 0.3.1
+      path-browserify: 1.0.1
       typescript: 5.3.2
       vue-template-compiler: 2.7.14
     dev: true
@@ -8927,14 +8862,6 @@ packages:
       '@vue/shared': 3.3.9
       vue: 3.3.9(typescript@5.3.2)
 
-  /@vue/shared@3.3.6:
-    resolution: {integrity: sha512-Xno5pEqg8SVhomD0kTSmfh30ZEmV/+jZtyh39q6QflrjdJCXah5lrnOLi9KB6a5k5aAHXMXjoMnxlzUkCNfWLQ==}
-    dev: true
-
-  /@vue/shared@3.3.7:
-    resolution: {integrity: sha512-N/tbkINRUDExgcPTBvxNkvHGu504k8lzlNQRITVnm6YjOjwa4r0nnbd4Jb01sNpur5hAllyRJzSK5PvB9PPwRg==}
-    dev: true
-
   /@vue/shared@3.3.8:
     resolution: {integrity: sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==}
     dev: false
@@ -8966,13 +8893,13 @@ packages:
     engines: {node: '>=10.0.0'}
     dev: true
 
-  /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.18.17):
+  /@yarnpkg/esbuild-plugin-pnp@3.0.0-rc.15(esbuild@0.18.20):
     resolution: {integrity: sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA==}
     engines: {node: '>=14.15.0'}
     peerDependencies:
       esbuild: '>=0.10.0'
     dependencies:
-      esbuild: 0.18.17
+      esbuild: 0.18.20
       tslib: 2.6.2
     dev: true
 
@@ -9089,8 +9016,9 @@ packages:
   /agent-base@6.0.2:
     resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
     engines: {node: '>= 6.0.0'}
+    requiresBuild: true
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -9098,7 +9026,7 @@ packages:
     resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
     engines: {node: '>= 14'}
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -9402,16 +9330,26 @@ packages:
       util: 0.12.5
     dev: true
 
+  /assert@2.1.0:
+    resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==}
+    dependencies:
+      call-bind: 1.0.2
+      is-nan: 1.3.2
+      object-is: 1.1.5
+      object.assign: 4.1.4
+      util: 0.12.5
+    dev: true
+
   /assertion-error@1.1.0:
     resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
     dev: true
 
-  /ast-kit@0.11.2(rollup@4.6.0):
+  /ast-kit@0.11.2(rollup@4.6.1):
     resolution: {integrity: sha512-Q0DjXK4ApbVoIf9GLyCo252tUH44iTnD/hiJ2TQaJeydYWSpKk0sI34+WMel8S9Wt5pbLgG02oJ+gkgX5DV3sQ==}
     engines: {node: '>=16.14.0'}
     dependencies:
       '@babel/parser': 7.23.3
-      '@rollup/pluginutils': 5.0.5(rollup@4.6.0)
+      '@rollup/pluginutils': 5.0.5(rollup@4.6.1)
       pathe: 1.1.1
     transitivePeerDependencies:
       - rollup
@@ -9487,7 +9425,7 @@ packages:
     resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==}
     dependencies:
       archy: 1.0.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       fastq: 1.15.0
     transitivePeerDependencies:
       - supports-color
@@ -9528,12 +9466,12 @@ packages:
   /b4a@1.6.4:
     resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==}
 
-  /babel-core@7.0.0-bridge.0(@babel/core@7.22.11):
+  /babel-core@7.0.0-bridge.0(@babel/core@7.23.5):
     resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
     dev: true
 
   /babel-jest@29.7.0(@babel/core@7.22.11):
@@ -9572,43 +9510,43 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@babel/template': 7.22.5
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
       '@types/babel__core': 7.20.0
       '@types/babel__traverse': 7.20.0
     dev: true
 
-  /babel-plugin-polyfill-corejs2@0.4.4(@babel/core@7.22.11):
-    resolution: {integrity: sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA==}
+  /babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.23.5):
+    resolution: {integrity: sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==}
     peerDependencies:
-      '@babel/core': ^7.0.0-0
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
     dependencies:
-      '@babel/compat-data': 7.22.9
-      '@babel/core': 7.22.11
-      '@babel/helper-define-polyfill-provider': 0.4.1(@babel/core@7.22.11)
-      '@nicolo-ribaudo/semver-v6': 6.3.3
+      '@babel/compat-data': 7.23.5
+      '@babel/core': 7.23.5
+      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.5)
+      semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /babel-plugin-polyfill-corejs3@0.8.2(@babel/core@7.22.11):
-    resolution: {integrity: sha512-Cid+Jv1BrY9ReW9lIfNlNpsI53N+FN7gE+f73zLAUbr9C52W4gKLWSByx47pfDJsEysojKArqOtOKZSVIIUTuQ==}
+  /babel-plugin-polyfill-corejs3@0.8.6(@babel/core@7.23.5):
+    resolution: {integrity: sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==}
     peerDependencies:
-      '@babel/core': ^7.0.0-0
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-define-polyfill-provider': 0.4.1(@babel/core@7.22.11)
-      core-js-compat: 3.31.1
+      '@babel/core': 7.23.5
+      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.5)
+      core-js-compat: 3.33.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /babel-plugin-polyfill-regenerator@0.5.1(@babel/core@7.22.11):
-    resolution: {integrity: sha512-L8OyySuI6OSQ5hFy9O+7zFjyr4WhAfRjLIOkhQGYl+emwJkd/S4XXT1JpfrgR1jrQ1NcGiOh+yAdGlF8pnC3Jw==}
+  /babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.23.5):
+    resolution: {integrity: sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==}
     peerDependencies:
-      '@babel/core': ^7.0.0-0
+      '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/helper-define-polyfill-provider': 0.4.1(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.5)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -9828,6 +9766,17 @@ packages:
       node-releases: 2.0.13
       update-browserslist-db: 1.0.11(browserslist@4.21.9)
 
+  /browserslist@4.22.2:
+    resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+    dependencies:
+      caniuse-lite: 1.0.30001566
+      electron-to-chromium: 1.4.601
+      node-releases: 2.0.14
+      update-browserslist-db: 1.0.13(browserslist@4.22.2)
+    dev: true
+
   /bser@2.1.1:
     resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
     dependencies:
@@ -9876,8 +9825,8 @@ packages:
     dependencies:
       node-gyp-build: 4.6.0
 
-  /bullmq@4.14.2:
-    resolution: {integrity: sha512-lzK4F6H61oH5S3Mg4JP4rnSxpQx00Qq7KQKt1oWjcQarka7TdN50CDsZGXg9z6kzvu26Pd3aiwTxwr4YvcEFgw==}
+  /bullmq@4.14.4:
+    resolution: {integrity: sha512-8tD3Zq4CP+2q+zZ1JSkKov5ra18Jhth8zdr2X1/V2MMOVpa8vfVCsQaRnKgDpiMpaF6AH9sucXUNtk4xamTEKw==}
     dependencies:
       cron-parser: 4.8.1
       glob: 8.1.0
@@ -10019,6 +9968,10 @@ packages:
   /caniuse-lite@1.0.30001516:
     resolution: {integrity: sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==}
 
+  /caniuse-lite@1.0.30001566:
+    resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==}
+    dev: true
+
   /canonicalize@1.0.8:
     resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==}
     dev: false
@@ -10111,23 +10064,11 @@ packages:
     resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
     engines: {node: '>=10'}
 
-  /character-entities-legacy@1.1.4:
-    resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==}
-    dev: true
-
-  /character-entities@1.2.4:
-    resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==}
-    dev: true
-
   /character-parser@2.2.0:
     resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==}
     dependencies:
       is-regex: 1.1.4
 
-  /character-reference-invalid@1.1.4:
-    resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==}
-    dev: true
-
   /chardet@0.7.0:
     resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
     dev: true
@@ -10409,10 +10350,6 @@ packages:
     dependencies:
       delayed-stream: 1.0.0
 
-  /comma-separated-tokens@1.0.8:
-    resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==}
-    dev: true
-
   /commander@10.0.1:
     resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
     engines: {node: '>=14'}
@@ -10561,6 +10498,12 @@ packages:
       browserslist: 4.21.9
     dev: true
 
+  /core-js-compat@3.33.3:
+    resolution: {integrity: sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow==}
+    dependencies:
+      browserslist: 4.22.2
+    dev: true
+
   /core-js@3.29.1:
     resolution: {integrity: sha512-+jwgnhg6cQxKYIIjGtAHq2nwUOolo9eoFZ4sHfUH09BLXBgxnH4gA0zEd+t+BO2cNB8idaBtZFcFTRjQJRJmAw==}
     requiresBuild: true
@@ -10586,7 +10529,7 @@ packages:
       readable-stream: 3.6.0
     dev: false
 
-  /create-jest@29.7.0(@types/node@20.10.0):
+  /create-jest@29.7.0(@types/node@20.10.3):
     resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -10595,7 +10538,7 @@ packages:
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.10.0)
+      jest-config: 29.7.0(@types/node@20.10.3)
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -10671,13 +10614,13 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
-  /css-declaration-sorter@6.4.1(postcss@8.4.31):
+  /css-declaration-sorter@6.4.1(postcss@8.4.32):
     resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==}
     engines: {node: ^10 || ^12 || >=14}
     peerDependencies:
       postcss: ^8.0.9
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
     dev: false
 
   /css-select@5.1.0:
@@ -10717,62 +10660,62 @@ packages:
     engines: {node: '>=4'}
     hasBin: true
 
-  /cssnano-preset-default@6.0.1(postcss@8.4.31):
+  /cssnano-preset-default@6.0.1(postcss@8.4.32):
     resolution: {integrity: sha512-7VzyFZ5zEB1+l1nToKyrRkuaJIx0zi/1npjvZfbBwbtNTzhLtlvYraK/7/uqmX2Wb2aQtd983uuGw79jAjLSuQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      css-declaration-sorter: 6.4.1(postcss@8.4.31)
-      cssnano-utils: 4.0.0(postcss@8.4.31)
-      postcss: 8.4.31
-      postcss-calc: 9.0.1(postcss@8.4.31)
-      postcss-colormin: 6.0.0(postcss@8.4.31)
-      postcss-convert-values: 6.0.0(postcss@8.4.31)
-      postcss-discard-comments: 6.0.0(postcss@8.4.31)
-      postcss-discard-duplicates: 6.0.0(postcss@8.4.31)
-      postcss-discard-empty: 6.0.0(postcss@8.4.31)
-      postcss-discard-overridden: 6.0.0(postcss@8.4.31)
-      postcss-merge-longhand: 6.0.0(postcss@8.4.31)
-      postcss-merge-rules: 6.0.1(postcss@8.4.31)
-      postcss-minify-font-values: 6.0.0(postcss@8.4.31)
-      postcss-minify-gradients: 6.0.0(postcss@8.4.31)
-      postcss-minify-params: 6.0.0(postcss@8.4.31)
-      postcss-minify-selectors: 6.0.0(postcss@8.4.31)
-      postcss-normalize-charset: 6.0.0(postcss@8.4.31)
-      postcss-normalize-display-values: 6.0.0(postcss@8.4.31)
-      postcss-normalize-positions: 6.0.0(postcss@8.4.31)
-      postcss-normalize-repeat-style: 6.0.0(postcss@8.4.31)
-      postcss-normalize-string: 6.0.0(postcss@8.4.31)
-      postcss-normalize-timing-functions: 6.0.0(postcss@8.4.31)
-      postcss-normalize-unicode: 6.0.0(postcss@8.4.31)
-      postcss-normalize-url: 6.0.0(postcss@8.4.31)
-      postcss-normalize-whitespace: 6.0.0(postcss@8.4.31)
-      postcss-ordered-values: 6.0.0(postcss@8.4.31)
-      postcss-reduce-initial: 6.0.0(postcss@8.4.31)
-      postcss-reduce-transforms: 6.0.0(postcss@8.4.31)
-      postcss-svgo: 6.0.0(postcss@8.4.31)
-      postcss-unique-selectors: 6.0.0(postcss@8.4.31)
+      css-declaration-sorter: 6.4.1(postcss@8.4.32)
+      cssnano-utils: 4.0.0(postcss@8.4.32)
+      postcss: 8.4.32
+      postcss-calc: 9.0.1(postcss@8.4.32)
+      postcss-colormin: 6.0.0(postcss@8.4.32)
+      postcss-convert-values: 6.0.0(postcss@8.4.32)
+      postcss-discard-comments: 6.0.0(postcss@8.4.32)
+      postcss-discard-duplicates: 6.0.0(postcss@8.4.32)
+      postcss-discard-empty: 6.0.0(postcss@8.4.32)
+      postcss-discard-overridden: 6.0.0(postcss@8.4.32)
+      postcss-merge-longhand: 6.0.0(postcss@8.4.32)
+      postcss-merge-rules: 6.0.1(postcss@8.4.32)
+      postcss-minify-font-values: 6.0.0(postcss@8.4.32)
+      postcss-minify-gradients: 6.0.0(postcss@8.4.32)
+      postcss-minify-params: 6.0.0(postcss@8.4.32)
+      postcss-minify-selectors: 6.0.0(postcss@8.4.32)
+      postcss-normalize-charset: 6.0.0(postcss@8.4.32)
+      postcss-normalize-display-values: 6.0.0(postcss@8.4.32)
+      postcss-normalize-positions: 6.0.0(postcss@8.4.32)
+      postcss-normalize-repeat-style: 6.0.0(postcss@8.4.32)
+      postcss-normalize-string: 6.0.0(postcss@8.4.32)
+      postcss-normalize-timing-functions: 6.0.0(postcss@8.4.32)
+      postcss-normalize-unicode: 6.0.0(postcss@8.4.32)
+      postcss-normalize-url: 6.0.0(postcss@8.4.32)
+      postcss-normalize-whitespace: 6.0.0(postcss@8.4.32)
+      postcss-ordered-values: 6.0.0(postcss@8.4.32)
+      postcss-reduce-initial: 6.0.0(postcss@8.4.32)
+      postcss-reduce-transforms: 6.0.0(postcss@8.4.32)
+      postcss-svgo: 6.0.0(postcss@8.4.32)
+      postcss-unique-selectors: 6.0.0(postcss@8.4.32)
     dev: false
 
-  /cssnano-utils@4.0.0(postcss@8.4.31):
+  /cssnano-utils@4.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
     dev: false
 
-  /cssnano@6.0.1(postcss@8.4.31):
+  /cssnano@6.0.1(postcss@8.4.32):
     resolution: {integrity: sha512-fVO1JdJ0LSdIGJq68eIxOqFpIJrZqXUsBt8fkrBcztCQqAjQD51OhZp7tc0ImcbwXD4k7ny84QTV90nZhmqbkg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      cssnano-preset-default: 6.0.1(postcss@8.4.31)
+      cssnano-preset-default: 6.0.1(postcss@8.4.32)
       lilconfig: 2.1.0
-      postcss: 8.4.31
+      postcss: 8.4.32
     dev: false
 
   /csso@5.0.5:
@@ -10896,17 +10839,6 @@ packages:
     dependencies:
       ms: 2.0.0
 
-  /debug@3.2.7(supports-color@5.5.0):
-    resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-    dependencies:
-      ms: 2.1.3
-      supports-color: 5.5.0
-
   /debug@3.2.7(supports-color@8.1.1):
     resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
     peerDependencies:
@@ -10917,7 +10849,18 @@ packages:
     dependencies:
       ms: 2.1.3
       supports-color: 8.1.1
-    dev: true
+
+  /debug@4.3.4(supports-color@5.5.0):
+    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.1.2
+      supports-color: 5.5.0
 
   /debug@4.3.4(supports-color@8.1.1):
     resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
@@ -10930,6 +10873,7 @@ packages:
     dependencies:
       ms: 2.1.2
       supports-color: 8.1.1
+    dev: true
 
   /decamelize-keys@1.1.1:
     resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
@@ -11146,7 +11090,7 @@ packages:
     hasBin: true
     dependencies:
       address: 1.2.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -11288,6 +11232,10 @@ packages:
   /electron-to-chromium@1.4.463:
     resolution: {integrity: sha512-fT3hvdUWLjDbaTGzyOjng/CQhQJSQP8ThO3XZAoaxHvHo2kUXiRQVMj9M235l8uDFiNPsPa6KHT1p3RaR6ugRw==}
 
+  /electron-to-chromium@1.4.601:
+    resolution: {integrity: sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==}
+    dev: true
+
   /emittery@0.13.1:
     resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
     engines: {node: '>=12'}
@@ -11465,47 +11413,17 @@ packages:
     resolution: {integrity: sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==}
     dev: true
 
-  /esbuild-register@3.5.0(esbuild@0.18.17):
+  /esbuild-register@3.5.0(esbuild@0.18.20):
     resolution: {integrity: sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==}
     peerDependencies:
       esbuild: '>=0.12 <1'
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
-      esbuild: 0.18.17
+      debug: 4.3.4(supports-color@5.5.0)
+      esbuild: 0.18.20
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /esbuild@0.18.17:
-    resolution: {integrity: sha512-1GJtYnUxsJreHYA0Y+iQz2UEykonY66HNWOb0yXYZi9/kNrORUEHVg87eQsCtqh59PEJ5YVZJO98JHznMJSWjg==}
-    engines: {node: '>=12'}
-    hasBin: true
-    requiresBuild: true
-    optionalDependencies:
-      '@esbuild/android-arm': 0.18.17
-      '@esbuild/android-arm64': 0.18.17
-      '@esbuild/android-x64': 0.18.17
-      '@esbuild/darwin-arm64': 0.18.17
-      '@esbuild/darwin-x64': 0.18.17
-      '@esbuild/freebsd-arm64': 0.18.17
-      '@esbuild/freebsd-x64': 0.18.17
-      '@esbuild/linux-arm': 0.18.17
-      '@esbuild/linux-arm64': 0.18.17
-      '@esbuild/linux-ia32': 0.18.17
-      '@esbuild/linux-loong64': 0.18.17
-      '@esbuild/linux-mips64el': 0.18.17
-      '@esbuild/linux-ppc64': 0.18.17
-      '@esbuild/linux-riscv64': 0.18.17
-      '@esbuild/linux-s390x': 0.18.17
-      '@esbuild/linux-x64': 0.18.17
-      '@esbuild/netbsd-x64': 0.18.17
-      '@esbuild/openbsd-x64': 0.18.17
-      '@esbuild/sunos-x64': 0.18.17
-      '@esbuild/win32-arm64': 0.18.17
-      '@esbuild/win32-ia32': 0.18.17
-      '@esbuild/win32-x64': 0.18.17
-    dev: true
-
   /esbuild@0.18.20:
     resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
     engines: {node: '>=12'}
@@ -11623,14 +11541,14 @@ packages:
   /eslint-import-resolver-node@0.3.9:
     resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
     dependencies:
-      debug: 3.2.7(supports-color@5.5.0)
+      debug: 3.2.7(supports-color@8.1.1)
       is-core-module: 2.13.1
       resolve: 1.22.8
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.12.0)(eslint-import-resolver-node@0.3.9)(eslint@8.54.0):
+  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.13.1)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0):
     resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11651,15 +11569,15 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
-      debug: 3.2.7(supports-color@5.5.0)
-      eslint: 8.54.0
+      '@typescript-eslint/parser': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+      debug: 3.2.7(supports-color@8.1.1)
+      eslint: 8.55.0
       eslint-import-resolver-node: 0.3.9
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.12.0)(eslint@8.54.0):
+  /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.13.1)(eslint@8.55.0):
     resolution: {integrity: sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11669,16 +11587,16 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.12.0(eslint@8.54.0)(typescript@5.3.2)
+      '@typescript-eslint/parser': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
       array.prototype.flatmap: 1.3.2
-      debug: 3.2.7(supports-color@5.5.0)
+      debug: 3.2.7(supports-color@8.1.1)
       doctrine: 2.1.0
-      eslint: 8.54.0
+      eslint: 8.55.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.12.0)(eslint-import-resolver-node@0.3.9)(eslint@8.54.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.13.1)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0)
       hasown: 2.0.0
       is-core-module: 2.13.1
       is-glob: 4.0.3
@@ -11694,19 +11612,19 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-plugin-vue@9.18.1(eslint@8.54.0):
-    resolution: {integrity: sha512-7hZFlrEgg9NIzuVik2I9xSnJA5RsmOfueYgsUGUokEDLJ1LHtxO0Pl4duje1BriZ/jDWb+44tcIlC3yi0tdlZg==}
+  /eslint-plugin-vue@9.19.2(eslint@8.55.0):
+    resolution: {integrity: sha512-CPDqTOG2K4Ni2o4J5wixkLVNwgctKXFu6oBpVJlpNq7f38lh9I80pRTouZSJ2MAebPJlINU/KTFSXyQfBUlymA==}
     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.54.0)
-      eslint: 8.54.0
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
+      eslint: 8.55.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.2(eslint@8.54.0)
+      vue-eslint-parser: 9.3.2(eslint@8.55.0)
       xml-name-validator: 4.0.0
     transitivePeerDependencies:
       - supports-color
@@ -11745,7 +11663,7 @@ packages:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
@@ -11776,15 +11694,15 @@ packages:
       - supports-color
     dev: true
 
-  /eslint@8.54.0:
-    resolution: {integrity: sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==}
+  /eslint@8.55.0:
+    resolution: {integrity: sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     hasBin: true
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
       '@eslint-community/regexpp': 4.6.2
-      '@eslint/eslintrc': 2.1.3
-      '@eslint/js': 8.54.0
+      '@eslint/eslintrc': 2.1.4
+      '@eslint/js': 8.55.0
       '@humanwhocodes/config-array': 0.11.13
       '@humanwhocodes/module-importer': 1.0.1
       '@nodelib/fs.walk': 1.2.8
@@ -11792,7 +11710,7 @@ packages:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
@@ -12204,12 +12122,6 @@ packages:
     dependencies:
       reusify: 1.0.4
 
-  /fault@1.0.4:
-    resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==}
-    dependencies:
-      format: 0.2.2
-    dev: true
-
   /fb-watchman@2.0.2:
     resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
     dependencies:
@@ -12408,7 +12320,7 @@ packages:
       debug:
         optional: true
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
 
   /for-each@0.3.3:
     resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
@@ -12455,11 +12367,6 @@ packages:
       combined-stream: 1.0.8
       mime-types: 2.1.35
 
-  /format@0.2.2:
-    resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
-    engines: {node: '>=0.4.x'}
-    dev: true
-
   /formdata-polyfill@4.0.10:
     resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
     engines: {node: '>=12.20.0'}
@@ -12753,18 +12660,6 @@ packages:
     resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
     dev: true
 
-  /glob@10.3.0:
-    resolution: {integrity: sha512-AQ1/SB9HH0yCx1jXAT4vmCbTOPe5RQ+kCurjbel5xSCGhebumUv+GJZfa1rEqor3XIViqwSEmlkZCQD43RWrBg==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    hasBin: true
-    dependencies:
-      foreground-child: 3.1.1
-      jackspeak: 2.2.1
-      minimatch: 9.0.3
-      minipass: 5.0.0
-      path-scurry: 1.9.2
-    dev: true
-
   /glob@10.3.10:
     resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
     engines: {node: '>=16 || 14 >=14.17'}
@@ -12775,7 +12670,6 @@ packages:
       minimatch: 9.0.3
       minipass: 7.0.4
       path-scurry: 1.10.1
-    dev: false
 
   /glob@7.2.3:
     resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
@@ -12908,8 +12802,8 @@ packages:
     engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
     dev: true
 
-  /gsap@3.12.2:
-    resolution: {integrity: sha512-EkYnpG8qHgYBFAwsgsGEqvT1WUidX0tt/ijepx7z8EUJHElykg91RvW1XbkT59T0gZzzszOpjQv7SE41XuIXyQ==}
+  /gsap@3.12.3:
+    resolution: {integrity: sha512-TySXTE+ABiAVa61W+h5wv2p5GkJT1Uj//4nWpK8EjmhcDqwH++35IvtbQlVVFj+rdcJdFCdCt0SKgb+SwdPq/A==}
     dev: false
 
   /gunzip-maybe@1.4.2:
@@ -13030,20 +12924,6 @@ packages:
       function-bind: 1.1.2
     dev: true
 
-  /hast-util-parse-selector@2.2.5:
-    resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==}
-    dev: true
-
-  /hastscript@6.0.0:
-    resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==}
-    dependencies:
-      '@types/hast': 2.3.4
-      comma-separated-tokens: 1.0.8
-      hast-util-parse-selector: 2.2.5
-      property-information: 5.6.0
-      space-separated-tokens: 1.1.5
-    dev: true
-
   /he@1.2.0:
     resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
     hasBin: true
@@ -13055,6 +12935,7 @@ packages:
 
   /highlight.js@10.7.3:
     resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==}
+    dev: false
 
   /highlight.js@11.8.0:
     resolution: {integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==}
@@ -13127,7 +13008,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -13178,7 +13059,7 @@ packages:
     requiresBuild: true
     dependencies:
       agent-base: 4.3.0
-      debug: 3.2.7(supports-color@5.5.0)
+      debug: 3.2.7(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -13189,7 +13070,7 @@ packages:
     engines: {node: '>= 6.0.0'}
     dependencies:
       agent-base: 5.1.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -13199,7 +13080,7 @@ packages:
     engines: {node: '>= 6'}
     dependencies:
       agent-base: 6.0.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -13208,7 +13089,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -13218,7 +13099,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -13364,7 +13245,7 @@ packages:
     dependencies:
       '@ioredis/commands': 1.2.0
       cluster-key-slot: 1.1.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       denque: 2.1.0
       lodash.defaults: 4.2.0
       lodash.isarguments: 3.1.0
@@ -13420,17 +13301,6 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
-  /is-alphabetical@1.0.4:
-    resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==}
-    dev: true
-
-  /is-alphanumerical@1.0.4:
-    resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==}
-    dependencies:
-      is-alphabetical: 1.0.4
-      is-decimal: 1.0.4
-    dev: true
-
   /is-arguments@1.1.1:
     resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
     engines: {node: '>= 0.4'}
@@ -13520,10 +13390,6 @@ packages:
       has-tostringtag: 1.0.0
     dev: true
 
-  /is-decimal@1.0.4:
-    resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==}
-    dev: true
-
   /is-deflate@1.0.0:
     resolution: {integrity: sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==}
     dev: true
@@ -13575,10 +13441,6 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
-  /is-hexadecimal@1.0.4:
-    resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==}
-    dev: true
-
   /is-installed-globally@0.4.0:
     resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==}
     engines: {node: '>=10'}
@@ -13830,7 +13692,7 @@ packages:
     resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
     engines: {node: '>=10'}
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       istanbul-lib-coverage: 3.2.0
       source-map: 0.6.1
     transitivePeerDependencies:
@@ -13850,15 +13712,6 @@ packages:
     engines: {node: '>=6'}
     dev: false
 
-  /jackspeak@2.2.1:
-    resolution: {integrity: sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==}
-    engines: {node: '>=14'}
-    dependencies:
-      '@isaacs/cliui': 8.0.2
-    optionalDependencies:
-      '@pkgjs/parseargs': 0.11.0
-    dev: true
-
   /jackspeak@2.3.6:
     resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
     engines: {node: '>=14'}
@@ -13866,7 +13719,6 @@ packages:
       '@isaacs/cliui': 8.0.2
     optionalDependencies:
       '@pkgjs/parseargs': 0.11.0
-    dev: false
 
   /jake@10.8.5:
     resolution: {integrity: sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==}
@@ -13895,7 +13747,7 @@ packages:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.3.0
@@ -13916,7 +13768,7 @@ packages:
       - supports-color
     dev: true
 
-  /jest-cli@29.7.0(@types/node@20.10.0):
+  /jest-cli@29.7.0(@types/node@20.10.3):
     resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -13930,10 +13782,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.10.0)
+      create-jest: 29.7.0(@types/node@20.10.3)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.10.0)
+      jest-config: 29.7.0(@types/node@20.10.3)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.6.2
@@ -13944,7 +13796,7 @@ packages:
       - ts-node
     dev: true
 
-  /jest-config@29.7.0(@types/node@20.10.0):
+  /jest-config@29.7.0(@types/node@20.10.3):
     resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     peerDependencies:
@@ -13959,7 +13811,7 @@ packages:
       '@babel/core': 7.22.11
       '@jest/test-sequencer': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       babel-jest: 29.7.0(@babel/core@7.22.11)
       chalk: 4.1.2
       ci-info: 3.7.1
@@ -14039,7 +13891,7 @@ packages:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       jest-mock: 29.7.0
       jest-util: 29.7.0
     dev: true
@@ -14069,7 +13921,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.6
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -14130,7 +13982,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.10.0
+      '@types/node': 20.10.3
     dev: true
 
   /jest-mock@29.7.0:
@@ -14138,7 +13990,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       jest-util: 29.7.0
     dev: true
 
@@ -14193,7 +14045,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -14224,7 +14076,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       chalk: 4.1.2
       cjs-module-lexer: 1.2.2
       collect-v8-coverage: 1.0.1
@@ -14276,7 +14128,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       chalk: 4.1.2
       ci-info: 3.7.1
       graceful-fs: 4.2.11
@@ -14301,7 +14153,7 @@ packages:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -14320,13 +14172,13 @@ packages:
     resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
     dev: true
 
-  /jest@29.7.0(@types/node@20.10.0):
+  /jest@29.7.0(@types/node@20.10.3):
     resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -14339,7 +14191,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.10.0)
+      jest-cli: 29.7.0(@types/node@20.10.3)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -14426,41 +14278,45 @@ packages:
     resolution: {integrity: sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ==}
     engines: {node: '>=0.1.90'}
 
-  /jscodeshift@0.14.0(@babel/preset-env@7.22.9):
-    resolution: {integrity: sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA==}
+  /jscodeshift@0.15.1(@babel/preset-env@7.23.5):
+    resolution: {integrity: sha512-hIJfxUy8Rt4HkJn/zZPU9ChKfKZM1342waJ1QC2e2YsPcWhM+3BJ4dcfQCzArTrk1jJeNLB341H+qOcEHRxJZg==}
     hasBin: true
     peerDependencies:
       '@babel/preset-env': ^7.1.6
+    peerDependenciesMeta:
+      '@babel/preset-env':
+        optional: true
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/parser': 7.23.0
-      '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.22.11)
-      '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.22.11)
-      '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.11)
-      '@babel/plugin-transform-modules-commonjs': 7.22.5(@babel/core@7.22.11)
-      '@babel/preset-env': 7.22.9(@babel/core@7.22.11)
-      '@babel/preset-flow': 7.18.6(@babel/core@7.22.11)
-      '@babel/preset-typescript': 7.21.0(@babel/core@7.22.11)
-      '@babel/register': 7.21.0(@babel/core@7.22.11)
-      babel-core: 7.0.0-bridge.0(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/parser': 7.23.3
+      '@babel/plugin-transform-class-properties': 7.22.5(@babel/core@7.23.5)
+      '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5)
+      '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.5)
+      '@babel/plugin-transform-private-methods': 7.22.5(@babel/core@7.23.5)
+      '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
+      '@babel/preset-flow': 7.23.3(@babel/core@7.23.5)
+      '@babel/preset-typescript': 7.23.3(@babel/core@7.23.5)
+      '@babel/register': 7.22.15(@babel/core@7.23.5)
+      babel-core: 7.0.0-bridge.0(@babel/core@7.23.5)
       chalk: 4.1.2
       flow-parser: 0.202.0
       graceful-fs: 4.2.11
       micromatch: 4.0.5
       neo-async: 2.6.2
       node-dir: 0.1.17
-      recast: 0.21.5
+      recast: 0.23.4
       temp: 0.8.4
       write-file-atomic: 2.4.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /jsdom@23.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3):
-    resolution: {integrity: sha512-cbL/UCtohJguhFC7c2/hgW6BeZCNvP7URQGnx9tSJRYKCdnfbfWOrtuLTMfiB2VxKsx5wPHVsh/J0aBy9lIIhQ==}
+  /jsdom@23.0.1(bufferutil@4.0.7)(utf-8-validate@6.0.3):
+    resolution: {integrity: sha512-2i27vgvlUsGEBO9+/kJQRbtqtm+191b5zAZrU/UezVmnC2dlDAFLgDYJvAEi94T4kjsRKkezEtLQTgsNEsW2lQ==}
     engines: {node: '>=18'}
     peerDependencies:
-      canvas: ^3.0.0
+      canvas: ^2.11.2
     peerDependenciesMeta:
       canvas:
         optional: true
@@ -14514,7 +14370,7 @@ packages:
     resolution: {integrity: sha512-pJ4XLQP4Q9HTxl6RVDLJ8Cyh1uitSs0CzDBAz1uoJ4sRD/Bk7cFSXL1FUXDW3zJ7YnfliJx6eu8Jn283bpZ4Yg==}
     engines: {node: '>=10'}
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       rfdc: 1.3.0
       uri-js: 4.4.1
     transitivePeerDependencies:
@@ -14860,19 +14716,11 @@ packages:
     resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
     engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
 
-  /lowlight@1.20.0:
-    resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==}
-    dependencies:
-      fault: 1.0.4
-      highlight.js: 10.7.3
-    dev: true
-
   /lru-cache@10.0.2:
     resolution: {integrity: sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==}
     engines: {node: 14 || >=16.14}
     dependencies:
       semver: 7.5.4
-    dev: false
 
   /lru-cache@4.1.5:
     resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
@@ -14898,11 +14746,6 @@ packages:
     engines: {node: '>=16.14'}
     dev: true
 
-  /lru-cache@9.1.2:
-    resolution: {integrity: sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==}
-    engines: {node: 14 || >=16.14}
-    dev: true
-
   /luxon@3.3.0:
     resolution: {integrity: sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==}
     engines: {node: '>=12'}
@@ -15268,11 +15111,11 @@ packages:
   /minipass@5.0.0:
     resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
     engines: {node: '>=8'}
+    dev: false
 
   /minipass@7.0.4:
     resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
     engines: {node: '>=16 || 14 >=14.17'}
-    dev: false
 
   /minizlib@1.3.3:
     resolution: {integrity: sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==}
@@ -15453,13 +15296,13 @@ packages:
     resolution: {integrity: sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==}
     dev: false
 
-  /nanoid@3.3.6:
-    resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
+  /nanoid@3.3.7:
+    resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     hasBin: true
 
-  /nanoid@5.0.3:
-    resolution: {integrity: sha512-I7X2b22cxA4LIHXPSqbBCEQSL+1wv8TuoefejsX4HFWyC6jc5JG7CEaxOltiKjc1M+YCS2YkrZZcj4+dytw9GA==}
+  /nanoid@5.0.4:
+    resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
     engines: {node: ^18 || >=20}
     hasBin: true
     dev: false
@@ -15507,7 +15350,7 @@ packages:
     resolution: {integrity: sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==}
     engines: {node: '>= 4.4.x'}
     dependencies:
-      debug: 3.2.7(supports-color@5.5.0)
+      debug: 3.2.7(supports-color@8.1.1)
       iconv-lite: 0.4.24
       sax: 1.2.4
     transitivePeerDependencies:
@@ -15665,18 +15508,22 @@ packages:
   /node-releases@2.0.13:
     resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==}
 
+  /node-releases@2.0.14:
+    resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
+    dev: true
+
   /nodemailer@6.9.7:
     resolution: {integrity: sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==}
     engines: {node: '>=6.0.0'}
     dev: false
 
-  /nodemon@3.0.1:
-    resolution: {integrity: sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==}
+  /nodemon@3.0.2:
+    resolution: {integrity: sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==}
     engines: {node: '>=10'}
     hasBin: true
     dependencies:
       chokidar: 3.5.3
-      debug: 3.2.7(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@5.5.0)
       ignore-by-default: 1.0.1
       minimatch: 3.1.2
       pstree.remy: 1.1.8
@@ -16122,17 +15969,6 @@ packages:
       data-uri-to-buffer: 0.0.3
     dev: false
 
-  /parse-entities@2.0.0:
-    resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==}
-    dependencies:
-      character-entities: 1.2.4
-      character-entities-legacy: 1.1.4
-      character-reference-invalid: 1.1.4
-      is-alphanumerical: 1.0.4
-      is-decimal: 1.0.4
-      is-hexadecimal: 1.0.4
-    dev: true
-
   /parse-json@5.2.0:
     resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
     engines: {node: '>=8'}
@@ -16215,15 +16051,6 @@ packages:
     dependencies:
       lru-cache: 10.0.2
       minipass: 7.0.4
-    dev: false
-
-  /path-scurry@1.9.2:
-    resolution: {integrity: sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    dependencies:
-      lru-cache: 9.1.2
-      minipass: 5.0.0
-    dev: true
 
   /path-to-regexp@0.1.7:
     resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
@@ -16359,8 +16186,8 @@ packages:
       split2: 4.1.0
     dev: false
 
-  /photoswipe@5.4.2:
-    resolution: {integrity: sha512-z5hr36nAIPOZbHJPbCJ/mQ3+ZlizttF9za5gKXKH/us1k4KNHaRbC63K1Px5sVVKUtGb/2+ixHpKqtwl0WAwvA==}
+  /photoswipe@5.4.3:
+    resolution: {integrity: sha512-9UC6oJBK4oXFZ5HcdlcvGkfEHsVrmE4csUdCQhEjHYb3PvPLO3PG7UhnPuOgjxwmhq5s17Un5NUdum01LgBDng==}
     engines: {node: '>= 0.12.0'}
     dev: false
 
@@ -16481,18 +16308,18 @@ packages:
       '@babel/runtime': 7.23.2
     dev: true
 
-  /postcss-calc@9.0.1(postcss@8.4.31):
+  /postcss-calc@9.0.1(postcss@8.4.32):
     resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.2
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-selector-parser: 6.0.13
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-colormin@6.0.0(postcss@8.4.31):
+  /postcss-colormin@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
@@ -16501,69 +16328,69 @@ packages:
       browserslist: 4.21.9
       caniuse-api: 3.0.0
       colord: 2.9.3
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-convert-values@6.0.0(postcss@8.4.31):
+  /postcss-convert-values@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
       browserslist: 4.21.9
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-discard-comments@6.0.0(postcss@8.4.31):
+  /postcss-discard-comments@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
     dev: false
 
-  /postcss-discard-duplicates@6.0.0(postcss@8.4.31):
+  /postcss-discard-duplicates@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
     dev: false
 
-  /postcss-discard-empty@6.0.0(postcss@8.4.31):
+  /postcss-discard-empty@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
     dev: false
 
-  /postcss-discard-overridden@6.0.0(postcss@8.4.31):
+  /postcss-discard-overridden@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
     dev: false
 
-  /postcss-merge-longhand@6.0.0(postcss@8.4.31):
+  /postcss-merge-longhand@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
-      stylehacks: 6.0.0(postcss@8.4.31)
+      stylehacks: 6.0.0(postcss@8.4.32)
     dev: false
 
-  /postcss-merge-rules@6.0.1(postcss@8.4.31):
+  /postcss-merge-rules@6.0.1(postcss@8.4.32):
     resolution: {integrity: sha512-a4tlmJIQo9SCjcfiCcCMg/ZCEe0XTkl/xK0XHBs955GWg9xDX3NwP9pwZ78QUOWB8/0XCjZeJn98Dae0zg6AAw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
@@ -16571,157 +16398,157 @@ packages:
     dependencies:
       browserslist: 4.21.9
       caniuse-api: 3.0.0
-      cssnano-utils: 4.0.0(postcss@8.4.31)
-      postcss: 8.4.31
+      cssnano-utils: 4.0.0(postcss@8.4.32)
+      postcss: 8.4.32
       postcss-selector-parser: 6.0.13
     dev: false
 
-  /postcss-minify-font-values@6.0.0(postcss@8.4.31):
+  /postcss-minify-font-values@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-minify-gradients@6.0.0(postcss@8.4.31):
+  /postcss-minify-gradients@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
       colord: 2.9.3
-      cssnano-utils: 4.0.0(postcss@8.4.31)
-      postcss: 8.4.31
+      cssnano-utils: 4.0.0(postcss@8.4.32)
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-minify-params@6.0.0(postcss@8.4.31):
+  /postcss-minify-params@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
       browserslist: 4.21.9
-      cssnano-utils: 4.0.0(postcss@8.4.31)
-      postcss: 8.4.31
+      cssnano-utils: 4.0.0(postcss@8.4.32)
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-minify-selectors@6.0.0(postcss@8.4.31):
+  /postcss-minify-selectors@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-selector-parser: 6.0.13
     dev: false
 
-  /postcss-normalize-charset@6.0.0(postcss@8.4.31):
+  /postcss-normalize-charset@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
     dev: false
 
-  /postcss-normalize-display-values@6.0.0(postcss@8.4.31):
+  /postcss-normalize-display-values@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-positions@6.0.0(postcss@8.4.31):
+  /postcss-normalize-positions@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-repeat-style@6.0.0(postcss@8.4.31):
+  /postcss-normalize-repeat-style@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-string@6.0.0(postcss@8.4.31):
+  /postcss-normalize-string@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-timing-functions@6.0.0(postcss@8.4.31):
+  /postcss-normalize-timing-functions@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-unicode@6.0.0(postcss@8.4.31):
+  /postcss-normalize-unicode@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
       browserslist: 4.21.9
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-url@6.0.0(postcss@8.4.31):
+  /postcss-normalize-url@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-whitespace@6.0.0(postcss@8.4.31):
+  /postcss-normalize-whitespace@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-ordered-values@6.0.0(postcss@8.4.31):
+  /postcss-ordered-values@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      cssnano-utils: 4.0.0(postcss@8.4.31)
-      postcss: 8.4.31
+      cssnano-utils: 4.0.0(postcss@8.4.32)
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-reduce-initial@6.0.0(postcss@8.4.31):
+  /postcss-reduce-initial@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
@@ -16729,16 +16556,16 @@ packages:
     dependencies:
       browserslist: 4.21.9
       caniuse-api: 3.0.0
-      postcss: 8.4.31
+      postcss: 8.4.32
     dev: false
 
-  /postcss-reduce-transforms@6.0.0(postcss@8.4.31):
+  /postcss-reduce-transforms@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
@@ -16749,24 +16576,24 @@ packages:
       cssesc: 3.0.0
       util-deprecate: 1.0.2
 
-  /postcss-svgo@6.0.0(postcss@8.4.31):
+  /postcss-svgo@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==}
     engines: {node: ^14 || ^16 || >= 18}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-value-parser: 4.2.0
       svgo: 3.0.2
     dev: false
 
-  /postcss-unique-selectors@6.0.0(postcss@8.4.31):
+  /postcss-unique-selectors@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-selector-parser: 6.0.13
     dev: false
 
@@ -16774,11 +16601,11 @@ packages:
     resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
     dev: false
 
-  /postcss@8.4.31:
-    resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
+  /postcss@8.4.32:
+    resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==}
     engines: {node: ^10 || ^12 || >=14}
     dependencies:
-      nanoid: 3.3.6
+      nanoid: 3.3.7
       picocolors: 1.0.0
       source-map-js: 1.0.2
 
@@ -16903,16 +16730,6 @@ packages:
     engines: {node: '>= 0.8'}
     dev: true
 
-  /prismjs@1.27.0:
-    resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /prismjs@1.29.0:
-    resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
-    engines: {node: '>=6'}
-    dev: true
-
   /private-ip@2.3.3:
     resolution: {integrity: sha512-5zyFfekIVUOTVbL92hc8LJOtE/gyGHeREHkJ2yTyByP8Q2YZVoBqLg3EfYLeF0oVvGqtaEX2t2Qovja0/gStXw==}
     dependencies:
@@ -16988,12 +16805,6 @@ packages:
       react-is: 16.13.1
     dev: true
 
-  /property-information@5.6.0:
-    resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==}
-    dependencies:
-      xtend: 4.0.2
-    dev: true
-
   /proto-list@1.2.4:
     resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
     dev: true
@@ -17141,7 +16952,7 @@ packages:
     engines: {node: '>=8.16.0'}
     dependencies:
       '@types/mime-types': 2.1.4
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       extract-zip: 1.7.0
       https-proxy-agent: 4.0.0
       mime: 2.6.0
@@ -17330,16 +17141,16 @@ packages:
       typescript: 5.3.2
     dev: true
 
-  /react-docgen@6.0.4:
-    resolution: {integrity: sha512-gF+p+1ZwC2eO66bt763Tepmh5q9kDiFIrqW3YjUV/a+L96h0m5+/wSFQoOHL2cffyrPMZMxP03IgbggJ11QbOw==}
-    engines: {node: '>=14.18.0'}
+  /react-docgen@7.0.1:
+    resolution: {integrity: sha512-rCz0HBIT0LWbIM+///LfRrJoTKftIzzwsYDf0ns5KwaEjejMHQRtphcns+IXFHDNY9pnz6G8l/JbbI6pD4EAIA==}
+    engines: {node: '>=16.14.0'}
     dependencies:
       '@babel/core': 7.22.11
       '@babel/traverse': 7.22.11
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.3
       '@types/babel__core': 7.20.0
       '@types/babel__traverse': 7.20.0
-      '@types/doctrine': 0.0.6
+      '@types/doctrine': 0.0.9
       '@types/resolve': 1.20.3
       doctrine: 3.0.0
       resolve: 1.22.8
@@ -17371,14 +17182,6 @@ packages:
       react-is: 18.1.0
     dev: true
 
-  /react-inspector@6.0.1(react@18.2.0):
-    resolution: {integrity: sha512-cxKSeFTf7jpSSVddm66sKdolG90qURAX3g1roTeaN6x0YEbtWc8JpmFN9+yIqLNH2uEkYerWLtJZIXRIFuBKrg==}
-    peerDependencies:
-      react: ^16.8.4 || ^17.0.0 || ^18.0.0
-    dependencies:
-      react: 18.2.0
-    dev: true
-
   /react-is@16.13.1:
     resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
     dev: true
@@ -17449,19 +17252,6 @@ packages:
       tslib: 2.6.2
     dev: true
 
-  /react-syntax-highlighter@15.5.0(react@18.2.0):
-    resolution: {integrity: sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==}
-    peerDependencies:
-      react: '>= 0.14.0'
-    dependencies:
-      '@babel/runtime': 7.23.2
-      highlight.js: 10.7.3
-      lowlight: 1.20.0
-      prismjs: 1.29.0
-      react: 18.2.0
-      refractor: 3.6.0
-    dev: true
-
   /react@18.2.0:
     resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
     engines: {node: '>=0.10.0'}
@@ -17550,16 +17340,6 @@ packages:
     engines: {node: '>= 12.13.0'}
     dev: false
 
-  /recast@0.21.5:
-    resolution: {integrity: sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==}
-    engines: {node: '>= 4'}
-    dependencies:
-      ast-types: 0.15.2
-      esprima: 4.0.1
-      source-map: 0.6.1
-      tslib: 2.6.2
-    dev: true
-
   /recast@0.22.0:
     resolution: {integrity: sha512-5AAx+mujtXijsEavc5lWXBPQqrM4+Dl5qNH96N2aNeuJFUzpiiToKPsxQD/zAIJHspz7zz0maX0PCtCTFVlixQ==}
     engines: {node: '>= 4'}
@@ -17582,6 +17362,17 @@ packages:
       tslib: 2.6.2
     dev: true
 
+  /recast@0.23.4:
+    resolution: {integrity: sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==}
+    engines: {node: '>= 4'}
+    dependencies:
+      assert: 2.0.0
+      ast-types: 0.16.1
+      esprima: 4.0.1
+      source-map: 0.6.1
+      tslib: 2.6.2
+    dev: true
+
   /reconnecting-websocket@4.4.0:
     resolution: {integrity: sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==}
     dev: false
@@ -17621,14 +17412,6 @@ packages:
     resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}
     dev: false
 
-  /refractor@3.6.0:
-    resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==}
-    dependencies:
-      hastscript: 6.0.0
-      parse-entities: 2.0.0
-      prismjs: 1.27.0
-    dev: true
-
   /regenerate-unicode-properties@10.1.0:
     resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
     engines: {node: '>=4'}
@@ -17646,8 +17429,8 @@ packages:
   /regenerator-runtime@0.14.0:
     resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==}
 
-  /regenerator-transform@0.15.1:
-    resolution: {integrity: sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==}
+  /regenerator-transform@0.15.2:
+    resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==}
     dependencies:
       '@babel/runtime': 7.23.2
     dev: true
@@ -17888,23 +17671,23 @@ packages:
       fsevents: 2.3.3
     dev: true
 
-  /rollup@4.6.0:
-    resolution: {integrity: sha512-R8i5Her4oO1LiMQ3jKf7MUglYV/mhQ5g5OKeld5CnkmPdIGo79FDDQYqPhq/PCVuTQVuxsWgIbDy9F+zdHn80w==}
+  /rollup@4.6.1:
+    resolution: {integrity: sha512-jZHaZotEHQaHLgKr8JnQiDT1rmatjgKlMekyksz+yk9jt/8z9quNjnKNRoaM0wd9DC2QKXjmWWuDYtM3jfF8pQ==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.6.0
-      '@rollup/rollup-android-arm64': 4.6.0
-      '@rollup/rollup-darwin-arm64': 4.6.0
-      '@rollup/rollup-darwin-x64': 4.6.0
-      '@rollup/rollup-linux-arm-gnueabihf': 4.6.0
-      '@rollup/rollup-linux-arm64-gnu': 4.6.0
-      '@rollup/rollup-linux-arm64-musl': 4.6.0
-      '@rollup/rollup-linux-x64-gnu': 4.6.0
-      '@rollup/rollup-linux-x64-musl': 4.6.0
-      '@rollup/rollup-win32-arm64-msvc': 4.6.0
-      '@rollup/rollup-win32-ia32-msvc': 4.6.0
-      '@rollup/rollup-win32-x64-msvc': 4.6.0
+      '@rollup/rollup-android-arm-eabi': 4.6.1
+      '@rollup/rollup-android-arm64': 4.6.1
+      '@rollup/rollup-darwin-arm64': 4.6.1
+      '@rollup/rollup-darwin-x64': 4.6.1
+      '@rollup/rollup-linux-arm-gnueabihf': 4.6.1
+      '@rollup/rollup-linux-arm64-gnu': 4.6.1
+      '@rollup/rollup-linux-arm64-musl': 4.6.1
+      '@rollup/rollup-linux-x64-gnu': 4.6.1
+      '@rollup/rollup-linux-x64-musl': 4.6.1
+      '@rollup/rollup-win32-arm64-msvc': 4.6.1
+      '@rollup/rollup-win32-ia32-msvc': 4.6.1
+      '@rollup/rollup-win32-x64-msvc': 4.6.1
       fsevents: 2.3.3
 
   /rrweb-cssom@0.6.0:
@@ -17979,7 +17762,7 @@ packages:
       htmlparser2: 8.0.1
       is-plain-object: 5.0.0
       parse-srcset: 1.0.2
-      postcss: 8.4.31
+      postcss: 8.4.32
     dev: false
 
   /sass@1.69.5:
@@ -18197,7 +17980,7 @@ packages:
     dependencies:
       '@hapi/hoek': 10.0.1
       '@hapi/wreck': 18.0.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       joi: 17.7.0
     transitivePeerDependencies:
       - supports-color
@@ -18402,7 +18185,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       socks: 2.7.1
     transitivePeerDependencies:
       - supports-color
@@ -18555,7 +18338,7 @@ packages:
       arg: 5.0.2
       bluebird: 3.7.2
       check-more-types: 2.24.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       execa: 5.1.1
       lazy-ass: 1.6.0
       ps-tree: 1.2.0
@@ -18583,11 +18366,11 @@ packages:
     resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==}
     dev: true
 
-  /storybook@7.5.3:
-    resolution: {integrity: sha512-lkn9hcedNmSNCzbDIrky2LpZJqlpS7Fy1KpGBZmLY34g5Mb0+KnXaUqzY0dxsd7aFm8Oa7Du/emceMYNNL4DMA==}
+  /storybook@7.6.3:
+    resolution: {integrity: sha512-H3odxahMiR8vVW7ltlqcHhn3UVH5ta03weKlY7xvpv5DV+thZ+mEO2cDYfsufCSg0Ldb5LQ4qq3OyLVdpDBN8g==}
     hasBin: true
     dependencies:
-      '@storybook/cli': 7.5.3
+      '@storybook/cli': 7.6.3
     transitivePeerDependencies:
       - bufferutil
       - encoding
@@ -18804,14 +18587,14 @@ packages:
       peek-readable: 5.0.0
     dev: false
 
-  /stylehacks@6.0.0(postcss@8.4.31):
+  /stylehacks@6.0.0(postcss@8.4.32):
     resolution: {integrity: sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
       postcss: ^8.2.15
     dependencies:
       browserslist: 4.21.9
-      postcss: 8.4.31
+      postcss: 8.4.32
       postcss-selector-parser: 6.0.13
     dev: false
 
@@ -18871,8 +18654,8 @@ packages:
     resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==}
     dev: true
 
-  /systeminformation@5.21.18:
-    resolution: {integrity: sha512-PEoWd95nI5170rvIk4fagLH0SmzwfGt18w0+ex1Ljb2bSXvDs9PQdLNexMazL5L6Pzd6wxlpoWUAjX+hNRKN7g==}
+  /systeminformation@5.21.20:
+    resolution: {integrity: sha512-AyS1fNc+MDoAJtFknFbbo587H8h6yejJwM+H9rVusnOToIEkiMehMyD5JM7o3j55Cto20MawIZrcgNMgd4BfOQ==}
     engines: {node: '>=8.0.0'}
     os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android]
     hasBin: true
@@ -19015,8 +18798,8 @@ packages:
       real-require: 0.2.0
     dev: false
 
-  /three@0.158.0:
-    resolution: {integrity: sha512-TALj4EOpdDPF1henk2Q+s17K61uEAAWQ7TJB68nr7FKxqwyDr3msOt5IWdbGm4TaWKjrtWS8DJJWe9JnvsWOhQ==}
+  /three@0.159.0:
+    resolution: {integrity: sha512-eCmhlLGbBgucuo4VEA9IO3Qpc7dh8Bd4VKzr7WfW4+8hMcIfoAVi1ev0pJYN9PTTsCslbcKgBwr2wNZ1EvLInA==}
     dev: false
 
   /throttle-debounce@5.0.0:
@@ -19186,6 +18969,7 @@ packages:
   /ts-case-convert@2.0.2:
     resolution: {integrity: sha512-vdKfx1VAdpvEBOBv5OpVu5ZFqRg9HdTI4sYt6qqMeICBeNyXvitrarCnFWNDAki51IKwCyx+ZssY46Q9jH5otA==}
     dev: true
+    bundledDependencies: []
 
   /ts-dedent@2.2.0:
     resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
@@ -19438,7 +19222,7 @@ packages:
       chalk: 4.1.2
       cli-highlight: 2.1.11
       date-fns: 2.30.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       dotenv: 16.0.3
       glob: 8.1.0
       ioredis: 5.3.2
@@ -19612,15 +19396,6 @@ packages:
     resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
     engines: {node: '>= 0.8'}
 
-  /unplugin@1.4.0:
-    resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==}
-    dependencies:
-      acorn: 8.11.2
-      chokidar: 3.5.3
-      webpack-sources: 3.2.3
-      webpack-virtual-modules: 0.5.0
-    dev: true
-
   /unplugin@1.5.1:
     resolution: {integrity: sha512-0QkvG13z6RD+1L1FoibQqnvTwVBXvS4XSPwAyinVgoOCl2jAgwzdUKmEj05o4Lt8xwQI85Hb6mSyYkcAGwZPew==}
     dependencies:
@@ -19628,7 +19403,6 @@ packages:
       chokidar: 3.5.3
       webpack-sources: 3.2.3
       webpack-virtual-modules: 0.6.0
-    dev: false
 
   /untildify@4.0.0:
     resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
@@ -19645,6 +19419,17 @@ packages:
       escalade: 3.1.1
       picocolors: 1.0.0
 
+  /update-browserslist-db@1.0.13(browserslist@4.22.2):
+    resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+    dependencies:
+      browserslist: 4.22.2
+      escalade: 3.1.1
+      picocolors: 1.0.0
+    dev: true
+
   /uri-js@4.4.1:
     resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
     dependencies:
@@ -19796,17 +19581,17 @@ packages:
       core-util-is: 1.0.2
       extsprintf: 1.3.0
 
-  /vite-node@0.34.6(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0):
+  /vite-node@0.34.6(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0):
     resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
     dependencies:
       cac: 6.7.14
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       mlly: 1.4.0
       pathe: 1.1.1
       picocolors: 1.0.0
-      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -19822,8 +19607,8 @@ packages:
     resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
     dev: true
 
-  /vite@5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0):
-    resolution: {integrity: sha512-6CCq1CAJCNM1ya2ZZA7+jS2KgnhbzvxakmlIjN24cF/PXhRMzpM/z8QgsVJA/Dm5fWUWnVEsmtBoMhmerPxT0g==}
+  /vite@5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0):
+    resolution: {integrity: sha512-OekeWqR9Ls56f3zd4CaxzbbS11gqYkEiBtnWFFgYR2WV8oPJRRKq0mpskYy/XaoCL3L7VINDhqqOMNDiYdGvGg==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
@@ -19850,10 +19635,10 @@ packages:
       terser:
         optional: true
     dependencies:
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       esbuild: 0.19.8
-      postcss: 8.4.31
-      rollup: 4.6.0
+      postcss: 8.4.32
+      rollup: 4.6.1
       sass: 1.69.5
       terser: 5.24.0
     optionalDependencies:
@@ -19904,7 +19689,7 @@ packages:
     dependencies:
       '@types/chai': 4.3.5
       '@types/chai-subset': 1.3.3
-      '@types/node': 20.10.0
+      '@types/node': 20.10.3
       '@vitest/expect': 0.34.6
       '@vitest/runner': 0.34.6
       '@vitest/snapshot': 0.34.6
@@ -19914,7 +19699,7 @@ packages:
       acorn-walk: 8.2.0
       cac: 6.7.14
       chai: 4.3.10
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       happy-dom: 10.0.3
       local-pkg: 0.4.3
       magic-string: 0.30.3
@@ -19924,8 +19709,8 @@ packages:
       strip-literal: 1.0.1
       tinybench: 2.5.0
       tinypool: 0.7.0
-      vite: 5.0.2(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
-      vite-node: 0.34.6(@types/node@20.10.0)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
+      vite-node: 0.34.6(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
       why-is-node-running: 2.2.2
     transitivePeerDependencies:
       - less
@@ -19949,8 +19734,8 @@ packages:
     resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
     dev: false
 
-  /vue-component-type-helpers@1.8.22:
-    resolution: {integrity: sha512-LK3wJHs3vJxHG292C8cnsRusgyC5SEZDCzDCD01mdE/AoREFMl2tzLRuzwyuEsOIz13tqgBcnvysN3Lxsa14Fw==}
+  /vue-component-type-helpers@1.8.24:
+    resolution: {integrity: sha512-lqWs/7fdRXoSBAlbouHBX+LNuaY6gI9xWW34m/ZIz9zVPYHEyw0b2/zaCBwlKx0NtKTeF/6pOpvrxVkh7nhIYg==}
     dev: true
 
   /vue-component-type-helpers@1.8.4:
@@ -19975,9 +19760,9 @@ packages:
   /vue-docgen-api@4.64.1(vue@3.3.9):
     resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==}
     dependencies:
-      '@babel/parser': 7.23.0
-      '@babel/types': 7.22.17
-      '@vue/compiler-dom': 3.3.7
+      '@babel/parser': 7.23.3
+      '@babel/types': 7.23.3
+      '@vue/compiler-dom': 3.3.9
       '@vue/compiler-sfc': 3.3.9
       ast-types: 0.14.2
       hash-sum: 2.0.0
@@ -19990,14 +19775,14 @@ packages:
       - vue
     dev: true
 
-  /vue-eslint-parser@9.3.2(eslint@8.54.0):
+  /vue-eslint-parser@9.3.2(eslint@8.55.0):
     resolution: {integrity: sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==}
     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.54.0
+      debug: 4.3.4(supports-color@5.5.0)
+      eslint: 8.55.0
       eslint-scope: 7.2.2
       eslint-visitor-keys: 3.4.3
       espree: 9.6.1
@@ -20023,14 +19808,14 @@ packages:
       he: 1.2.0
     dev: true
 
-  /vue-tsc@1.8.22(typescript@5.3.2):
-    resolution: {integrity: sha512-j9P4kHtW6eEE08aS5McFZE/ivmipXy0JzrnTgbomfABMaVKx37kNBw//irL3+LlE3kOo63XpnRigyPC3w7+z+A==}
+  /vue-tsc@1.8.24(typescript@5.3.2):
+    resolution: {integrity: sha512-eH1CSj231OzVEY5Hi7wS6ubzyOEwgr5jCptR0Ddf2SitGcaXIsPVDvrprm3eolCdyhDt3WS1Eb2F4fGX9BsUUw==}
     hasBin: true
     peerDependencies:
       typescript: '*'
     dependencies:
-      '@volar/typescript': 1.10.7
-      '@vue/language-core': 1.8.22(typescript@5.3.2)
+      '@volar/typescript': 1.11.1
+      '@vue/language-core': 1.8.24(typescript@5.3.2)
       semver: 7.5.4
       typescript: 5.3.2
     dev: true
@@ -20138,13 +19923,8 @@ packages:
     resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
     engines: {node: '>=10.13.0'}
 
-  /webpack-virtual-modules@0.5.0:
-    resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
-    dev: true
-
   /webpack-virtual-modules@0.6.0:
     resolution: {integrity: sha512-KnaMTE6EItz/f2q4Gwg5/rmeKVi79OR58NoYnwDJqCk9ywMtTGbBnBcfoBtN4QbYu0lWXvyMoH2Owxuhe4qI6Q==}
-    dev: false
 
   /whatwg-encoding@2.0.0:
     resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
@@ -20534,7 +20314,7 @@ packages:
       sharp: 0.31.3
     dev: false
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.5.3)(@storybook/components@7.5.3)(@storybook/core-events@7.5.3)(@storybook/manager-api@7.5.3)(@storybook/preview-api@7.5.3)(@storybook/theming@7.5.3)(@storybook/types@7.5.3)(react-dom@18.2.0)(react@18.2.0):
+  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.3)(@storybook/components@7.5.3)(@storybook/core-events@7.6.3)(@storybook/manager-api@7.6.3)(@storybook/preview-api@7.6.3)(@storybook/theming@7.6.3)(@storybook/types@7.6.3)(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
@@ -20555,13 +20335,13 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/blocks': 7.5.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/blocks': 7.6.3(react-dom@18.2.0)(react@18.2.0)
       '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.5.3
-      '@storybook/manager-api': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.5.3
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.3
+      '@storybook/core-events': 7.6.3
+      '@storybook/manager-api': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.6.3
+      '@storybook/theming': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.3
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     dev: true

From 920e52117682204c0e254eea298d5e231c7cce31 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 4 Dec 2023 20:04:34 +0900
Subject: [PATCH 135/435] 2023.12.0-beta.2

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

diff --git a/package.json b/package.json
index aa67b006db..e814d71dc0 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.12.0-beta.1",
+	"version": "2023.12.0-beta.2",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From 8866c530c41fe4144a72b0c4190362b6249b2629 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 4 Dec 2023 20:33:11 +0900
Subject: [PATCH 136/435] =?UTF-8?q?fix(backend):=20=E3=82=A8=E3=83=9D?=
 =?UTF-8?q?=E3=83=83=E3=82=AF=E3=82=92=E5=9B=BA=E5=AE=9A=E3=81=99=E3=82=8B?=
 =?UTF-8?q?=E3=81=93=E3=81=A8=E3=81=A7=E5=B9=B4=E8=B6=8A=E3=81=97=E6=99=82?=
 =?UTF-8?q?=E3=81=AB=E3=83=88=E3=83=AC=E3=83=B3=E3=83=89=E3=81=8C=E5=A3=8A?=
 =?UTF-8?q?=E3=82=8C=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#12567)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                 | 1 +
 packages/backend/src/core/FeaturedService.ts | 4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 51eb5400ba..d025e5e1c0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -58,6 +58,7 @@
 - Fix: Social/Local/Home Timelineにてインスタンスミュートが効かない問題
 - Fix: ユーザのノート一覧にてインスタンスミュートが効かない問題
 - Fix: チャンネルのノート一覧にてインスタンスミュートが効かない問題
+- Fix: 「みつける」が年越し時に壊れる問題を修正
 
 ## 2023.11.1
 
diff --git a/packages/backend/src/core/FeaturedService.ts b/packages/backend/src/core/FeaturedService.ts
index 507fc464ff..d970ffa43b 100644
--- a/packages/backend/src/core/FeaturedService.ts
+++ b/packages/backend/src/core/FeaturedService.ts
@@ -14,6 +14,8 @@ export const GALLERY_POSTS_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 3; // 3日ご
 const PER_USER_NOTES_RANKING_WINDOW = 1000 * 60 * 60 * 24 * 7; // 1週間ごと
 const HASHTAG_RANKING_WINDOW = 1000 * 60 * 60; // 1時間ごと
 
+const featuredEpoc = new Date('2023-01-01T00:00:00Z').getTime();
+
 @Injectable()
 export class FeaturedService {
 	constructor(
@@ -24,7 +26,7 @@ export class FeaturedService {
 
 	@bindThis
 	private getCurrentWindow(windowRange: number): number {
-		const passed = new Date().getTime() - new Date(new Date().getFullYear(), 0, 1).getTime();
+		const passed = new Date().getTime() - featuredEpoc;
 		return Math.floor(passed / windowRange);
 	}
 

From c05de08a3b2ca08931b22d0ddd62b5cce690f881 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Mon, 4 Dec 2023 14:13:52 +0000
Subject: [PATCH 137/435] prettier query for bubble timeline
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

I *think* this works; in my test instance (with no federated notes…)
the generated query looks correct, and PostgreSQL doesn't complain.
---
 .../backend/src/server/api/endpoints/notes/bubble-timeline.ts | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts
index 0652c82a9d..4b326a4ee4 100644
--- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts
@@ -79,9 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
 				.andWhere('note.visibility = \'public\'')
 				.andWhere('note.channelId IS NULL')
-				.andWhere(
-					`(note.userHost = ANY ('{"${instance.bubbleInstances.join('","')}"}'))`,
-				)		
+				.andWhere('note.userHost IN (:...hosts)', { hosts: instance.bubbleInstances})
 				.innerJoinAndSelect('note.user', 'user')
 				.leftJoinAndSelect('note.reply', 'reply')
 				.leftJoinAndSelect('note.renote', 'renote')

From 216d1779974512effe8218949aceb8da21a1a549 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Mon, 4 Dec 2023 14:26:37 +0000
Subject: [PATCH 138/435] fix the spacing

thanks, linter
---
 .../backend/src/server/api/endpoints/notes/bubble-timeline.ts   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts
index 4b326a4ee4..c5e3a5a5f7 100644
--- a/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/bubble-timeline.ts
@@ -79,7 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
 				.andWhere('note.visibility = \'public\'')
 				.andWhere('note.channelId IS NULL')
-				.andWhere('note.userHost IN (:...hosts)', { hosts: instance.bubbleInstances})
+				.andWhere('note.userHost IN (:...hosts)', { hosts: instance.bubbleInstances })
 				.innerJoinAndSelect('note.user', 'user')
 				.leftJoinAndSelect('note.reply', 'reply')
 				.leftJoinAndSelect('note.renote', 'renote')

From d5b598d69608e66dabae65216a6d90d3f0cbc0e1 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Mon, 4 Dec 2023 15:45:26 +0100
Subject: [PATCH 139/435] fix: edit when it comes to quotes

---
 .../src/server/api/endpoints/notes/edit.ts    | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/packages/backend/src/server/api/endpoints/notes/edit.ts b/packages/backend/src/server/api/endpoints/notes/edit.ts
index d5592c4726..cfbc207853 100644
--- a/packages/backend/src/server/api/endpoints/notes/edit.ts
+++ b/packages/backend/src/server/api/endpoints/notes/edit.ts
@@ -123,6 +123,18 @@ export const meta = {
 			code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL',
 			id: '33510210-8452-094c-6227-4a6c05d99f00',
 		},
+
+		cannotQuoteaQuoteOfCurrentPost: {
+			message: 'Cannot quote a quote of edited note.',
+			code: 'CANNOT_QUOTE_A_QUOTE_OF_EDITED_NOTE',
+			id: '33510210-8452-094c-6227-4a6c05d99f01',
+		},
+
+		cannotQuoteCurrentPost: {
+			message: 'Cannot quote the current note.',
+			code: 'CANNOT_QUOTE_THE_CURRENT_NOTE',
+			id: '33510210-8452-094c-6227-4a6c05d99f02',
+		},
 	},
 } as const;
 
@@ -268,6 +280,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}
 
 			let renote: MiNote | null = null;
+
+			if (ps.renoteId === ps.editId) {
+				throw new ApiError(meta.errors.cannotQuoteCurrentPost);
+			}
+			
 			if (ps.renoteId != null) {
 				// Fetch renote to note
 				renote = await this.notesRepository.findOneBy({ id: ps.renoteId });
@@ -278,6 +295,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					throw new ApiError(meta.errors.cannotReRenote);
 				}
 
+				if (renote.renoteId === ps.editId) {
+					throw new ApiError(meta.errors.cannotQuoteaQuoteOfCurrentPost);
+				}
+
 				// Check blocking
 				if (renote.userId !== me.id) {
 					const blockExist = await this.blockingsRepository.exist({

From b78f4af50feda680cdafe4ebceae94e9b8b53193 Mon Sep 17 00:00:00 2001
From: David <daj@findmyinbox.co.uk>
Date: Mon, 4 Dec 2023 21:01:29 +0000
Subject: [PATCH 140/435] Update es-ES.yml

Added description for multiple choice polls
---
 locales/es-ES.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 0fff937d84..fe7aaecdec 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -1871,6 +1871,7 @@ _poll:
   remainingHours: "Quedan {h} horas y {m} minutos para que finalice"
   remainingMinutes: "Quedan {m} minutos y {s} segundos para que finalice"
   remainingSeconds: "Quedan {s} segundos para que finalice"
+  multiple: "Opciones múltiples"
 _visibility:
   public: "Público"
   publicDescription: "Visible para todos los usuarios"

From 2993c5b99f98b4ee13ad7231d3994992cab36fe8 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 5 Dec 2023 13:20:18 +0100
Subject: [PATCH 141/435] chore(deps): bump actions/labeler from 4 to 5 (#208)

Bumps [actions/labeler](https://github.com/actions/labeler) from 4 to 5.
- [Release notes](https://github.com/actions/labeler/releases)
- [Commits](https://github.com/actions/labeler/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/labeler
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/labeler.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index fa4a58c3a9..88e2aceaed 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -11,6 +11,6 @@ jobs:
       pull-requests: write
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/labeler@v4
+    - uses: actions/labeler@v5
       with:
         repo-token: "${{ secrets.GITHUB_TOKEN }}"

From 4ddf847a26fd1b30c7e0691816878b3113776934 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 5 Dec 2023 13:20:29 +0100
Subject: [PATCH 142/435] chore(deps): bump actions/setup-python from 4.7.1 to
 4.8.0 (#209)

Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.7.1 to 4.8.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4.7.1...v4.8.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  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/package.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
index d10d0fcafd..32e3c8a717 100644
--- a/.github/workflows/package.yml
+++ b/.github/workflows/package.yml
@@ -34,7 +34,7 @@ jobs:
           node-version: ${{ matrix.node-version }}
 
       - name: Setup Python
-        uses: actions/setup-python@v4.7.1
+        uses: actions/setup-python@v4.8.0
         with:
           python-version: ${{ matrix.python-version }}
 

From 62d1cb490b20675b49cd4811059ee016eb9362b2 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Tue, 5 Dec 2023 20:58:53 +0100
Subject: [PATCH 143/435] add: custom icon font

---
 .../sharkey-icons/custom-sharkey-icons.svg    |  11 +++++++
 .../sharkey-icons/custom-sharkey-icons.ttf    | Bin 0 -> 1644 bytes
 .../sharkey-icons/custom-sharkey-icons.woff   | Bin 0 -> 1088 bytes
 .../assets/fonts/sharkey-icons/style.css      |  27 ++++++++++++++++++
 .../backend/src/server/web/views/base.pug     |   1 +
 .../src/components/MkVisitorDashboard.vue     |   2 +-
 packages/frontend/src/ui/_common_/common.ts   |   1 +
 7 files changed, 41 insertions(+), 1 deletion(-)
 create mode 100644 packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.svg
 create mode 100644 packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.ttf
 create mode 100644 packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.woff
 create mode 100644 packages/backend/assets/fonts/sharkey-icons/style.css

diff --git a/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.svg b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.svg
new file mode 100644
index 0000000000..c5f79a9861
--- /dev/null
+++ b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>Generated by Fontastic.me</metadata>
+<defs>
+<font id="custom-sharkey-icons" horiz-adv-x="512">
+<font-face font-family="custom-sharkey-icons" units-per-em="512" ascent="480" descent="-32"/>
+<missing-glyph horiz-adv-x="512" />
+
+<glyph glyph-name="shark" unicode="&#97;" d="M469 171l0-43-42 0c-30 0-60 9-86 21-53-27-117-27-170 0-26-12-56-21-86-21l-42 0 0 43 42 0c30 0 60 10 86 27 51-36 119-36 170 0 26-17 56-27 86-27z m-356 47c11 3 23 9 34 16l24 16c14 49 16 107-9 174 93-16 177-97 209-193 16-10 32-16 48-17-30 140-155 255-291 255-7 0-14-4-18-10-4-6-4-14-1-21 46-92 32-167 4-220m228-105c-51-36-119-36-170 0-26-17-56-28-86-28l-42 0 0-42 42 0c30 0 60 8 86 21 53-28 117-28 170 0 26-13 56-21 86-21l42 0 0 42-42 0c-30 0-60 11-86 28z"/>
+</font></defs></svg>
diff --git a/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.ttf b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.ttf
new file mode 100644
index 0000000000000000000000000000000000000000..f4d6d406b8eefd56f73d876a0fa6e4f348948480
GIT binary patch
literal 1644
zcmc&!O-~b16g{ug4qr78r3HkTk<WI<sig=iYGeT=8jU0feny#;LMyaG+7cmdT)1)P
zLQRZ2H!dW`rArsan7Gh|T^L=!4{+gvgoRqqn@$x!Vl*y%nK$p8bMJd|@0<571b{8L
z3I${F_{rmU0-sI+>KN6w(czIe!l)&WlJ7V%zB_i})%h!cI88p8$XE+_txS<WVLv)+
z<rhjNt^_qid-d%6^349)m1RI_CSOS<t?ADA#$Dof>;owZ${UZ5a~sJ!Q<=h&3K-;%
z$Q$PEgoQ~WD1N?bGuF}qj^GgO`^dGdl}UEyk1x^w6!7d>u=9oVrORu8Z-e$IWNz#q
zZoPeevHsvX>O2&HwVUT&ejN=Mq9}^w)hL^tIgS!33&e7z&CTm5UJS}?*`%Xzv>{2^
zga_gVe5e-3#0>eUJFcSz*W?6WjpOI|enkXhGb7U&f(1*gmR1oF#gdH9ji3^u9ND>7
zP6RTFHrHpEiu7)S7xF)-9EB61#lIt)GnVdE*5u;c_DYHjyqu5Y>b@$*&D!7M@=pEj
ztpDLJ&z!=&EuW07=)!)WE6~y2qxPtV=<kc|4f5&h8aONldUlAWU{geNv{$JFDxC}w
za9s|F{*HE0)Whb)gc;U#Gu(ZlJ8XV_R==&edFM!>xuvP;ezLAg^$k9x#V~YzmmWGB
z;#)T!7y*M%E8A|bzhUBxfBr~=xOA(w#=rQgwY5zTu<6bQ`ElF*fdY-tUF_k_KIdYE
z_u++$y=WBOF7}~LjJsH+eqEj(Zw(cBcFNd;?KtdW1ye}7Se~zsF7_cP4!BrFNL)k$
zi^!t@8yT2HDW2&Z=8(iPd75?`S?WkE<_mVl%%`l}Tyoh=C+sZM-%R+yGwv)@fE;s}
z<p(sx5Mja^FR1p%ft0=b8C);to9t2cJ|J#q3voL)o77^Fs5YqmDqza}W^c@lMPr=t
zkGW4Wha7hx?PR7g9+?d$lev7_&T738l7IX}rI3{G*U$IcO9QKh@c5kFmW<VdMU6;S
a;~=|aiu~uSFyCs1vsUr_$YW~X)!HvMdf*-a

literal 0
HcmV?d00001

diff --git a/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.woff b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.woff
new file mode 100644
index 0000000000000000000000000000000000000000..2ee0f12d8554dff7998fcb4b00a0031a15e1b235
GIT binary patch
literal 1088
zcmXT-cXMN4WB>vd2L@gc&6We<gV?CR%`L>2fq~HgC@KZSGBZX0hP%7Cx&g&}fP6VH
z)@0!F57svVisb<L&OjXIe<7wiIXAHYD7FE}cL&m53|E*F(i4k8Vn2Xl93cGvKLc}m
zPGuSc0~3(Tump(LSaPkY%t%d40g8e6`ao=^;`U>922c<vb_K|n0b&*An@p@3xg`}q
zu@68#KM)JBF)(Q7<R=5wF$1{_Twol=z>u3*0koHy2gnDxS%JZm!8R{3Hx($R1LS`N
znqi_+>|0TgUt9tV7Zsow$PR<dtn!BybCMGh5>gnNR_Di-H@s*MUUdF&216JN`+uMc
zP&k526$4@>AdeXc85vl>JRkwmo060;=fHte2M)eCJmc7b0}uF??l<x<+G1c}V7Q^k
z=m3K<vk-?zv2S8Qf=7x+O3jD=jLmY7ul^5D%wp)?$^8y&MnXbbLxLom8=F7@<0pxO
zHDYYM3TN4tNJ`FVSaan6|ASfwzWjF$`08-r#4(2F22CMGh6|Y7*iJDTi=J7a$iVQ3
zg&i0@K-U7pn+fEmv<pDE=Tya1#}3D-2PZW;hK59X&h?$^JNx358z*9TI(kCSzL7k8
z<*CHcqes3<Or3h;W&@9z>I9*{6++o=!KtdpR!7a+wQ%9CSy55DW@Qy-&D!<9?<2Rj
zhsLgH9v)rY`u<PT(v!DFu}`^p?b@YF7cX^OVwe<lYxP#wt&JX19A6?USe%qAi)>gW
zHZLnG{4^zQ!nA1@g;<$%+1MC#ugV64!!03!;Zc9^`|u;5Z`Y(gNK0a91c!ZQE?e%g
z)ji>jF??(rOc-L1b6<mmb3($NQwI(#IDUkI$-zvSSu25&LEnP$G0?1}1_m{bYrw=&
z%m5AtP*?y{DKvh7v75LdDItx4QB5H&g`v~3;U^CR#}tNkko?mAvtEZCL|XU1l<(N3
ztrv6FtII&Kvm{vj-7h95^Vkf|g}h58XDsQqYtnz<{nJa&rgO89;6mo<KcAhkyi?t%
zeb2DuxWQAqPYv1ECbloBagj4LEuVNiX4dl)!Y}iSW?gKZ5;^}>#Q|OaiS2uWT;)!F
zmaII}TiIiim=>pDap}S%%LgvjW-Q;P%=xnJ%lnl}gJou4laJYayQQ|WDD+|W%6gVM
zi|KpseJfAU-mj{D@zAu^SNof5c0a#l^mMLPsO^fz^|zPu-@f$BGfVv2_D0q7vD0?j
zoSkY>{C#SV<NeV5&9j!@ZVQ#1b?UmA_od?K8pf2CbRl5SfWkk?fvtz<MFXqOsUs(j
QM9rBPF@=|by_ZP`07@x<!2kdN

literal 0
HcmV?d00001

diff --git a/packages/backend/assets/fonts/sharkey-icons/style.css b/packages/backend/assets/fonts/sharkey-icons/style.css
new file mode 100644
index 0000000000..36279ac0eb
--- /dev/null
+++ b/packages/backend/assets/fonts/sharkey-icons/style.css
@@ -0,0 +1,27 @@
+@charset "UTF-8";
+
+@font-face {
+  font-family: "custom-sharkey-icons";
+  src: url("./custom-sharkey-icons.woff") format("woff"),
+    url("./custom-sharkey-icons.ttf") format("truetype"),
+    url("./custom-sharkey-icons.svg#custom-sharkey-icons") format("svg");
+  font-weight: normal;
+  font-style: normal;
+  font-display: block;
+}
+
+.sk-icons {
+  font-family: "custom-sharkey-icons" !important;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  speak: none;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.sk-icons.sk-shark:before {
+  content: "\61";
+}
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 243d18202f..018b0bed16 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -41,6 +41,7 @@ html
 		link(rel='prefetch' href=notFoundImageUrl)
 		//- https://github.com/misskey-dev/misskey/issues/9842
 		link(rel='stylesheet' href='/assets/phosphor-icons/bold/style.css')
+		link(rel='stylesheet' href='/static-assets/fonts/sharkey-icons/style.css')
 		link(rel='modulepreload' href=`/vite/${clientEntry.file}`)
 		script(src='/client-assets/libopenmpt.js')
 
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 79465e6ab4..fe76ded7b4 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -103,7 +103,7 @@ function showMenu(ev) {
 		},
 	}, {
 		text: i18n.ts.aboutMisskey,
-		icon: 'ph-info ph-bold ph-lg',
+		icon: 'sk-icons sk-shark ph-bold',
 		action: () => {
 			os.pageWindow('/about-sharkey');
 		},
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index 9baf5861d7..7a3f643561 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -112,6 +112,7 @@ export function openInstanceMenu(ev: MouseEvent) {
 	} : undefined, {
 		type: 'link',
 		text: i18n.ts.aboutMisskey,
+		icon: 'sk-icons sk-shark ph-bold',
 		to: '/about-sharkey',
 	}], ev.currentTarget ?? ev.target, {
 		align: 'left',

From 6f9ba940b9abff8dda2c9ea6c92e5d2786e948b4 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Tue, 5 Dec 2023 21:11:30 +0100
Subject: [PATCH 144/435] fix: inproper borde-radius on search widget

Closes transfem-org/Sharkey#198
---
 packages/frontend/src/widgets/WidgetSearch.vue | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/packages/frontend/src/widgets/WidgetSearch.vue b/packages/frontend/src/widgets/WidgetSearch.vue
index 26cc726717..979341e1ae 100644
--- a/packages/frontend/src/widgets/WidgetSearch.vue
+++ b/packages/frontend/src/widgets/WidgetSearch.vue
@@ -153,3 +153,9 @@ defineExpose<WidgetComponentExpose>({
 	id: props.widget ? props.widget.id : null,
 });
 </script>
+
+<style lang="scss" scoped>
+.skw-search {
+	border-radius: var(--radius-sm) !important;
+}
+</style>

From 93869a5f34386a7bd6e99df779150733fb1730c4 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Tue, 5 Dec 2023 22:19:53 +0100
Subject: [PATCH 145/435] add: mark instance as NSFW

Closes transfem-org/Sharkey#197
---
 .../migration/1701809447000-NSFW-Instance.js  | 11 ++++++
 .../core/activitypub/models/ApImageService.ts | 13 ++++++-
 .../core/entities/InstanceEntityService.ts    |  1 +
 packages/backend/src/models/Instance.ts       |  5 +++
 .../models/json-schema/federation-instance.ts |  5 +++
 .../admin/federation/update-instance.ts       | 39 ++++++++++++-------
 .../api/endpoints/federation/instances.ts     |  9 +++++
 .../frontend/src/pages/about.federation.vue   |  3 ++
 .../frontend/src/pages/admin/federation.vue   |  3 ++
 packages/frontend/src/pages/instance-info.vue | 11 ++++++
 packages/misskey-js/src/entities.ts           |  1 +
 11 files changed, 85 insertions(+), 16 deletions(-)
 create mode 100644 packages/backend/migration/1701809447000-NSFW-Instance.js

diff --git a/packages/backend/migration/1701809447000-NSFW-Instance.js b/packages/backend/migration/1701809447000-NSFW-Instance.js
new file mode 100644
index 0000000000..882aa9865d
--- /dev/null
+++ b/packages/backend/migration/1701809447000-NSFW-Instance.js
@@ -0,0 +1,11 @@
+export class NSFWInstance1701809447000 {
+    name = 'NSFWInstance1701809447000'
+
+    async up(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "instance" ADD "isNSFW" boolean NOT NULL DEFAULT false`);
+    }
+
+    async down(queryRunner) {
+        await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "isNSFW"`);
+    }
+}
diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts
index a4cd533892..b7b8acd661 100644
--- a/packages/backend/src/core/activitypub/models/ApImageService.ts
+++ b/packages/backend/src/core/activitypub/models/ApImageService.ts
@@ -5,7 +5,7 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
-import type { DriveFilesRepository } from '@/models/_.js';
+import type { DriveFilesRepository, InstancesRepository } from '@/models/_.js';
 import type { MiRemoteUser } from '@/models/User.js';
 import type { MiDriveFile } from '@/models/DriveFile.js';
 import { MetaService } from '@/core/MetaService.js';
@@ -18,6 +18,7 @@ import { checkHttps } from '@/misc/check-https.js';
 import { ApResolverService } from '../ApResolverService.js';
 import { ApLoggerService } from '../ApLoggerService.js';
 import type { IObject } from '../type.js';
+import { UtilityService } from '@/core/UtilityService.js';
 
 @Injectable()
 export class ApImageService {
@@ -27,10 +28,14 @@ export class ApImageService {
 		@Inject(DI.driveFilesRepository)
 		private driveFilesRepository: DriveFilesRepository,
 
+		@Inject(DI.instancesRepository)
+		private instancesRepository: InstancesRepository,
+
 		private metaService: MetaService,
 		private apResolverService: ApResolverService,
 		private driveService: DriveService,
 		private apLoggerService: ApLoggerService,
+		private utilityService: UtilityService,
 	) {
 		this.logger = this.apLoggerService.logger;
 	}
@@ -68,6 +73,12 @@ export class ApImageService {
 		// 2. or the image is not sensitive
 		const shouldBeCached = instance.cacheRemoteFiles && (instance.cacheRemoteSensitiveFiles || !image.sensitive);
 
+		const shouldBeSensitive = await this.instancesRepository.findOneBy({ host: this.utilityService.toPuny(actor.host), isNSFW: true });
+
+		if (shouldBeSensitive) {
+			image.sensitive = true;
+		}
+
 		const file = await this.driveService.uploadFromUrl({
 			url: image.url,
 			user: actor,
diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts
index 7d16a7a80e..515b356dee 100644
--- a/packages/backend/src/core/entities/InstanceEntityService.ts
+++ b/packages/backend/src/core/entities/InstanceEntityService.ts
@@ -48,6 +48,7 @@ export class InstanceEntityService {
 			themeColor: instance.themeColor,
 			infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
 			latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null,
+			isNSFW: instance.isNSFW,
 		};
 	}
 
diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts
index b225d918d6..4200b1b461 100644
--- a/packages/backend/src/models/Instance.ts
+++ b/packages/backend/src/models/Instance.ts
@@ -144,4 +144,9 @@ export class MiInstance {
 		nullable: true,
 	})
 	public infoUpdatedAt: Date | null;
+
+	@Column('boolean', {
+		default: false,
+	})
+	public isNSFW: boolean;
 }
diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts
index 442e1076f2..ac4b37fb57 100644
--- a/packages/backend/src/models/json-schema/federation-instance.ts
+++ b/packages/backend/src/models/json-schema/federation-instance.ts
@@ -108,5 +108,10 @@ export const packedFederationInstanceSchema = {
 			optional: false, nullable: true,
 			format: 'date-time',
 		},
+		isNSFW: {
+			type: 'boolean',
+			optional: false,
+			nullable: false,
+		},
 	},
 } as const;
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 357bf83e87..4db52b1052 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
@@ -23,8 +23,9 @@ export const paramDef = {
 	properties: {
 		host: { type: 'string' },
 		isSuspended: { type: 'boolean' },
+		isNSFW: { type: 'boolean' },
 	},
-	required: ['host', 'isSuspended'],
+	required: ['host'],
 } as const;
 
 @Injectable()
@@ -44,23 +45,31 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new Error('instance not found');
 			}
 
-			await this.federatedInstanceService.update(instance.id, {
-				isSuspended: ps.isSuspended,
-			});
+			if (ps.isSuspended != null) {
+				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,
-					});
+				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,
+						});
+					}
 				}
 			}
+
+			if (ps.isNSFW != null) {
+				await this.federatedInstanceService.update(instance.id, {
+					isNSFW: ps.isNSFW,
+				});
+			}
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts
index c8beefa9c7..e143dcfe89 100644
--- a/packages/backend/src/server/api/endpoints/federation/instances.ts
+++ b/packages/backend/src/server/api/endpoints/federation/instances.ts
@@ -40,6 +40,7 @@ export const paramDef = {
 		federating: { type: 'boolean', nullable: true },
 		subscribing: { type: 'boolean', nullable: true },
 		publishing: { type: 'boolean', nullable: true },
+		nsfw: { type: 'boolean', nullable: true },
 		limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 },
 		offset: { type: 'integer', default: 0 },
 		sort: { type: 'string' },
@@ -103,6 +104,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				}
 			}
 
+			if (typeof ps.nsfw === 'boolean') {
+				if (ps.nsfw) {
+					query.andWhere('instance.isNSFW = TRUE');
+				} else {
+					query.andWhere('instance.isNSFW = FALSE');
+				}
+			}
+
 			if (typeof ps.silenced === "boolean") {
 				const meta = await this.metaService.fetch(true);
 
diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue
index db4fff4e37..27f7784007 100644
--- a/packages/frontend/src/pages/about.federation.vue
+++ b/packages/frontend/src/pages/about.federation.vue
@@ -17,6 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<option value="federating">{{ i18n.ts.federating }}</option>
 				<option value="subscribing">{{ i18n.ts.subscribing }}</option>
 				<option value="publishing">{{ i18n.ts.publishing }}</option>
+				<option value="nsfw">NSFW</option>
 				<option value="suspended">{{ i18n.ts.suspended }}</option>
 				<option value="silenced">{{ i18n.ts.silence }}</option>
 				<option value="blocked">{{ i18n.ts.blocked }}</option>
@@ -78,6 +79,7 @@ const pagination = {
 			state === 'blocked' ? { blocked: true } :
 			state === 'silenced' ? { silenced: true } :
 			state === 'notResponding' ? { notResponding: true } :
+			state === 'nsfw' ? { nsfw: true } :
 			{}),
 	})),
 } as Paging;
@@ -87,6 +89,7 @@ function getStatus(instance) {
 	if (instance.isBlocked) return 'Blocked';
 	if (instance.isSilenced) return 'Silenced';
 	if (instance.isNotResponding) return 'Error';
+	if (instance.isNSFW) return 'NSFW';
 	return 'Alive';
 }
 </script>
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index 39285deb13..e09d5181c5 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -21,6 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<option value="federating">{{ i18n.ts.federating }}</option>
 							<option value="subscribing">{{ i18n.ts.subscribing }}</option>
 							<option value="publishing">{{ i18n.ts.publishing }}</option>
+							<option value="nsfw">NSFW</option>
 							<option value="suspended">{{ i18n.ts.suspended }}</option>
 							<option value="blocked">{{ i18n.ts.blocked }}</option>
 							<option value="silenced">{{ i18n.ts.silence }}</option>
@@ -86,6 +87,7 @@ const pagination = {
 			state === 'blocked' ? { blocked: true } :
 			state === 'silenced' ? { silenced: true } :
 			state === 'notResponding' ? { notResponding: true } :
+			state === 'nsfw' ? { nsfw: true } :
 			{}),
 	})),
 };
@@ -95,6 +97,7 @@ function getStatus(instance) {
 	if (instance.isBlocked) return 'Blocked';
 	if (instance.isSilenced) return 'Silenced';
 	if (instance.isNotResponding) return 'Error';
+	if (instance.isNSFW) return 'NSFW';
 	return 'Alive';
 }
 
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 51e6939c2d..668e4e61bf 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -37,6 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkSwitch v-model="suspended" :disabled="!instance" @update:modelValue="toggleSuspend">{{ i18n.ts.stopActivityDelivery }}</MkSwitch>
 					<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
 					<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
+					<MkSwitch v-model="isNSFW" :disabled="!instance" @update:modelValue="toggleNSFW">Mark as NSFW</MkSwitch>
 					<MkButton @click="refreshMetadata"><i class="ph-arrows-counter-clockwise ph-bold ph-lg"></i> Refresh metadata</MkButton>
 				</div>
 			</FormSection>
@@ -149,6 +150,7 @@ let instance = $ref<Misskey.entities.Instance | null>(null);
 let suspended = $ref(false);
 let isBlocked = $ref(false);
 let isSilenced = $ref(false);
+let isNSFW = $ref(false);
 let faviconUrl = $ref<string | null>(null);
 
 const usersPagination = {
@@ -172,6 +174,7 @@ async function fetch(): Promise<void> {
 	suspended = instance.isSuspended;
 	isBlocked = instance.isBlocked;
 	isSilenced = instance.isSilenced;
+	isNSFW = instance.isNSFW;
 	faviconUrl = getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.iconUrl, 'preview');
 }
 
@@ -201,6 +204,14 @@ async function toggleSuspend(): Promise<void> {
 	});
 }
 
+async function toggleNSFW(): Promise<void> {
+	if (!instance) throw new Error('No instance?');
+	await os.api('admin/federation/update-instance', {
+		host: instance.host,
+		isNSFW: isNSFW,
+	});
+}
+
 function refreshMetadata(): void {
 	if (!instance) throw new Error('No instance?');
 	os.api('admin/federation/refresh-remote-instance-metadata', {
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 05960a5719..1a4509cabf 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -609,6 +609,7 @@ export type Instance = {
 	faviconUrl: string | null;
 	themeColor: string | null;
 	infoUpdatedAt: DateString | null;
+	isNSFW: boolean;
 };
 
 export type Signin = {

From ad60e43ae43e24c1c2341a60d8b81e6bae6cdb64 Mon Sep 17 00:00:00 2001
From: Yuriha <121590760+yuriha-chan@users.noreply.github.com>
Date: Wed, 6 Dec 2023 12:07:53 +0900
Subject: [PATCH 146/435] =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9?=
 =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=81=AE=E3=80=8C=E3=83=AA=E3=83=8E=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E3=82=92=E8=A1=A8=E7=A4=BA=E3=80=8D=E3=81=AE=E3=83=88?=
 =?UTF-8?q?=E3=82=B0=E3=83=AB=E3=82=B9=E3=82=A4=E3=83=83=E3=83=81=E3=81=8C?=
 =?UTF-8?q?=E5=8F=8D=E5=BF=9C=E3=81=97=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C?=
 =?UTF-8?q?=E3=82=92=E7=9B=B4=E3=81=99=20(#12577)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* [frontend] Fix renote toggle switch

* Fix MkMenu rather than usage
---
 packages/frontend/src/components/MkMenu.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 4fafd35f72..736f48ea3c 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -202,7 +202,7 @@ function focusDown() {
 }
 
 function switchItem(item: MenuSwitch & { ref: any }) {
-	if (typeof item.disabled === 'boolean' ? item.disabled : item.disabled.value) return;
+	if (item.disabled !== undefined && (typeof item.disabled === 'boolean' ? item.disabled : item.disabled.value)) return;
 	item.ref = !item.ref;
 }
 

From 00b11b1f752c29a432a78d55aafafd631dcf4805 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Wed, 6 Dec 2023 13:46:10 +0900
Subject: [PATCH 147/435] chore: hide thumbnail if website is sensitive
 (#12581)

---
 CHANGELOG.md                                      | 2 ++
 packages/frontend/src/components/MkUrlPreview.vue | 4 +++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d025e5e1c0..0316ab3c1f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,8 @@
 - Enhance: ノートプレビューに「内容を隠す」が反映されるように
 - Enhance: データセーバーの適用範囲を個別で設定できるように
 	- 従来のデータセーバーの設定はリセットされます
+- Feat: センシティブと判断されたウェブサイトのサムネイルをぼかすように
+  - ウェブサイトをセンシティブと判断する仕組みが動いていないため、summalyProxyを使用しないと機能しません。
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Enhance: 絵文字の詳細ページに記載される情報を追加
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index d3c486a98b..2332fe9a7c 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 <div v-else>
 	<component :is="self ? 'MkA' : 'a'" :class="[$style.link, { [$style.compact]: compact }]" :[attr]="self ? url.substring(local.length) : url" rel="nofollow noopener" :target="target" :title="url">
-		<div v-if="thumbnail" :class="$style.thumbnail" :style="defaultStore.state.dataSaver.urlPreview ? '' : `background-image: url('${thumbnail}')`">
+		<div v-if="thumbnail && !sensitive" :class="$style.thumbnail" :style="defaultStore.state.dataSaver.urlPreview ? '' : `background-image: url('${thumbnail}')`">
 		</div>
 		<article :class="$style.body">
 			<header :class="$style.header">
@@ -118,6 +118,7 @@ let description = $ref<string | null>(null);
 let thumbnail = $ref<string | null>(null);
 let icon = $ref<string | null>(null);
 let sitename = $ref<string | null>(null);
+let sensitive = $ref<boolean>(false);
 let player = $ref({
 	url: null,
 	width: null,
@@ -170,6 +171,7 @@ window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLa
 		icon = info.icon;
 		sitename = info.sitename;
 		player = info.player;
+		sensitive = info.sensitive ?? false;
 	});
 
 function adjustTweetHeight(message: any) {

From e42c91dee77bbbe7bff848aac51ab7ec0cd3fe6e Mon Sep 17 00:00:00 2001
From: yupix <yupi0982@outlook.jp>
Date: Wed, 6 Dec 2023 15:47:57 +0900
Subject: [PATCH 148/435] =?UTF-8?q?feat:=20Role=E3=81=AB=E9=96=A2=E3=81=99?=
 =?UTF-8?q?=E3=82=8BSchema=E3=82=92=E8=BF=BD=E5=8A=A0=20(#12572)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: Roleに関連するschemaを追加

* feat: 新しいRoleSchemaを使うように

* chore: misskey.jsのデータを更新

* chore: misskey-js.api.mdを更新
---
 packages/backend/src/misc/json-schema.ts      |   3 +
 .../backend/src/models/json-schema/role.ts    | 157 +++++++++++++
 .../backend/src/models/json-schema/user.ts    |  36 +--
 .../api/endpoints/admin/roles/create.ts       |   6 +
 .../server/api/endpoints/admin/roles/list.ts  |  10 +
 .../server/api/endpoints/admin/roles/show.ts  |   6 +
 .../src/server/api/endpoints/roles/list.ts    |  10 +
 .../src/server/api/endpoints/roles/show.ts    |   6 +
 packages/misskey-js/etc/misskey-js.api.md     |  30 ++-
 packages/misskey-js/src/autogen/endpoint.ts   |  17 +-
 packages/misskey-js/src/autogen/entities.ts   |   7 +-
 packages/misskey-js/src/autogen/models.ts     |   4 +-
 packages/misskey-js/src/autogen/types.ts      | 208 +++++++++++++++---
 13 files changed, 429 insertions(+), 71 deletions(-)
 create mode 100644 packages/backend/src/models/json-schema/role.ts

diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index 7e7fc447c3..49f35b9b74 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -37,6 +37,7 @@ import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/jso
 import { packedFlashSchema } from '@/models/json-schema/flash.js';
 import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
 import { packedSigninSchema } from '@/models/json-schema/signin.js';
+import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js';
 
 export const refs = {
 	UserLite: packedUserLiteSchema,
@@ -73,6 +74,8 @@ export const refs = {
 	EmojiDetailed: packedEmojiDetailedSchema,
 	Flash: packedFlashSchema,
 	Signin: packedSigninSchema,
+	RoleLite: packedRoleLiteSchema,
+	Role: packedRoleSchema,
 };
 
 export type Packed<x extends keyof typeof refs> = SchemaType<typeof refs[x]>;
diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
new file mode 100644
index 0000000000..dd2f32b14d
--- /dev/null
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -0,0 +1,157 @@
+const rolePolicyValue = {
+	type: 'object',
+	properties: {
+		value: {
+			oneOf: [
+				{
+					type: 'integer',
+					optional: false, nullable: false,
+				},
+				{
+					type: 'boolean',
+					optional: false, nullable: false,
+				},
+			],
+		},
+		priority: {
+			type: 'integer',
+			optional: false, nullable: false,
+		},
+		useDefault: {
+			type: 'boolean',
+			optional: false, nullable: false,
+		},
+	},
+} as const;
+
+export const packedRoleLiteSchema = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string',
+			optional: false, nullable: false,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+		name: {
+			type: 'string',
+			optional: false, nullable: false,
+			example: 'New Role',
+		},
+		color: {
+			type: 'string',
+			optional: false, nullable: true,
+			example: '#000000',
+		},
+		iconUrl: {
+			type: 'string',
+			optional: false, nullable: true,
+		},
+		description: {
+			type: 'string',
+			optional: false, nullable: false,
+		},
+		isModerator: {
+			type: 'boolean',
+			optional: false, nullable: false,
+			example: false,
+		},
+		isAdministrator: {
+			type: 'boolean',
+			optional: false, nullable: false,
+			example: false,
+		},
+		displayOrder: {
+			type: 'integer',
+			optional: false, nullable: false,
+			example: 0,
+		},
+	},
+} as const;
+
+export const packedRoleSchema = {
+	type: 'object',
+	allOf: [
+		{
+			type: 'object',
+			ref: 'RoleLite',
+		},
+		{
+			type: 'object',
+			properties: {
+				createdAt: {
+					type: 'string',
+					optional: false, nullable: false,
+					format: 'date-time',
+				},
+				updatedAt: {
+					type: 'string',
+					optional: false, nullable: false,
+					format: 'date-time',
+				},
+				target: {
+					type: 'string',
+					optional: false, nullable: false,
+					enum: ['manual', 'conditional'],
+				},
+				condFormula: {
+					type: 'object',
+					optional: false, nullable: false,
+				},
+				isPublic: {
+					type: 'boolean',
+					optional: false, nullable: false,
+					example: false,
+				},
+				isExplorable: {
+					type: 'boolean',
+					optional: false, nullable: false,
+					example: false,
+				},
+				asBadge: {
+					type: 'boolean',
+					optional: false, nullable: false,
+					example: false,
+				},
+				canEditMembersByModerator: {
+					type: 'boolean',
+					optional: false, nullable: false,
+					example: false,
+				},
+				policies: {
+					type: 'object',
+					optional: false, nullable: false,
+					properties: {
+						pinLimit: rolePolicyValue,
+						canInvite: rolePolicyValue,
+						clipLimit: rolePolicyValue,
+						canHideAds: rolePolicyValue,
+						inviteLimit: rolePolicyValue,
+						antennaLimit: rolePolicyValue,
+						gtlAvailable: rolePolicyValue,
+						ltlAvailable: rolePolicyValue,
+						webhookLimit: rolePolicyValue,
+						canPublicNote: rolePolicyValue,
+						userListLimit: rolePolicyValue,
+						wordMuteLimit: rolePolicyValue,
+						alwaysMarkNsfw: rolePolicyValue,
+						canSearchNotes: rolePolicyValue,
+						driveCapacityMb: rolePolicyValue,
+						rateLimitFactor: rolePolicyValue,
+						inviteLimitCycle: rolePolicyValue,
+						noteEachClipsLimit: rolePolicyValue,
+						inviteExpirationTime: rolePolicyValue,
+						canManageCustomEmojis: rolePolicyValue,
+						userEachUserListsLimit: rolePolicyValue,
+						canManageAvatarDecorations: rolePolicyValue,
+						canUseTranslator: rolePolicyValue,
+					},
+				},
+				usersCount: {
+					type: 'integer',
+					optional: false, nullable: false,
+				},
+			},
+		},
+	],
+} as const;
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index 2621e7e6c0..c6b2707b80 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -329,41 +329,7 @@ export const packedUserDetailedNotMeOnlySchema = {
 			items: {
 				type: 'object',
 				nullable: false, optional: false,
-				properties: {
-					id: {
-						type: 'string',
-						nullable: false, optional: false,
-						format: 'id',
-					},
-					name: {
-						type: 'string',
-						nullable: false, optional: false,
-					},
-					color: {
-						type: 'string',
-						nullable: true, optional: false,
-					},
-					iconUrl: {
-						type: 'string',
-						nullable: true, optional: false,
-					},
-					description: {
-						type: 'string',
-						nullable: false, optional: false,
-					},
-					isModerator: {
-						type: 'boolean',
-						nullable: false, optional: false,
-					},
-					isAdministrator: {
-						type: 'boolean',
-						nullable: false, optional: false,
-					},
-					displayOrder: {
-						type: 'number',
-						nullable: false, optional: false,
-					},
-				},
+				ref: 'RoleLite',
 			},
 		},
 		memo: {
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 8451b1955f..fb53815333 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
@@ -13,6 +13,12 @@ export const meta = {
 
 	requireCredential: true,
 	requireAdmin: true,
+
+	res: {
+		type: 'object',
+		optional: false, nullable: false,
+		ref: 'Role',
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/list.ts b/packages/backend/src/server/api/endpoints/admin/roles/list.ts
index 3ed4b324dc..71b8e44e77 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/list.ts
@@ -14,6 +14,16 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
+
+	res: {
+		type: 'array',
+		optional: false, nullable: false,
+		items: {
+			type: 'object',
+			optional: false, nullable: false,
+			ref: 'Role',
+		},
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/show.ts b/packages/backend/src/server/api/endpoints/admin/roles/show.ts
index 5f0accab6f..1ca952a3f8 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/show.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/show.ts
@@ -23,6 +23,12 @@ export const meta = {
 			id: '07dc7d34-c0d8-49b7-96c6-db3ce64ee0b3',
 		},
 	},
+
+	res: {
+		type: 'object',
+		optional: false, nullable: false,
+		ref: 'Role',
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/roles/list.ts b/packages/backend/src/server/api/endpoints/roles/list.ts
index d1de73ad32..dc2be8e11d 100644
--- a/packages/backend/src/server/api/endpoints/roles/list.ts
+++ b/packages/backend/src/server/api/endpoints/roles/list.ts
@@ -13,6 +13,16 @@ export const meta = {
 	tags: ['role'],
 
 	requireCredential: true,
+
+	res: {
+		type: 'array',
+		optional: false, nullable: false,
+		items: {
+			type: 'object',
+			optional: false, nullable: false,
+			ref: 'Role',
+		},
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/roles/show.ts b/packages/backend/src/server/api/endpoints/roles/show.ts
index 2afa0e7b7f..6bfe52bb1a 100644
--- a/packages/backend/src/server/api/endpoints/roles/show.ts
+++ b/packages/backend/src/server/api/endpoints/roles/show.ts
@@ -22,6 +22,12 @@ export const meta = {
 			id: 'de5502bf-009a-4639-86c1-fec349e46dcb',
 		},
 	},
+
+	res: {
+		type: 'object',
+		optional: false, nullable: false,
+		ref: 'Role',
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index e2e3349c08..6225f9e236 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -227,12 +227,21 @@ type AdminRolesAssignRequest = operations['admin/roles/assign']['requestBody']['
 // @public (undocumented)
 type AdminRolesCreateRequest = operations['admin/roles/create']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminRolesCreateResponse = operations['admin/roles/create']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminRolesDeleteRequest = operations['admin/roles/delete']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminRolesListResponse = operations['admin/roles/list']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminRolesShowRequest = operations['admin/roles/show']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminRolesShowResponse = operations['admin/roles/show']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminRolesUnassignRequest = operations['admin/roles/unassign']['requestBody']['content']['application/json'];
 
@@ -1099,8 +1108,11 @@ declare namespace entities {
         AdminDeleteAccountResponse,
         AdminUpdateUserNoteRequest,
         AdminRolesCreateRequest,
+        AdminRolesCreateResponse,
         AdminRolesDeleteRequest,
+        AdminRolesListResponse,
         AdminRolesShowRequest,
+        AdminRolesShowResponse,
         AdminRolesUpdateRequest,
         AdminRolesAssignRequest,
         AdminRolesUnassignRequest,
@@ -1414,7 +1426,9 @@ declare namespace entities {
         PingResponse,
         PinnedUsersResponse,
         PromoReadRequest,
+        RolesListResponse,
         RolesShowRequest,
+        RolesShowResponse,
         RolesUsersRequest,
         RolesNotesRequest,
         RolesNotesResponse,
@@ -1519,7 +1533,9 @@ declare namespace entities {
         EmojiSimple,
         EmojiDetailed,
         Flash,
-        Signin
+        Signin,
+        RoleLite,
+        Role
     }
 }
 export { entities }
@@ -2312,6 +2328,15 @@ type ResetPasswordRequest = operations['reset-password']['requestBody']['content
 // @public (undocumented)
 type RetentionResponse = operations['retention']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type Role = components['schemas']['Role'];
+
+// @public (undocumented)
+type RoleLite = components['schemas']['RoleLite'];
+
+// @public (undocumented)
+type RolesListResponse = operations['roles/list']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type RolesNotesRequest = operations['roles/notes']['requestBody']['content']['application/json'];
 
@@ -2321,6 +2346,9 @@ type RolesNotesResponse = operations['roles/notes']['responses']['200']['content
 // @public (undocumented)
 type RolesShowRequest = operations['roles/show']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type RolesShowResponse = operations['roles/show']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type RolesUsersRequest = operations['roles/users']['requestBody']['content']['application/json'];
 
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index cf571fc689..89faea6b40 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-04T07:13:58.541Z
+ * generatedAt: 2023-12-04T11:17:52.156Z
  */
 
 import type {
@@ -87,8 +87,11 @@ import type {
 	AdminDeleteAccountResponse,
 	AdminUpdateUserNoteRequest,
 	AdminRolesCreateRequest,
+	AdminRolesCreateResponse,
 	AdminRolesDeleteRequest,
+	AdminRolesListResponse,
 	AdminRolesShowRequest,
+	AdminRolesShowResponse,
 	AdminRolesUpdateRequest,
 	AdminRolesAssignRequest,
 	AdminRolesUnassignRequest,
@@ -402,7 +405,9 @@ import type {
 	PingResponse,
 	PinnedUsersResponse,
 	PromoReadRequest,
+	RolesListResponse,
 	RolesShowRequest,
+	RolesShowResponse,
 	RolesUsersRequest,
 	RolesNotesRequest,
 	RolesNotesResponse,
@@ -543,10 +548,10 @@ export type Endpoints = {
 	'admin/update-meta': { req: AdminUpdateMetaRequest; res: EmptyResponse };
 	'admin/delete-account': { req: AdminDeleteAccountRequest; res: AdminDeleteAccountResponse };
 	'admin/update-user-note': { req: AdminUpdateUserNoteRequest; res: EmptyResponse };
-	'admin/roles/create': { req: AdminRolesCreateRequest; res: EmptyResponse };
+	'admin/roles/create': { req: AdminRolesCreateRequest; res: AdminRolesCreateResponse };
 	'admin/roles/delete': { req: AdminRolesDeleteRequest; res: EmptyResponse };
-	'admin/roles/list': { req: EmptyRequest; res: EmptyResponse };
-	'admin/roles/show': { req: AdminRolesShowRequest; res: EmptyResponse };
+	'admin/roles/list': { req: EmptyRequest; res: AdminRolesListResponse };
+	'admin/roles/show': { req: AdminRolesShowRequest; res: AdminRolesShowResponse };
 	'admin/roles/update': { req: AdminRolesUpdateRequest; res: EmptyResponse };
 	'admin/roles/assign': { req: AdminRolesAssignRequest; res: EmptyResponse };
 	'admin/roles/unassign': { req: AdminRolesUnassignRequest; res: EmptyResponse };
@@ -750,8 +755,8 @@ export type Endpoints = {
 	'ping': { req: EmptyRequest; res: PingResponse };
 	'pinned-users': { req: EmptyRequest; res: PinnedUsersResponse };
 	'promo/read': { req: PromoReadRequest; res: EmptyResponse };
-	'roles/list': { req: EmptyRequest; res: EmptyResponse };
-	'roles/show': { req: RolesShowRequest; res: EmptyResponse };
+	'roles/list': { req: EmptyRequest; res: RolesListResponse };
+	'roles/show': { req: RolesShowRequest; res: RolesShowResponse };
 	'roles/users': { req: RolesUsersRequest; res: EmptyResponse };
 	'roles/notes': { req: RolesNotesRequest; res: RolesNotesResponse };
 	'request-reset-password': { req: RequestResetPasswordRequest; res: EmptyResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index bbfa14913f..611f1b9504 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-04T07:13:58.538Z
+ * generatedAt: 2023-12-04T11:17:52.154Z
  */
 
 import { operations } from './types.js';
@@ -89,8 +89,11 @@ export type AdminDeleteAccountRequest = operations['admin/delete-account']['requ
 export type AdminDeleteAccountResponse = operations['admin/delete-account']['responses']['200']['content']['application/json'];
 export type AdminUpdateUserNoteRequest = operations['admin/update-user-note']['requestBody']['content']['application/json'];
 export type AdminRolesCreateRequest = operations['admin/roles/create']['requestBody']['content']['application/json'];
+export type AdminRolesCreateResponse = operations['admin/roles/create']['responses']['200']['content']['application/json'];
 export type AdminRolesDeleteRequest = operations['admin/roles/delete']['requestBody']['content']['application/json'];
+export type AdminRolesListResponse = operations['admin/roles/list']['responses']['200']['content']['application/json'];
 export type AdminRolesShowRequest = operations['admin/roles/show']['requestBody']['content']['application/json'];
+export type AdminRolesShowResponse = operations['admin/roles/show']['responses']['200']['content']['application/json'];
 export type AdminRolesUpdateRequest = operations['admin/roles/update']['requestBody']['content']['application/json'];
 export type AdminRolesAssignRequest = operations['admin/roles/assign']['requestBody']['content']['application/json'];
 export type AdminRolesUnassignRequest = operations['admin/roles/unassign']['requestBody']['content']['application/json'];
@@ -404,7 +407,9 @@ export type FlashMyLikesResponse = operations['flash/my-likes']['responses']['20
 export type PingResponse = operations['ping']['responses']['200']['content']['application/json'];
 export type PinnedUsersResponse = operations['pinned-users']['responses']['200']['content']['application/json'];
 export type PromoReadRequest = operations['promo/read']['requestBody']['content']['application/json'];
+export type RolesListResponse = operations['roles/list']['responses']['200']['content']['application/json'];
 export type RolesShowRequest = operations['roles/show']['requestBody']['content']['application/json'];
+export type RolesShowResponse = operations['roles/show']['responses']['200']['content']['application/json'];
 export type RolesUsersRequest = operations['roles/users']['requestBody']['content']['application/json'];
 export type RolesNotesRequest = operations['roles/notes']['requestBody']['content']['application/json'];
 export type RolesNotesResponse = operations['roles/notes']['responses']['200']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index 566b6dfb97..0607cd1370 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-04T07:13:58.535Z
+ * generatedAt: 2023-12-04T11:17:52.151Z
  */
 
 import { components } from './types.js';
@@ -38,3 +38,5 @@ export type EmojiSimple = components['schemas']['EmojiSimple'];
 export type EmojiDetailed = components['schemas']['EmojiDetailed'];
 export type Flash = components['schemas']['Flash'];
 export type Signin = components['schemas']['Signin'];
+export type RoleLite = components['schemas']['RoleLite'];
+export type Role = components['schemas']['Role'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index b734b36e49..e09f24d46f 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -3,7 +3,7 @@
 
 /*
  * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-04T07:13:58.362Z
+ * generatedAt: 2023-12-04T11:17:51.997Z
  */
 
 /**
@@ -3197,17 +3197,7 @@ export type components = {
       usePasswordLessLogin: boolean;
       /** @default false */
       securityKeys: boolean;
-      roles: ({
-          /** Format: id */
-          id: string;
-          name: string;
-          color: string | null;
-          iconUrl: string | null;
-          description: string;
-          isModerator: boolean;
-          isAdministrator: boolean;
-          displayOrder: number;
-        })[];
+      roles: components['schemas']['RoleLite'][];
       memo: string | null;
       moderationNote?: string;
       isFollowing?: boolean;
@@ -3850,6 +3840,160 @@ export type components = {
       headers: Record<string, never>;
       success: boolean;
     };
+    RoleLite: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** @example New Role */
+      name: string;
+      /** @example #000000 */
+      color: string | null;
+      iconUrl: string | null;
+      description: string;
+      /** @example false */
+      isModerator: boolean;
+      /** @example false */
+      isAdministrator: boolean;
+      /** @example 0 */
+      displayOrder: number;
+    };
+    Role: components['schemas']['RoleLite'] & ({
+      /** Format: date-time */
+      createdAt: string;
+      /** Format: date-time */
+      updatedAt: string;
+      /** @enum {string} */
+      target: 'manual' | 'conditional';
+      condFormula: Record<string, never>;
+      /** @example false */
+      isPublic: boolean;
+      /** @example false */
+      isExplorable: boolean;
+      /** @example false */
+      asBadge: boolean;
+      /** @example false */
+      canEditMembersByModerator: boolean;
+      policies: {
+        pinLimit: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        canInvite: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        clipLimit: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        canHideAds: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        inviteLimit: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        antennaLimit: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        gtlAvailable: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        ltlAvailable: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        webhookLimit: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        canPublicNote: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        userListLimit: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        wordMuteLimit: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        alwaysMarkNsfw: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        canSearchNotes: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        driveCapacityMb: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        rateLimitFactor: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        inviteLimitCycle: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        noteEachClipsLimit: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        inviteExpirationTime: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        canManageCustomEmojis: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        userEachUserListsLimit: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        canManageAvatarDecorations: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+        canUseTranslator: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
+      };
+      usersCount: number;
+    });
   };
   responses: never;
   parameters: never;
@@ -7847,9 +7991,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Role'];
+        };
       };
       /** @description Client error */
       400: {
@@ -7943,9 +8089,11 @@ export type operations = {
    */
   'admin/roles/list': {
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Role'][];
+        };
       };
       /** @description Client error */
       400: {
@@ -7995,9 +8143,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Role'];
+        };
       };
       /** @description Client error */
       400: {
@@ -19841,9 +19991,11 @@ export type operations = {
    */
   'roles/list': {
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Role'][];
+        };
       };
       /** @description Client error */
       400: {
@@ -19893,9 +20045,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Role'];
+        };
       };
       /** @description Client error */
       400: {

From 7d7c1d4db5bccc60316c0a7ee18b6c1558433052 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 6 Dec 2023 12:36:44 +0000
Subject: [PATCH 149/435] chore(deps): bump actions/setup-python from 4.8.0 to
 5.0.0

Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4.8.0 to 5.0.0.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4.8.0...v5.0.0)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/package.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
index 32e3c8a717..1f9238780f 100644
--- a/.github/workflows/package.yml
+++ b/.github/workflows/package.yml
@@ -34,7 +34,7 @@ jobs:
           node-version: ${{ matrix.node-version }}
 
       - name: Setup Python
-        uses: actions/setup-python@v4.8.0
+        uses: actions/setup-python@v5.0.0
         with:
           python-version: ${{ matrix.python-version }}
 

From cc8d3edbc8b61b67e0551bd356901f484714bf93 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Thu, 7 Dec 2023 01:48:26 +0100
Subject: [PATCH 150/435] upd: add misskey icon to font

---
 .../sharkey-icons/custom-sharkey-icons.svg    |   2 +-
 .../sharkey-icons/custom-sharkey-icons.ttf    | Bin 1644 -> 1896 bytes
 .../sharkey-icons/custom-sharkey-icons.woff   | Bin 1088 -> 1292 bytes
 .../assets/fonts/sharkey-icons/style.css      |   4 ++++
 4 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.svg b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.svg
index c5f79a9861..9d21137072 100644
--- a/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.svg
+++ b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.svg
@@ -1,11 +1,11 @@
 <?xml version="1.0" standalone="no"?>
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
 <svg xmlns="http://www.w3.org/2000/svg">
-<metadata>Generated by Fontastic.me</metadata>
 <defs>
 <font id="custom-sharkey-icons" horiz-adv-x="512">
 <font-face font-family="custom-sharkey-icons" units-per-em="512" ascent="480" descent="-32"/>
 <missing-glyph horiz-adv-x="512" />
 
 <glyph glyph-name="shark" unicode="&#97;" d="M469 171l0-43-42 0c-30 0-60 9-86 21-53-27-117-27-170 0-26-12-56-21-86-21l-42 0 0 43 42 0c30 0 60 10 86 27 51-36 119-36 170 0 26-17 56-27 86-27z m-356 47c11 3 23 9 34 16l24 16c14 49 16 107-9 174 93-16 177-97 209-193 16-10 32-16 48-17-30 140-155 255-291 255-7 0-14-4-18-10-4-6-4-14-1-21 46-92 32-167 4-220m228-105c-51-36-119-36-170 0-26-17-56-28-86-28l-42 0 0-42 42 0c30 0 60 8 86 21 53-28 117-28 170 0 26-13 56-21 86-21l42 0 0 42-42 0c-30 0-60 11-86 28z"/>
+<glyph glyph-name="misskey" unicode="&#98;" d="M190 152c-22 0-41 13-50 29-5 7-14 9-15 0l0-43c0-17-6-32-18-45-12-12-27-18-45-18-17 0-31 6-44 18-12 13-18 28-18 45l0 236c0 13 4 26 11 36 8 11 18 19 30 24 7 2 14 3 21 3 20 0 36-7 49-22l63-75c2-1 6-10 16-10 10 0 15 9 16 10l64 75c13 15 29 22 48 22 7 0 14-1 22-3 12-5 21-12 29-24 8-10 12-23 12-36l0-236c0-17-6-32-19-45-12-12-27-18-44-18-17 0-32 6-45 18-12 13-18 28-18 45l0 43c-1 12-11 4-15 0-9-18-28-29-50-29z m268 176c-15 0-28 6-39 16-10 11-15 24-15 39 0 15 5 28 15 38 11 11 24 16 39 16 14 0 27-5 38-16 11-10 16-23 16-38 0-15-5-28-16-39-11-10-24-16-38-16z m0-10c15 0 28-6 38-17 11-10 16-23 16-39l0-133c0-15-5-28-16-39-10-10-23-15-38-15-15 0-28 5-38 15-11 11-16 24-16 39l0 133c0 16 5 29 16 39 10 11 23 17 38 17z"/>
 </font></defs></svg>
diff --git a/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.ttf b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.ttf
index f4d6d406b8eefd56f73d876a0fa6e4f348948480..a2601e0f1b00c4f2e72249911b911e48c55d6de1 100644
GIT binary patch
delta 711
zcmYjP&ubG=5T4n$*$tbx``k^kH6~GFNYVHM(wO*@1Zq=gED>9(qEOeGHqypMw}%wO
zc<DdjLaE?I@Fqe*1yAC^-f~cA@#01BkVDjyhk{UNH${Cg^XB`$dBe`k_JUot4F~{x
z;1MvmR4z}BKZ$;N0U%!_Ha2=;qzs24K=>%(M0Mgsw)*kL0|2~Acxt9$HN`D<lkf+!
zu{q0b?(9${kW-}l=N6V{e?RVA17I=I*XlLvcB<0(N!k~(e7#<4TcRlVNDL8?syCKe
z5<rIVI$?cbamIov=uiPpIM}dSO&EhKq@N;u@UGRUrR{19Kzt1#<eH22(&*;5B7oE-
z{fyTfHTZ0J_VToL?iU0Eq5;0HZfy5&zZYZ#a3oB`NOi~Y{}DaL2a`?-uj4J}DGnky
z@9`MLKky-n&OPC|QxZf*-{}mnmu+evDgxje$iXm702Gpg>7*PE$<iT;&SS2SFXV@^
z2ZRI;Ws69m88IVq{F{)LCHKuqN_4f5tRh`r!ft_gNiHg=>-+Z^dLXC=l!Q?fm@G<4
zscpm+VuFSqjT$<tYB#KF8aE@Hb3Joh<6LvyT3nJcl%N~YUWju)i&PjBl}d#9xfbsA
zm5i#!*Eu&$?-l3DUTt8-<Zii@0j)o#Agt~V4Iu<a;MHe#7O~(MzSHE#eVu7qFJ%ZQ
zAoJOE>;AmlxMSP%wPmjW(jP<9qBuwvA#)J}ckrTz$kFi@v-N%CtqcAwaAW%qC5?Nc

delta 412
zcmaFC_lBpQfsuiMftR6yftew|%`L=trs&^r1_rhOps0+yi>n)hCIc6cZvf=W`3LJ8
z`Co{sW?*290P@3<a}x^~t}rJ6`3Hd5D?PEe;QxQ1NepZnKzWYzoXRvyt~Hem49pTh
z{+f)`#1s{`AG3k%J3wrfk&#-T$b6HD6{uMRD4>#&TT;Qsz@QD}ZvgTIa`KZC8Nz@p
z24)^0!Ihg>QNZBIU<;JD0P+>`5_3~kihV19@<$jLm`n=ti%T-I${#W?u>Jtb8-P6r
z^x(&-w@<|L+k9o<W&#Q`Fg%<ReI7z{02$g0+Kg8xvoXraFkS`g0IPu#EMO592CK;q
zjM0pVljkt1Pd?751!R3<teafOw1NfXz{xVqYd3FY@nvK*oy^Q;2_$XV&S<j$&0u6;
nVq{`1&PXiE2JwKw!vr=F<R3902Jx5~m?t~1%W}hA6@4B6R`FH|

diff --git a/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.woff b/packages/backend/assets/fonts/sharkey-icons/custom-sharkey-icons.woff
index 2ee0f12d8554dff7998fcb4b00a0031a15e1b235..d9f471fa35a861eb745fb136094ffd9c90b7922f 100644
GIT binary patch
delta 1163
zcmX@W(ZeND?(gQtz{mgutUL_7Aeud6qKIa_)Wy~AF0O7242(WNF*z{SWZ?D>);9u*
z<pBB4Kpf_OA*LodH?aUHwgJfZ1kzqCSD6#i6N^D&e}G~fApHM719N&#Wf}tm6AMs0
z3W#U?nRF&HBQ-IFfq^*zs74ov%~ZV4yw3m%)&m7600m@#ScQd!i8Uj)qyi|m2FMo#
zVj(sL2JM{u<V2v@0U)0TjKdht<R(@CEoMFg<bxcoz~IFYo|l-L3KV+-<bMS^z(lRs
zzoH<&xP*a$<q1$s4T!DWuRO7@n3J54kdVUAv^rlkw!Gm*`{llWEf*PL_Hb$gm4E^X
zWQ`aQGXZ(b3@noq7;S};YCimDY?ga`^?!I`7DM+L?)Q_|Gb+|E`Tzf*mXPC@|E>XF
z9S)p0#_-&rDa6Qd0h1fsDMn*%QJ#ge3=B_avYP{q0|G{12r*U6vF$(ab=X1RSo-W8
zD>FTg`F>vByLQH<B{xk)Cz(0AYIU%`46M4)Y_9((P<~;*!Vfk%mXD9FE>734W#_MD
zD-7;Fd}hO$@3S992h=+>C~$9LV5_xYcwxQQV40z4%AEA2+Q#q2g@ca7KH7P2!WXCB
zQ|!FjM<SKoQ|@q|Y*K%jGfi#Ia?cW3)tBd&r~dx;?&jY6pY}YT-R5S_7r#`A<#}~Y
zf$4;~-Qo{9W;XWJUOjMgX<>QalW&3dP2~19CCs)qvS85Iw9HiDy+KoO{SF3=!xmho
z%U1@zw6s!Nyh)?m=Jv``&v~BbE7j)ilKsT*BDJ>c*y5t4rlylqvJ+|_D7%EJ_ps{D
zHs!f?VfVKs%QCdC`oHPZV{bcqD|fcw<v(RJQWri~QWAFNzPRdEaC?v<W7_uVzDIUF
z@)LEvx_n)b`{~GknukoR^E>8kzvWbaWxG!OyTsDGchR3_@4qq0qKkn66u&762@H=~
z&q)@3{9_g`xnrh;!-I!g7TZtE$W>!Iw%TD<GoubKgIO%s1z?;&)$ciV;J||8M;MqK
zCQW1%-N3*gU(3i3luK%0P~*6kmH<?FodK9D5CE7C<)E4qQaKV6(ij-k6w*={IxQQ1
z@-T2)GAx>`#Vl3d?G(G?H8;oIl7*@*W>eA<Ch7g+*w>){RD0(Km2+M$EqpQ0W*a{*
zc+a}7;`jyG!&82rU|tvLE;s3uVC9i3cU<L)Gv_J$FWG-G(@i`mt#;L8#_cw)a-Y0J
zD^<>?KQT0aX85UPX63>M9?sp*8-(B95V#RsbZPgc{mZBM73M_M7u2846aMz}WN7R9
zrT+wfJdF8Pb^Ctf+CS6gxNt{{t@_XR>Fqr4(^JY<g?wGWwl8nGU5@u`^_8(V?z2s^
zDUW>j$t3*InY-aeY_;Kg&t}cblMYMG(2BpU<9R;!BWwDKbWk<{g+Y=7TMy5R22q_;
hM@}3$aVBcc#E2<p=gp3cnZq?VijzTgI;$-M0|000yZitE

delta 956
zcmeC-I=~@P?(gQtz{mguEDj93Aet>_qKIa_=-+U67gsk121XyCm>d{uGI03^>l*>Z
za)5kiAP)1t5L2C;n^*u8+W_Rd18FaYE6fS$iNzqXA3!k<5dQz4fjK>=GL3<O3CLww
z0>o=9xz<!>q$Z{S#Xx+0AU0ER`!PEMC|C~^yaE)E0b&*An@p@3xg`}qu@68#KM)JB
zF)(Q7<R>RGFfaqT3|wFw#=wx9SOK({nFq)RIa-0ilfgDGF*g+`rUT@E1)5=^QtVq%
zkY8K^^s@?33}lBvW>)#biaE&%2?;3-O{?=&W6K*}v<ELbe>j67jD`I_PzflIK*ovz
zF%yu-%)mT3fzehdrRKwb#%8(4SO146W-)Z{<bF4KJ)>g%k^lb>Y909U-!<T?!+{gW
z7@iw6g%}wwU~*$S#b_*gW`QCD!y^`UU|<4G28Iq3$nvxcK)B~r#Z<=*$EgP=H9Cfd
zM0(Eko$EXM;*}dGVt6`wLeIXDJbUG-#L=TizDi7;dgEpTkD2NOp}-YF*>1t9s>fDG
z&Dyna;jURx^-;TKWff-4+V#KhBe%DQ#;$1|9$nq~{!i1=leb2(Pq}#Q+NDbuFLhjE
zm=twu^;XxdjUG}QUm`15oRlkzY*-~WFDokiG$n4rv}qTGSebO$*cf!L$_9fRnUave
z@TfodefW{jw`)=#q$M#lf&(fumo4|$>Ynh%7(TWQCNT`L$GNXDKsD?+b>P5)<3|{n
z9L$uNwGtQ^^eq@41Lcw$7}Pkf0pqfm0UQ#b&;X_?2AIBt#0^OaX$*{N3TY_}ot6zh
zc^EjRFtks8#Vl1{5-k4i7n75DYzF5--ldW=mUP=S=|AxP>7{4WxmieXA@lT~&(2uh
zsczK1XIOIF;HllGhU{w-+n3b1$QhcJPdpwo>-h=cm-$7rF1AjIod2rgfUf_<_B}za
zawk7aR-Wmt?6FBqi_@^Ubm5WZ0~c#EmTyz$d|CJ9{mP}mGPAGA$JB1V-BMdw6#6iG
zWj#xs#q_=RzLh6v?^jj7cxYPdtNqP2yPsb&dOBAt)OJPV`rAwSZ(n-mnI-;hd!y?4
z*lD|M&Q7%`{yw$G@qTFj=2^>cw}nd1I(6O5`%-aq4P#16x)3lJKw+2Uz}Ca_qJdTC
U)R7ZMqUKDDn8M4z-pix|0BYb|s{jB1

diff --git a/packages/backend/assets/fonts/sharkey-icons/style.css b/packages/backend/assets/fonts/sharkey-icons/style.css
index 36279ac0eb..7fb0f94504 100644
--- a/packages/backend/assets/fonts/sharkey-icons/style.css
+++ b/packages/backend/assets/fonts/sharkey-icons/style.css
@@ -25,3 +25,7 @@
 .sk-icons.sk-shark:before {
   content: "\61";
 }
+
+.sk-icons.sk-misskey:before {
+  content: "\62";
+}

From 532d55180bc894db0c467ce5f537fbdb5bdb3442 Mon Sep 17 00:00:00 2001
From: Mar0xy <marie@kaifa.ch>
Date: Thu, 7 Dec 2023 01:58:00 +0100
Subject: [PATCH 151/435] upd: add icons to radio buttons for note design and
 corner radius

---
 packages/frontend/src/pages/settings/general.vue | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 1acdf6f692..38a1b5c2a7 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -63,8 +63,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</MkRadios>
 				<MkRadios v-model="noteDesign">
 					<template #label>Note Design</template>
-					<option value="sharkey">Sharkey</option>
-					<option value="misskey">Misskey</option>
+					<option value="sharkey"><i class="sk-icons sk-shark ph-bold" style="top: 2px;position: relative;"></i> Sharkey</option>
+					<option value="misskey"><i class="sk-icons sk-misskey ph-bold" style="top: 2px;position: relative;"></i> Misskey</option>
 				</MkRadios>
 			</div>
 
@@ -153,8 +153,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 			<MkRadios v-model="cornerRadius">
 				<template #label>{{ i18n.ts.cornerRadius }}</template>
-				<option :value="null">Sharkey</option>
-				<option value="misskey">Misskey</option>
+				<option :value="null"><i class="sk-icons sk-shark ph-bold" style="top: 2px;position: relative;"></i> Sharkey</option>
+				<option value="misskey"><i class="sk-icons sk-misskey ph-bold" style="top: 2px;position: relative;"></i> Misskey</option>
 			</MkRadios>
 		</div>
 	</FormSection>

From 406b4bdbe79b5b0b68fcdcb3c4b6e419460a0258 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Thu, 7 Dec 2023 14:42:09 +0900
Subject: [PATCH 152/435] =?UTF-8?q?refactor(frontend):=20=E9=9D=9E?=
 =?UTF-8?q?=E6=8E=A8=E5=A5=A8=E3=81=A8=E3=81=AA=E3=81=A3=E3=81=9FReactivit?=
 =?UTF-8?q?y=20Transform=E3=82=92=E4=BD=BF=E3=82=8F=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=20(#12539)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor(frontend): 非推奨となったReactivity Transformを使わないように

* refactor: 不要な括弧を除去

* fix: 不要なアノテーションを除去

* fix: Refの配列をrefしている部分の対応

* refactor: 不要な括弧を除去

* fix: lint

* refactor: Ref、ShallowRef、ComputedRefの変数の宣言をletからconstに置換

* fix: type error

* chore: drop reactivity transform from eslint configuration

* refactor: remove unnecessary import

* fix: 対応漏れ
---
 packages/frontend/.eslintrc.cjs               |   6 -
 packages/frontend/package.json                |   1 -
 .../frontend/src/components/MkAbuseReport.vue |   5 +-
 .../src/components/MkAchievements.vue         |  10 +-
 .../frontend/src/components/MkAnalogClock.vue |  64 +--
 packages/frontend/src/components/MkAsUi.vue   |   6 +-
 packages/frontend/src/components/MkButton.vue |  14 +-
 packages/frontend/src/components/MkChart.vue  |   4 +-
 .../frontend/src/components/MkChartLegend.vue |  19 +-
 .../frontend/src/components/MkClickerGame.vue |  14 +-
 .../frontend/src/components/MkContextMenu.vue |  16 +-
 .../src/components/MkCropperDialog.vue        |  16 +-
 packages/frontend/src/components/MkDialog.vue |  19 +-
 .../src/components/MkEmojiPickerDialog.vue    |  13 +-
 .../components/MkFileCaptionEditWindow.vue    |  10 +-
 packages/frontend/src/components/MkFolder.vue |  20 +-
 .../src/components/MkFollowButton.vue         |  24 +-
 .../src/components/MkForgotPassword.vue       |  18 +-
 .../frontend/src/components/MkHeatmap.vue     |  18 +-
 .../src/components/MkImgWithBlurhash.vue      |  45 ++-
 .../src/components/MkInstanceCardMini.vue     |   5 +-
 .../src/components/MkInstanceStats.vue        |  16 +-
 .../src/components/MkInstanceTicker.vue       |   4 +-
 .../frontend/src/components/MkLaunchPad.vue   |   6 +-
 packages/frontend/src/components/MkLink.vue   |   8 +-
 .../frontend/src/components/MkMediaBanner.vue |   4 +-
 .../frontend/src/components/MkMediaImage.vue  |  18 +-
 .../frontend/src/components/MkMediaList.vue   |   4 +-
 packages/frontend/src/components/MkMenu.vue   |  30 +-
 .../frontend/src/components/MkMiniChart.vue   |  20 +-
 packages/frontend/src/components/MkModal.vue  |  92 ++---
 .../frontend/src/components/MkModalWindow.vue |  24 +-
 packages/frontend/src/components/MkNote.vue   |  78 ++--
 .../src/components/MkNoteDetailed.vue         |  76 ++--
 .../frontend/src/components/MkNoteSimple.vue  |   4 +-
 .../frontend/src/components/MkNoteSub.vue     |   6 +-
 .../components/MkNotificationSelectWindow.vue |   6 +-
 .../src/components/MkNotifications.vue        |   2 +-
 packages/frontend/src/components/MkOmit.vue   |  14 +-
 .../frontend/src/components/MkPageWindow.vue  |  38 +-
 .../frontend/src/components/MkPagination.vue  |  60 +--
 .../src/components/MkPasswordDialog.vue       |  18 +-
 .../src/components/MkPlusOneEffect.vue        |   6 +-
 .../frontend/src/components/MkPopupMenu.vue   |   8 +-
 .../frontend/src/components/MkPostForm.vue    | 368 +++++++++---------
 .../src/components/MkPostFormDialog.vue       |   8 +-
 .../src/components/MkPullToRefresh.vue        |  78 ++--
 .../MkPushNotificationAllowButton.vue         |  41 +-
 packages/frontend/src/components/MkRadio.vue  |   4 +-
 .../src/components/MkReactionEffect.vue       |   6 +-
 .../src/components/MkReactionsViewer.vue      |  26 +-
 .../src/components/MkRetentionHeatmap.vue     |  16 +-
 packages/frontend/src/components/MkSignin.vue |  90 ++---
 .../src/components/MkSigninDialog.vue         |   8 +-
 .../src/components/MkSignupDialog.form.vue    | 148 +++----
 .../src/components/MkSignupDialog.vue         |  12 +-
 .../src/components/MkSubNoteContent.vue       |   4 +-
 .../frontend/src/components/MkTagCloud.vue    |  20 +-
 .../frontend/src/components/MkTimeline.vue    |  10 +-
 packages/frontend/src/components/MkToast.vue  |   6 +-
 .../src/components/MkTokenGenerateWindow.vue  |  26 +-
 .../frontend/src/components/MkUrlPreview.vue  |  60 +--
 .../src/components/MkUrlPreviewPopup.vue      |  10 +-
 .../MkUserAnnouncementEditDialog.vue          |  32 +-
 .../src/components/MkUserCardMini.vue         |   6 +-
 .../src/components/MkUserOnlineIndicator.vue  |   4 +-
 .../frontend/src/components/MkUserPopup.vue   |  18 +-
 .../src/components/MkUserSelectDialog.vue     |  40 +-
 .../components/MkUserSetupDialog.Privacy.vue  |   8 +-
 .../src/components/MkVisibilityPicker.vue     |  10 +-
 .../MkVisitorDashboard.ActiveUsersChart.vue   |  10 +-
 .../src/components/MkVisitorDashboard.vue     |  10 +-
 packages/frontend/src/components/MkWindow.vue |  94 ++---
 .../src/components/MkYouTubePlayer.vue        |  15 +-
 .../frontend/src/components/global/MkA.vue    |   3 +-
 .../frontend/src/components/global/MkAd.vue   |   2 +-
 .../src/components/global/MkAvatar.vue        |  16 +-
 .../src/components/global/MkCustomEmoji.vue   |   4 +-
 .../src/components/global/MkPageHeader.vue    |  28 +-
 .../components/global/MkStickyContainer.vue   |  62 +--
 .../frontend/src/components/global/MkTime.vue |  42 +-
 .../src/components/global/RouterView.vue      |  14 +-
 packages/frontend/src/pages/_error_.vue       |  22 +-
 packages/frontend/src/pages/about-misskey.vue |  26 +-
 packages/frontend/src/pages/about.emojis.vue  |  32 +-
 .../frontend/src/pages/about.federation.vue   |  26 +-
 packages/frontend/src/pages/about.vue         |  16 +-
 packages/frontend/src/pages/admin-file.vue    |  30 +-
 packages/frontend/src/pages/admin-user.vue    |  94 ++---
 .../frontend/src/pages/admin/_header_.vue     |   8 +-
 packages/frontend/src/pages/admin/abuses.vue  |  26 +-
 packages/frontend/src/pages/admin/ads.vue     |  20 +-
 .../src/pages/admin/announcements.vue         |  20 +-
 .../src/pages/admin/bot-protection.vue        |  48 +--
 .../frontend/src/pages/admin/branding.vue     |  76 ++--
 .../frontend/src/pages/admin/database.vue     |   6 +-
 .../src/pages/admin/email-settings.vue        |  46 +--
 .../src/pages/admin/external-services.vue     |  18 +-
 .../frontend/src/pages/admin/federation.vue   |  30 +-
 packages/frontend/src/pages/admin/files.vue   |  24 +-
 packages/frontend/src/pages/admin/index.vue   | 100 ++---
 .../src/pages/admin/instance-block.vue        |  19 +-
 packages/frontend/src/pages/admin/invites.vue |   8 +-
 .../frontend/src/pages/admin/moderation.vue   |  46 +--
 packages/frontend/src/pages/admin/modlog.vue  |  16 +-
 .../src/pages/admin/object-storage.vue        |  82 ++--
 .../src/pages/admin/other-settings.vue        |  30 +-
 .../src/pages/admin/overview.active-users.vue |  10 +-
 .../src/pages/admin/overview.ap-requests.vue  |  14 +-
 .../src/pages/admin/overview.federation.vue   |  30 +-
 .../src/pages/admin/overview.heatmap.vue      |   3 +-
 .../src/pages/admin/overview.moderators.vue   |  10 +-
 .../src/pages/admin/overview.queue.vue        |  26 +-
 .../src/pages/admin/overview.stats.vue        |  22 +-
 .../src/pages/admin/overview.users.vue        |   9 +-
 .../frontend/src/pages/admin/overview.vue     |  44 +--
 .../src/pages/admin/proxy-account.vue         |  22 +-
 .../frontend/src/pages/admin/queue.chart.vue  |  26 +-
 packages/frontend/src/pages/admin/queue.vue   |   9 +-
 packages/frontend/src/pages/admin/relays.vue  |  10 +-
 .../frontend/src/pages/admin/roles.edit.vue   |  28 +-
 .../frontend/src/pages/admin/roles.editor.vue |  54 +--
 .../frontend/src/pages/admin/roles.role.vue   |  14 +-
 packages/frontend/src/pages/admin/roles.vue   |   4 +-
 .../frontend/src/pages/admin/security.vue     |  80 ++--
 .../frontend/src/pages/admin/server-rules.vue |  10 +-
 .../frontend/src/pages/admin/settings.vue     | 118 +++---
 packages/frontend/src/pages/admin/users.vue   |  30 +-
 packages/frontend/src/pages/announcements.vue |   6 +-
 .../frontend/src/pages/antenna-timeline.vue   |  30 +-
 packages/frontend/src/pages/api-console.vue   |   6 +-
 packages/frontend/src/pages/auth.form.vue     |   8 +-
 packages/frontend/src/pages/auth.vue          |  28 +-
 .../frontend/src/pages/avatar-decorations.vue |  14 +-
 .../frontend/src/pages/channel-editor.vue     |  66 ++--
 packages/frontend/src/pages/channel.vue       |  64 +--
 packages/frontend/src/pages/channels.vue      |  30 +-
 packages/frontend/src/pages/clip.vue          |  50 +--
 .../src/pages/custom-emojis-manager.vue       |   4 +-
 packages/frontend/src/pages/drive.vue         |  10 +-
 .../frontend/src/pages/emoji-edit-dialog.vue  |  66 ++--
 .../frontend/src/pages/explore.featured.vue   |   3 +-
 packages/frontend/src/pages/explore.roles.vue |   6 +-
 packages/frontend/src/pages/explore.users.vue |  18 +-
 packages/frontend/src/pages/explore.vue       |  12 +-
 .../frontend/src/pages/flash/flash-edit.vue   |  58 +--
 .../frontend/src/pages/flash/flash-index.vue  |   8 +-
 packages/frontend/src/pages/flash/flash.vue   |  78 ++--
 .../frontend/src/pages/follow-requests.vue    |   4 +-
 packages/frontend/src/pages/gallery/edit.vue  |  46 +--
 packages/frontend/src/pages/gallery/index.vue |  16 +-
 packages/frontend/src/pages/gallery/post.vue  |  44 +--
 packages/frontend/src/pages/instance-info.vue |  62 +--
 packages/frontend/src/pages/list.vue          |  38 +-
 packages/frontend/src/pages/miauth.vue        |  14 +-
 .../frontend/src/pages/my-antennas/create.vue |   3 +-
 .../frontend/src/pages/my-antennas/edit.vue   |   5 +-
 .../frontend/src/pages/my-antennas/editor.vue |  60 +--
 .../frontend/src/pages/my-antennas/index.vue  |   8 +-
 .../frontend/src/pages/my-clips/index.vue     |  22 +-
 .../frontend/src/pages/my-lists/index.vue     |   8 +-
 packages/frontend/src/pages/my-lists/list.vue |  40 +-
 packages/frontend/src/pages/not-found.vue     |   5 +-
 packages/frontend/src/pages/note.vue          |  56 +--
 packages/frontend/src/pages/notifications.vue |  24 +-
 .../page-editor/els/page-editor.el.image.vue  |  10 +-
 .../page-editor/els/page-editor.el.note.vue   |  16 +-
 .../els/page-editor.el.section.vue            |  10 +-
 .../page-editor/els/page-editor.el.text.vue   |   8 +-
 .../src/pages/page-editor/page-editor.vue     | 128 +++---
 packages/frontend/src/pages/page.vue          |  60 +--
 packages/frontend/src/pages/pages.vue         |   8 +-
 packages/frontend/src/pages/registry.keys.vue |  16 +-
 .../frontend/src/pages/registry.value.vue     |  34 +-
 packages/frontend/src/pages/registry.vue      |   9 +-
 .../frontend/src/pages/reset-password.vue     |  10 +-
 packages/frontend/src/pages/role.vue          |  28 +-
 packages/frontend/src/pages/scratchpad.vue    |  16 +-
 packages/frontend/src/pages/search.note.vue   |  28 +-
 packages/frontend/src/pages/search.user.vue   |  18 +-
 packages/frontend/src/pages/search.vue        |   8 +-
 packages/frontend/src/pages/settings/2fa.vue  |   4 +-
 .../frontend/src/pages/settings/accounts.vue  |   6 +-
 packages/frontend/src/pages/settings/api.vue  |   6 +-
 packages/frontend/src/pages/settings/apps.vue |   6 +-
 .../src/pages/settings/custom-css.vue         |   6 +-
 packages/frontend/src/pages/settings/deck.vue |   4 +-
 .../src/pages/settings/drive-cleaner.vue      |   2 +-
 .../frontend/src/pages/settings/drive.vue     |  14 +-
 .../frontend/src/pages/settings/email.vue     |   6 +-
 .../frontend/src/pages/settings/general.vue   |   4 +-
 .../src/pages/settings/import-export.vue      |   6 +-
 .../frontend/src/pages/settings/index.vue     |  64 +--
 .../src/pages/settings/mute-block.vue         |  30 +-
 .../frontend/src/pages/settings/navbar.vue    |   4 +-
 .../notifications.notification-config.vue     |   8 +-
 .../src/pages/settings/notifications.vue      |  20 +-
 .../frontend/src/pages/settings/other.vue     |   4 +-
 .../src/pages/settings/plugin.install.vue     |   6 +-
 .../frontend/src/pages/settings/plugin.vue    |   6 +-
 .../frontend/src/pages/settings/privacy.vue   |  46 +--
 .../frontend/src/pages/settings/profile.vue   |   8 +-
 .../frontend/src/pages/settings/reaction.vue  |  28 +-
 .../frontend/src/pages/settings/roles.vue     |   4 +-
 .../frontend/src/pages/settings/security.vue  |   5 +-
 .../frontend/src/pages/settings/sounds.vue    |   4 +-
 .../frontend/src/pages/settings/statusbar.vue |  10 +-
 .../src/pages/settings/theme.install.vue      |   8 +-
 .../src/pages/settings/theme.manage.vue       |   4 +-
 .../frontend/src/pages/settings/theme.vue     |   4 +-
 .../src/pages/settings/webhook.edit.vue       |  50 +--
 .../src/pages/settings/webhook.new.vue        |  46 +--
 .../frontend/src/pages/settings/webhook.vue   |   6 +-
 packages/frontend/src/pages/share.vue         |  44 +--
 .../frontend/src/pages/signup-complete.vue    |  10 +-
 packages/frontend/src/pages/tag.vue           |   4 +-
 packages/frontend/src/pages/theme-editor.vue  |  64 +--
 packages/frontend/src/pages/timeline.vue      |  58 +--
 .../frontend/src/pages/user-list-timeline.vue |  24 +-
 packages/frontend/src/pages/user-tag.vue      |   2 +-
 .../src/pages/user/activity.following.vue     |  14 +-
 .../src/pages/user/activity.heatmap.vue       |  18 +-
 .../src/pages/user/activity.notes.vue         |  14 +-
 .../frontend/src/pages/user/activity.pv.vue   |  14 +-
 .../frontend/src/pages/user/followers.vue     |  24 +-
 .../frontend/src/pages/user/following.vue     |  24 +-
 packages/frontend/src/pages/user/home.vue     |  60 +--
 .../src/pages/user/index.activity.vue         |  12 +-
 .../frontend/src/pages/user/index.files.vue   |  12 +-
 packages/frontend/src/pages/user/index.vue    |  36 +-
 .../frontend/src/pages/welcome.entrance.a.vue |  10 +-
 packages/frontend/src/pages/welcome.setup.vue |  18 +-
 .../frontend/src/pages/welcome.timeline.vue   |  18 +-
 packages/frontend/src/pages/welcome.vue       |  10 +-
 packages/frontend/src/ui/_common_/common.vue  |   8 +-
 .../src/ui/_common_/statusbar-federation.vue  |   4 +-
 .../src/ui/_common_/statusbar-rss.vue         |   4 +-
 .../src/ui/_common_/statusbar-user-list.vue   |   4 +-
 .../src/ui/_common_/stream-indicator.vue      |   8 +-
 packages/frontend/src/ui/classic.header.vue   |  12 +-
 packages/frontend/src/ui/classic.sidebar.vue  |  16 +-
 packages/frontend/src/ui/classic.vue          |  42 +-
 packages/frontend/src/ui/deck.vue             |   6 +-
 .../frontend/src/ui/deck/antenna-column.vue   |   4 +-
 .../frontend/src/ui/deck/channel-column.vue   |  11 +-
 packages/frontend/src/ui/deck/column.vue      |  36 +-
 .../frontend/src/ui/deck/direct-column.vue    |   6 +-
 packages/frontend/src/ui/deck/list-column.vue |  10 +-
 packages/frontend/src/ui/deck/main-column.vue |   6 +-
 .../frontend/src/ui/deck/mentions-column.vue  |   6 +-
 .../src/ui/deck/notifications-column.vue      |   4 +-
 .../src/ui/deck/role-timeline-column.vue      |   4 +-
 packages/frontend/src/ui/deck/tl-column.vue   |  30 +-
 .../frontend/src/ui/deck/widgets-column.vue   |   6 +-
 packages/frontend/src/ui/minimum.vue          |  10 +-
 packages/frontend/src/ui/universal.vue        |  26 +-
 .../frontend/src/ui/universal.widgets.vue     |   6 +-
 packages/frontend/src/ui/visitor.vue          |  32 +-
 packages/frontend/src/ui/zen.vue              |  10 +-
 .../src/widgets/WidgetActivity.chart.vue      |  35 +-
 .../src/widgets/WidgetAiscriptApp.vue         |   4 +-
 packages/frontend/src/widgets/WidgetClock.vue |   8 +-
 .../src/widgets/WidgetDigitalClock.vue        |   7 +-
 .../src/widgets/WidgetInstanceCloud.vue       |  10 +-
 .../frontend/src/widgets/WidgetJobQueue.vue   |  16 +-
 packages/frontend/src/widgets/WidgetRss.vue   |   8 +-
 .../frontend/src/widgets/WidgetRssTicker.vue  |  12 +-
 .../frontend/src/widgets/WidgetUserList.vue   |  17 +-
 .../src/widgets/server-metric/cpu-mem.vue     |  56 +--
 .../src/widgets/server-metric/cpu.vue         |   6 +-
 .../src/widgets/server-metric/disk.vue        |  10 +-
 .../src/widgets/server-metric/mem.vue         |  18 +-
 .../src/widgets/server-metric/net.vue         |  60 +--
 .../src/widgets/server-metric/pie.vue         |   6 +-
 packages/frontend/tsconfig.json               |   1 -
 packages/frontend/vite.config.ts              |   7 +-
 pnpm-lock.yaml                                | 166 ++------
 277 files changed, 3353 insertions(+), 3441 deletions(-)

diff --git a/packages/frontend/.eslintrc.cjs b/packages/frontend/.eslintrc.cjs
index 77038f0dfa..20f88dc078 100644
--- a/packages/frontend/.eslintrc.cjs
+++ b/packages/frontend/.eslintrc.cjs
@@ -69,12 +69,6 @@ module.exports = {
 		'require': false,
 		'__dirname': false,
 
-		// Vue
-		'$$': false,
-		'$ref': false,
-		'$shallowRef': false,
-		'$computed': false,
-
 		// Misskey
 		'_DEV_': false,
 		'_LANGS_': false,
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 4f3b74649a..9ecb47afba 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -25,7 +25,6 @@
 		"@syuilo/aiscript": "0.16.0",
 		"@tabler/icons-webfont": "2.37.0",
 		"@vitejs/plugin-vue": "4.5.1",
-		"@vue-macros/reactivity-transform": "0.4.0",
 		"@vue/compiler-sfc": "3.3.9",
 		"astring": "1.8.6",
 		"autosize": "6.0.1",
diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue
index 2c7be319cb..ce7e134b70 100644
--- a/packages/frontend/src/components/MkAbuseReport.vue
+++ b/packages/frontend/src/components/MkAbuseReport.vue
@@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
@@ -56,11 +57,11 @@ const emit = defineEmits<{
 	(ev: 'resolved', reportId: string): void;
 }>();
 
-let forward = $ref(props.report.forwarded);
+const forward = ref(props.report.forwarded);
 
 function resolve() {
 	os.apiWithDialog('admin/resolve-abuse-user-report', {
-		forward: forward,
+		forward: forward.value,
 		reportId: props.report.id,
 	}).then(() => {
 		emit('resolved', props.report.id);
diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue
index bea0ed26d8..b067e94a6d 100644
--- a/packages/frontend/src/components/MkAchievements.vue
+++ b/packages/frontend/src/components/MkAchievements.vue
@@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
-import { onMounted } from 'vue';
+import { onMounted, ref, computed } from 'vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js';
@@ -67,15 +67,15 @@ const props = withDefaults(defineProps<{
 	withDescription: true,
 });
 
-let achievements = $ref();
-const lockedAchievements = $computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements ?? []).some(a => a.name === x)));
+const achievements = ref();
+const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x)));
 
 function fetch() {
 	os.api('users/achievements', { userId: props.user.id }).then(res => {
-		achievements = [];
+		achievements.value = [];
 		for (const t of ACHIEVEMENT_TYPES) {
 			const a = res.find(x => x.name === t);
-			if (a) achievements.push(a);
+			if (a) achievements.value.push(a);
 		}
 		//achievements = res.sort((a, b) => b.unlockedAt - a.unlockedAt);
 	});
diff --git a/packages/frontend/src/components/MkAnalogClock.vue b/packages/frontend/src/components/MkAnalogClock.vue
index cd2c4d8264..0e252f7b1d 100644
--- a/packages/frontend/src/components/MkAnalogClock.vue
+++ b/packages/frontend/src/components/MkAnalogClock.vue
@@ -138,45 +138,45 @@ const texts = computed(() => {
 });
 
 let enabled = true;
-let majorGraduationColor = $ref<string>();
+const majorGraduationColor = ref<string>();
 //let minorGraduationColor = $ref<string>();
-let sHandColor = $ref<string>();
-let mHandColor = $ref<string>();
-let hHandColor = $ref<string>();
-let nowColor = $ref<string>();
-let h = $ref<number>(0);
-let m = $ref<number>(0);
-let s = $ref<number>(0);
-let hAngle = $ref<number>(0);
-let mAngle = $ref<number>(0);
-let sAngle = $ref<number>(0);
-let disableSAnimate = $ref(false);
+const sHandColor = ref<string>();
+const mHandColor = ref<string>();
+const hHandColor = ref<string>();
+const nowColor = ref<string>();
+const h = ref<number>(0);
+const m = ref<number>(0);
+const s = ref<number>(0);
+const hAngle = ref<number>(0);
+const mAngle = ref<number>(0);
+const sAngle = ref<number>(0);
+const disableSAnimate = ref(false);
 let sOneRound = false;
 const sLine = ref<SVGPathElement>();
 
 function tick() {
 	const now = props.now();
 	now.setMinutes(now.getMinutes() + now.getTimezoneOffset() + props.offset);
-	const previousS = s;
-	const previousM = m;
-	const previousH = h;
-	s = now.getSeconds();
-	m = now.getMinutes();
-	h = now.getHours();
-	if (previousS === s && previousM === m && previousH === h) {
+	const previousS = s.value;
+	const previousM = m.value;
+	const previousH = h.value;
+	s.value = now.getSeconds();
+	m.value = now.getMinutes();
+	h.value = now.getHours();
+	if (previousS === s.value && previousM === m.value && previousH === h.value) {
 		return;
 	}
-	hAngle = Math.PI * (h % (props.twentyfour ? 24 : 12) + (m + s / 60) / 60) / (props.twentyfour ? 12 : 6);
-	mAngle = Math.PI * (m + s / 60) / 30;
+	hAngle.value = Math.PI * (h.value % (props.twentyfour ? 24 : 12) + (m.value + s.value / 60) / 60) / (props.twentyfour ? 12 : 6);
+	mAngle.value = Math.PI * (m.value + s.value / 60) / 30;
 	if (sOneRound && sLine.value) { // 秒針が一周した際のアニメーションをよしなに処理する(これが無いと秒が59->0になったときに期待したアニメーションにならない)
-		sAngle = Math.PI * 60 / 30;
+		sAngle.value = Math.PI * 60 / 30;
 		defaultIdlingRenderScheduler.delete(tick);
 		sLine.value.addEventListener('transitionend', () => {
-			disableSAnimate = true;
+			disableSAnimate.value = true;
 			requestAnimationFrame(() => {
-				sAngle = 0;
+				sAngle.value = 0;
 				requestAnimationFrame(() => {
-					disableSAnimate = false;
+					disableSAnimate.value = false;
 					if (enabled) {
 						defaultIdlingRenderScheduler.add(tick);
 					}
@@ -184,9 +184,9 @@ function tick() {
 			});
 		}, { once: true });
 	} else {
-		sAngle = Math.PI * s / 30;
+		sAngle.value = Math.PI * s.value / 30;
 	}
-	sOneRound = s === 59;
+	sOneRound = s.value === 59;
 }
 
 tick();
@@ -195,12 +195,12 @@ function calcColors() {
 	const computedStyle = getComputedStyle(document.documentElement);
 	const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark();
 	const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString();
-	majorGraduationColor = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
+	majorGraduationColor.value = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
 	//minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
-	sHandColor = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
-	mHandColor = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString();
-	hHandColor = accent;
-	nowColor = accent;
+	sHandColor.value = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
+	mHandColor.value = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString();
+	hHandColor.value = accent;
+	nowColor.value = accent;
 }
 
 calcColors();
diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue
index 099baf0d72..7670b54f16 100644
--- a/packages/frontend/src/components/MkAsUi.vue
+++ b/packages/frontend/src/components/MkAsUi.vue
@@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { Ref } from 'vue';
+import { Ref, ref } from 'vue';
 import * as os from '@/os.js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -87,10 +87,10 @@ function g(id) {
 	return props.components.find(x => x.value.id === id).value;
 }
 
-let valueForSwitch = $ref(c.default ?? false);
+const valueForSwitch = ref(c.default ?? false);
 
 function onSwitchUpdate(v) {
-	valueForSwitch = v;
+	valueForSwitch.value = v;
 	if (c.onChange) c.onChange(v);
 }
 
diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue
index bcd58ae516..8b176eedaa 100644
--- a/packages/frontend/src/components/MkButton.vue
+++ b/packages/frontend/src/components/MkButton.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { nextTick, onMounted } from 'vue';
+import { nextTick, onMounted, shallowRef } from 'vue';
 
 const props = defineProps<{
 	type?: 'button' | 'submit' | 'reset';
@@ -59,13 +59,13 @@ const emit = defineEmits<{
 	(ev: 'click', payload: MouseEvent): void;
 }>();
 
-let el = $shallowRef<HTMLElement | null>(null);
-let ripples = $shallowRef<HTMLElement | null>(null);
+const el = shallowRef<HTMLElement | null>(null);
+const ripples = shallowRef<HTMLElement | null>(null);
 
 onMounted(() => {
 	if (props.autofocus) {
 		nextTick(() => {
-			el!.focus();
+			el.value!.focus();
 		});
 	}
 });
@@ -88,11 +88,11 @@ function onMousedown(evt: MouseEvent): void {
 	const rect = target.getBoundingClientRect();
 
 	const ripple = document.createElement('div');
-	ripple.classList.add(ripples!.dataset.childrenClass!);
+	ripple.classList.add(ripples.value!.dataset.childrenClass!);
 	ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px';
 	ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px';
 
-	ripples!.appendChild(ripple);
+	ripples.value!.appendChild(ripple);
 
 	const circleCenterX = evt.clientX - rect.left;
 	const circleCenterY = evt.clientY - rect.top;
@@ -107,7 +107,7 @@ function onMousedown(evt: MouseEvent): void {
 		ripple.style.opacity = '0';
 	}, 1000);
 	window.setTimeout(() => {
-		if (ripples) ripples.removeChild(ripple);
+		if (ripples.value) ripples.value.removeChild(ripple);
 	}, 2000);
 }
 </script>
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index fe7077bdbf..adb3c134ae 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -74,7 +74,7 @@ const props = defineProps({
 	},
 });
 
-let legendEl = $shallowRef<InstanceType<typeof MkChartLegend>>();
+const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
 
 const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
 const negate = arr => arr.map(x => -x);
@@ -268,7 +268,7 @@ const render = () => {
 				gradient,
 			},
 		},
-		plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl)] : [])],
+		plugins: [chartVLine(vLineColor), ...(props.detailed ? [chartLegend(legendEl.value)] : [])],
 	});
 };
 
diff --git a/packages/frontend/src/components/MkChartLegend.vue b/packages/frontend/src/components/MkChartLegend.vue
index d321114cba..1a1b4323d9 100644
--- a/packages/frontend/src/components/MkChartLegend.vue
+++ b/packages/frontend/src/components/MkChartLegend.vue
@@ -13,29 +13,30 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { shallowRef } from 'vue';
 import { Chart, LegendItem } from 'chart.js';
 
 const props = defineProps({
 });
 
-let chart = $shallowRef<Chart>();
-let items = $shallowRef<LegendItem[]>([]);
+const chart = shallowRef<Chart>();
+const items = shallowRef<LegendItem[]>([]);
 
 function update(_chart: Chart, _items: LegendItem[]) {
-	chart = _chart,
-	items = _items;
+	chart.value = _chart,
+	items.value = _items;
 }
 
 function onClick(item: LegendItem) {
-	if (chart == null) return;
-	const { type } = chart.config;
+	if (chart.value == null) return;
+	const { type } = chart.value.config;
 	if (type === 'pie' || type === 'doughnut') {
 		// Pie and doughnut charts only have a single dataset and visibility is per item
-		chart.toggleDataVisibility(item.index);
+		chart.value.toggleDataVisibility(item.index);
 	} else {
-		chart.setDatasetVisibility(item.datasetIndex, !chart.isDatasetVisible(item.datasetIndex));
+		chart.value.setDatasetVisibility(item.datasetIndex, !chart.value.isDatasetVisible(item.datasetIndex));
 	}
-	chart.update();
+	chart.value.update();
 }
 
 defineExpose({
diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue
index 1c3920962e..f255961e25 100644
--- a/packages/frontend/src/components/MkClickerGame.vue
+++ b/packages/frontend/src/components/MkClickerGame.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, onMounted, onUnmounted } from 'vue';
+import { computed, onMounted, onUnmounted, ref } from 'vue';
 import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue';
 import * as os from '@/os.js';
 import { useInterval } from '@/scripts/use-interval.js';
@@ -29,8 +29,8 @@ import { claimAchievement } from '@/scripts/achievements.js';
 
 const saveData = game.saveData;
 const cookies = computed(() => saveData.value?.cookies);
-let cps = $ref(0);
-let prevCookies = $ref(0);
+const cps = ref(0);
+const prevCookies = ref(0);
 
 function onClick(ev: MouseEvent) {
 	const x = ev.clientX;
@@ -48,9 +48,9 @@ function onClick(ev: MouseEvent) {
 }
 
 useInterval(() => {
-	const diff = saveData.value!.cookies - prevCookies;
-	cps = diff;
-	prevCookies = saveData.value!.cookies;
+	const diff = saveData.value!.cookies - prevCookies.value;
+	cps.value = diff;
+	prevCookies.value = saveData.value!.cookies;
 }, 1000, {
 	immediate: false,
 	afterMounted: true,
@@ -63,7 +63,7 @@ useInterval(game.save, 1000 * 5, {
 
 onMounted(async () => {
 	await game.load();
-	prevCookies = saveData.value!.cookies;
+	prevCookies.value = saveData.value!.cookies;
 });
 
 onUnmounted(() => {
diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue
index 6cca7fc353..b78252be89 100644
--- a/packages/frontend/src/components/MkContextMenu.vue
+++ b/packages/frontend/src/components/MkContextMenu.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onBeforeUnmount } from 'vue';
+import { onMounted, onBeforeUnmount, shallowRef, ref } from 'vue';
 import MkMenu from './MkMenu.vue';
 import { MenuItem } from './types/menu.vue';
 import contains from '@/scripts/contains.js';
@@ -34,9 +34,9 @@ const emit = defineEmits<{
 	(ev: 'closed'): void;
 }>();
 
-let rootEl = $shallowRef<HTMLDivElement>();
+const rootEl = shallowRef<HTMLDivElement>();
 
-let zIndex = $ref<number>(os.claimZIndex('high'));
+const zIndex = ref<number>(os.claimZIndex('high'));
 
 const SCROLLBAR_THICKNESS = 16;
 
@@ -44,8 +44,8 @@ onMounted(() => {
 	let left = props.ev.pageX + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
 	let top = props.ev.pageY + 1; // 間違って右ダブルクリックした場合に意図せずアイテムがクリックされるのを防ぐため + 1
 
-	const width = rootEl.offsetWidth;
-	const height = rootEl.offsetHeight;
+	const width = rootEl.value.offsetWidth;
+	const height = rootEl.value.offsetHeight;
 
 	if (left + width - window.pageXOffset >= (window.innerWidth - SCROLLBAR_THICKNESS)) {
 		left = (window.innerWidth - SCROLLBAR_THICKNESS) - width + window.pageXOffset;
@@ -63,8 +63,8 @@ onMounted(() => {
 		left = 0;
 	}
 
-	rootEl.style.top = `${top}px`;
-	rootEl.style.left = `${left}px`;
+	rootEl.value.style.top = `${top}px`;
+	rootEl.value.style.left = `${left}px`;
 
 	document.body.addEventListener('mousedown', onMousedown);
 });
@@ -74,7 +74,7 @@ onBeforeUnmount(() => {
 });
 
 function onMousedown(evt: Event) {
-	if (!contains(rootEl, evt.target) && (rootEl !== evt.target)) emit('closed');
+	if (!contains(rootEl.value, evt.target) && (rootEl.value !== evt.target)) emit('closed');
 }
 </script>
 
diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue
index 81f3936600..0a1ddd3171 100644
--- a/packages/frontend/src/components/MkCropperDialog.vue
+++ b/packages/frontend/src/components/MkCropperDialog.vue
@@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import Cropper from 'cropperjs';
 import tinycolor from 'tinycolor2';
@@ -56,10 +56,10 @@ const props = defineProps<{
 }>();
 
 const imgUrl = getProxiedImageUrl(props.file.url, undefined, true);
-let dialogEl = $shallowRef<InstanceType<typeof MkModalWindow>>();
-let imgEl = $shallowRef<HTMLImageElement>();
+const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
+const imgEl = shallowRef<HTMLImageElement>();
 let cropper: Cropper | null = null;
-let loading = $ref(true);
+const loading = ref(true);
 
 const ok = async () => {
 	const promise = new Promise<Misskey.entities.DriveFile>(async (res) => {
@@ -94,16 +94,16 @@ const ok = async () => {
 	const f = await promise;
 
 	emit('ok', f);
-	dialogEl!.close();
+	dialogEl.value!.close();
 };
 
 const cancel = () => {
 	emit('cancel');
-	dialogEl!.close();
+	dialogEl.value!.close();
 };
 
 const onImageLoad = () => {
-	loading = false;
+	loading.value = false;
 
 	if (cropper) {
 		cropper.getCropperImage()!.$center('contain');
@@ -112,7 +112,7 @@ const onImageLoad = () => {
 };
 
 onMounted(() => {
-	cropper = new Cropper(imgEl!, {
+	cropper = new Cropper(imgEl.value!, {
 	});
 
 	const computedStyle = getComputedStyle(document.documentElement);
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 33757ccc83..3c1f83d335 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -30,8 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown">
 			<template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template>
 			<template #caption>
-				<span v-if="okButtonDisabled && disabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"/>
-				<span v-else-if="okButtonDisabled && disabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/>
+				<span v-if="okButtonDisabledReason === 'charactersExceeded'" v-text="i18n.t('_dialog.charactersExceeded', { current: (inputValue as string).length, max: input.maxLength ?? 'NaN' })"/>
+				<span v-else-if="okButtonDisabledReason === 'charactersBelow'" v-text="i18n.t('_dialog.charactersBelow', { current: (inputValue as string).length, min: input.minLength ?? 'NaN' })"/>
 			</template>
 		</MkInput>
 		<MkSelect v-if="select" v-model="selectedValue" autofocus>
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</template>
 		</MkSelect>
 		<div v-if="(showOkButton || showCancelButton) && !actions" :class="$style.buttons">
-			<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabled" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
+			<MkButton v-if="showOkButton" data-cy-modal-dialog-ok inline primary rounded :autofocus="!input && !select" :disabled="okButtonDisabledReason" @click="ok">{{ okText ?? ((showCancelButton || input || select) ? i18n.ts.ok : i18n.ts.gotIt) }}</MkButton>
 			<MkButton v-if="showCancelButton || input || select" data-cy-modal-dialog-cancel inline rounded @click="cancel">{{ cancelText ?? i18n.ts.cancel }}</MkButton>
 		</div>
 		<div v-if="actions" :class="$style.buttons">
@@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onBeforeUnmount, onMounted, ref, shallowRef } from 'vue';
+import { onBeforeUnmount, onMounted, ref, shallowRef, computed } from 'vue';
 import MkModal from '@/components/MkModal.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -122,24 +122,21 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
 const inputValue = ref<string | number | null>(props.input?.default ?? null);
 const selectedValue = ref(props.select?.default ?? null);
 
-let disabledReason = $ref<null | 'charactersExceeded' | 'charactersBelow'>(null);
-const okButtonDisabled = $computed<boolean>(() => {
+const okButtonDisabledReason = computed<null | 'charactersExceeded' | 'charactersBelow'>(() => {
 	if (props.input) {
 		if (props.input.minLength) {
 			if ((inputValue.value || inputValue.value === '') && (inputValue.value as string).length < props.input.minLength) {
-				disabledReason = 'charactersBelow';
-				return true;
+				return 'charactersBelow';
 			}
 		}
 		if (props.input.maxLength) {
 			if (inputValue.value && (inputValue.value as string).length > props.input.maxLength) {
-				disabledReason = 'charactersExceeded';
-				return true;
+				return 'charactersExceeded';
 			}
 		}
 	}
 
-	return false;
+	return null;
 });
 
 function done(canceled: boolean, result?) {
diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue
index 05b137e335..2cce1f5520 100644
--- a/packages/frontend/src/components/MkEmojiPickerDialog.vue
+++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue
@@ -31,6 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { shallowRef } from 'vue';
 import MkModal from '@/components/MkModal.vue';
 import MkEmojiPicker from '@/components/MkEmojiPicker.vue';
 import { defaultStore } from '@/store.js';
@@ -54,23 +55,23 @@ const emit = defineEmits<{
 	(ev: 'closed'): void;
 }>();
 
-const modal = $shallowRef<InstanceType<typeof MkModal>>();
-const picker = $shallowRef<InstanceType<typeof MkEmojiPicker>>();
+const modal = shallowRef<InstanceType<typeof MkModal>>();
+const picker = shallowRef<InstanceType<typeof MkEmojiPicker>>();
 
 function chosen(emoji: any) {
 	emit('done', emoji);
 	if (props.choseAndClose) {
-		modal?.close();
+		modal.value?.close();
 	}
 }
 
 function opening() {
-	picker?.reset();
-	picker?.focus();
+	picker.value?.reset();
+	picker.value?.focus();
 
 	// 何故かちょっと待たないとフォーカスされない
 	setTimeout(() => {
-		picker?.focus();
+		picker.value?.focus();
 	}, 10);
 }
 </script>
diff --git a/packages/frontend/src/components/MkFileCaptionEditWindow.vue b/packages/frontend/src/components/MkFileCaptionEditWindow.vue
index 28888fb9c8..922089a78b 100644
--- a/packages/frontend/src/components/MkFileCaptionEditWindow.vue
+++ b/packages/frontend/src/components/MkFileCaptionEditWindow.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { shallowRef, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
@@ -42,12 +42,12 @@ const emit = defineEmits<{
 	(ev: 'closed'): void;
 }>();
 
-const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
+const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
 
-let caption = $ref(props.default);
+const caption = ref(props.default);
 
 async function ok() {
-	emit('done', caption);
-	dialog.close();
+	emit('done', caption.value);
+	dialog.value.close();
 }
 </script>
diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue
index 60ecc13056..6b7dfb20e3 100644
--- a/packages/frontend/src/components/MkFolder.vue
+++ b/packages/frontend/src/components/MkFolder.vue
@@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { nextTick, onMounted } from 'vue';
+import { nextTick, onMounted, shallowRef, ref } from 'vue';
 import { defaultStore } from '@/store.js';
 
 const props = withDefaults(defineProps<{
@@ -70,10 +70,10 @@ const getBgColor = (el: HTMLElement) => {
 	}
 };
 
-let rootEl = $shallowRef<HTMLElement>();
-let bgSame = $ref(false);
-let opened = $ref(props.defaultOpen);
-let openedAtLeastOnce = $ref(props.defaultOpen);
+const rootEl = shallowRef<HTMLElement>();
+const bgSame = ref(false);
+const opened = ref(props.defaultOpen);
+const openedAtLeastOnce = ref(props.defaultOpen);
 
 function enter(el) {
 	const elementHeight = el.getBoundingClientRect().height;
@@ -98,20 +98,20 @@ function afterLeave(el) {
 }
 
 function toggle() {
-	if (!opened) {
-		openedAtLeastOnce = true;
+	if (!opened.value) {
+		openedAtLeastOnce.value = true;
 	}
 
 	nextTick(() => {
-		opened = !opened;
+		opened.value = !opened.value;
 	});
 }
 
 onMounted(() => {
 	const computedStyle = getComputedStyle(document.documentElement);
-	const parentBg = getBgColor(rootEl.parentElement);
+	const parentBg = getBgColor(rootEl.value.parentElement);
 	const myBg = computedStyle.getPropertyValue('--panel');
-	bgSame = parentBg === myBg;
+	bgSame.value = parentBg === myBg;
 });
 </script>
 
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index b8de71e3b7..88d3188189 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onBeforeUnmount, onMounted } from 'vue';
+import { onBeforeUnmount, onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
 import { useStream } from '@/stream.js';
@@ -57,9 +57,9 @@ const emit = defineEmits<{
 	(_: 'update:user', value: Misskey.entities.UserDetailed): void
 }>();
 
-let isFollowing = $ref(props.user.isFollowing);
-let hasPendingFollowRequestFromYou = $ref(props.user.hasPendingFollowRequestFromYou);
-let wait = $ref(false);
+const isFollowing = ref(props.user.isFollowing);
+const hasPendingFollowRequestFromYou = ref(props.user.hasPendingFollowRequestFromYou);
+const wait = ref(false);
 const connection = useStream().useChannel('main');
 
 if (props.user.isFollowing == null) {
@@ -71,16 +71,16 @@ if (props.user.isFollowing == null) {
 
 function onFollowChange(user: Misskey.entities.UserDetailed) {
 	if (user.id === props.user.id) {
-		isFollowing = user.isFollowing;
-		hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
+		isFollowing.value = user.isFollowing;
+		hasPendingFollowRequestFromYou.value = user.hasPendingFollowRequestFromYou;
 	}
 }
 
 async function onClick() {
-	wait = true;
+	wait.value = true;
 
 	try {
-		if (isFollowing) {
+		if (isFollowing.value) {
 			const { canceled } = await os.confirm({
 				type: 'warning',
 				text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }),
@@ -92,11 +92,11 @@ async function onClick() {
 				userId: props.user.id,
 			});
 		} else {
-			if (hasPendingFollowRequestFromYou) {
+			if (hasPendingFollowRequestFromYou.value) {
 				await os.api('following/requests/cancel', {
 					userId: props.user.id,
 				});
-				hasPendingFollowRequestFromYou = false;
+				hasPendingFollowRequestFromYou.value = false;
 			} else {
 				await os.api('following/create', {
 					userId: props.user.id,
@@ -106,7 +106,7 @@ async function onClick() {
 					...props.user,
 					withReplies: defaultStore.state.defaultWithReplies
 				});
-				hasPendingFollowRequestFromYou = true;
+				hasPendingFollowRequestFromYou.value = true;
 
 				claimAchievement('following1');
 
@@ -127,7 +127,7 @@ async function onClick() {
 	} catch (err) {
 		console.error(err);
 	} finally {
-		wait = false;
+		wait.value = false;
 	}
 }
 
diff --git a/packages/frontend/src/components/MkForgotPassword.vue b/packages/frontend/src/components/MkForgotPassword.vue
index 521ac11d12..9b57688a02 100644
--- a/packages/frontend/src/components/MkForgotPassword.vue
+++ b/packages/frontend/src/components/MkForgotPassword.vue
@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -53,19 +53,19 @@ const emit = defineEmits<{
 	(ev: 'closed'): void;
 }>();
 
-let dialog: InstanceType<typeof MkModalWindow> = $ref();
+const dialog = ref<InstanceType<typeof MkModalWindow>>();
 
-let username = $ref('');
-let email = $ref('');
-let processing = $ref(false);
+const username = ref('');
+const email = ref('');
+const processing = ref(false);
 
 async function onSubmit() {
-	processing = true;
+	processing.value = true;
 	await os.apiWithDialog('request-reset-password', {
-		username,
-		email,
+		username: username.value,
+		email: email.value,
 	});
 	emit('done');
-	dialog.close();
+	dialog.value.close();
 }
 </script>
diff --git a/packages/frontend/src/components/MkHeatmap.vue b/packages/frontend/src/components/MkHeatmap.vue
index 0022531e58..a57e6c9292 100644
--- a/packages/frontend/src/components/MkHeatmap.vue
+++ b/packages/frontend/src/components/MkHeatmap.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, nextTick, watch } from 'vue';
+import { onMounted, nextTick, watch, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
 import * as os from '@/os.js';
 import { defaultStore } from '@/store.js';
@@ -27,11 +27,11 @@ const props = defineProps<{
 	src: string;
 }>();
 
-const rootEl = $shallowRef<HTMLDivElement>(null);
-const chartEl = $shallowRef<HTMLCanvasElement>(null);
+const rootEl = shallowRef<HTMLDivElement>(null);
+const chartEl = shallowRef<HTMLCanvasElement>(null);
 const now = new Date();
 let chartInstance: Chart = null;
-let fetching = $ref(true);
+const fetching = ref(true);
 
 const { handler: externalTooltipHandler } = useChartTooltip({
 	position: 'middle',
@@ -42,8 +42,8 @@ async function renderChart() {
 		chartInstance.destroy();
 	}
 
-	const wide = rootEl.offsetWidth > 700;
-	const narrow = rootEl.offsetWidth < 400;
+	const wide = rootEl.value.offsetWidth > 700;
+	const narrow = rootEl.value.offsetWidth < 400;
 
 	const weeks = wide ? 50 : narrow ? 10 : 25;
 	const chartLimit = 7 * weeks;
@@ -88,7 +88,7 @@ async function renderChart() {
 		values = raw.deliverFailed;
 	}
 
-	fetching = false;
+	fetching.value = false;
 
 	await nextTick();
 
@@ -101,7 +101,7 @@ async function renderChart() {
 
 	const marginEachCell = 4;
 
-	chartInstance = new Chart(chartEl, {
+	chartInstance = new Chart(chartEl.value, {
 		type: 'matrix',
 		data: {
 			datasets: [{
@@ -210,7 +210,7 @@ async function renderChart() {
 }
 
 watch(() => props.src, () => {
-	fetching = true;
+	fetching.value = true;
 	renderChart();
 });
 
diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue
index 4fb573fdbc..942861e1f4 100644
--- a/packages/frontend/src/components/MkImgWithBlurhash.vue
+++ b/packages/frontend/src/components/MkImgWithBlurhash.vue
@@ -21,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts">
-import { $ref } from 'vue/macros';
 import DrawBlurhash from '@/workers/draw-blurhash?worker';
 import TestWebGL2 from '@/workers/test-webgl2?worker';
 import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch.js';
@@ -58,7 +57,7 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
 </script>
 
 <script lang="ts" setup>
-import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue';
+import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch, ref } from 'vue';
 import { v4 as uuid } from 'uuid';
 import { render } from 'buraha';
 import { defaultStore } from '@/store.js';
@@ -98,41 +97,41 @@ const viewId = uuid();
 const canvas = shallowRef<HTMLCanvasElement>();
 const root = shallowRef<HTMLDivElement>();
 const img = shallowRef<HTMLImageElement>();
-let loaded = $ref(false);
-let canvasWidth = $ref(64);
-let canvasHeight = $ref(64);
-let imgWidth = $ref(props.width);
-let imgHeight = $ref(props.height);
-let bitmapTmp = $ref<CanvasImageSource | undefined>();
-const hide = computed(() => !loaded || props.forceBlurhash);
+const loaded = ref(false);
+const canvasWidth = ref(64);
+const canvasHeight = ref(64);
+const imgWidth = ref(props.width);
+const imgHeight = ref(props.height);
+const bitmapTmp = ref<CanvasImageSource | undefined>();
+const hide = computed(() => !loaded.value || props.forceBlurhash);
 
 function waitForDecode() {
 	if (props.src != null && props.src !== '') {
 		nextTick()
 			.then(() => img.value?.decode())
 			.then(() => {
-				loaded = true;
+				loaded.value = true;
 			}, error => {
 				console.log('Error occurred during decoding image', img.value, error);
 			});
 	} else {
-		loaded = false;
+		loaded.value = false;
 	}
 }
 
 watch([() => props.width, () => props.height, root], () => {
 	const ratio = props.width / props.height;
 	if (ratio > 1) {
-		canvasWidth = Math.round(64 * ratio);
-		canvasHeight = 64;
+		canvasWidth.value = Math.round(64 * ratio);
+		canvasHeight.value = 64;
 	} else {
-		canvasWidth = 64;
-		canvasHeight = Math.round(64 / ratio);
+		canvasWidth.value = 64;
+		canvasHeight.value = Math.round(64 / ratio);
 	}
 
 	const clientWidth = root.value?.clientWidth ?? 300;
-	imgWidth = clientWidth;
-	imgHeight = Math.round(clientWidth / ratio);
+	imgWidth.value = clientWidth;
+	imgHeight.value = Math.round(clientWidth / ratio);
 }, {
 	immediate: true,
 });
@@ -140,15 +139,15 @@ watch([() => props.width, () => props.height, root], () => {
 function drawImage(bitmap: CanvasImageSource) {
 	// canvasがない(mountedされていない)場合はTmpに保存しておく
 	if (!canvas.value) {
-		bitmapTmp = bitmap;
+		bitmapTmp.value = bitmap;
 		return;
 	}
 
 	// canvasがあれば描画する
-	bitmapTmp = undefined;
+	bitmapTmp.value = undefined;
 	const ctx = canvas.value.getContext('2d');
 	if (!ctx) return;
-	ctx.drawImage(bitmap, 0, 0, canvasWidth, canvasHeight);
+	ctx.drawImage(bitmap, 0, 0, canvasWidth.value, canvasHeight.value);
 }
 
 function drawAvg() {
@@ -160,7 +159,7 @@ function drawAvg() {
 	// avgColorでお茶をにごす
 	ctx.beginPath();
 	ctx.fillStyle = extractAvgColorFromBlurhash(props.hash) ?? '#888';
-	ctx.fillRect(0, 0, canvasWidth, canvasHeight);
+	ctx.fillRect(0, 0, canvasWidth.value, canvasHeight.value);
 }
 
 async function draw() {
@@ -212,8 +211,8 @@ watch(() => props.hash, () => {
 
 onMounted(() => {
 	// drawImageがmountedより先に呼ばれている場合はここで描画する
-	if (bitmapTmp) {
-		drawImage(bitmapTmp);
+	if (bitmapTmp.value) {
+		drawImage(bitmapTmp.value);
 	}
 	waitForDecode();
 });
diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue
index 9710f779d5..8a63e0cced 100644
--- a/packages/frontend/src/components/MkInstanceCardMini.vue
+++ b/packages/frontend/src/components/MkInstanceCardMini.vue
@@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkMiniChart from '@/components/MkMiniChart.vue';
 import * as os from '@/os.js';
@@ -24,12 +25,12 @@ const props = defineProps<{
 	instance: Misskey.entities.FederationInstance;
 }>();
 
-let chartValues = $ref<number[] | null>(null);
+const chartValues = ref<number[] | null>(null);
 
 os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
 	// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
 	res['requests.received'].splice(0, 1);
-	chartValues = res['requests.received'];
+	chartValues.value = res['requests.received'];
 });
 
 function getInstanceIcon(instance): string {
diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue
index 509254de74..7b763ad385 100644
--- a/packages/frontend/src/components/MkInstanceStats.vue
+++ b/packages/frontend/src/components/MkInstanceStats.vue
@@ -84,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref, shallowRef } from 'vue';
 import { Chart } from 'chart.js';
 import MkSelect from '@/components/MkSelect.vue';
 import MkChart from '@/components/MkChart.vue';
@@ -100,11 +100,11 @@ import { initChart } from '@/scripts/init-chart.js';
 initChart();
 
 const chartLimit = 500;
-let chartSpan = $ref<'hour' | 'day'>('hour');
-let chartSrc = $ref('active-users');
-let heatmapSrc = $ref('active-users');
-let subDoughnutEl = $shallowRef<HTMLCanvasElement>();
-let pubDoughnutEl = $shallowRef<HTMLCanvasElement>();
+const chartSpan = ref<'hour' | 'day'>('hour');
+const chartSrc = ref('active-users');
+const heatmapSrc = ref('active-users');
+const subDoughnutEl = shallowRef<HTMLCanvasElement>();
+const pubDoughnutEl = shallowRef<HTMLCanvasElement>();
 
 const { handler: externalTooltipHandler1 } = useChartTooltip({
 	position: 'middle',
@@ -163,7 +163,7 @@ function createDoughnut(chartEl, tooltip, data) {
 
 onMounted(() => {
 	os.apiGet('federation/stats', { limit: 30 }).then(fedStats => {
-		createDoughnut(subDoughnutEl, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({
+		createDoughnut(subDoughnutEl.value, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({
 			name: x.host,
 			color: x.themeColor,
 			value: x.followersCount,
@@ -172,7 +172,7 @@ onMounted(() => {
 			},
 		})).concat([{ name: '(other)', color: '#80808080', value: fedStats.otherFollowersCount }]));
 
-		createDoughnut(pubDoughnutEl, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({
+		createDoughnut(pubDoughnutEl.value, externalTooltipHandler2, fedStats.topPubInstances.map(x => ({
 			name: x.host,
 			color: x.themeColor,
 			value: x.followingCount,
diff --git a/packages/frontend/src/components/MkInstanceTicker.vue b/packages/frontend/src/components/MkInstanceTicker.vue
index 1a4f2bfbb9..3ee2aa7174 100644
--- a/packages/frontend/src/components/MkInstanceTicker.vue
+++ b/packages/frontend/src/components/MkInstanceTicker.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { computed } from 'vue';
 import { instanceName } from '@/config.js';
 import { instance as Instance } from '@/instance.js';
 import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
@@ -30,7 +30,7 @@ const instance = props.instance ?? {
 	themeColor: (document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement).content,
 };
 
-const faviconUrl = $computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(Instance.faviconUrl, 'preview') ?? '/favicon.ico');
+const faviconUrl = computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(Instance.faviconUrl, 'preview') ?? '/favicon.ico');
 
 const themeColor = instance.themeColor ?? '#777777';
 
diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue
index b16c05f575..120ed7a86c 100644
--- a/packages/frontend/src/components/MkLaunchPad.vue
+++ b/packages/frontend/src/components/MkLaunchPad.vue
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { shallowRef } from 'vue';
 import MkModal from '@/components/MkModal.vue';
 import { navbarItemDef } from '@/navbar.js';
 import { defaultStore } from '@/store.js';
@@ -48,7 +48,7 @@ const preferedModalType = (deviceKind === 'desktop' && props.src != null) ? 'pop
 	deviceKind === 'smartphone' ? 'drawer' :
 	'dialog';
 
-const modal = $shallowRef<InstanceType<typeof MkModal>>();
+const modal = shallowRef<InstanceType<typeof MkModal>>();
 
 const menu = defaultStore.state.menu;
 
@@ -63,7 +63,7 @@ const items = Object.keys(navbarItemDef).filter(k => !menu.includes(k)).map(k =>
 }));
 
 function close() {
-	modal.close();
+	modal.value.close();
 }
 </script>
 
diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue
index 0501d797f8..808a071d10 100644
--- a/packages/frontend/src/components/MkLink.vue
+++ b/packages/frontend/src/components/MkLink.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, ref } from 'vue';
 import { url as local } from '@/config.js';
 import { useTooltip } from '@/scripts/use-tooltip.js';
 import * as os from '@/os.js';
@@ -29,13 +29,13 @@ const self = props.url.startsWith(local);
 const attr = self ? 'to' : 'href';
 const target = self ? null : '_blank';
 
-const el = $ref();
+const el = ref();
 
-useTooltip($$(el), (showing) => {
+useTooltip(el, (showing) => {
 	os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
 		showing,
 		url: props.url,
-		source: el,
+		source: el.value,
 	}, {}, 'closed');
 });
 </script>
diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue
index 122f8ad794..92b5388c34 100644
--- a/packages/frontend/src/components/MkMediaBanner.vue
+++ b/packages/frontend/src/components/MkMediaBanner.vue
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, shallowRef, watch } from 'vue';
+import { onMounted, shallowRef, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { i18n } from '@/i18n.js';
 
@@ -42,7 +42,7 @@ const props = withDefaults(defineProps<{
 });
 
 const audioEl = shallowRef<HTMLAudioElement>();
-let hide = $ref(true);
+const hide = ref(true);
 
 watch(audioEl, () => {
 	if (audioEl.value) {
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index 003c0979c7..d236b222aa 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import { getStaticImageUrl } from '@/scripts/media-proxy.js';
 import bytes from '@/filters/bytes.js';
@@ -73,10 +73,10 @@ const props = withDefaults(defineProps<{
 	controls: true,
 });
 
-let hide = $ref(true);
-let darkMode: boolean = $ref(defaultStore.state.darkMode);
+const hide = ref(true);
+const darkMode = ref<boolean>(defaultStore.state.darkMode);
 
-const url = $computed(() => (props.raw || defaultStore.state.loadRawImages)
+const url = computed(() => (props.raw || defaultStore.state.loadRawImages)
 	? props.image.url
 	: defaultStore.state.disableShowingAnimatedImages
 		? getStaticImageUrl(props.image.url)
@@ -87,14 +87,14 @@ function onclick() {
 	if (!props.controls) {
 		return;
 	}
-	if (hide) {
-		hide = false;
+	if (hide.value) {
+		hide.value = false;
 	}
 }
 
 // Plugin:register_note_view_interruptor を使って書き換えられる可能性があるためwatchする
 watch(() => props.image, () => {
-	hide = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
+	hide.value = (defaultStore.state.nsfw === 'force' || defaultStore.state.dataSaver.media) ? true : (props.image.isSensitive && defaultStore.state.nsfw !== 'ignore');
 }, {
 	deep: true,
 	immediate: true,
@@ -105,7 +105,7 @@ function showMenu(ev: MouseEvent) {
 		text: i18n.ts.hide,
 		icon: 'ti ti-eye-off',
 		action: () => {
-			hide = true;
+			hide.value = true;
 		},
 	}, ...(iAmModerator ? [{
 		text: i18n.ts.markAsSensitive,
@@ -126,7 +126,7 @@ function showMenu(ev: MouseEvent) {
 
 .sensitive {
 	position: relative;
-	
+
 	&::after {
 		content: "";
 		position: absolute;
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index f134f2021d..b154eb0202 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -63,7 +63,7 @@ async function getClientWidthWithCache(targetEl: HTMLElement, containerEl: HTMLE
 </script>
 
 <script lang="ts" setup>
-import { onMounted, onUnmounted, shallowRef } from 'vue';
+import { computed, onMounted, onUnmounted, shallowRef } from 'vue';
 import * as Misskey from 'misskey-js';
 import PhotoSwipeLightbox from 'photoswipe/lightbox';
 import PhotoSwipe from 'photoswipe';
@@ -86,7 +86,7 @@ const container = shallowRef<HTMLElement | null | undefined>(undefined);
 const gallery = shallowRef<HTMLDivElement>();
 const pswpZIndex = os.claimZIndex('middle');
 document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
-const count = $computed(() => props.mediaList.filter(media => previewable(media)).length);
+const count = computed(() => props.mediaList.filter(media => previewable(media)).length);
 let lightbox: PhotoSwipeLightbox | null;
 
 const popstateHandler = (): void => {
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 736f48ea3c..951a0b2815 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts">
-import { Ref, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
+import { Ref, computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
 import { focusPrev, focusNext } from '@/scripts/focus.js';
 import MkSwitchButton from '@/components/MkSwitch.button.vue';
 import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu';
@@ -90,19 +90,19 @@ const emit = defineEmits<{
 	(ev: 'hide'): void;
 }>();
 
-let itemsEl = $shallowRef<HTMLDivElement>();
+const itemsEl = shallowRef<HTMLDivElement>();
 
-let items2: InnerMenuItem[] = $ref([]);
+const items2 = ref<InnerMenuItem[]>([]);
 
-let child = $shallowRef<InstanceType<typeof XChild>>();
+const child = shallowRef<InstanceType<typeof XChild>>();
 
-let keymap = $computed(() => ({
+const keymap = computed(() => ({
 	'up|k|shift+tab': focusUp,
 	'down|j|tab': focusDown,
 	'esc': close,
 }));
 
-let childShowingItem = $ref<MenuItem | null>();
+const childShowingItem = ref<MenuItem | null>();
 
 let preferClick = isTouchUsing || props.asDrawer;
 
@@ -115,22 +115,22 @@ watch(() => props.items, () => {
 		if (item && 'then' in item) { // if item is Promise
 			items[i] = { type: 'pending' };
 			item.then(actualItem => {
-				items2[i] = actualItem;
+				items2.value[i] = actualItem;
 			});
 		}
 	}
 
-	items2 = items as InnerMenuItem[];
+	items2.value = items as InnerMenuItem[];
 }, {
 	immediate: true,
 });
 
 const childMenu = ref<MenuItem[] | null>();
-let childTarget = $shallowRef<HTMLElement | null>();
+const childTarget = shallowRef<HTMLElement | null>();
 
 function closeChild() {
 	childMenu.value = null;
-	childShowingItem = null;
+	childShowingItem.value = null;
 }
 
 function childActioned() {
@@ -139,8 +139,8 @@ function childActioned() {
 }
 
 const onGlobalMousedown = (event: MouseEvent) => {
-	if (childTarget && (event.target === childTarget || childTarget.contains(event.target))) return;
-	if (child && child.checkHit(event)) return;
+	if (childTarget.value && (event.target === childTarget.value || childTarget.value.contains(event.target))) return;
+	if (child.value && child.value.checkHit(event)) return;
 	closeChild();
 };
 
@@ -177,10 +177,10 @@ async function showChildren(item: MenuParent, ev: MouseEvent) {
 		});
 		emit('hide');
 	} else {
-		childTarget = ev.currentTarget ?? ev.target;
+		childTarget.value = ev.currentTarget ?? ev.target;
 		// これでもリアクティビティは保たれる
 		childMenu.value = children;
-		childShowingItem = item;
+		childShowingItem.value = item;
 	}
 }
 
@@ -209,7 +209,7 @@ function switchItem(item: MenuSwitch & { ref: any }) {
 onMounted(() => {
 	if (props.viaKeyboard) {
 		nextTick(() => {
-			if (itemsEl) focusNext(itemsEl.children[0], true, false);
+			if (itemsEl.value) focusNext(itemsEl.value.children[0], true, false);
 		});
 	}
 
diff --git a/packages/frontend/src/components/MkMiniChart.vue b/packages/frontend/src/components/MkMiniChart.vue
index 8d2a147306..f0a2c232bd 100644
--- a/packages/frontend/src/components/MkMiniChart.vue
+++ b/packages/frontend/src/components/MkMiniChart.vue
@@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, ref } from 'vue';
 import { v4 as uuid } from 'uuid';
 import tinycolor from 'tinycolor2';
 import { useInterval } from '@/scripts/use-interval.js';
@@ -43,11 +43,11 @@ const props = defineProps<{
 const viewBoxX = 50;
 const viewBoxY = 50;
 const gradientId = uuid();
-let polylinePoints = $ref('');
-let polygonPoints = $ref('');
-let headX = $ref<number | null>(null);
-let headY = $ref<number | null>(null);
-let clock = $ref<number | null>(null);
+const polylinePoints = ref('');
+const polygonPoints = ref('');
+const headX = ref<number | null>(null);
+const headY = ref<number | null>(null);
+const clock = ref<number | null>(null);
 const accent = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent'));
 const color = accent.toRgbString();
 
@@ -60,12 +60,12 @@ function draw(): void {
 		(1 - (n / peak)) * viewBoxY,
 	]);
 
-	polylinePoints = _polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+	polylinePoints.value = _polylinePoints.map(xy => `${xy[0]},${xy[1]}`).join(' ');
 
-	polygonPoints = `0,${ viewBoxY } ${ polylinePoints } ${ viewBoxX },${ viewBoxY }`;
+	polygonPoints.value = `0,${ viewBoxY } ${ polylinePoints.value } ${ viewBoxX },${ viewBoxY }`;
 
-	headX = _polylinePoints.at(-1)![0];
-	headY = _polylinePoints.at(-1)![1];
+	headX.value = _polylinePoints.at(-1)![0];
+	headY.value = _polylinePoints.at(-1)![1];
 }
 
 watch(() => props.src, draw, { immediate: true });
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index ec5039c504..5cd31cdf7c 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch } from 'vue';
+import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch, ref, shallowRef, computed } from 'vue';
 import * as os from '@/os.js';
 import { isTouchUsing } from '@/scripts/touch.js';
 import { defaultStore } from '@/store.js';
@@ -89,14 +89,14 @@ const emit = defineEmits<{
 
 provide('modal', true);
 
-let maxHeight = $ref<number>();
-let fixed = $ref(false);
-let transformOrigin = $ref('center');
-let showing = $ref(true);
-let content = $shallowRef<HTMLElement>();
+const maxHeight = ref<number>();
+const fixed = ref(false);
+const transformOrigin = ref('center');
+const showing = ref(true);
+const content = shallowRef<HTMLElement>();
 const zIndex = os.claimZIndex(props.zPriority);
-let useSendAnime = $ref(false);
-const type = $computed<ModalTypes>(() => {
+const useSendAnime = ref(false);
+const type = computed<ModalTypes>(() => {
 	if (props.preferType === 'auto') {
 		if (!defaultStore.state.disableDrawer && isTouchUsing && deviceKind === 'smartphone') {
 			return 'drawer';
@@ -107,26 +107,26 @@ const type = $computed<ModalTypes>(() => {
 		return props.preferType!;
 	}
 });
-const isEnableBgTransparent = $computed(() => props.transparentBg && (type === 'popup'));
-let transitionName = $computed((() =>
+const isEnableBgTransparent = computed(() => props.transparentBg && (type.value === 'popup'));
+const transitionName = computed((() =>
 	defaultStore.state.animation
-		? useSendAnime
+		? useSendAnime.value
 			? 'send'
-			: type === 'drawer'
+			: type.value === 'drawer'
 				? 'modal-drawer'
-				: type === 'popup'
+				: type.value === 'popup'
 					? 'modal-popup'
 					: 'modal'
 		: ''
 ));
-let transitionDuration = $computed((() =>
-	transitionName === 'send'
+const transitionDuration = computed((() =>
+	transitionName.value === 'send'
 		? 400
-		: transitionName === 'modal-popup'
+		: transitionName.value === 'modal-popup'
 			? 100
-			: transitionName === 'modal'
+			: transitionName.value === 'modal'
 				? 200
-				: transitionName === 'modal-drawer'
+				: transitionName.value === 'modal-drawer'
 					? 200
 					: 0
 ));
@@ -135,12 +135,12 @@ let contentClicking = false;
 
 function close(opts: { useSendAnimation?: boolean } = {}) {
 	if (opts.useSendAnimation) {
-		useSendAnime = true;
+		useSendAnime.value = true;
 	}
 
 	// eslint-disable-next-line vue/no-mutating-props
 	if (props.src) props.src.style.pointerEvents = 'auto';
-	showing = false;
+	showing.value = false;
 	emit('close');
 }
 
@@ -149,8 +149,8 @@ function onBgClick() {
 	emit('click');
 }
 
-if (type === 'drawer') {
-	maxHeight = window.innerHeight / 1.5;
+if (type.value === 'drawer') {
+	maxHeight.value = window.innerHeight / 1.5;
 }
 
 const keymap = {
@@ -162,21 +162,21 @@ const SCROLLBAR_THICKNESS = 16;
 
 const align = () => {
 	if (props.src == null) return;
-	if (type === 'drawer') return;
-	if (type === 'dialog') return;
+	if (type.value === 'drawer') return;
+	if (type.value === 'dialog') return;
 
-	if (content == null) return;
+	if (content.value == null) return;
 
 	const srcRect = props.src.getBoundingClientRect();
 
-	const width = content!.offsetWidth;
-	const height = content!.offsetHeight;
+	const width = content.value!.offsetWidth;
+	const height = content.value!.offsetHeight;
 
 	let left;
 	let top;
 
-	const x = srcRect.left + (fixed ? 0 : window.pageXOffset);
-	const y = srcRect.top + (fixed ? 0 : window.pageYOffset);
+	const x = srcRect.left + (fixed.value ? 0 : window.pageXOffset);
+	const y = srcRect.top + (fixed.value ? 0 : window.pageYOffset);
 
 	if (props.anchor.x === 'center') {
 		left = x + (props.src.offsetWidth / 2) - (width / 2);
@@ -194,7 +194,7 @@ const align = () => {
 		top = y + props.src.offsetHeight;
 	}
 
-	if (fixed) {
+	if (fixed.value) {
 		// 画面から横にはみ出る場合
 		if (left + width > (window.innerWidth - SCROLLBAR_THICKNESS)) {
 			left = (window.innerWidth - SCROLLBAR_THICKNESS) - width;
@@ -207,16 +207,16 @@ const align = () => {
 		if (top + height > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
 			if (props.noOverlap && props.anchor.x === 'center') {
 				if (underSpace >= (upperSpace / 3)) {
-					maxHeight = underSpace;
+					maxHeight.value = underSpace;
 				} else {
-					maxHeight = upperSpace;
+					maxHeight.value = upperSpace;
 					top = (upperSpace + MARGIN) - height;
 				}
 			} else {
 				top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height;
 			}
 		} else {
-			maxHeight = underSpace;
+			maxHeight.value = underSpace;
 		}
 	} else {
 		// 画面から横にはみ出る場合
@@ -231,16 +231,16 @@ const align = () => {
 		if (top + height - window.pageYOffset > ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN)) {
 			if (props.noOverlap && props.anchor.x === 'center') {
 				if (underSpace >= (upperSpace / 3)) {
-					maxHeight = underSpace;
+					maxHeight.value = underSpace;
 				} else {
-					maxHeight = upperSpace;
+					maxHeight.value = upperSpace;
 					top = window.pageYOffset + ((upperSpace + MARGIN) - height);
 				}
 			} else {
 				top = ((window.innerHeight - SCROLLBAR_THICKNESS) - MARGIN) - height + window.pageYOffset - 1;
 			}
 		} else {
-			maxHeight = underSpace;
+			maxHeight.value = underSpace;
 		}
 	}
 
@@ -255,29 +255,29 @@ const align = () => {
 	let transformOriginX = 'center';
 	let transformOriginY = 'center';
 
-	if (top >= srcRect.top + props.src.offsetHeight + (fixed ? 0 : window.pageYOffset)) {
+	if (top >= srcRect.top + props.src.offsetHeight + (fixed.value ? 0 : window.pageYOffset)) {
 		transformOriginY = 'top';
-	} else if ((top + height) <= srcRect.top + (fixed ? 0 : window.pageYOffset)) {
+	} else if ((top + height) <= srcRect.top + (fixed.value ? 0 : window.pageYOffset)) {
 		transformOriginY = 'bottom';
 	}
 
-	if (left >= srcRect.left + props.src.offsetWidth + (fixed ? 0 : window.pageXOffset)) {
+	if (left >= srcRect.left + props.src.offsetWidth + (fixed.value ? 0 : window.pageXOffset)) {
 		transformOriginX = 'left';
-	} else if ((left + width) <= srcRect.left + (fixed ? 0 : window.pageXOffset)) {
+	} else if ((left + width) <= srcRect.left + (fixed.value ? 0 : window.pageXOffset)) {
 		transformOriginX = 'right';
 	}
 
-	transformOrigin = `${transformOriginX} ${transformOriginY}`;
+	transformOrigin.value = `${transformOriginX} ${transformOriginY}`;
 
-	content.style.left = left + 'px';
-	content.style.top = top + 'px';
+	content.value.style.left = left + 'px';
+	content.value.style.top = top + 'px';
 };
 
 const onOpened = () => {
 	emit('opened');
 
 	// モーダルコンテンツにマウスボタンが押され、コンテンツ外でマウスボタンが離されたときにモーダルバックグラウンドクリックと判定させないためにマウスイベントを監視しフラグ管理する
-	const el = content!.children[0];
+	const el = content.value!.children[0];
 	el.addEventListener('mousedown', ev => {
 		contentClicking = true;
 		window.addEventListener('mouseup', ev => {
@@ -299,7 +299,7 @@ onMounted(() => {
 			// eslint-disable-next-line vue/no-mutating-props
 			props.src.style.pointerEvents = 'none';
 		}
-		fixed = (type === 'drawer') || (getFixedContainer(props.src) != null);
+		fixed.value = (type.value === 'drawer') || (getFixedContainer(props.src) != null);
 
 		await nextTick();
 
@@ -307,7 +307,7 @@ onMounted(() => {
 	}, { immediate: true });
 
 	nextTick(() => {
-		alignObserver.observe(content!);
+		alignObserver.observe(content.value!);
 	});
 });
 
diff --git a/packages/frontend/src/components/MkModalWindow.vue b/packages/frontend/src/components/MkModalWindow.vue
index 1fdf0beb26..7e185e8453 100644
--- a/packages/frontend/src/components/MkModalWindow.vue
+++ b/packages/frontend/src/components/MkModalWindow.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onUnmounted } from 'vue';
+import { onMounted, onUnmounted, shallowRef, ref } from 'vue';
 import MkModal from './MkModal.vue';
 
 const props = withDefaults(defineProps<{
@@ -44,14 +44,14 @@ const emit = defineEmits<{
 	(event: 'ok'): void;
 }>();
 
-let modal = $shallowRef<InstanceType<typeof MkModal>>();
-let rootEl = $shallowRef<HTMLElement>();
-let headerEl = $shallowRef<HTMLElement>();
-let bodyWidth = $ref(0);
-let bodyHeight = $ref(0);
+const modal = shallowRef<InstanceType<typeof MkModal>>();
+const rootEl = shallowRef<HTMLElement>();
+const headerEl = shallowRef<HTMLElement>();
+const bodyWidth = ref(0);
+const bodyHeight = ref(0);
 
 const close = () => {
-	modal.close();
+	modal.value.close();
 };
 
 const onBgClick = () => {
@@ -67,14 +67,14 @@ const onKeydown = (evt) => {
 };
 
 const ro = new ResizeObserver((entries, observer) => {
-	bodyWidth = rootEl.offsetWidth;
-	bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight;
+	bodyWidth.value = rootEl.value.offsetWidth;
+	bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
 });
 
 onMounted(() => {
-	bodyWidth = rootEl.offsetWidth;
-	bodyHeight = rootEl.offsetHeight - headerEl.offsetHeight;
-	ro.observe(rootEl);
+	bodyWidth.value = rootEl.value.offsetWidth;
+	bodyHeight.value = rootEl.value.offsetHeight - headerEl.value.offsetHeight;
+	ro.observe(rootEl.value);
 });
 
 onUnmounted(() => {
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 596895efb9..36e3b253a2 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -205,12 +205,12 @@ const emit = defineEmits<{
 const inChannel = inject('inChannel', null);
 const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
 
-let note = $ref(deepClone(props.note));
+const note = ref(deepClone(props.note));
 
 // plugin
 if (noteViewInterruptors.length > 0) {
 	onMounted(async () => {
-		let result: Misskey.entities.Note | null = deepClone(note);
+		let result: Misskey.entities.Note | null = deepClone(note.value);
 		for (const interruptor of noteViewInterruptors) {
 			try {
 				result = await interruptor.handler(result);
@@ -222,15 +222,15 @@ if (noteViewInterruptors.length > 0) {
 				console.error(err);
 			}
 		}
-		note = result;
+		note.value = result;
 	});
 }
 
 const isRenote = (
-	note.renote != null &&
-	note.text == null &&
-	note.fileIds.length === 0 &&
-	note.poll == null
+	note.value.renote != null &&
+	note.value.text == null &&
+	note.value.fileIds.length === 0 &&
+	note.value.poll == null
 );
 
 const el = shallowRef<HTMLElement>();
@@ -239,21 +239,21 @@ const renoteButton = shallowRef<HTMLElement>();
 const renoteTime = shallowRef<HTMLElement>();
 const reactButton = shallowRef<HTMLElement>();
 const clipButton = shallowRef<HTMLElement>();
-let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note);
-const isMyRenote = $i && ($i.id === note.userId);
+const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
+const isMyRenote = $i && ($i.id === note.value.userId);
 const showContent = ref(false);
-const parsed = $computed(() => appearNote.text ? mfm.parse(appearNote.text) : null);
-const urls = $computed(() => parsed ? extractUrlFromMfm(parsed) : null);
-const isLong = shouldCollapsed(appearNote, urls ?? []);
-const collapsed = ref(appearNote.cw == null && isLong);
+const parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null);
+const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value) : null);
+const isLong = shouldCollapsed(appearNote.value, urls.value ?? []);
+const collapsed = ref(appearNote.value.cw == null && isLong);
 const isDeleted = ref(false);
-const muted = ref(checkMute(appearNote, $i?.mutedWords));
-const hardMuted = ref(props.withHardMute && checkMute(appearNote, $i?.hardMutedWords));
+const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
+const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords));
 const translation = ref<any>(null);
 const translating = ref(false);
-const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
-const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || (appearNote.visibility === 'followers' && appearNote.userId === $i.id));
-let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId || $i.id === appearNote.userId)) || (appearNote.myReaction != null)));
+const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
+const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i.id));
+const renoteCollapsed = ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || (appearNote.value.myReaction != null)));
 
 function checkMute(note: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null): boolean {
 	if (mutedWords == null) return false;
@@ -277,20 +277,20 @@ const keymap = {
 
 provide('react', (reaction: string) => {
 	os.api('notes/reactions/create', {
-		noteId: appearNote.id,
+		noteId: appearNote.value.id,
 		reaction: reaction,
 	});
 });
 
 if (props.mock) {
 	watch(() => props.note, (to) => {
-		note = deepClone(to);
+		note.value = deepClone(to);
 	}, { deep: true });
 } else {
 	useNoteCapture({
 		rootEl: el,
-		note: $$(appearNote),
-		pureNote: $$(note),
+		note: appearNote,
+		pureNote: note,
 		isDeletedRef: isDeleted,
 	});
 }
@@ -298,7 +298,7 @@ if (props.mock) {
 if (!props.mock) {
 	useTooltip(renoteButton, async (showing) => {
 		const renotes = await os.api('notes/renotes', {
-			noteId: appearNote.id,
+			noteId: appearNote.value.id,
 			limit: 11,
 		});
 
@@ -309,7 +309,7 @@ if (!props.mock) {
 		os.popup(MkUsersTooltip, {
 			showing,
 			users,
-			count: appearNote.renoteCount,
+			count: appearNote.value.renoteCount,
 			targetElement: renoteButton.value,
 		}, {}, 'closed');
 	});
@@ -319,7 +319,7 @@ function renote(viaKeyboard = false) {
 	pleaseLogin();
 	showMovedDialog();
 
-	const { menu } = getRenoteMenu({ note: note, renoteButton, mock: props.mock });
+	const { menu } = getRenoteMenu({ note: note.value, renoteButton, mock: props.mock });
 	os.popupMenu(menu, renoteButton.value, {
 		viaKeyboard,
 	});
@@ -331,8 +331,8 @@ function reply(viaKeyboard = false): void {
 		return;
 	}
 	os.post({
-		reply: appearNote,
-		channel: appearNote.channel,
+		reply: appearNote.value,
+		channel: appearNote.value.channel,
 		animation: !viaKeyboard,
 	}, () => {
 		focus();
@@ -342,7 +342,7 @@ function reply(viaKeyboard = false): void {
 function react(viaKeyboard = false): void {
 	pleaseLogin();
 	showMovedDialog();
-	if (appearNote.reactionAcceptance === 'likeOnly') {
+	if (appearNote.value.reactionAcceptance === 'likeOnly') {
 		sound.play('reaction');
 
 		if (props.mock) {
@@ -350,7 +350,7 @@ function react(viaKeyboard = false): void {
 		}
 
 		os.api('notes/reactions/create', {
-			noteId: appearNote.id,
+			noteId: appearNote.value.id,
 			reaction: '❤️',
 		});
 		const el = reactButton.value as HTMLElement | null | undefined;
@@ -371,10 +371,10 @@ function react(viaKeyboard = false): void {
 			}
 
 			os.api('notes/reactions/create', {
-				noteId: appearNote.id,
+				noteId: appearNote.value.id,
 				reaction: reaction,
 			});
-			if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) {
+			if (appearNote.value.text && appearNote.value.text.length > 100 && (Date.now() - new Date(appearNote.value.createdAt).getTime() < 1000 * 3)) {
 				claimAchievement('reactWithoutRead');
 			}
 		}, () => {
@@ -417,7 +417,7 @@ function onContextmenu(ev: MouseEvent): void {
 		ev.preventDefault();
 		react();
 	} else {
-		const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
+		const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
 		os.contextMenu(menu, ev).then(focus).finally(cleanup);
 	}
 }
@@ -427,7 +427,7 @@ function menu(viaKeyboard = false): void {
 		return;
 	}
 
-	const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
+	const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value });
 	os.popupMenu(menu, menuButton.value, {
 		viaKeyboard,
 	}).then(focus).finally(cleanup);
@@ -438,7 +438,7 @@ async function clip() {
 		return;
 	}
 
-	os.popupMenu(await getNoteClipMenu({ note: note, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus);
+	os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus);
 }
 
 function showRenoteMenu(viaKeyboard = false): void {
@@ -453,7 +453,7 @@ function showRenoteMenu(viaKeyboard = false): void {
 			danger: true,
 			action: () => {
 				os.api('notes/delete', {
-					noteId: note.id,
+					noteId: note.value.id,
 				});
 				isDeleted.value = true;
 			},
@@ -463,7 +463,7 @@ function showRenoteMenu(viaKeyboard = false): void {
 	if (isMyRenote) {
 		pleaseLogin();
 		os.popupMenu([
-			getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
+			getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
 			null,
 			getUnrenote(),
 		], renoteTime.value, {
@@ -471,9 +471,9 @@ function showRenoteMenu(viaKeyboard = false): void {
 		});
 	} else {
 		os.popupMenu([
-			getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
+			getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
 			null,
-			getAbuseNoteMenu(note, i18n.ts.reportAbuseRenote),
+			getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote),
 			$i.isModerator || $i.isAdmin ? getUnrenote() : undefined,
 		], renoteTime.value, {
 			viaKeyboard: viaKeyboard,
@@ -499,7 +499,7 @@ function focusAfter() {
 
 function readPromo() {
 	os.api('promo/read', {
-		noteId: appearNote.id,
+		noteId: appearNote.value.id,
 	});
 	isDeleted.value = true;
 }
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 31e97b6aaa..a8ccf26c4c 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -235,12 +235,12 @@ const props = defineProps<{
 
 const inChannel = inject('inChannel', null);
 
-let note = $ref(deepClone(props.note));
+const note = ref(deepClone(props.note));
 
 // plugin
 if (noteViewInterruptors.length > 0) {
 	onMounted(async () => {
-		let result: Misskey.entities.Note | null = deepClone(note);
+		let result: Misskey.entities.Note | null = deepClone(note.value);
 		for (const interruptor of noteViewInterruptors) {
 			try {
 				result = await interruptor.handler(result);
@@ -252,15 +252,15 @@ if (noteViewInterruptors.length > 0) {
 				console.error(err);
 			}
 		}
-		note = result;
+		note.value = result;
 	});
 }
 
 const isRenote = (
-	note.renote != null &&
-	note.text == null &&
-	note.fileIds.length === 0 &&
-	note.poll == null
+	note.value.renote != null &&
+	note.value.text == null &&
+	note.value.fileIds.length === 0 &&
+	note.value.poll == null
 );
 
 const el = shallowRef<HTMLElement>();
@@ -269,19 +269,19 @@ const renoteButton = shallowRef<HTMLElement>();
 const renoteTime = shallowRef<HTMLElement>();
 const reactButton = shallowRef<HTMLElement>();
 const clipButton = shallowRef<HTMLElement>();
-let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note);
-const isMyRenote = $i && ($i.id === note.userId);
+const appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
+const isMyRenote = $i && ($i.id === note.value.userId);
 const showContent = ref(false);
 const isDeleted = ref(false);
-const muted = ref($i ? checkWordMute(appearNote, $i, $i.mutedWords) : false);
+const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false);
 const translation = ref(null);
 const translating = ref(false);
-const parsed = appearNote.text ? mfm.parse(appearNote.text) : null;
+const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null;
 const urls = parsed ? extractUrlFromMfm(parsed) : null;
-const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
+const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
 const conversation = ref<Misskey.entities.Note[]>([]);
 const replies = ref<Misskey.entities.Note[]>([]);
-const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || appearNote.userId === $i.id);
+const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || appearNote.value.userId === $i.id);
 
 const keymap = {
 	'r': () => reply(true),
@@ -294,41 +294,41 @@ const keymap = {
 
 provide('react', (reaction: string) => {
 	os.api('notes/reactions/create', {
-		noteId: appearNote.id,
+		noteId: appearNote.value.id,
 		reaction: reaction,
 	});
 });
 
-let tab = $ref('replies');
-let reactionTabType = $ref(null);
+const tab = ref('replies');
+const reactionTabType = ref(null);
 
-const renotesPagination = $computed(() => ({
+const renotesPagination = computed(() => ({
 	endpoint: 'notes/renotes',
 	limit: 10,
 	params: {
-		noteId: appearNote.id,
+		noteId: appearNote.value.id,
 	},
 }));
 
-const reactionsPagination = $computed(() => ({
+const reactionsPagination = computed(() => ({
 	endpoint: 'notes/reactions',
 	limit: 10,
 	params: {
-		noteId: appearNote.id,
-		type: reactionTabType,
+		noteId: appearNote.value.id,
+		type: reactionTabType.value,
 	},
 }));
 
 useNoteCapture({
 	rootEl: el,
-	note: $$(appearNote),
-	pureNote: $$(note),
+	note: appearNote,
+	pureNote: note,
 	isDeletedRef: isDeleted,
 });
 
 useTooltip(renoteButton, async (showing) => {
 	const renotes = await os.api('notes/renotes', {
-		noteId: appearNote.id,
+		noteId: appearNote.value.id,
 		limit: 11,
 	});
 
@@ -339,7 +339,7 @@ useTooltip(renoteButton, async (showing) => {
 	os.popup(MkUsersTooltip, {
 		showing,
 		users,
-		count: appearNote.renoteCount,
+		count: appearNote.value.renoteCount,
 		targetElement: renoteButton.value,
 	}, {}, 'closed');
 });
@@ -348,7 +348,7 @@ function renote(viaKeyboard = false) {
 	pleaseLogin();
 	showMovedDialog();
 
-	const { menu } = getRenoteMenu({ note: note, renoteButton });
+	const { menu } = getRenoteMenu({ note: note.value, renoteButton });
 	os.popupMenu(menu, renoteButton.value, {
 		viaKeyboard,
 	});
@@ -358,8 +358,8 @@ function reply(viaKeyboard = false): void {
 	pleaseLogin();
 	showMovedDialog();
 	os.post({
-		reply: appearNote,
-		channel: appearNote.channel,
+		reply: appearNote.value,
+		channel: appearNote.value.channel,
 		animation: !viaKeyboard,
 	}, () => {
 		focus();
@@ -369,11 +369,11 @@ function reply(viaKeyboard = false): void {
 function react(viaKeyboard = false): void {
 	pleaseLogin();
 	showMovedDialog();
-	if (appearNote.reactionAcceptance === 'likeOnly') {
+	if (appearNote.value.reactionAcceptance === 'likeOnly') {
 		sound.play('reaction');
 
 		os.api('notes/reactions/create', {
-			noteId: appearNote.id,
+			noteId: appearNote.value.id,
 			reaction: '❤️',
 		});
 		const el = reactButton.value as HTMLElement | null | undefined;
@@ -389,10 +389,10 @@ function react(viaKeyboard = false): void {
 			sound.play('reaction');
 
 			os.api('notes/reactions/create', {
-				noteId: appearNote.id,
+				noteId: appearNote.value.id,
 				reaction: reaction,
 			});
-			if (appearNote.text && appearNote.text.length > 100 && (Date.now() - new Date(appearNote.createdAt).getTime() < 1000 * 3)) {
+			if (appearNote.value.text && appearNote.value.text.length > 100 && (Date.now() - new Date(appearNote.value.createdAt).getTime() < 1000 * 3)) {
 				claimAchievement('reactWithoutRead');
 			}
 		}, () => {
@@ -423,20 +423,20 @@ function onContextmenu(ev: MouseEvent): void {
 		ev.preventDefault();
 		react();
 	} else {
-		const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted });
+		const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted });
 		os.contextMenu(menu, ev).then(focus).finally(cleanup);
 	}
 }
 
 function menu(viaKeyboard = false): void {
-	const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted });
+	const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, menuButton, isDeleted });
 	os.popupMenu(menu, menuButton.value, {
 		viaKeyboard,
 	}).then(focus).finally(cleanup);
 }
 
 async function clip() {
-	os.popupMenu(await getNoteClipMenu({ note: note, isDeleted }), clipButton.value).then(focus);
+	os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted }), clipButton.value).then(focus);
 }
 
 function showRenoteMenu(viaKeyboard = false): void {
@@ -448,7 +448,7 @@ function showRenoteMenu(viaKeyboard = false): void {
 		danger: true,
 		action: () => {
 			os.api('notes/delete', {
-				noteId: note.id,
+				noteId: note.value.id,
 			});
 			isDeleted.value = true;
 		},
@@ -470,7 +470,7 @@ const repliesLoaded = ref(false);
 function loadReplies() {
 	repliesLoaded.value = true;
 	os.api('notes/children', {
-		noteId: appearNote.id,
+		noteId: appearNote.value.id,
 		limit: 30,
 	}).then(res => {
 		replies.value = res;
@@ -482,7 +482,7 @@ const conversationLoaded = ref(false);
 function loadConversation() {
 	conversationLoaded.value = true;
 	os.api('notes/conversation', {
-		noteId: appearNote.replyId,
+		noteId: appearNote.value.replyId,
 	}).then(res => {
 		conversation.value = res.reverse();
 	});
diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue
index f3ab6b2723..868f64a4b8 100644
--- a/packages/frontend/src/components/MkNoteSimple.vue
+++ b/packages/frontend/src/components/MkNoteSimple.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkNoteHeader from '@/components/MkNoteHeader.vue';
 import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
@@ -33,7 +33,7 @@ const props = defineProps<{
 	note: Misskey.entities.Note;
 }>();
 
-const showContent = $ref(false);
+const showContent = ref(false);
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 1e901a1fd6..5649ce1e6c 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -65,15 +65,15 @@ const props = withDefaults(defineProps<{
 
 const muted = ref($i ? checkWordMute(props.note, $i, $i.mutedWords) : false);
 
-let showContent = $ref(false);
-let replies: Misskey.entities.Note[] = $ref([]);
+const showContent = ref(false);
+const replies = ref<Misskey.entities.Note[]>([]);
 
 if (props.detail) {
 	os.api('notes/children', {
 		noteId: props.note.id,
 		limit: 5,
 	}).then(res => {
-		replies = res;
+		replies.value = res;
 	});
 }
 </script>
diff --git a/packages/frontend/src/components/MkNotificationSelectWindow.vue b/packages/frontend/src/components/MkNotificationSelectWindow.vue
index 3d5a56975b..6725776f43 100644
--- a/packages/frontend/src/components/MkNotificationSelectWindow.vue
+++ b/packages/frontend/src/components/MkNotificationSelectWindow.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref, Ref } from 'vue';
+import { ref, Ref, shallowRef } from 'vue';
 import MkSwitch from './MkSwitch.vue';
 import MkInfo from './MkInfo.vue';
 import MkButton from './MkButton.vue';
@@ -51,7 +51,7 @@ const props = withDefaults(defineProps<{
 	excludeTypes: () => [],
 });
 
-const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
+const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
 
 const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as any);
 
@@ -61,7 +61,7 @@ function ok() {
 			.filter(type => !typesMap[type].value),
 	});
 
-	if (dialog) dialog.close();
+	if (dialog.value) dialog.value.close();
 }
 
 function disableAll() {
diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index 2a0082204a..cefef91285 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -43,7 +43,7 @@ const props = defineProps<{
 
 const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
 
-let pagination = $computed(() => defaultStore.reactiveState.useGroupedNotifications.value ? {
+const pagination = computed(() => defaultStore.reactiveState.useGroupedNotifications.value ? {
 	endpoint: 'i/notifications-grouped' as const,
 	limit: 20,
 	params: computed(() => ({
diff --git a/packages/frontend/src/components/MkOmit.vue b/packages/frontend/src/components/MkOmit.vue
index 8c113bd777..1b0ec72e41 100644
--- a/packages/frontend/src/components/MkOmit.vue
+++ b/packages/frontend/src/components/MkOmit.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onUnmounted } from 'vue';
+import { onMounted, onUnmounted, shallowRef, ref } from 'vue';
 import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
@@ -22,13 +22,13 @@ const props = withDefaults(defineProps<{
 	maxHeight: 200,
 });
 
-let content = $shallowRef<HTMLElement>();
-let omitted = $ref(false);
-let ignoreOmit = $ref(false);
+const content = shallowRef<HTMLElement>();
+const omitted = ref(false);
+const ignoreOmit = ref(false);
 
 const calcOmit = () => {
-	if (omitted || ignoreOmit) return;
-	omitted = content.offsetHeight > props.maxHeight;
+	if (omitted.value || ignoreOmit.value) return;
+	omitted.value = content.value.offsetHeight > props.maxHeight;
 };
 
 const omitObserver = new ResizeObserver((entries, observer) => {
@@ -37,7 +37,7 @@ const omitObserver = new ResizeObserver((entries, observer) => {
 
 onMounted(() => {
 	calcOmit();
-	omitObserver.observe(content);
+	omitObserver.observe(content.value);
 });
 
 onUnmounted(() => {
diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue
index 1b1eb11444..441296e05d 100644
--- a/packages/frontend/src/components/MkPageWindow.vue
+++ b/packages/frontend/src/components/MkPageWindow.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ComputedRef, onMounted, onUnmounted, provide, shallowRef } from 'vue';
+import { ComputedRef, onMounted, onUnmounted, provide, shallowRef, ref, computed } from 'vue';
 import RouterView from '@/components/global/RouterView.vue';
 import MkWindow from '@/components/MkWindow.vue';
 import { popout as _popout } from '@/scripts/popout.js';
@@ -55,16 +55,16 @@ defineEmits<{
 const router = new Router(routes, props.initialPath, !!$i, page(() => import('@/pages/not-found.vue')));
 
 const contents = shallowRef<HTMLElement>();
-let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
-let windowEl = $shallowRef<InstanceType<typeof MkWindow>>();
-const history = $ref<{ path: string; key: any; }[]>([{
+const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
+const windowEl = shallowRef<InstanceType<typeof MkWindow>>();
+const history = ref<{ path: string; key: any; }[]>([{
 	path: router.getCurrentPath(),
 	key: router.getCurrentKey(),
 }]);
-const buttonsLeft = $computed(() => {
+const buttonsLeft = computed(() => {
 	const buttons = [];
 
-	if (history.length > 1) {
+	if (history.value.length > 1) {
 		buttons.push({
 			icon: 'ti ti-arrow-left',
 			onClick: back,
@@ -73,7 +73,7 @@ const buttonsLeft = $computed(() => {
 
 	return buttons;
 });
-const buttonsRight = $computed(() => {
+const buttonsRight = computed(() => {
 	const buttons = [{
 		icon: 'ti ti-reload',
 		title: i18n.ts.reload,
@@ -86,21 +86,21 @@ const buttonsRight = $computed(() => {
 
 	return buttons;
 });
-let reloadCount = $ref(0);
+const reloadCount = ref(0);
 
 router.addListener('push', ctx => {
-	history.push({ path: ctx.path, key: ctx.key });
+	history.value.push({ path: ctx.path, key: ctx.key });
 });
 
 provide('router', router);
 provideMetadataReceiver((info) => {
-	pageMetadata = info;
+	pageMetadata.value = info;
 });
 provide('shouldOmitHeaderTitle', true);
 provide('shouldHeaderThin', true);
 provide('forceSpacerMin', true);
 
-const contextmenu = $computed(() => ([{
+const contextmenu = computed(() => ([{
 	icon: 'ti ti-player-eject',
 	text: i18n.ts.showInPage,
 	action: expand,
@@ -113,7 +113,7 @@ const contextmenu = $computed(() => ([{
 	text: i18n.ts.openInNewTab,
 	action: () => {
 		window.open(url + router.getCurrentPath(), '_blank');
-		windowEl.close();
+		windowEl.value.close();
 	},
 }, {
 	icon: 'ti ti-link',
@@ -124,26 +124,26 @@ const contextmenu = $computed(() => ([{
 }]));
 
 function back() {
-	history.pop();
-	router.replace(history.at(-1)!.path, history.at(-1)!.key);
+	history.value.pop();
+	router.replace(history.value.at(-1)!.path, history.value.at(-1)!.key);
 }
 
 function reload() {
-	reloadCount++;
+	reloadCount.value++;
 }
 
 function close() {
-	windowEl.close();
+	windowEl.value.close();
 }
 
 function expand() {
 	mainRouter.push(router.getCurrentPath(), 'forcePage');
-	windowEl.close();
+	windowEl.value.close();
 }
 
 function popout() {
-	_popout(router.getCurrentPath(), windowEl.$el);
-	windowEl.close();
+	_popout(router.getCurrentPath(), windowEl.value.$el);
+	windowEl.value.close();
 }
 
 useScrollPositionManager(() => getScrollContainer(contents.value), router);
diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue
index 57348cde53..07347eda29 100644
--- a/packages/frontend/src/components/MkPagination.vue
+++ b/packages/frontend/src/components/MkPagination.vue
@@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts">
-import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, watch } from 'vue';
+import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
 import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js';
@@ -105,12 +105,12 @@ const emit = defineEmits<{
 	(ev: 'status', error: boolean): void;
 }>();
 
-let rootEl = $shallowRef<HTMLElement>();
+const rootEl = shallowRef<HTMLElement>();
 
 // 遡り中かどうか
-let backed = $ref(false);
+const backed = ref(false);
 
-let scrollRemove = $ref<(() => void) | null>(null);
+const scrollRemove = ref<(() => void) | null>(null);
 
 /**
  * 表示するアイテムのソース
@@ -142,8 +142,8 @@ const {
 	enableInfiniteScroll,
 } = defaultStore.reactiveState;
 
-const contentEl = $computed(() => props.pagination.pageEl ?? rootEl);
-const scrollableElement = $computed(() => contentEl ? getScrollContainer(contentEl) : document.body);
+const contentEl = computed(() => props.pagination.pageEl ?? rootEl.value);
+const scrollableElement = computed(() => contentEl.value ? getScrollContainer(contentEl.value) : document.body);
 
 const visibility = useDocumentVisibility();
 
@@ -153,35 +153,35 @@ const BACKGROUND_PAUSE_WAIT_SEC = 10;
 
 // 先頭が表示されているかどうかを検出
 // https://qiita.com/mkataigi/items/0154aefd2223ce23398e
-let scrollObserver = $ref<IntersectionObserver>();
+const scrollObserver = ref<IntersectionObserver>();
 
-watch([() => props.pagination.reversed, $$(scrollableElement)], () => {
-	if (scrollObserver) scrollObserver.disconnect();
+watch([() => props.pagination.reversed, scrollableElement], () => {
+	if (scrollObserver.value) scrollObserver.value.disconnect();
 
-	scrollObserver = new IntersectionObserver(entries => {
-		backed = entries[0].isIntersecting;
+	scrollObserver.value = new IntersectionObserver(entries => {
+		backed.value = entries[0].isIntersecting;
 	}, {
-		root: scrollableElement,
+		root: scrollableElement.value,
 		rootMargin: props.pagination.reversed ? '-100% 0px 100% 0px' : '100% 0px -100% 0px',
 		threshold: 0.01,
 	});
 }, { immediate: true });
 
-watch($$(rootEl), () => {
-	scrollObserver?.disconnect();
+watch(rootEl, () => {
+	scrollObserver.value?.disconnect();
 	nextTick(() => {
-		if (rootEl) scrollObserver?.observe(rootEl);
+		if (rootEl.value) scrollObserver.value?.observe(rootEl.value);
 	});
 });
 
-watch([$$(backed), $$(contentEl)], () => {
-	if (!backed) {
-		if (!contentEl) return;
+watch([backed, contentEl], () => {
+	if (!backed.value) {
+		if (!contentEl.value) return;
 
-		scrollRemove = (props.pagination.reversed ? onScrollBottom : onScrollTop)(contentEl, executeQueue, TOLERANCE);
+		scrollRemove.value = (props.pagination.reversed ? onScrollBottom : onScrollTop)(contentEl.value, executeQueue, TOLERANCE);
 	} else {
-		if (scrollRemove) scrollRemove();
-		scrollRemove = null;
+		if (scrollRemove.value) scrollRemove.value();
+		scrollRemove.value = null;
 	}
 });
 
@@ -254,14 +254,14 @@ const fetchMore = async (): Promise<void> => {
 		}
 
 		const reverseConcat = _res => {
-			const oldHeight = scrollableElement ? scrollableElement.scrollHeight : getBodyScrollHeight();
-			const oldScroll = scrollableElement ? scrollableElement.scrollTop : window.scrollY;
+			const oldHeight = scrollableElement.value ? scrollableElement.value.scrollHeight : getBodyScrollHeight();
+			const oldScroll = scrollableElement.value ? scrollableElement.value.scrollTop : window.scrollY;
 
 			items.value = concatMapWithArray(items.value, _res);
 
 			return nextTick(() => {
-				if (scrollableElement) {
-					scroll(scrollableElement, { top: oldScroll + (scrollableElement.scrollHeight - oldHeight), behavior: 'instant' });
+				if (scrollableElement.value) {
+					scroll(scrollableElement.value, { top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight), behavior: 'instant' });
 				} else {
 					window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' });
 				}
@@ -351,7 +351,7 @@ const appearFetchMoreAhead = async (): Promise<void> => {
 	fetchMoreAppearTimeout();
 };
 
-const isTop = (): boolean => isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl!, TOLERANCE);
+const isTop = (): boolean => isBackTop.value || (props.pagination.reversed ? isBottomVisible : isTopVisible)(contentEl.value!, TOLERANCE);
 
 watch(visibility, () => {
 	if (visibility.value === 'hidden') {
@@ -445,11 +445,11 @@ onActivated(() => {
 });
 
 onDeactivated(() => {
-	isBackTop.value = props.pagination.reversed ? window.scrollY >= (rootEl ? rootEl.scrollHeight - window.innerHeight : 0) : window.scrollY === 0;
+	isBackTop.value = props.pagination.reversed ? window.scrollY >= (rootEl.value ? rootEl.value.scrollHeight - window.innerHeight : 0) : window.scrollY === 0;
 });
 
 function toBottom() {
-	scrollToBottom(contentEl!);
+	scrollToBottom(contentEl.value!);
 }
 
 onBeforeMount(() => {
@@ -477,13 +477,13 @@ onBeforeUnmount(() => {
 		clearTimeout(preventAppearFetchMoreTimer.value);
 		preventAppearFetchMoreTimer.value = null;
 	}
-	scrollObserver?.disconnect();
+	scrollObserver.value?.disconnect();
 });
 
 defineExpose({
 	items,
 	queue,
-	backed,
+	backed: backed.value,
 	more,
 	reload,
 	prepend,
diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue
index 3abca7826f..118f9a6a91 100644
--- a/packages/frontend/src/components/MkPasswordDialog.vue
+++ b/packages/frontend/src/components/MkPasswordDialog.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef, ref } from 'vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
@@ -49,22 +49,22 @@ const emit = defineEmits<{
 	(ev: 'cancelled'): void;
 }>();
 
-const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
-const passwordInput = $shallowRef<InstanceType<typeof MkInput>>();
-const password = $ref('');
-const token = $ref(null);
+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();
+	if (dialog.value) dialog.value.close();
 }
 
 function done(res) {
-	emit('done', { password, token });
-	if (dialog) dialog.close();
+	emit('done', { password: password.value, token: token.value });
+	if (dialog.value) dialog.value.close();
 }
 
 onMounted(() => {
-	if (passwordInput) passwordInput.focus();
+	if (passwordInput.value) passwordInput.value.focus();
 });
 </script>
diff --git a/packages/frontend/src/components/MkPlusOneEffect.vue b/packages/frontend/src/components/MkPlusOneEffect.vue
index 0bc98f4334..a741a3f7a8 100644
--- a/packages/frontend/src/components/MkPlusOneEffect.vue
+++ b/packages/frontend/src/components/MkPlusOneEffect.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import * as os from '@/os.js';
 
 const props = withDefaults(defineProps<{
@@ -23,13 +23,13 @@ const emit = defineEmits<{
 	(ev: 'end'): void;
 }>();
 
-let up = $ref(false);
+const up = ref(false);
 const zIndex = os.claimZIndex('middle');
 const angle = (45 - (Math.random() * 90)) + 'deg';
 
 onMounted(() => {
 	window.setTimeout(() => {
-		up = true;
+		up.value = true;
 	}, 10);
 
 	window.setTimeout(() => {
diff --git a/packages/frontend/src/components/MkPopupMenu.vue b/packages/frontend/src/components/MkPopupMenu.vue
index ee7dbecf05..67fac003bf 100644
--- a/packages/frontend/src/components/MkPopupMenu.vue
+++ b/packages/frontend/src/components/MkPopupMenu.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, shallowRef } from 'vue';
 import MkModal from './MkModal.vue';
 import MkMenu from './MkMenu.vue';
 import { MenuItem } from '@/types/menu';
@@ -28,7 +28,7 @@ const emit = defineEmits<{
 	(ev: 'closing'): void;
 }>();
 
-let modal = $shallowRef<InstanceType<typeof MkModal>>();
+const modal = shallowRef<InstanceType<typeof MkModal>>();
 const manualShowing = ref(true);
 const hiding = ref(false);
 
@@ -60,14 +60,14 @@ function hide() {
 	hiding.value = true;
 
 	// closeは呼ぶ必要がある
-	modal?.close();
+	modal.value?.close();
 }
 
 function close() {
 	manualShowing.value = false;
 
 	// closeは呼ぶ必要がある
-	modal?.close();
+	modal.value?.close();
 }
 </script>
 
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 13b615d104..9e5c4ca3f1 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -98,7 +98,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, ref } from 'vue';
+import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue';
 import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import insertTextAtCursor from 'insert-text-at-cursor';
@@ -162,42 +162,42 @@ const emit = defineEmits<{
 	(ev: 'fileChangeSensitive', fileId: string, to: boolean): void;
 }>();
 
-const textareaEl = $shallowRef<HTMLTextAreaElement | null>(null);
-const cwInputEl = $shallowRef<HTMLInputElement | null>(null);
-const hashtagsInputEl = $shallowRef<HTMLInputElement | null>(null);
-const visibilityButton = $shallowRef<HTMLElement | null>(null);
+const textareaEl = shallowRef<HTMLTextAreaElement | null>(null);
+const cwInputEl = shallowRef<HTMLInputElement | null>(null);
+const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null);
+const visibilityButton = shallowRef<HTMLElement | null>(null);
 
-let posting = $ref(false);
-let posted = $ref(false);
-let text = $ref(props.initialText ?? '');
-let files = $ref(props.initialFiles ?? []);
-let poll = $ref<{
+const posting = ref(false);
+const posted = ref(false);
+const text = ref(props.initialText ?? '');
+const files = ref(props.initialFiles ?? []);
+const poll = ref<{
 	choices: string[];
 	multiple: boolean;
 	expiresAt: string | null;
 	expiredAfter: string | null;
 } | null>(null);
-let useCw = $ref(false);
-let showPreview = $ref(defaultStore.state.showPreview);
-watch($$(showPreview), () => defaultStore.set('showPreview', showPreview));
-let cw = $ref<string | null>(null);
-let localOnly = $ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
-let visibility = $ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
-let visibleUsers = $ref([]);
+const useCw = ref(false);
+const showPreview = ref(defaultStore.state.showPreview);
+watch(showPreview, () => defaultStore.set('showPreview', showPreview.value));
+const cw = ref<string | null>(null);
+const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
+const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
+const visibleUsers = ref([]);
 if (props.initialVisibleUsers) {
 	props.initialVisibleUsers.forEach(pushVisibleUser);
 }
-let reactionAcceptance = $ref(defaultStore.state.reactionAcceptance);
-let autocomplete = $ref(null);
-let draghover = $ref(false);
-let quoteId = $ref(null);
-let hasNotSpecifiedMentions = $ref(false);
-let recentHashtags = $ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
-let imeText = $ref('');
-let showingOptions = $ref(false);
+const reactionAcceptance = ref(defaultStore.state.reactionAcceptance);
+const autocomplete = ref(null);
+const draghover = ref(false);
+const quoteId = ref(null);
+const hasNotSpecifiedMentions = ref(false);
+const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
+const imeText = ref('');
+const showingOptions = ref(false);
 const textAreaReadOnly = ref(false);
 
-const draftKey = $computed((): string => {
+const draftKey = computed((): string => {
 	let key = props.channel ? `channel:${props.channel.id}` : '';
 
 	if (props.renote) {
@@ -211,7 +211,7 @@ const draftKey = $computed((): string => {
 	return key;
 });
 
-const placeholder = $computed((): string => {
+const placeholder = computed((): string => {
 	if (props.renote) {
 		return i18n.ts._postForm.quotePlaceholder;
 	} else if (props.reply) {
@@ -231,7 +231,7 @@ const placeholder = $computed((): string => {
 	}
 });
 
-const submitText = $computed((): string => {
+const submitText = computed((): string => {
 	return props.renote
 		? i18n.ts.quote
 		: props.reply
@@ -239,45 +239,45 @@ const submitText = $computed((): string => {
 			: i18n.ts.note;
 });
 
-const textLength = $computed((): number => {
-	return (text + imeText).trim().length;
+const textLength = computed((): number => {
+	return (text.value + imeText.value).trim().length;
 });
 
-const maxTextLength = $computed((): number => {
+const maxTextLength = computed((): number => {
 	return instance ? instance.maxNoteTextLength : 1000;
 });
 
-const canPost = $computed((): boolean => {
-	return !props.mock && !posting && !posted &&
-		(1 <= textLength || 1 <= files.length || !!poll || !!props.renote) &&
-		(textLength <= maxTextLength) &&
-		(!poll || poll.choices.length >= 2);
+const canPost = computed((): boolean => {
+	return !props.mock && !posting.value && !posted.value &&
+		(1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!props.renote) &&
+		(textLength.value <= maxTextLength.value) &&
+		(!poll.value || poll.value.choices.length >= 2);
 });
 
-const withHashtags = $computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
-const hashtags = $computed(defaultStore.makeGetterSetter('postFormHashtags'));
+const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
+const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags'));
 
-watch($$(text), () => {
+watch(text, () => {
 	checkMissingMention();
 }, { immediate: true });
 
-watch($$(visibility), () => {
+watch(visibility, () => {
 	checkMissingMention();
 }, { immediate: true });
 
-watch($$(visibleUsers), () => {
+watch(visibleUsers, () => {
 	checkMissingMention();
 }, {
 	deep: true,
 });
 
 if (props.mention) {
-	text = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`;
-	text += ' ';
+	text.value = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`;
+	text.value += ' ';
 }
 
 if (props.reply && (props.reply.user.username !== $i.username || (props.reply.user.host != null && props.reply.user.host !== host))) {
-	text = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `;
+	text.value = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `;
 }
 
 if (props.reply && props.reply.text != null) {
@@ -295,32 +295,32 @@ if (props.reply && props.reply.text != null) {
 		if ($i.username === x.username && (x.host == null || x.host === host)) continue;
 
 		// 重複は除外
-		if (text.includes(`${mention} `)) continue;
+		if (text.value.includes(`${mention} `)) continue;
 
-		text += `${mention} `;
+		text.value += `${mention} `;
 	}
 }
 
-if ($i?.isSilenced && visibility === 'public') {
-	visibility = 'home';
+if ($i?.isSilenced && visibility.value === 'public') {
+	visibility.value = 'home';
 }
 
 if (props.channel) {
-	visibility = 'public';
-	localOnly = true; // TODO: チャンネルが連合するようになった折には消す
+	visibility.value = 'public';
+	localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
 }
 
 // 公開以外へのリプライ時は元の公開範囲を引き継ぐ
 if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) {
-	if (props.reply.visibility === 'home' && visibility === 'followers') {
-		visibility = 'followers';
-	} else if (['home', 'followers'].includes(props.reply.visibility) && visibility === 'specified') {
-		visibility = 'specified';
+	if (props.reply.visibility === 'home' && visibility.value === 'followers') {
+		visibility.value = 'followers';
+	} else if (['home', 'followers'].includes(props.reply.visibility) && visibility.value === 'specified') {
+		visibility.value = 'specified';
 	} else {
-		visibility = props.reply.visibility;
+		visibility.value = props.reply.visibility;
 	}
 
-	if (visibility === 'specified') {
+	if (visibility.value === 'specified') {
 		if (props.reply.visibleUserIds) {
 			os.api('users/show', {
 				userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId),
@@ -338,57 +338,57 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib
 }
 
 if (props.specified) {
-	visibility = 'specified';
+	visibility.value = 'specified';
 	pushVisibleUser(props.specified);
 }
 
 // keep cw when reply
 if (defaultStore.state.keepCw && props.reply && props.reply.cw) {
-	useCw = true;
-	cw = props.reply.cw;
+	useCw.value = true;
+	cw.value = props.reply.cw;
 }
 
 function watchForDraft() {
-	watch($$(text), () => saveDraft());
-	watch($$(useCw), () => saveDraft());
-	watch($$(cw), () => saveDraft());
-	watch($$(poll), () => saveDraft());
-	watch($$(files), () => saveDraft(), { deep: true });
-	watch($$(visibility), () => saveDraft());
-	watch($$(localOnly), () => saveDraft());
+	watch(text, () => saveDraft());
+	watch(useCw, () => saveDraft());
+	watch(cw, () => saveDraft());
+	watch(poll, () => saveDraft());
+	watch(files, () => saveDraft(), { deep: true });
+	watch(visibility, () => saveDraft());
+	watch(localOnly, () => saveDraft());
 }
 
 function checkMissingMention() {
-	if (visibility === 'specified') {
-		const ast = mfm.parse(text);
+	if (visibility.value === 'specified') {
+		const ast = mfm.parse(text.value);
 
 		for (const x of extractMentions(ast)) {
-			if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) {
-				hasNotSpecifiedMentions = true;
+			if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) {
+				hasNotSpecifiedMentions.value = true;
 				return;
 			}
 		}
 	}
-	hasNotSpecifiedMentions = false;
+	hasNotSpecifiedMentions.value = false;
 }
 
 function addMissingMention() {
-	const ast = mfm.parse(text);
+	const ast = mfm.parse(text.value);
 
 	for (const x of extractMentions(ast)) {
-		if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) {
+		if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) {
 			os.api('users/show', { username: x.username, host: x.host }).then(user => {
-				visibleUsers.push(user);
+				visibleUsers.value.push(user);
 			});
 		}
 	}
 }
 
 function togglePoll() {
-	if (poll) {
-		poll = null;
+	if (poll.value) {
+		poll.value = null;
 	} else {
-		poll = {
+		poll.value = {
 			choices: ['', ''],
 			multiple: false,
 			expiresAt: null,
@@ -398,13 +398,13 @@ function togglePoll() {
 }
 
 function addTag(tag: string) {
-	insertTextAtCursor(textareaEl, ` #${tag} `);
+	insertTextAtCursor(textareaEl.value, ` #${tag} `);
 }
 
 function focus() {
-	if (textareaEl) {
-		textareaEl.focus();
-		textareaEl.setSelectionRange(textareaEl.value.length, textareaEl.value.length);
+	if (textareaEl.value) {
+		textareaEl.value.focus();
+		textareaEl.value.setSelectionRange(textareaEl.value.value.length, textareaEl.value.value.length);
 	}
 }
 
@@ -413,55 +413,55 @@ function chooseFileFrom(ev) {
 
 	selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => {
 		for (const file of files_) {
-			files.push(file);
+			files.value.push(file);
 		}
 	});
 }
 
 function detachFile(id) {
-	files = files.filter(x => x.id !== id);
+	files.value = files.value.filter(x => x.id !== id);
 }
 
 function updateFileSensitive(file, sensitive) {
 	if (props.mock) {
 		emit('fileChangeSensitive', file.id, sensitive);
 	}
-	files[files.findIndex(x => x.id === file.id)].isSensitive = sensitive;
+	files.value[files.value.findIndex(x => x.id === file.id)].isSensitive = sensitive;
 }
 
 function updateFileName(file, name) {
-	files[files.findIndex(x => x.id === file.id)].name = name;
+	files.value[files.value.findIndex(x => x.id === file.id)].name = name;
 }
 
 function replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void {
-	files[files.findIndex(x => x.id === file.id)] = newFile;
+	files.value[files.value.findIndex(x => x.id === file.id)] = newFile;
 }
 
 function upload(file: File, name?: string): void {
 	if (props.mock) return;
 
 	uploadFile(file, defaultStore.state.uploadFolder, name).then(res => {
-		files.push(res);
+		files.value.push(res);
 	});
 }
 
 function setVisibility() {
 	if (props.channel) {
-		visibility = 'public';
-		localOnly = true; // TODO: チャンネルが連合するようになった折には消す
+		visibility.value = 'public';
+		localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
 		return;
 	}
 
 	os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
-		currentVisibility: visibility,
+		currentVisibility: visibility.value,
 		isSilenced: $i?.isSilenced,
-		localOnly: localOnly,
-		src: visibilityButton,
+		localOnly: localOnly.value,
+		src: visibilityButton.value,
 	}, {
 		changeVisibility: v => {
-			visibility = v;
+			visibility.value = v;
 			if (defaultStore.state.rememberNoteVisibility) {
-				defaultStore.set('visibility', visibility);
+				defaultStore.set('visibility', visibility.value);
 			}
 		},
 	}, 'closed');
@@ -469,14 +469,14 @@ function setVisibility() {
 
 async function toggleLocalOnly() {
 	if (props.channel) {
-		visibility = 'public';
-		localOnly = true; // TODO: チャンネルが連合するようになった折には消す
+		visibility.value = 'public';
+		localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
 		return;
 	}
 
 	const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo');
 
-	if (!localOnly && neverShowInfo !== 'true') {
+	if (!localOnly.value && neverShowInfo !== 'true') {
 		const confirm = await os.actions({
 			type: 'question',
 			title: i18n.ts.disableFederationConfirm,
@@ -506,7 +506,7 @@ async function toggleLocalOnly() {
 		}
 	}
 
-	localOnly = !localOnly;
+	localOnly.value = !localOnly.value;
 }
 
 async function toggleReactionAcceptance() {
@@ -519,15 +519,15 @@ async function toggleReactionAcceptance() {
 			{ value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote },
 			{ value: 'likeOnly' as const, text: i18n.ts.likeOnly },
 		],
-		default: reactionAcceptance,
+		default: reactionAcceptance.value,
 	});
 	if (select.canceled) return;
-	reactionAcceptance = select.result;
+	reactionAcceptance.value = select.result;
 }
 
 function pushVisibleUser(user) {
-	if (!visibleUsers.some(u => u.username === user.username && u.host === user.host)) {
-		visibleUsers.push(user);
+	if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) {
+		visibleUsers.value.push(user);
 	}
 }
 
@@ -535,34 +535,34 @@ function addVisibleUser() {
 	os.selectUser().then(user => {
 		pushVisibleUser(user);
 
-		if (!text.toLowerCase().includes(`@${user.username.toLowerCase()}`)) {
-			text = `@${Misskey.acct.toString(user)} ${text}`;
+		if (!text.value.toLowerCase().includes(`@${user.username.toLowerCase()}`)) {
+			text.value = `@${Misskey.acct.toString(user)} ${text.value}`;
 		}
 	});
 }
 
 function removeVisibleUser(user) {
-	visibleUsers = erase(user, visibleUsers);
+	visibleUsers.value = erase(user, visibleUsers.value);
 }
 
 function clear() {
-	text = '';
-	files = [];
-	poll = null;
-	quoteId = null;
+	text.value = '';
+	files.value = [];
+	poll.value = null;
+	quoteId.value = null;
 }
 
 function onKeydown(ev: KeyboardEvent) {
-	if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost) post();
+	if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post();
 	if (ev.key === 'Escape') emit('esc');
 }
 
 function onCompositionUpdate(ev: CompositionEvent) {
-	imeText = ev.data;
+	imeText.value = ev.data;
 }
 
 function onCompositionEnd(ev: CompositionEvent) {
-	imeText = '';
+	imeText.value = '';
 }
 
 async function onPaste(ev: ClipboardEvent) {
@@ -580,7 +580,7 @@ async function onPaste(ev: ClipboardEvent) {
 
 	const paste = ev.clipboardData.getData('text');
 
-	if (!props.renote && !quoteId && paste.startsWith(url + '/notes/')) {
+	if (!props.renote && !quoteId.value && paste.startsWith(url + '/notes/')) {
 		ev.preventDefault();
 
 		os.confirm({
@@ -588,11 +588,11 @@ async function onPaste(ev: ClipboardEvent) {
 			text: i18n.ts.quoteQuestion,
 		}).then(({ canceled }) => {
 			if (canceled) {
-				insertTextAtCursor(textareaEl, paste);
+				insertTextAtCursor(textareaEl.value, paste);
 				return;
 			}
 
-			quoteId = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1];
+			quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1];
 		});
 	}
 }
@@ -603,7 +603,7 @@ function onDragover(ev) {
 	const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
 	if (isFile || isDriveFile) {
 		ev.preventDefault();
-		draghover = true;
+		draghover.value = true;
 		switch (ev.dataTransfer.effectAllowed) {
 			case 'all':
 			case 'uninitialized':
@@ -624,15 +624,15 @@ function onDragover(ev) {
 }
 
 function onDragenter(ev) {
-	draghover = true;
+	draghover.value = true;
 }
 
 function onDragleave(ev) {
-	draghover = false;
+	draghover.value = false;
 }
 
 function onDrop(ev): void {
-	draghover = false;
+	draghover.value = false;
 
 	// ファイルだったら
 	if (ev.dataTransfer.files.length > 0) {
@@ -645,7 +645,7 @@ function onDrop(ev): void {
 	const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
 	if (driveFile != null && driveFile !== '') {
 		const file = JSON.parse(driveFile);
-		files.push(file);
+		files.value.push(file);
 		ev.preventDefault();
 	}
 	//#endregion
@@ -656,16 +656,16 @@ function saveDraft() {
 
 	const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
 
-	draftData[draftKey] = {
+	draftData[draftKey.value] = {
 		updatedAt: new Date(),
 		data: {
-			text: text,
-			useCw: useCw,
-			cw: cw,
-			visibility: visibility,
-			localOnly: localOnly,
-			files: files,
-			poll: poll,
+			text: text.value,
+			useCw: useCw.value,
+			cw: cw.value,
+			visibility: visibility.value,
+			localOnly: localOnly.value,
+			files: files.value,
+			poll: poll.value,
 		},
 	};
 
@@ -675,13 +675,13 @@ function saveDraft() {
 function deleteDraft() {
 	const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
 
-	delete draftData[draftKey];
+	delete draftData[draftKey.value];
 
 	miLocalStorage.setItem('drafts', JSON.stringify(draftData));
 }
 
 async function post(ev?: MouseEvent) {
-	if (useCw && (cw == null || cw.trim() === '')) {
+	if (useCw.value && (cw.value == null || cw.value.trim() === '')) {
 		os.alert({
 			type: 'error',
 			text: i18n.ts.cwNotationRequired,
@@ -700,13 +700,13 @@ async function post(ev?: MouseEvent) {
 	if (props.mock) return;
 
 	const annoying =
-		text.includes('$[x2') ||
-		text.includes('$[x3') ||
-		text.includes('$[x4') ||
-		text.includes('$[scale') ||
-		text.includes('$[position');
+		text.value.includes('$[x2') ||
+		text.value.includes('$[x3') ||
+		text.value.includes('$[x4') ||
+		text.value.includes('$[scale') ||
+		text.value.includes('$[position');
 
-	if (annoying && visibility === 'public') {
+	if (annoying && visibility.value === 'public') {
 		const { canceled, result } = await os.actions({
 			type: 'warning',
 			text: i18n.ts.thisPostMayBeAnnoying,
@@ -726,26 +726,26 @@ async function post(ev?: MouseEvent) {
 		if (canceled) return;
 		if (result === 'cancel') return;
 		if (result === 'home') {
-			visibility = 'home';
+			visibility.value = 'home';
 		}
 	}
 
 	let postData = {
-		text: text === '' ? null : text,
-		fileIds: files.length > 0 ? files.map(f => f.id) : undefined,
+		text: text.value === '' ? null : text.value,
+		fileIds: files.value.length > 0 ? files.value.map(f => f.id) : undefined,
 		replyId: props.reply ? props.reply.id : undefined,
-		renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined,
+		renoteId: props.renote ? props.renote.id : quoteId.value ? quoteId.value : undefined,
 		channelId: props.channel ? props.channel.id : undefined,
-		poll: poll,
-		cw: useCw ? cw ?? '' : null,
-		localOnly: localOnly,
-		visibility: visibility,
-		visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined,
-		reactionAcceptance,
+		poll: poll.value,
+		cw: useCw.value ? cw.value ?? '' : null,
+		localOnly: localOnly.value,
+		visibility: visibility.value,
+		visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
+		reactionAcceptance: reactionAcceptance.value,
 	};
 
-	if (withHashtags && hashtags && hashtags.trim() !== '') {
-		const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
+	if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') {
+		const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' ');
 		postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_;
 	}
 
@@ -762,15 +762,15 @@ async function post(ev?: MouseEvent) {
 
 	let token = undefined;
 
-	if (postAccount) {
+	if (postAccount.value) {
 		const storedAccounts = await getAccounts();
-		token = storedAccounts.find(x => x.id === postAccount.id)?.token;
+		token = storedAccounts.find(x => x.id === postAccount.value.id)?.token;
 	}
 
-	posting = true;
+	posting.value = true;
 	os.api('notes/create', postData, token).then(() => {
 		if (props.freezeAfterPosted) {
-			posted = true;
+			posted.value = true;
 		} else {
 			clear();
 		}
@@ -782,8 +782,8 @@ async function post(ev?: MouseEvent) {
 				const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[];
 				miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history))));
 			}
-			posting = false;
-			postAccount = null;
+			posting.value = false;
+			postAccount.value = null;
 
 			incNotesCount();
 			if (notesCount === 1) {
@@ -828,7 +828,7 @@ async function post(ev?: MouseEvent) {
 			}
 		});
 	}).catch(err => {
-		posting = false;
+		posting.value = false;
 		os.alert({
 			type: 'error',
 			text: err.message + '\n' + (err as any).id,
@@ -842,7 +842,7 @@ function cancel() {
 
 function insertMention() {
 	os.selectUser().then(user => {
-		insertTextAtCursor(textareaEl, '@' + Misskey.acct.toString(user) + ' ');
+		insertTextAtCursor(textareaEl.value, '@' + Misskey.acct.toString(user) + ' ');
 	});
 }
 
@@ -852,7 +852,7 @@ async function insertEmoji(ev: MouseEvent) {
 	emojiPicker.show(
 		ev.currentTarget ?? ev.target,
 		emoji => {
-			insertTextAtCursor(textareaEl, emoji);
+			insertTextAtCursor(textareaEl.value, emoji);
 		},
 		() => {
 			textAreaReadOnly.value = false;
@@ -866,17 +866,17 @@ function showActions(ev) {
 		text: action.title,
 		action: () => {
 			action.handler({
-				text: text,
-				cw: cw,
+				text: text.value,
+				cw: cw.value,
 			}, (key, value) => {
-				if (key === 'text') { text = value; }
-				if (key === 'cw') { useCw = value !== null; cw = value; }
+				if (key === 'text') { text.value = value; }
+				if (key === 'cw') { useCw.value = value !== null; cw.value = value; }
 			});
 		},
 	})), ev.currentTarget ?? ev.target);
 }
 
-let postAccount = $ref<Misskey.entities.UserDetailed | null>(null);
+const postAccount = ref<Misskey.entities.UserDetailed | null>(null);
 
 function openAccountMenu(ev: MouseEvent) {
 	if (props.mock) return;
@@ -884,12 +884,12 @@ function openAccountMenu(ev: MouseEvent) {
 	openAccountMenu_({
 		withExtraOperation: false,
 		includeCurrentAccount: true,
-		active: postAccount != null ? postAccount.id : $i.id,
+		active: postAccount.value != null ? postAccount.value.id : $i.id,
 		onChoose: (account) => {
 			if (account.id === $i.id) {
-				postAccount = null;
+				postAccount.value = null;
 			} else {
-				postAccount = account;
+				postAccount.value = account;
 			}
 		},
 	}, ev);
@@ -905,23 +905,23 @@ onMounted(() => {
 	}
 
 	// TODO: detach when unmount
-	new Autocomplete(textareaEl, $$(text));
-	new Autocomplete(cwInputEl, $$(cw));
-	new Autocomplete(hashtagsInputEl, $$(hashtags));
+	new Autocomplete(textareaEl.value, text);
+	new Autocomplete(cwInputEl.value, cw);
+	new Autocomplete(hashtagsInputEl.value, hashtags);
 
 	nextTick(() => {
 		// 書きかけの投稿を復元
 		if (!props.instant && !props.mention && !props.specified && !props.mock) {
-			const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey];
+			const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey.value];
 			if (draft) {
-				text = draft.data.text;
-				useCw = draft.data.useCw;
-				cw = draft.data.cw;
-				visibility = draft.data.visibility;
-				localOnly = draft.data.localOnly;
-				files = (draft.data.files || []).filter(draftFile => draftFile);
+				text.value = draft.data.text;
+				useCw.value = draft.data.useCw;
+				cw.value = draft.data.cw;
+				visibility.value = draft.data.visibility;
+				localOnly.value = draft.data.localOnly;
+				files.value = (draft.data.files || []).filter(draftFile => draftFile);
 				if (draft.data.poll) {
-					poll = draft.data.poll;
+					poll.value = draft.data.poll;
 				}
 			}
 		}
@@ -929,21 +929,21 @@ onMounted(() => {
 		// 削除して編集
 		if (props.initialNote) {
 			const init = props.initialNote;
-			text = init.text ? init.text : '';
-			files = init.files;
-			cw = init.cw;
-			useCw = init.cw != null;
+			text.value = init.text ? init.text : '';
+			files.value = init.files;
+			cw.value = init.cw;
+			useCw.value = init.cw != null;
 			if (init.poll) {
-				poll = {
+				poll.value = {
 					choices: init.poll.choices.map(x => x.text),
 					multiple: init.poll.multiple,
 					expiresAt: init.poll.expiresAt,
 					expiredAfter: init.poll.expiredAfter,
 				};
 			}
-			visibility = init.visibility;
-			localOnly = init.localOnly;
-			quoteId = init.renote ? init.renote.id : null;
+			visibility.value = init.visibility;
+			localOnly.value = init.localOnly;
+			quoteId.value = init.renote ? init.renote.id : null;
 		}
 
 		nextTick(() => watchForDraft());
diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue
index c07a166a83..a0fad1ab41 100644
--- a/packages/frontend/src/components/MkPostFormDialog.vue
+++ b/packages/frontend/src/components/MkPostFormDialog.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { shallowRef } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkModal from '@/components/MkModal.vue';
 import MkPostForm from '@/components/MkPostForm.vue';
@@ -36,11 +36,11 @@ const emit = defineEmits<{
 	(ev: 'closed'): void;
 }>();
 
-let modal = $shallowRef<InstanceType<typeof MkModal>>();
-let form = $shallowRef<InstanceType<typeof MkPostForm>>();
+const modal = shallowRef<InstanceType<typeof MkModal>>();
+const form = shallowRef<InstanceType<typeof MkPostForm>>();
 
 function onPosted() {
-	modal.close({
+	modal.value.close({
 		useSendAnimation: true,
 	});
 }
diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue
index 00c1d3808e..44555f2c13 100644
--- a/packages/frontend/src/components/MkPullToRefresh.vue
+++ b/packages/frontend/src/components/MkPullToRefresh.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onUnmounted, watch } from 'vue';
+import { onMounted, onUnmounted, watch, ref, shallowRef } from 'vue';
 import { deviceKind } from '@/scripts/device-kind.js';
 import { i18n } from '@/i18n.js';
 import { getScrollContainer } from '@/scripts/scroll.js';
@@ -35,15 +35,15 @@ const RELEASE_TRANSITION_DURATION = 200;
 const PULL_BRAKE_BASE = 1.5;
 const PULL_BRAKE_FACTOR = 170;
 
-let isPullStart = $ref(false);
-let isPullEnd = $ref(false);
-let isRefreshing = $ref(false);
-let pullDistance = $ref(0);
+const isPullStart = ref(false);
+const isPullEnd = ref(false);
+const isRefreshing = ref(false);
+const pullDistance = ref(0);
 
 let supportPointerDesktop = false;
 let startScreenY: number | null = null;
 
-const rootEl = $shallowRef<HTMLDivElement>();
+const rootEl = shallowRef<HTMLDivElement>();
 let scrollEl: HTMLElement | null = null;
 
 let disabled = false;
@@ -66,17 +66,17 @@ function getScreenY(event) {
 }
 
 function moveStart(event) {
-	if (!isPullStart && !isRefreshing && !disabled) {
-		isPullStart = true;
+	if (!isPullStart.value && !isRefreshing.value && !disabled) {
+		isPullStart.value = true;
 		startScreenY = getScreenY(event);
-		pullDistance = 0;
+		pullDistance.value = 0;
 	}
 }
 
 function moveBySystem(to: number): Promise<void> {
 	return new Promise(r => {
-		const startHeight = pullDistance;
-		const overHeight = pullDistance - to;
+		const startHeight = pullDistance.value;
+		const overHeight = pullDistance.value - to;
 		if (overHeight < 1) {
 			r();
 			return;
@@ -85,36 +85,36 @@ function moveBySystem(to: number): Promise<void> {
 		let intervalId = setInterval(() => {
 			const time = Date.now() - startTime;
 			if (time > RELEASE_TRANSITION_DURATION) {
-				pullDistance = to;
+				pullDistance.value = to;
 				clearInterval(intervalId);
 				r();
 				return;
 			}
 			const nextHeight = startHeight - (overHeight / RELEASE_TRANSITION_DURATION) * time;
-			if (pullDistance < nextHeight) return;
-			pullDistance = nextHeight;
+			if (pullDistance.value < nextHeight) return;
+			pullDistance.value = nextHeight;
 		}, 1);
 	});
 }
 
 async function fixOverContent() {
-	if (pullDistance > FIRE_THRESHOLD) {
+	if (pullDistance.value > FIRE_THRESHOLD) {
 		await moveBySystem(FIRE_THRESHOLD);
 	}
 }
 
 async function closeContent() {
-	if (pullDistance > 0) {
+	if (pullDistance.value > 0) {
 		await moveBySystem(0);
 	}
 }
 
 function moveEnd() {
-	if (isPullStart && !isRefreshing) {
+	if (isPullStart.value && !isRefreshing.value) {
 		startScreenY = null;
-		if (isPullEnd) {
-			isPullEnd = false;
-			isRefreshing = true;
+		if (isPullEnd.value) {
+			isPullEnd.value = false;
+			isRefreshing.value = true;
 			fixOverContent().then(() => {
 				emit('refresh');
 				props.refresher().then(() => {
@@ -122,17 +122,17 @@ function moveEnd() {
 				});
 			});
 		} else {
-			closeContent().then(() => isPullStart = false);
+			closeContent().then(() => isPullStart.value = false);
 		}
 	}
 }
 
 function moving(event: TouchEvent | PointerEvent) {
-	if (!isPullStart || isRefreshing || disabled) return;
+	if (!isPullStart.value || isRefreshing.value || disabled) return;
 
-	if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance)) {
-		pullDistance = 0;
-		isPullEnd = false;
+	if ((scrollEl?.scrollTop ?? 0) > (supportPointerDesktop ? SCROLL_STOP : SCROLL_STOP + pullDistance.value)) {
+		pullDistance.value = 0;
+		isPullEnd.value = false;
 		moveEnd();
 		return;
 	}
@@ -143,13 +143,13 @@ function moving(event: TouchEvent | PointerEvent) {
 	const moveScreenY = getScreenY(event);
 
 	const moveHeight = moveScreenY - startScreenY!;
-	pullDistance = Math.min(Math.max(moveHeight, 0), MAX_PULL_DISTANCE);
+	pullDistance.value = Math.min(Math.max(moveHeight, 0), MAX_PULL_DISTANCE);
 
-	if (pullDistance > 0) {
+	if (pullDistance.value > 0) {
 		if (event.cancelable) event.preventDefault();
 	}
 
-	isPullEnd = pullDistance >= FIRE_THRESHOLD;
+	isPullEnd.value = pullDistance.value >= FIRE_THRESHOLD;
 }
 
 /**
@@ -159,8 +159,8 @@ function moving(event: TouchEvent | PointerEvent) {
  */
 function refreshFinished() {
 	closeContent().then(() => {
-		isPullStart = false;
-		isRefreshing = false;
+		isPullStart.value = false;
+		isRefreshing.value = false;
 	});
 }
 
@@ -182,26 +182,26 @@ function onScrollContainerScroll() {
 }
 
 function registerEventListenersForReadyToPull() {
-	if (rootEl == null) return;
-	rootEl.addEventListener('touchstart', moveStart, { passive: true });
-	rootEl.addEventListener('touchmove', moving, { passive: false }); // passive: falseにしないとpreventDefaultが使えない
+	if (rootEl.value == null) return;
+	rootEl.value.addEventListener('touchstart', moveStart, { passive: true });
+	rootEl.value.addEventListener('touchmove', moving, { passive: false }); // passive: falseにしないとpreventDefaultが使えない
 }
 
 function unregisterEventListenersForReadyToPull() {
-	if (rootEl == null) return;
-	rootEl.removeEventListener('touchstart', moveStart);
-	rootEl.removeEventListener('touchmove', moving);
+	if (rootEl.value == null) return;
+	rootEl.value.removeEventListener('touchstart', moveStart);
+	rootEl.value.removeEventListener('touchmove', moving);
 }
 
 onMounted(() => {
-	if (rootEl == null) return;
+	if (rootEl.value == null) return;
 
-	scrollEl = getScrollContainer(rootEl);
+	scrollEl = getScrollContainer(rootEl.value);
 	if (scrollEl == null) return;
 
 	scrollEl.addEventListener('scroll', onScrollContainerScroll, { passive: true });
 
-	rootEl.addEventListener('touchend', moveEnd, { passive: true });
+	rootEl.value.addEventListener('touchend', moveEnd, { passive: true });
 
 	registerEventListenersForReadyToPull();
 });
diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
index ba64775298..ebbd5e6cdc 100644
--- a/packages/frontend/src/components/MkPushNotificationAllowButton.vue
+++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
@@ -41,6 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script setup lang="ts">
+import { ref } from 'vue';
 import { $i, getAccounts } from '@/account.js';
 import MkButton from '@/components/MkButton.vue';
 import { instance } from '@/instance.js';
@@ -62,26 +63,26 @@ defineProps<{
 }>();
 
 // ServiceWorker registration
-let registration = $ref<ServiceWorkerRegistration | undefined>();
+const registration = ref<ServiceWorkerRegistration | undefined>();
 // If this browser supports push notification
-let supported = $ref(false);
+const supported = ref(false);
 // If this browser has already subscribed to push notification
-let pushSubscription = $ref<PushSubscription | null>(null);
-let pushRegistrationInServer = $ref<{ state?: string; key?: string; userId: string; endpoint: string; sendReadMessage: boolean; } | undefined>();
+const pushSubscription = ref<PushSubscription | null>(null);
+const pushRegistrationInServer = ref<{ state?: string; key?: string; userId: string; endpoint: string; sendReadMessage: boolean; } | undefined>();
 
 function subscribe() {
-	if (!registration || !supported || !instance.swPublickey) return;
+	if (!registration.value || !supported.value || !instance.swPublickey) return;
 
 	// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
-	return promiseDialog(registration.pushManager.subscribe({
+	return promiseDialog(registration.value.pushManager.subscribe({
 		userVisibleOnly: true,
 		applicationServerKey: urlBase64ToUint8Array(instance.swPublickey),
 	})
 		.then(async subscription => {
-			pushSubscription = subscription;
+			pushSubscription.value = subscription;
 
 			// Register
-			pushRegistrationInServer = await api('sw/register', {
+			pushRegistrationInServer.value = await api('sw/register', {
 				endpoint: subscription.endpoint,
 				auth: encode(subscription.getKey('auth')),
 				publickey: encode(subscription.getKey('p256dh')),
@@ -102,12 +103,12 @@ function subscribe() {
 }
 
 async function unsubscribe() {
-	if (!pushSubscription) return;
+	if (!pushSubscription.value) return;
 
-	const endpoint = pushSubscription.endpoint;
+	const endpoint = pushSubscription.value.endpoint;
 	const accounts = await getAccounts();
 
-	pushRegistrationInServer = undefined;
+	pushRegistrationInServer.value = undefined;
 
 	if ($i && accounts.length >= 2) {
 		apiWithDialog('sw/unregister', {
@@ -115,11 +116,11 @@ async function unsubscribe() {
 			endpoint,
 		});
 	} else {
-		pushSubscription.unsubscribe();
+		pushSubscription.value.unsubscribe();
 		apiWithDialog('sw/unregister', {
 			endpoint,
 		});
-		pushSubscription = null;
+		pushSubscription.value = null;
 	}
 }
 
@@ -150,20 +151,20 @@ if (navigator.serviceWorker == null) {
 	// TODO: よしなに?
 } else {
 	navigator.serviceWorker.ready.then(async swr => {
-		registration = swr;
+		registration.value = swr;
 
-		pushSubscription = await registration.pushManager.getSubscription();
+		pushSubscription.value = await registration.value.pushManager.getSubscription();
 
 		if (instance.swPublickey && ('PushManager' in window) && $i && $i.token) {
-			supported = true;
+			supported.value = true;
 
-			if (pushSubscription) {
+			if (pushSubscription.value) {
 				const res = await api('sw/show-registration', {
-					endpoint: pushSubscription.endpoint,
+					endpoint: pushSubscription.value.endpoint,
 				});
 
 				if (res) {
-					pushRegistrationInServer = res;
+					pushRegistrationInServer.value = res;
 				}
 			}
 		}
@@ -171,6 +172,6 @@ if (navigator.serviceWorker == null) {
 }
 
 defineExpose({
-	pushRegistrationInServer: $$(pushRegistrationInServer),
+	pushRegistrationInServer: pushRegistrationInServer,
 });
 </script>
diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue
index c4df3e991b..2d68557aad 100644
--- a/packages/frontend/src/components/MkRadio.vue
+++ b/packages/frontend/src/components/MkRadio.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { computed } from 'vue';
 
 const props = defineProps<{
 	modelValue: any;
@@ -36,7 +36,7 @@ const emit = defineEmits<{
 	(ev: 'update:modelValue', value: any): void;
 }>();
 
-let checked = $computed(() => props.modelValue === props.value);
+const checked = computed(() => props.modelValue === props.value);
 
 function toggle(): void {
 	if (props.disabled) return;
diff --git a/packages/frontend/src/components/MkReactionEffect.vue b/packages/frontend/src/components/MkReactionEffect.vue
index 88e262d880..75eb91e7ad 100644
--- a/packages/frontend/src/components/MkReactionEffect.vue
+++ b/packages/frontend/src/components/MkReactionEffect.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import * as os from '@/os.js';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 
@@ -27,13 +27,13 @@ const emit = defineEmits<{
 	(ev: 'end'): void;
 }>();
 
-let up = $ref(false);
+const up = ref(false);
 const zIndex = os.claimZIndex('middle');
 const angle = (90 - (Math.random() * 180)) + 'deg';
 
 onMounted(() => {
 	window.setTimeout(() => {
-		up = true;
+		up.value = true;
 	}, 10);
 
 	window.setTimeout(() => {
diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue
index eaa7faa4f9..a14f2512f8 100644
--- a/packages/frontend/src/components/MkReactionsViewer.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
-import { inject, watch } from 'vue';
+import { inject, watch, ref } from 'vue';
 import XReaction from '@/components/MkReactionsViewer.reaction.vue';
 import { defaultStore } from '@/store.js';
 
@@ -38,31 +38,31 @@ const emit = defineEmits<{
 
 const initialReactions = new Set(Object.keys(props.note.reactions));
 
-let reactions = $ref<[string, number][]>([]);
-let hasMoreReactions = $ref(false);
+const reactions = ref<[string, number][]>([]);
+const hasMoreReactions = ref(false);
 
-if (props.note.myReaction && !Object.keys(reactions).includes(props.note.myReaction)) {
-	reactions[props.note.myReaction] = props.note.reactions[props.note.myReaction];
+if (props.note.myReaction && !Object.keys(reactions.value).includes(props.note.myReaction)) {
+	reactions.value[props.note.myReaction] = props.note.reactions[props.note.myReaction];
 }
 
 function onMockToggleReaction(emoji: string, count: number) {
 	if (!mock) return;
 
-	const i = reactions.findIndex((item) => item[0] === emoji);
+	const i = reactions.value.findIndex((item) => item[0] === emoji);
 	if (i < 0) return;
 
-	emit('mockUpdateMyReaction', emoji, (count - reactions[i][1]));
+	emit('mockUpdateMyReaction', emoji, (count - reactions.value[i][1]));
 }
 
 watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumber]) => {
 	let newReactions: [string, number][] = [];
-	hasMoreReactions = Object.keys(newSource).length > maxNumber;
+	hasMoreReactions.value = Object.keys(newSource).length > maxNumber;
 
-	for (let i = 0; i < reactions.length; i++) {
-		const reaction = reactions[i][0];
+	for (let i = 0; i < reactions.value.length; i++) {
+		const reaction = reactions.value[i][0];
 		if (reaction in newSource && newSource[reaction] !== 0) {
-			reactions[i][1] = newSource[reaction];
-			newReactions.push(reactions[i]);
+			reactions.value[i][1] = newSource[reaction];
+			newReactions.push(reactions.value[i]);
 		}
 	}
 
@@ -80,7 +80,7 @@ watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumbe
 		newReactions.push([props.note.myReaction, newSource[props.note.myReaction]]);
 	}
 
-	reactions = newReactions;
+	reactions.value = newReactions;
 }, { immediate: true, deep: true });
 </script>
 
diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue
index 3dc9a94ae2..e69aa1be80 100644
--- a/packages/frontend/src/components/MkRetentionHeatmap.vue
+++ b/packages/frontend/src/components/MkRetentionHeatmap.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, nextTick } from 'vue';
+import { onMounted, nextTick, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
 import * as os from '@/os.js';
 import { defaultStore } from '@/store.js';
@@ -23,11 +23,11 @@ import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
-const rootEl = $shallowRef<HTMLDivElement>(null);
-const chartEl = $shallowRef<HTMLCanvasElement>(null);
+const rootEl = shallowRef<HTMLDivElement>(null);
+const chartEl = shallowRef<HTMLCanvasElement>(null);
 const now = new Date();
 let chartInstance: Chart = null;
-let fetching = $ref(true);
+const fetching = ref(true);
 
 const { handler: externalTooltipHandler } = useChartTooltip({
 	position: 'middle',
@@ -38,8 +38,8 @@ async function renderChart() {
 		chartInstance.destroy();
 	}
 
-	const wide = rootEl.offsetWidth > 600;
-	const narrow = rootEl.offsetWidth < 400;
+	const wide = rootEl.value.offsetWidth > 600;
+	const narrow = rootEl.value.offsetWidth < 400;
 
 	const maxDays = wide ? 10 : narrow ? 5 : 7;
 
@@ -66,7 +66,7 @@ async function renderChart() {
 		}
 	}
 
-	fetching = false;
+	fetching.value = false;
 
 	await nextTick();
 
@@ -83,7 +83,7 @@ async function renderChart() {
 
 	const marginEachCell = 12;
 
-	chartInstance = new Chart(chartEl, {
+	chartInstance = new Chart(chartEl.value, {
 		type: 'matrix',
 		data: {
 			datasets: [{
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index 0fd67a0b46..6051db1cad 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, ref } from 'vue';
 import { toUnicode } from 'punycode/';
 import * as Misskey from 'misskey-js';
 import { supported as webAuthnSupported, get as webAuthnRequest, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
@@ -62,17 +62,17 @@ import * as os from '@/os.js';
 import { login } from '@/account.js';
 import { i18n } from '@/i18n.js';
 
-let signing = $ref(false);
-let user = $ref<Misskey.entities.UserDetailed | null>(null);
-let username = $ref('');
-let password = $ref('');
-let token = $ref('');
-let host = $ref(toUnicode(configHost));
-let totpLogin = $ref(false);
-let queryingKey = $ref(false);
-let credentialRequest = $ref<CredentialRequestOptions | null>(null);
-let hCaptchaResponse = $ref(null);
-let reCaptchaResponse = $ref(null);
+const signing = ref(false);
+const user = ref<Misskey.entities.UserDetailed | null>(null);
+const username = ref('');
+const password = ref('');
+const token = ref('');
+const host = ref(toUnicode(configHost));
+const totpLogin = ref(false);
+const queryingKey = ref(false);
+const credentialRequest = ref<CredentialRequestOptions | null>(null);
+const hCaptchaResponse = ref(null);
+const reCaptchaResponse = ref(null);
 
 const emit = defineEmits<{
 	(ev: 'login', v: any): void;
@@ -98,11 +98,11 @@ const props = defineProps({
 
 function onUsernameChange(): void {
 	os.api('users/show', {
-		username: username,
+		username: username.value,
 	}).then(userResponse => {
-		user = userResponse;
+		user.value = userResponse;
 	}, () => {
-		user = null;
+		user.value = null;
 	});
 }
 
@@ -113,21 +113,21 @@ function onLogin(res: any): Promise<void> | void {
 }
 
 async function queryKey(): Promise<void> {
-	queryingKey = true;
-	await webAuthnRequest(credentialRequest)
+	queryingKey.value = true;
+	await webAuthnRequest(credentialRequest.value)
 		.catch(() => {
-			queryingKey = false;
+			queryingKey.value = false;
 			return Promise.reject(null);
 		}).then(credential => {
-			credentialRequest = null;
-			queryingKey = false;
-			signing = true;
+			credentialRequest.value = null;
+			queryingKey.value = false;
+			signing.value = true;
 			return os.api('signin', {
-				username,
-				password,
+				username: username.value,
+				password: password.value,
 				credential: credential.toJSON(),
-				'hcaptcha-response': hCaptchaResponse,
-				'g-recaptcha-response': reCaptchaResponse,
+				'hcaptcha-response': hCaptchaResponse.value,
+				'g-recaptcha-response': reCaptchaResponse.value,
 			});
 		}).then(res => {
 			emit('login', res);
@@ -138,39 +138,39 @@ async function queryKey(): Promise<void> {
 				type: 'error',
 				text: i18n.ts.signinFailed,
 			});
-			signing = false;
+			signing.value = false;
 		});
 }
 
 function onSubmit(): void {
-	signing = true;
-	if (!totpLogin && user && user.twoFactorEnabled) {
-		if (webAuthnSupported() && user.securityKeys) {
+	signing.value = true;
+	if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
+		if (webAuthnSupported() && user.value.securityKeys) {
 			os.api('signin', {
-				username,
-				password,
-				'hcaptcha-response': hCaptchaResponse,
-				'g-recaptcha-response': reCaptchaResponse,
+				username: username.value,
+				password: password.value,
+				'hcaptcha-response': hCaptchaResponse.value,
+				'g-recaptcha-response': reCaptchaResponse.value,
 			}).then(res => {
-				totpLogin = true;
-				signing = false;
-				credentialRequest = parseRequestOptionsFromJSON({
+				totpLogin.value = true;
+				signing.value = false;
+				credentialRequest.value = parseRequestOptionsFromJSON({
 					publicKey: res,
 				});
 			})
 				.then(() => queryKey())
 				.catch(loginFailed);
 		} else {
-			totpLogin = true;
-			signing = false;
+			totpLogin.value = true;
+			signing.value = false;
 		}
 	} else {
 		os.api('signin', {
-			username,
-			password,
-			'hcaptcha-response': hCaptchaResponse,
-			'g-recaptcha-response': reCaptchaResponse,
-			token: user?.twoFactorEnabled ? token : undefined,
+			username: username.value,
+			password: password.value,
+			'hcaptcha-response': hCaptchaResponse.value,
+			'g-recaptcha-response': reCaptchaResponse.value,
+			token: user.value?.twoFactorEnabled ? token.value : undefined,
 		}).then(res => {
 			emit('login', res);
 			onLogin(res);
@@ -218,8 +218,8 @@ function loginFailed(err: any): void {
 		}
 	}
 
-	totpLogin = false;
-	signing = false;
+	totpLogin.value = false;
+	signing.value = false;
 }
 
 function resetPassword(): void {
diff --git a/packages/frontend/src/components/MkSigninDialog.vue b/packages/frontend/src/components/MkSigninDialog.vue
index 05cef6ed3b..6f961cff05 100644
--- a/packages/frontend/src/components/MkSigninDialog.vue
+++ b/packages/frontend/src/components/MkSigninDialog.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { shallowRef } from 'vue';
 import MkSignin from '@/components/MkSignin.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import { i18n } from '@/i18n.js';
@@ -39,15 +39,15 @@ const emit = defineEmits<{
 	(ev: 'cancelled'): void;
 }>();
 
-const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
+const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
 
 function onClose() {
 	emit('cancelled');
-	if (dialog) dialog.close();
+	if (dialog.value) dialog.value.close();
 }
 
 function onLogin(res) {
 	emit('done', res);
-	if (dialog) dialog.close();
+	if (dialog.value) dialog.value.close();
 }
 </script>
diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue
index a67251eda1..08e57fd8a6 100644
--- a/packages/frontend/src/components/MkSignupDialog.form.vue
+++ b/packages/frontend/src/components/MkSignupDialog.form.vue
@@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import { toUnicode } from 'punycode/';
 import MkButton from './MkButton.vue';
 import MkInput from './MkInput.vue';
@@ -101,34 +101,34 @@ const emit = defineEmits<{
 
 const host = toUnicode(config.host);
 
-let hcaptcha = $ref<Captcha | undefined>();
-let recaptcha = $ref<Captcha | undefined>();
-let turnstile = $ref<Captcha | undefined>();
+const hcaptcha = ref<Captcha | undefined>();
+const recaptcha = ref<Captcha | undefined>();
+const turnstile = ref<Captcha | undefined>();
 
-let username: string = $ref('');
-let password: string = $ref('');
-let retypedPassword: string = $ref('');
-let invitationCode: string = $ref('');
-let email = $ref('');
-let usernameState: null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range' = $ref(null);
-let emailState: null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error' = $ref(null);
-let passwordStrength: '' | 'low' | 'medium' | 'high' = $ref('');
-let passwordRetypeState: null | 'match' | 'not-match' = $ref(null);
-let submitting: boolean = $ref(false);
-let hCaptchaResponse = $ref(null);
-let reCaptchaResponse = $ref(null);
-let turnstileResponse = $ref(null);
-let usernameAbortController: null | AbortController = $ref(null);
-let emailAbortController: null | AbortController = $ref(null);
+const username = ref<string>('');
+const password = ref<string>('');
+const retypedPassword = ref<string>('');
+const invitationCode = ref<string>('');
+const email = ref('');
+const usernameState = ref<null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range'>(null);
+const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error'>(null);
+const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>('');
+const passwordRetypeState = ref<null | 'match' | 'not-match'>(null);
+const submitting = ref<boolean>(false);
+const hCaptchaResponse = ref(null);
+const reCaptchaResponse = ref(null);
+const turnstileResponse = ref(null);
+const usernameAbortController = ref<null | AbortController>(null);
+const emailAbortController = ref<null | AbortController>(null);
 
-const shouldDisableSubmitting = $computed((): boolean => {
-	return submitting ||
-		instance.enableHcaptcha && !hCaptchaResponse ||
-		instance.enableRecaptcha && !reCaptchaResponse ||
-		instance.enableTurnstile && !turnstileResponse ||
-		instance.emailRequiredForSignup && emailState !== 'ok' ||
-		usernameState !== 'ok' ||
-		passwordRetypeState !== 'match';
+const shouldDisableSubmitting = computed((): boolean => {
+	return submitting.value ||
+		instance.enableHcaptcha && !hCaptchaResponse.value ||
+		instance.enableRecaptcha && !reCaptchaResponse.value ||
+		instance.enableTurnstile && !turnstileResponse.value ||
+		instance.emailRequiredForSignup && emailState.value !== 'ok' ||
+		usernameState.value !== 'ok' ||
+		passwordRetypeState.value !== 'match';
 });
 
 function getPasswordStrength(source: string): number {
@@ -156,57 +156,57 @@ function getPasswordStrength(source: string): number {
 }
 
 function onChangeUsername(): void {
-	if (username === '') {
-		usernameState = null;
+	if (username.value === '') {
+		usernameState.value = null;
 		return;
 	}
 
 	{
 		const err =
-			!username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
-			username.length < 1 ? 'min-range' :
-			username.length > 20 ? 'max-range' :
+			!username.value.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' :
+			username.value.length < 1 ? 'min-range' :
+			username.value.length > 20 ? 'max-range' :
 			null;
 
 		if (err) {
-			usernameState = err;
+			usernameState.value = err;
 			return;
 		}
 	}
 
-	if (usernameAbortController != null) {
-		usernameAbortController.abort();
+	if (usernameAbortController.value != null) {
+		usernameAbortController.value.abort();
 	}
-	usernameState = 'wait';
-	usernameAbortController = new AbortController();
+	usernameState.value = 'wait';
+	usernameAbortController.value = new AbortController();
 
 	os.api('username/available', {
-		username,
-	}, undefined, usernameAbortController.signal).then(result => {
-		usernameState = result.available ? 'ok' : 'unavailable';
+		username: username.value,
+	}, undefined, usernameAbortController.value.signal).then(result => {
+		usernameState.value = result.available ? 'ok' : 'unavailable';
 	}).catch((err) => {
 		if (err.name !== 'AbortError') {
-			usernameState = 'error';
+			usernameState.value = 'error';
 		}
 	});
 }
 
 function onChangeEmail(): void {
-	if (email === '') {
-		emailState = null;
+	if (email.value === '') {
+		emailState.value = null;
 		return;
 	}
 
-	if (emailAbortController != null) {
-		emailAbortController.abort();
+	if (emailAbortController.value != null) {
+		emailAbortController.value.abort();
 	}
-	emailState = 'wait';
-	emailAbortController = new AbortController();
+	emailState.value = 'wait';
+	emailAbortController.value = new AbortController();
 
 	os.api('email-address/available', {
-		emailAddress: email,
-	}, undefined, emailAbortController.signal).then(result => {
-		emailState = result.available ? 'ok' :
+		emailAddress: email.value,
+	}, undefined, emailAbortController.value.signal).then(result => {
+		emailState.value = result.available ? 'ok' :
 			result.reason === 'used' ? 'unavailable:used' :
 			result.reason === 'format' ? 'unavailable:format' :
 			result.reason === 'disposable' ? 'unavailable:disposable' :
@@ -215,55 +215,55 @@ function onChangeEmail(): void {
 			'unavailable';
 	}).catch((err) => {
 		if (err.name !== 'AbortError') {
-			emailState = 'error';
+			emailState.value = 'error';
 		}
 	});
 }
 
 function onChangePassword(): void {
-	if (password === '') {
-		passwordStrength = '';
+	if (password.value === '') {
+		passwordStrength.value = '';
 		return;
 	}
 
-	const strength = getPasswordStrength(password);
-	passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
+	const strength = getPasswordStrength(password.value);
+	passwordStrength.value = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low';
 }
 
 function onChangePasswordRetype(): void {
-	if (retypedPassword === '') {
-		passwordRetypeState = null;
+	if (retypedPassword.value === '') {
+		passwordRetypeState.value = null;
 		return;
 	}
 
-	passwordRetypeState = password === retypedPassword ? 'match' : 'not-match';
+	passwordRetypeState.value = password.value === retypedPassword.value ? 'match' : 'not-match';
 }
 
 async function onSubmit(): Promise<void> {
-	if (submitting) return;
-	submitting = true;
+	if (submitting.value) return;
+	submitting.value = true;
 
 	try {
 		await os.api('signup', {
-			username,
-			password,
-			emailAddress: email,
-			invitationCode,
-			'hcaptcha-response': hCaptchaResponse,
-			'g-recaptcha-response': reCaptchaResponse,
-			'turnstile-response': turnstileResponse,
+			username: username.value,
+			password: password.value,
+			emailAddress: email.value,
+			invitationCode: invitationCode.value,
+			'hcaptcha-response': hCaptchaResponse.value,
+			'g-recaptcha-response': reCaptchaResponse.value,
+			'turnstile-response': turnstileResponse.value,
 		});
 		if (instance.emailRequiredForSignup) {
 			os.alert({
 				type: 'success',
 				title: i18n.ts._signup.almostThere,
-				text: i18n.t('_signup.emailSent', { email }),
+				text: i18n.t('_signup.emailSent', { email: email.value }),
 			});
 			emit('signupEmailPending');
 		} else {
 			const res = await os.api('signin', {
-				username,
-				password,
+				username: username.value,
+				password: password.value,
 			});
 			emit('signup', res);
 
@@ -272,10 +272,10 @@ async function onSubmit(): Promise<void> {
 			}
 		}
 	} catch {
-		submitting = false;
-		hcaptcha?.reset?.();
-		recaptcha?.reset?.();
-		turnstile?.reset?.();
+		submitting.value = false;
+		hcaptcha.value?.reset?.();
+		recaptcha.value?.reset?.();
+		turnstile.value?.reset?.();
 
 		os.alert({
 			type: 'error',
diff --git a/packages/frontend/src/components/MkSignupDialog.vue b/packages/frontend/src/components/MkSignupDialog.vue
index d860ba5fe6..1467049e25 100644
--- a/packages/frontend/src/components/MkSignupDialog.vue
+++ b/packages/frontend/src/components/MkSignupDialog.vue
@@ -33,8 +33,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
-import { $ref } from 'vue/macros';
+import { shallowRef, ref } from 'vue';
+
 import XSignup from '@/components/MkSignupDialog.form.vue';
 import XServerRules from '@/components/MkSignupDialog.rules.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
@@ -52,17 +52,17 @@ const emit = defineEmits<{
 	(ev: 'closed'): void;
 }>();
 
-const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
+const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
 
-const isAcceptedServerRule = $ref(false);
+const isAcceptedServerRule = ref(false);
 
 function onSignup(res) {
 	emit('done', res);
-	dialog.close();
+	dialog.value.close();
 }
 
 function onSignupEmailPending() {
-	dialog.close();
+	dialog.value.close();
 }
 </script>
 
diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue
index 638407872f..370894d4f4 100644
--- a/packages/frontend/src/components/MkSubNoteContent.vue
+++ b/packages/frontend/src/components/MkSubNoteContent.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkMediaList from '@/components/MkMediaList.vue';
 import MkPoll from '@/components/MkPoll.vue';
@@ -44,7 +44,7 @@ const props = defineProps<{
 
 const isLong = shouldCollapsed(props.note, []);
 
-const collapsed = $ref(isLong);
+const collapsed = ref(isLong);
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/components/MkTagCloud.vue b/packages/frontend/src/components/MkTagCloud.vue
index a3d82fee5e..083c34906f 100644
--- a/packages/frontend/src/components/MkTagCloud.vue
+++ b/packages/frontend/src/components/MkTagCloud.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, watch, onBeforeUnmount } from 'vue';
+import { onMounted, watch, onBeforeUnmount, ref, shallowRef } from 'vue';
 import tinycolor from 'tinycolor2';
 
 const loaded = !!window.TagCanvas;
@@ -23,13 +23,13 @@ const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz';
 const computedStyle = getComputedStyle(document.documentElement);
 const idForCanvas = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
 const idForTags = Array.from({ length: 16 }, () => SAFE_FOR_HTML_ID[Math.floor(Math.random() * SAFE_FOR_HTML_ID.length)]).join('');
-let available = $ref(false);
-let rootEl = $shallowRef<HTMLElement | null>(null);
-let canvasEl = $shallowRef<HTMLCanvasElement | null>(null);
-let tagsEl = $shallowRef<HTMLElement | null>(null);
-let width = $ref(300);
+const available = ref(false);
+const rootEl = shallowRef<HTMLElement | null>(null);
+const canvasEl = shallowRef<HTMLCanvasElement | null>(null);
+const tagsEl = shallowRef<HTMLElement | null>(null);
+const width = ref(300);
 
-watch($$(available), () => {
+watch(available, () => {
 	try {
 		window.TagCanvas.Start(idForCanvas, idForTags, {
 			textColour: '#ffffff',
@@ -52,15 +52,15 @@ watch($$(available), () => {
 });
 
 onMounted(() => {
-	width = rootEl.offsetWidth;
+	width.value = rootEl.value.offsetWidth;
 
 	if (loaded) {
-		available = true;
+		available.value = true;
 	} else {
 		document.head.appendChild(Object.assign(document.createElement('script'), {
 			async: true,
 			src: '/client-assets/tagcanvas.min.js',
-		})).addEventListener('load', () => available = true);
+		})).addEventListener('load', () => available.value = true);
 	}
 });
 
diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue
index 04716ebab2..23afb922f3 100644
--- a/packages/frontend/src/components/MkTimeline.vue
+++ b/packages/frontend/src/components/MkTimeline.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch, onUnmounted, provide } from 'vue';
+import { computed, watch, onUnmounted, provide, ref } from 'vue';
 import { Connection } from 'misskey-js/built/streaming.js';
 import MkNotes from '@/components/MkNotes.vue';
 import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
@@ -62,8 +62,8 @@ type TimelineQueryType = {
   roleId?: string
 }
 
-const prComponent: InstanceType<typeof MkPullToRefresh> = $ref();
-const tlComponent: InstanceType<typeof MkNotes> = $ref();
+const prComponent = ref<InstanceType<typeof MkPullToRefresh>>();
+const tlComponent = ref<InstanceType<typeof MkNotes>>();
 
 let tlNotesCount = 0;
 
@@ -74,7 +74,7 @@ const prepend = note => {
 		note._shouldInsertAd_ = true;
 	}
 
-	tlComponent.pagingComponent?.prepend(note);
+	tlComponent.value.pagingComponent?.prepend(note);
 
 	emit('note');
 
@@ -248,7 +248,7 @@ function reloadTimeline() {
 	return new Promise<void>((res) => {
 		tlNotesCount = 0;
 
-		tlComponent.pagingComponent?.reload().then(() => {
+		tlComponent.value.pagingComponent?.reload().then(() => {
 			res();
 		});
 	});
diff --git a/packages/frontend/src/components/MkToast.vue b/packages/frontend/src/components/MkToast.vue
index 48908cf3e6..0446f9196a 100644
--- a/packages/frontend/src/components/MkToast.vue
+++ b/packages/frontend/src/components/MkToast.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import * as os from '@/os.js';
 import { defaultStore } from '@/store.js';
 
@@ -35,11 +35,11 @@ const emit = defineEmits<{
 }>();
 
 const zIndex = os.claimZIndex('high');
-let showing = $ref(true);
+const showing = ref(true);
 
 onMounted(() => {
 	window.setTimeout(() => {
-		showing = false;
+		showing.value = false;
 	}, 4000);
 });
 </script>
diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue
index 8958accc4a..f5fa86a908 100644
--- a/packages/frontend/src/components/MkTokenGenerateWindow.vue
+++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { shallowRef, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkInput from './MkInput.vue';
 import MkSwitch from './MkSwitch.vue';
@@ -67,37 +67,37 @@ const emit = defineEmits<{
 	(ev: 'done', result: { name: string | null, permissions: string[] }): void;
 }>();
 
-const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
-let name = $ref(props.initialName);
-let permissions = $ref({});
+const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
+const name = ref(props.initialName);
+const permissions = ref({});
 
 if (props.initialPermissions) {
 	for (const kind of props.initialPermissions) {
-		permissions[kind] = true;
+		permissions.value[kind] = true;
 	}
 } else {
 	for (const kind of Misskey.permissions) {
-		permissions[kind] = false;
+		permissions.value[kind] = false;
 	}
 }
 
 function ok(): void {
 	emit('done', {
-		name: name,
-		permissions: Object.keys(permissions).filter(p => permissions[p]),
+		name: name.value,
+		permissions: Object.keys(permissions.value).filter(p => permissions.value[p]),
 	});
-	dialog.close();
+	dialog.value.close();
 }
 
 function disableAll(): void {
-	for (const p in permissions) {
-		permissions[p] = false;
+	for (const p in permissions.value) {
+		permissions.value[p] = false;
 	}
 }
 
 function enableAll(): void {
-	for (const p in permissions) {
-		permissions[p] = true;
+	for (const p in permissions.value) {
+		permissions.value[p] = true;
 	}
 }
 </script>
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index 2332fe9a7c..f0f1a13d0b 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -83,7 +83,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, onUnmounted } from 'vue';
+import { defineAsyncComponent, onUnmounted, ref } from 'vue';
 import type { summaly } from 'summaly';
 import { url as local } from '@/config.js';
 import { i18n } from '@/i18n.js';
@@ -107,36 +107,36 @@ const props = withDefaults(defineProps<{
 });
 
 const MOBILE_THRESHOLD = 500;
-const isMobile = $ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
+const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD);
 
 const self = props.url.startsWith(local);
 const attr = self ? 'to' : 'href';
 const target = self ? null : '_blank';
-let fetching = $ref(true);
-let title = $ref<string | null>(null);
-let description = $ref<string | null>(null);
-let thumbnail = $ref<string | null>(null);
-let icon = $ref<string | null>(null);
-let sitename = $ref<string | null>(null);
-let sensitive = $ref<boolean>(false);
-let player = $ref({
+const fetching = ref(true);
+const title = ref<string | null>(null);
+const description = ref<string | null>(null);
+const thumbnail = ref<string | null>(null);
+const icon = ref<string | null>(null);
+const sitename = ref<string | null>(null);
+const sensitive = ref<boolean>(false);
+const player = ref({
 	url: null,
 	width: null,
 	height: null,
 } as SummalyResult['player']);
-let playerEnabled = $ref(false);
-let tweetId = $ref<string | null>(null);
-let tweetExpanded = $ref(props.detail);
+const playerEnabled = ref(false);
+const tweetId = ref<string | null>(null);
+const tweetExpanded = ref(props.detail);
 const embedId = `embed${Math.random().toString().replace(/\D/, '')}`;
-let tweetHeight = $ref(150);
-let unknownUrl = $ref(false);
+const tweetHeight = ref(150);
+const unknownUrl = ref(false);
 
 const requestUrl = new URL(props.url);
 if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url');
 
 if (requestUrl.hostname === 'twitter.com' || requestUrl.hostname === 'mobile.twitter.com' || requestUrl.hostname === 'x.com' || requestUrl.hostname === 'mobile.x.com') {
 	const m = requestUrl.pathname.match(/^\/.+\/status(?:es)?\/(\d+)/);
-	if (m) tweetId = m[1];
+	if (m) tweetId.value = m[1];
 }
 
 if (requestUrl.hostname === 'music.youtube.com' && requestUrl.pathname.match('^/(?:watch|channel)')) {
@@ -148,8 +148,8 @@ requestUrl.hash = '';
 window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`)
 	.then(res => {
 		if (!res.ok) {
-			fetching = false;
-			unknownUrl = true;
+			fetching.value = false;
+			unknownUrl.value = true;
 			return;
 		}
 
@@ -157,21 +157,21 @@ window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLa
 	})
 	.then((info: SummalyResult) => {
 		if (info.url == null) {
-			fetching = false;
-			unknownUrl = true;
+			fetching.value = false;
+			unknownUrl.value = true;
 			return;
 		}
 
-		fetching = false;
-		unknownUrl = false;
+		fetching.value = false;
+		unknownUrl.value = false;
 
-		title = info.title;
-		description = info.description;
-		thumbnail = info.thumbnail;
-		icon = info.icon;
-		sitename = info.sitename;
-		player = info.player;
-		sensitive = info.sensitive ?? false;
+		title.value = info.title;
+		description.value = info.description;
+		thumbnail.value = info.thumbnail;
+		icon.value = info.icon;
+		sitename.value = info.sitename;
+		player.value = info.player;
+		sensitive.value = info.sensitive ?? false;
 	});
 
 function adjustTweetHeight(message: any) {
@@ -180,7 +180,7 @@ function adjustTweetHeight(message: any) {
 	if (embed?.method !== 'twttr.private.resize') return;
 	if (embed?.id !== embedId) return;
 	const height = embed?.params[0]?.height;
-	if (height) tweetHeight = height;
+	if (height) tweetHeight.value = height;
 }
 
 const openPlayer = (): void => {
diff --git a/packages/frontend/src/components/MkUrlPreviewPopup.vue b/packages/frontend/src/components/MkUrlPreviewPopup.vue
index 0ab012dfb7..81c383540c 100644
--- a/packages/frontend/src/components/MkUrlPreviewPopup.vue
+++ b/packages/frontend/src/components/MkUrlPreviewPopup.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import MkUrlPreview from '@/components/MkUrlPreview.vue';
 import * as os from '@/os.js';
 import { defaultStore } from '@/store.js';
@@ -28,16 +28,16 @@ const emit = defineEmits<{
 }>();
 
 const zIndex = os.claimZIndex('middle');
-let top = $ref(0);
-let left = $ref(0);
+const top = ref(0);
+const left = ref(0);
 
 onMounted(() => {
 	const rect = props.source.getBoundingClientRect();
 	const x = Math.max((rect.left + (props.source.offsetWidth / 2)) - (300 / 2), 6) + window.pageXOffset;
 	const y = rect.top + props.source.offsetHeight + window.pageYOffset;
 
-	top = y;
-	left = x;
+	top.value = y;
+	left.value = x;
 });
 </script>
 
diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
index 235df8822f..b9fd084409 100644
--- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
+++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
@@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -66,12 +66,12 @@ const props = defineProps<{
 	announcement?: any,
 }>();
 
-let dialog = $ref(null);
-let title: string = $ref(props.announcement ? props.announcement.title : '');
-let text: string = $ref(props.announcement ? props.announcement.text : '');
-let icon: string = $ref(props.announcement ? props.announcement.icon : 'info');
-let display: string = $ref(props.announcement ? props.announcement.display : 'dialog');
-let needConfirmationToRead = $ref(props.announcement ? props.announcement.needConfirmationToRead : false);
+const dialog = ref(null);
+const title = ref<string>(props.announcement ? props.announcement.title : '');
+const text = ref<string>(props.announcement ? props.announcement.text : '');
+const icon = ref<string>(props.announcement ? props.announcement.icon : 'info');
+const display = ref<string>(props.announcement ? props.announcement.display : 'dialog');
+const needConfirmationToRead = ref(props.announcement ? props.announcement.needConfirmationToRead : false);
 
 const emit = defineEmits<{
 	(ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void,
@@ -80,12 +80,12 @@ const emit = defineEmits<{
 
 async function done() {
 	const params = {
-		title: title,
-		text: text,
-		icon: icon,
+		title: title.value,
+		text: text.value,
+		icon: icon.value,
 		imageUrl: null,
-		display: display,
-		needConfirmationToRead: needConfirmationToRead,
+		display: display.value,
+		needConfirmationToRead: needConfirmationToRead.value,
 		userId: props.user.id,
 	};
 
@@ -102,7 +102,7 @@ async function done() {
 			},
 		});
 
-		dialog.close();
+		dialog.value.close();
 	} else {
 		const created = await os.apiWithDialog('admin/announcements/create', params);
 
@@ -110,14 +110,14 @@ async function done() {
 			created: created,
 		});
 
-		dialog.close();
+		dialog.value.close();
 	}
 }
 
 async function del() {
 	const { canceled } = await os.confirm({
 		type: 'warning',
-		text: i18n.t('removeAreYouSure', { x: title }),
+		text: i18n.t('removeAreYouSure', { x: title.value }),
 	});
 	if (canceled) return;
 
@@ -127,7 +127,7 @@ async function del() {
 		emit('done', {
 			deleted: true,
 		});
-		dialog.close();
+		dialog.value.close();
 	});
 }
 </script>
diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue
index fbc2e09b0b..75288aac02 100644
--- a/packages/frontend/src/components/MkUserCardMini.vue
+++ b/packages/frontend/src/components/MkUserCardMini.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import MkMiniChart from '@/components/MkMiniChart.vue';
 import * as os from '@/os.js';
 import { acct } from '@/filters/user.js';
@@ -28,14 +28,14 @@ const props = withDefaults(defineProps<{
 	withChart: true,
 });
 
-let chartValues = $ref<number[] | null>(null);
+const chartValues = ref<number[] | null>(null);
 
 onMounted(() => {
 	if (props.withChart) {
 		os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => {
 			// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
 			res.inc.splice(0, 1);
-			chartValues = res.inc;
+			chartValues.value = res.inc;
 		});
 	}
 });
diff --git a/packages/frontend/src/components/MkUserOnlineIndicator.vue b/packages/frontend/src/components/MkUserOnlineIndicator.vue
index 8b792fe496..31d6fe6d04 100644
--- a/packages/frontend/src/components/MkUserOnlineIndicator.vue
+++ b/packages/frontend/src/components/MkUserOnlineIndicator.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import { i18n } from '@/i18n.js';
 
@@ -24,7 +24,7 @@ const props = defineProps<{
 	user: Misskey.entities.User;
 }>();
 
-const text = $computed(() => {
+const text = computed(() => {
 	switch (props.user.onlineStatus) {
 		case 'online': return i18n.ts.online;
 		case 'active': return i18n.ts.active;
diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue
index 20eb9b3e93..b703369433 100644
--- a/packages/frontend/src/components/MkUserPopup.vue
+++ b/packages/frontend/src/components/MkUserPopup.vue
@@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkFollowButton from '@/components/MkFollowButton.vue';
 import { userPage } from '@/filters/user.js';
@@ -80,18 +80,18 @@ const emit = defineEmits<{
 }>();
 
 const zIndex = os.claimZIndex('middle');
-let user = $ref<Misskey.entities.UserDetailed | null>(null);
-let top = $ref(0);
-let left = $ref(0);
+const user = ref<Misskey.entities.UserDetailed | null>(null);
+const top = ref(0);
+const left = ref(0);
 
 function showMenu(ev: MouseEvent) {
-	const { menu, cleanup } = getUserMenu(user);
+	const { menu, cleanup } = getUserMenu(user.value);
 	os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup);
 }
 
 onMounted(() => {
 	if (typeof props.q === 'object') {
-		user = props.q;
+		user.value = props.q;
 	} else {
 		const query = props.q.startsWith('@') ?
 			Misskey.acct.parse(props.q.substring(1)) :
@@ -99,7 +99,7 @@ onMounted(() => {
 
 		os.api('users/show', query).then(res => {
 			if (!props.showing) return;
-			user = res;
+			user.value = res;
 		});
 	}
 
@@ -107,8 +107,8 @@ onMounted(() => {
 	const x = ((rect.left + (props.source.offsetWidth / 2)) - (300 / 2)) + window.pageXOffset;
 	const y = rect.top + props.source.offsetHeight + window.pageYOffset;
 
-	top = y;
-	left = x;
+	top.value = y;
+	left.value = x;
 });
 </script>
 
diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue
index ac38c4b62f..9d41147bd2 100644
--- a/packages/frontend/src/components/MkUserSelectDialog.vue
+++ b/packages/frontend/src/components/MkUserSelectDialog.vue
@@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkInput from '@/components/MkInput.vue';
 import FormSplit from '@/components/form/split.vue';
@@ -78,43 +78,43 @@ const props = defineProps<{
 	includeSelf?: boolean;
 }>();
 
-let username = $ref('');
-let host = $ref('');
-let users: Misskey.entities.UserDetailed[] = $ref([]);
-let recentUsers: Misskey.entities.UserDetailed[] = $ref([]);
-let selected: Misskey.entities.UserDetailed | null = $ref(null);
-let dialogEl = $ref();
+const username = ref('');
+const host = ref('');
+const users = ref<Misskey.entities.UserDetailed[]>([]);
+const recentUsers = ref<Misskey.entities.UserDetailed[]>([]);
+const selected = ref<Misskey.entities.UserDetailed | null>(null);
+const dialogEl = ref();
 
 const search = () => {
-	if (username === '' && host === '') {
-		users = [];
+	if (username.value === '' && host.value === '') {
+		users.value = [];
 		return;
 	}
 	os.api('users/search-by-username-and-host', {
-		username: username,
-		host: host,
+		username: username.value,
+		host: host.value,
 		limit: 10,
 		detail: false,
 	}).then(_users => {
-		users = _users;
+		users.value = _users;
 	});
 };
 
 const ok = () => {
-	if (selected == null) return;
-	emit('ok', selected);
-	dialogEl.close();
+	if (selected.value == null) return;
+	emit('ok', selected.value);
+	dialogEl.value.close();
 
 	// 最近使ったユーザー更新
 	let recents = defaultStore.state.recentlyUsedUsers;
-	recents = recents.filter(x => x !== selected.id);
-	recents.unshift(selected.id);
+	recents = recents.filter(x => x !== selected.value.id);
+	recents.unshift(selected.value.id);
 	defaultStore.set('recentlyUsedUsers', recents.splice(0, 16));
 };
 
 const cancel = () => {
 	emit('cancel');
-	dialogEl.close();
+	dialogEl.value.close();
 };
 
 onMounted(() => {
@@ -122,9 +122,9 @@ onMounted(() => {
 		userIds: defaultStore.state.recentlyUsedUsers,
 	}).then(users => {
 		if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) {
-			recentUsers = [$i, ...users];
+			recentUsers.value = [$i, ...users];
 		} else {
-			recentUsers = users;
+			recentUsers.value = users;
 		}
 	});
 });
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
index 841ab5ba0c..4bca72511d 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
@@ -53,10 +53,10 @@ import MkFolder from '@/components/MkFolder.vue';
 import * as os from '@/os.js';
 import { $i } from '@/account.js';
 
-let isLocked = ref(false);
-let hideOnlineStatus = ref(false);
-let noCrawle = ref(false);
-let preventAiLearning = ref(true);
+const isLocked = ref(false);
+const hideOnlineStatus = ref(false);
+const noCrawle = ref(false);
+const preventAiLearning = ref(true);
 
 watch([isLocked, hideOnlineStatus, noCrawle, preventAiLearning], () => {
 	os.api('i/update', {
diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue
index bbb3d3dbf5..1324ed12e1 100644
--- a/packages/frontend/src/components/MkVisibilityPicker.vue
+++ b/packages/frontend/src/components/MkVisibilityPicker.vue
@@ -42,12 +42,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { nextTick } from 'vue';
+import { nextTick, shallowRef, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkModal from '@/components/MkModal.vue';
 import { i18n } from '@/i18n.js';
 
-const modal = $shallowRef<InstanceType<typeof MkModal>>();
+const modal = shallowRef<InstanceType<typeof MkModal>>();
 
 const props = withDefaults(defineProps<{
 	currentVisibility: typeof Misskey.noteVisibilities[number];
@@ -62,13 +62,13 @@ const emit = defineEmits<{
 	(ev: 'closed'): void;
 }>();
 
-let v = $ref(props.currentVisibility);
+const v = ref(props.currentVisibility);
 
 function choose(visibility: typeof Misskey.noteVisibilities[number]): void {
-	v = visibility;
+	v.value = visibility;
 	emit('changeVisibility', visibility);
 	nextTick(() => {
-		if (modal) modal.close();
+		if (modal.value) modal.value.close();
 	});
 }
 </script>
diff --git a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
index 26de7dee52..746ed3e0de 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
 import gradient from 'chartjs-plugin-gradient';
 import tinycolor from 'tinycolor2';
@@ -25,11 +25,11 @@ import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
-const chartEl = $shallowRef<HTMLCanvasElement>(null);
+const chartEl = shallowRef<HTMLCanvasElement>(null);
 const now = new Date();
 let chartInstance: Chart = null;
 const chartLimit = 30;
-let fetching = $ref(true);
+const fetching = ref(true);
 
 const { handler: externalTooltipHandler } = useChartTooltip();
 
@@ -65,7 +65,7 @@ async function renderChart() {
 
 	const max = Math.max(...raw.read);
 
-	chartInstance = new Chart(chartEl, {
+	chartInstance = new Chart(chartEl.value, {
 		type: 'bar',
 		data: {
 			datasets: [{
@@ -147,7 +147,7 @@ async function renderChart() {
 		plugins: [chartVLine(vLineColor)],
 	});
 
-	fetching = false;
+	fetching.value = false;
 }
 
 onMounted(async () => {
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 3eb5c19660..7a41720e3f 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import XTimeline from './welcome.timeline.vue';
 import XSigninDialog from '@/components/MkSigninDialog.vue';
@@ -67,15 +67,15 @@ import number from '@/filters/number.js';
 import MkNumber from '@/components/MkNumber.vue';
 import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
 
-let meta = $ref<Misskey.entities.MetaResponse | null>(null);
-let stats = $ref<Misskey.entities.StatsResponse | null>(null);
+const meta = ref<Misskey.entities.MetaResponse | null>(null);
+const stats = ref<Misskey.entities.StatsResponse | null>(null);
 
 os.api('meta', { detail: true }).then(_meta => {
-	meta = _meta;
+	meta.value = _meta;
 });
 
 os.api('stats', {}).then((res) => {
-	stats = res;
+	stats.value = res;
 });
 
 function signin() {
diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue
index ccb8b09b6c..1150a29e03 100644
--- a/packages/frontend/src/components/MkWindow.vue
+++ b/packages/frontend/src/components/MkWindow.vue
@@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onBeforeUnmount, onMounted, provide } from 'vue';
+import { onBeforeUnmount, onMounted, provide, shallowRef, ref } from 'vue';
 import contains from '@/scripts/contains.js';
 import * as os from '@/os.js';
 import { MenuItem } from '@/types/menu';
@@ -107,18 +107,18 @@ const emit = defineEmits<{
 
 provide('inWindow', true);
 
-let rootEl = $shallowRef<HTMLElement | null>();
-let showing = $ref(true);
+const rootEl = shallowRef<HTMLElement | null>();
+const showing = ref(true);
 let beforeClickedAt = 0;
-let maximized = $ref(false);
-let minimized = $ref(false);
+const maximized = ref(false);
+const minimized = ref(false);
 let unResizedTop = '';
 let unResizedLeft = '';
 let unResizedWidth = '';
 let unResizedHeight = '';
 
 function close() {
-	showing = false;
+	showing.value = false;
 }
 
 function onKeydown(evt) {
@@ -137,46 +137,46 @@ function onContextmenu(ev: MouseEvent) {
 
 // 最前面へ移動
 function top() {
-	if (rootEl) {
-		rootEl.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low');
+	if (rootEl.value) {
+		rootEl.value.style.zIndex = os.claimZIndex(props.front ? 'middle' : 'low');
 	}
 }
 
 function maximize() {
-	maximized = true;
-	unResizedTop = rootEl.style.top;
-	unResizedLeft = rootEl.style.left;
-	unResizedWidth = rootEl.style.width;
-	unResizedHeight = rootEl.style.height;
-	rootEl.style.top = '0';
-	rootEl.style.left = '0';
-	rootEl.style.width = '100%';
-	rootEl.style.height = '100%';
+	maximized.value = true;
+	unResizedTop = rootEl.value.style.top;
+	unResizedLeft = rootEl.value.style.left;
+	unResizedWidth = rootEl.value.style.width;
+	unResizedHeight = rootEl.value.style.height;
+	rootEl.value.style.top = '0';
+	rootEl.value.style.left = '0';
+	rootEl.value.style.width = '100%';
+	rootEl.value.style.height = '100%';
 }
 
 function unMaximize() {
-	maximized = false;
-	rootEl.style.top = unResizedTop;
-	rootEl.style.left = unResizedLeft;
-	rootEl.style.width = unResizedWidth;
-	rootEl.style.height = unResizedHeight;
+	maximized.value = false;
+	rootEl.value.style.top = unResizedTop;
+	rootEl.value.style.left = unResizedLeft;
+	rootEl.value.style.width = unResizedWidth;
+	rootEl.value.style.height = unResizedHeight;
 }
 
 function minimize() {
-	minimized = true;
-	unResizedWidth = rootEl.style.width;
-	unResizedHeight = rootEl.style.height;
-	rootEl.style.width = minWidth + 'px';
-	rootEl.style.height = props.mini ? '32px' : '39px';
+	minimized.value = true;
+	unResizedWidth = rootEl.value.style.width;
+	unResizedHeight = rootEl.value.style.height;
+	rootEl.value.style.width = minWidth + 'px';
+	rootEl.value.style.height = props.mini ? '32px' : '39px';
 }
 
 function unMinimize() {
-	const main = rootEl;
+	const main = rootEl.value;
 	if (main == null) return;
 
-	minimized = false;
-	rootEl.style.width = unResizedWidth;
-	rootEl.style.height = unResizedHeight;
+	minimized.value = false;
+	rootEl.value.style.width = unResizedWidth;
+	rootEl.value.style.height = unResizedHeight;
 	const browserWidth = window.innerWidth;
 	const browserHeight = window.innerHeight;
 	const windowWidth = main.offsetWidth;
@@ -192,7 +192,7 @@ function onBodyMousedown() {
 }
 
 function onDblClick() {
-	if (minimized) {
+	if (minimized.value) {
 		unMinimize();
 	} else {
 		maximize();
@@ -205,7 +205,7 @@ function onHeaderMousedown(evt: MouseEvent) {
 
 	let beforeMaximized = false;
 
-	if (maximized) {
+	if (maximized.value) {
 		beforeMaximized = true;
 		unMaximize();
 	}
@@ -219,7 +219,7 @@ function onHeaderMousedown(evt: MouseEvent) {
 
 	beforeClickedAt = Date.now();
 
-	const main = rootEl;
+	const main = rootEl.value;
 	if (main == null) return;
 
 	if (!contains(main, document.activeElement)) main.focus();
@@ -251,8 +251,8 @@ function onHeaderMousedown(evt: MouseEvent) {
 		// 右はみ出し
 		if (moveLeft + windowWidth > browserWidth) moveLeft = browserWidth - windowWidth;
 
-		rootEl.style.left = moveLeft + 'px';
-		rootEl.style.top = moveTop + 'px';
+		rootEl.value.style.left = moveLeft + 'px';
+		rootEl.value.style.top = moveTop + 'px';
 	}
 
 	if (beforeMaximized) {
@@ -270,7 +270,7 @@ function onHeaderMousedown(evt: MouseEvent) {
 
 // 上ハンドル掴み時
 function onTopHandleMousedown(evt) {
-	const main = rootEl;
+	const main = rootEl.value;
 	// どういうわけかnullになることがある
 	if (main == null) return;
 
@@ -298,7 +298,7 @@ function onTopHandleMousedown(evt) {
 
 // 右ハンドル掴み時
 function onRightHandleMousedown(evt) {
-	const main = rootEl;
+	const main = rootEl.value;
 	if (main == null) return;
 
 	const base = evt.clientX;
@@ -323,7 +323,7 @@ function onRightHandleMousedown(evt) {
 
 // 下ハンドル掴み時
 function onBottomHandleMousedown(evt) {
-	const main = rootEl;
+	const main = rootEl.value;
 	if (main == null) return;
 
 	const base = evt.clientY;
@@ -348,7 +348,7 @@ function onBottomHandleMousedown(evt) {
 
 // 左ハンドル掴み時
 function onLeftHandleMousedown(evt) {
-	const main = rootEl;
+	const main = rootEl.value;
 	if (main == null) return;
 
 	const base = evt.clientX;
@@ -400,27 +400,27 @@ function onBottomLeftHandleMousedown(evt) {
 // 高さを適用
 function applyTransformHeight(height) {
 	if (height > window.innerHeight) height = window.innerHeight;
-	rootEl.style.height = height + 'px';
+	rootEl.value.style.height = height + 'px';
 }
 
 // 幅を適用
 function applyTransformWidth(width) {
 	if (width > window.innerWidth) width = window.innerWidth;
-	rootEl.style.width = width + 'px';
+	rootEl.value.style.width = width + 'px';
 }
 
 // Y座標を適用
 function applyTransformTop(top) {
-	rootEl.style.top = top + 'px';
+	rootEl.value.style.top = top + 'px';
 }
 
 // X座標を適用
 function applyTransformLeft(left) {
-	rootEl.style.left = left + 'px';
+	rootEl.value.style.left = left + 'px';
 }
 
 function onBrowserResize() {
-	const main = rootEl;
+	const main = rootEl.value;
 	if (main == null) return;
 
 	const position = main.getBoundingClientRect();
@@ -438,8 +438,8 @@ onMounted(() => {
 	applyTransformWidth(props.initialWidth);
 	if (props.initialHeight) applyTransformHeight(props.initialHeight);
 
-	applyTransformTop((window.innerHeight / 2) - (rootEl.offsetHeight / 2));
-	applyTransformLeft((window.innerWidth / 2) - (rootEl.offsetWidth / 2));
+	applyTransformTop((window.innerHeight / 2) - (rootEl.value.offsetHeight / 2));
+	applyTransformLeft((window.innerWidth / 2) - (rootEl.value.offsetWidth / 2));
 
 	// 他のウィンドウ内のボタンなどを押してこのウィンドウが開かれた場合、親が最前面になろうとするのでそれに隠されないようにする
 	top();
diff --git a/packages/frontend/src/components/MkYouTubePlayer.vue b/packages/frontend/src/components/MkYouTubePlayer.vue
index d74ad0eda4..c6b18aeceb 100644
--- a/packages/frontend/src/components/MkYouTubePlayer.vue
+++ b/packages/frontend/src/components/MkYouTubePlayer.vue
@@ -24,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref } from 'vue';
 import MkWindow from '@/components/MkWindow.vue';
 import { versatileLang } from '@/scripts/intl-const.js';
 import { defaultStore } from '@/store.js';
@@ -35,22 +36,22 @@ const props = defineProps<{
 const requestUrl = new URL(props.url);
 if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url');
 
-let fetching = $ref(true);
-let title = $ref<string | null>(null);
-let player = $ref({
+const fetching = ref(true);
+const title = ref<string | null>(null);
+const player = ref({
 	url: null,
 	width: null,
 	height: null,
 });
 
 const ytFetch = (): void => {
-	fetching = true;
+	fetching.value = true;
 	window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`).then(res => {
 		res.json().then(info => {
 			if (info.url == null) return;
-			title = info.title;
-			fetching = false;
-			player = info.player;
+			title.value = info.title;
+			fetching.value = false;
+			player.value = info.player;
 		});
 	});
 };
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index 2c50511b8b..008d10f8eb 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { computed } from 'vue';
 import * as os from '@/os.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { url } from '@/config.js';
@@ -28,7 +29,7 @@ const props = withDefaults(defineProps<{
 
 const router = useRouter();
 
-const active = $computed(() => {
+const active = computed(() => {
 	if (props.activeClass == null) return false;
 	const resolved = router.resolve(props.to);
 	if (resolved == null) return false;
diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue
index 421fe99127..3ef5db3fe3 100644
--- a/packages/frontend/src/components/global/MkAd.vue
+++ b/packages/frontend/src/components/global/MkAd.vue
@@ -96,7 +96,7 @@ const choseAd = (): Ad | null => {
 };
 
 const chosen = ref(choseAd());
-const shouldHide = $ref(!defaultStore.state.forceShowAds && $i && $i.policies.canHideAds && (props.specify == null));
+const shouldHide = ref(!defaultStore.state.forceShowAds && $i && $i.policies.canHideAds && (props.specify == null));
 
 function reduceFrequency(): void {
 	if (chosen.value == null) return;
diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index 51a454b2cc..c7e50e275a 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkImgWithBlurhash from '../MkImgWithBlurhash.vue';
 import MkA from './MkA.vue';
@@ -47,9 +47,9 @@ import { acct, userPage } from '@/filters/user.js';
 import MkUserOnlineIndicator from '@/components/MkUserOnlineIndicator.vue';
 import { defaultStore } from '@/store.js';
 
-const animation = $ref(defaultStore.state.animation);
-const squareAvatars = $ref(defaultStore.state.squareAvatars);
-const useBlurEffect = $ref(defaultStore.state.useBlurEffect);
+const animation = ref(defaultStore.state.animation);
+const squareAvatars = ref(defaultStore.state.squareAvatars);
+const useBlurEffect = ref(defaultStore.state.useBlurEffect);
 
 const props = withDefaults(defineProps<{
 	user: Misskey.entities.User;
@@ -79,11 +79,11 @@ const emit = defineEmits<{
 
 const showDecoration = props.forceShowDecoration || defaultStore.state.showAvatarDecorations;
 
-const bound = $computed(() => props.link
+const bound = computed(() => props.link
 	? { to: userPage(props.user), target: props.target }
 	: {});
 
-const url = $computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar)
+const url = computed(() => (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar)
 	? getStaticImageUrl(props.user.avatarUrl)
 	: props.user.avatarUrl);
 
@@ -116,10 +116,10 @@ function getDecorationScale() {
 	return scaleX === 1 ? undefined : `${scaleX} 1`;
 }
 
-let color = $ref<string | undefined>();
+const color = ref<string | undefined>();
 
 watch(() => props.user.avatarBlurhash, () => {
-	color = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
+	color.value = extractAvgColorFromBlurhash(props.user.avatarBlurhash);
 }, {
 	immediate: true,
 });
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index 1e17bab849..a092497307 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, inject } from 'vue';
+import { computed, inject, ref } from 'vue';
 import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js';
 import { defaultStore } from '@/store.js';
 import { customEmojisMap } from '@/custom-emojis.js';
@@ -71,7 +71,7 @@ const url = computed(() => {
 });
 
 const alt = computed(() => `:${customEmojiName.value}:`);
-let errored = $ref(url.value == null);
+const errored = ref(url.value == null);
 
 function onClick(ev: MouseEvent) {
 	if (props.menu) {
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 935ca33eb5..301e691fa0 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onUnmounted, ref, inject } from 'vue';
+import { onMounted, onUnmounted, ref, inject, shallowRef, computed } from 'vue';
 import tinycolor from 'tinycolor2';
 import XTabs, { Tab } from './MkPageHeader.tabs.vue';
 import { scrollToTop } from '@/scripts/scroll.js';
@@ -69,13 +69,13 @@ const metadata = injectPageMetadata();
 const hideTitle = inject('shouldOmitHeaderTitle', false);
 const thin_ = props.thin || inject('shouldHeaderThin', false);
 
-let el = $shallowRef<HTMLElement | undefined>(undefined);
+const el = shallowRef<HTMLElement | undefined>(undefined);
 const bg = ref<string | undefined>(undefined);
-let narrow = $ref(false);
-const hasTabs = $computed(() => props.tabs.length > 0);
-const hasActions = $computed(() => props.actions && props.actions.length > 0);
-const show = $computed(() => {
-	return !hideTitle || hasTabs || hasActions;
+const narrow = ref(false);
+const hasTabs = computed(() => props.tabs.length > 0);
+const hasActions = computed(() => props.actions && props.actions.length > 0);
+const show = computed(() => {
+	return !hideTitle || hasTabs.value || hasActions.value;
 });
 
 const preventDrag = (ev: TouchEvent) => {
@@ -83,8 +83,8 @@ const preventDrag = (ev: TouchEvent) => {
 };
 
 const top = () => {
-	if (el) {
-		scrollToTop(el as HTMLElement, { behavior: 'smooth' });
+	if (el.value) {
+		scrollToTop(el.value as HTMLElement, { behavior: 'smooth' });
 	}
 };
 
@@ -111,14 +111,14 @@ onMounted(() => {
 	calcBg();
 	globalEvents.on('themeChanged', calcBg);
 
-	if (el && el.parentElement) {
-		narrow = el.parentElement.offsetWidth < 500;
+	if (el.value && el.value.parentElement) {
+		narrow.value = el.value.parentElement.offsetWidth < 500;
 		ro = new ResizeObserver((entries, observer) => {
-			if (el && el.parentElement && document.body.contains(el as HTMLElement)) {
-				narrow = el.parentElement.offsetWidth < 500;
+			if (el.value && el.value.parentElement && document.body.contains(el.value as HTMLElement)) {
+				narrow.value = el.value.parentElement.offsetWidth < 500;
 			}
 		});
-		ro.observe(el.parentElement as HTMLElement);
+		ro.observe(el.value.parentElement as HTMLElement);
 	}
 });
 
diff --git a/packages/frontend/src/components/global/MkStickyContainer.vue b/packages/frontend/src/components/global/MkStickyContainer.vue
index 8e9bff11d1..1d707af2d1 100644
--- a/packages/frontend/src/components/global/MkStickyContainer.vue
+++ b/packages/frontend/src/components/global/MkStickyContainer.vue
@@ -18,36 +18,36 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onUnmounted, provide, inject, Ref, ref, watch } from 'vue';
-import { $$ } from 'vue/macros';
+import { onMounted, onUnmounted, provide, inject, Ref, ref, watch, shallowRef } from 'vue';
+
 import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const';
 
-const rootEl = $shallowRef<HTMLElement>();
-const headerEl = $shallowRef<HTMLElement>();
-const footerEl = $shallowRef<HTMLElement>();
-const bodyEl = $shallowRef<HTMLElement>();
+const rootEl = shallowRef<HTMLElement>();
+const headerEl = shallowRef<HTMLElement>();
+const footerEl = shallowRef<HTMLElement>();
+const bodyEl = shallowRef<HTMLElement>();
 
-let headerHeight = $ref<string | undefined>();
-let childStickyTop = $ref(0);
+const headerHeight = ref<string | undefined>();
+const childStickyTop = ref(0);
 const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
-provide(CURRENT_STICKY_TOP, $$(childStickyTop));
+provide(CURRENT_STICKY_TOP, childStickyTop);
 
-let footerHeight = $ref<string | undefined>();
-let childStickyBottom = $ref(0);
+const footerHeight = ref<string | undefined>();
+const childStickyBottom = ref(0);
 const parentStickyBottom = inject<Ref<number>>(CURRENT_STICKY_BOTTOM, ref(0));
-provide(CURRENT_STICKY_BOTTOM, $$(childStickyBottom));
+provide(CURRENT_STICKY_BOTTOM, childStickyBottom);
 
 const calc = () => {
 	// コンポーネントが表示されてないけどKeepAliveで残ってる場合などは null になる
-	if (headerEl != null) {
-		childStickyTop = parentStickyTop.value + headerEl.offsetHeight;
-		headerHeight = headerEl.offsetHeight.toString();
+	if (headerEl.value != null) {
+		childStickyTop.value = parentStickyTop.value + headerEl.value.offsetHeight;
+		headerHeight.value = headerEl.value.offsetHeight.toString();
 	}
 
 	// コンポーネントが表示されてないけどKeepAliveで残ってる場合などは null になる
-	if (footerEl != null) {
-		childStickyBottom = parentStickyBottom.value + footerEl.offsetHeight;
-		footerHeight = footerEl.offsetHeight.toString();
+	if (footerEl.value != null) {
+		childStickyBottom.value = parentStickyBottom.value + footerEl.value.offsetHeight;
+		footerHeight.value = footerEl.value.offsetHeight.toString();
 	}
 };
 
@@ -62,28 +62,28 @@ onMounted(() => {
 
 	watch([parentStickyTop, parentStickyBottom], calc);
 
-	watch($$(childStickyTop), () => {
-		bodyEl.style.setProperty('--stickyTop', `${childStickyTop}px`);
+	watch(childStickyTop, () => {
+		bodyEl.value.style.setProperty('--stickyTop', `${childStickyTop.value}px`);
 	}, {
 		immediate: true,
 	});
 
-	watch($$(childStickyBottom), () => {
-		bodyEl.style.setProperty('--stickyBottom', `${childStickyBottom}px`);
+	watch(childStickyBottom, () => {
+		bodyEl.value.style.setProperty('--stickyBottom', `${childStickyBottom.value}px`);
 	}, {
 		immediate: true,
 	});
 
-	headerEl.style.position = 'sticky';
-	headerEl.style.top = 'var(--stickyTop, 0)';
-	headerEl.style.zIndex = '1000';
+	headerEl.value.style.position = 'sticky';
+	headerEl.value.style.top = 'var(--stickyTop, 0)';
+	headerEl.value.style.zIndex = '1000';
 
-	footerEl.style.position = 'sticky';
-	footerEl.style.bottom = 'var(--stickyBottom, 0)';
-	footerEl.style.zIndex = '1000';
+	footerEl.value.style.position = 'sticky';
+	footerEl.value.style.bottom = 'var(--stickyBottom, 0)';
+	footerEl.value.style.zIndex = '1000';
 
-	observer.observe(headerEl);
-	observer.observe(footerEl);
+	observer.observe(headerEl.value);
+	observer.observe(footerEl.value);
 });
 
 onUnmounted(() => {
@@ -91,6 +91,6 @@ onUnmounted(() => {
 });
 
 defineExpose({
-	rootEl: $$(rootEl),
+	rootEl: rootEl,
 });
 </script>
diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue
index 2eeab4d284..e11db9dc31 100644
--- a/packages/frontend/src/components/global/MkTime.vue
+++ b/packages/frontend/src/components/global/MkTime.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import isChromatic from 'chromatic/isChromatic';
-import { onMounted, onUnmounted } from 'vue';
+import { onMounted, onUnmounted, ref, computed } from 'vue';
 import { i18n } from '@/i18n.js';
 import { dateTimeFormat } from '@/scripts/intl-const.js';
 
@@ -47,29 +47,29 @@ const invalid = Number.isNaN(_time);
 const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid;
 
 // eslint-disable-next-line vue/no-setup-props-destructure
-let now = $ref((props.origin ?? new Date()).getTime());
-const ago = $computed(() => (now - _time) / 1000/*ms*/);
+const now = ref((props.origin ?? new Date()).getTime());
+const ago = computed(() => (now.value - _time) / 1000/*ms*/);
 
-const relative = $computed<string>(() => {
+const relative = computed<string>(() => {
 	if (props.mode === 'absolute') return ''; // absoluteではrelativeを使わないので計算しない
 	if (invalid) return i18n.ts._ago.invalid;
 
 	return (
-		ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) :
-		ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) :
-		ago >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago / 604800).toString() }) :
-		ago >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago / 86400).toString() }) :
-		ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) :
-		ago >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) :
-		ago >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) :
-		ago >= -3 ? i18n.ts._ago.justNow :
-		ago < -31536000 ? i18n.t('_timeIn.years', { n: Math.round(-ago / 31536000).toString() }) :
-		ago < -2592000 ? i18n.t('_timeIn.months', { n: Math.round(-ago / 2592000).toString() }) :
-		ago < -604800 ? i18n.t('_timeIn.weeks', { n: Math.round(-ago / 604800).toString() }) :
-		ago < -86400 ? i18n.t('_timeIn.days', { n: Math.round(-ago / 86400).toString() }) :
-		ago < -3600 ? i18n.t('_timeIn.hours', { n: Math.round(-ago / 3600).toString() }) :
-		ago < -60 ? i18n.t('_timeIn.minutes', { n: (~~(-ago / 60)).toString() }) :
-		i18n.t('_timeIn.seconds', { n: (~~(-ago % 60)).toString() })
+		ago.value >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago.value / 31536000).toString() }) :
+		ago.value >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago.value / 2592000).toString() }) :
+		ago.value >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago.value / 604800).toString() }) :
+		ago.value >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago.value / 86400).toString() }) :
+		ago.value >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago.value / 3600).toString() }) :
+		ago.value >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago.value / 60)).toString() }) :
+		ago.value >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago.value % 60)).toString() }) :
+		ago.value >= -3 ? i18n.ts._ago.justNow :
+		ago.value < -31536000 ? i18n.t('_timeIn.years', { n: Math.round(-ago.value / 31536000).toString() }) :
+		ago.value < -2592000 ? i18n.t('_timeIn.months', { n: Math.round(-ago.value / 2592000).toString() }) :
+		ago.value < -604800 ? i18n.t('_timeIn.weeks', { n: Math.round(-ago.value / 604800).toString() }) :
+		ago.value < -86400 ? i18n.t('_timeIn.days', { n: Math.round(-ago.value / 86400).toString() }) :
+		ago.value < -3600 ? i18n.t('_timeIn.hours', { n: Math.round(-ago.value / 3600).toString() }) :
+		ago.value < -60 ? i18n.t('_timeIn.minutes', { n: (~~(-ago.value / 60)).toString() }) :
+		i18n.t('_timeIn.seconds', { n: (~~(-ago.value % 60)).toString() })
 	);
 });
 
@@ -77,8 +77,8 @@ let tickId: number;
 let currentInterval: number;
 
 function tick() {
-	now = (new Date()).getTime();
-	const nextInterval = ago < 60 ? 10000 : ago < 3600 ? 60000 : 180000;
+	now.value = (new Date()).getTime();
+	const nextInterval = ago.value < 60 ? 10000 : ago.value < 3600 ? 60000 : 180000;
 
 	if (currentInterval !== nextInterval) {
 		if (tickId) window.clearInterval(tickId);
diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue
index 99f42f4fcb..9da8f8c379 100644
--- a/packages/frontend/src/components/global/RouterView.vue
+++ b/packages/frontend/src/components/global/RouterView.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { inject, onBeforeUnmount, provide } from 'vue';
+import { inject, onBeforeUnmount, provide, shallowRef, ref } from 'vue';
 import { Resolved, Router } from '@/nirax';
 import { defaultStore } from '@/store.js';
 
@@ -46,16 +46,16 @@ function resolveNested(current: Resolved, d = 0): Resolved | null {
 }
 
 const current = resolveNested(router.current)!;
-let currentPageComponent = $shallowRef(current.route.component);
-let currentPageProps = $ref(current.props);
-let key = $ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
+const currentPageComponent = shallowRef(current.route.component);
+const currentPageProps = ref(current.props);
+const key = ref(current.route.path + JSON.stringify(Object.fromEntries(current.props)));
 
 function onChange({ resolved, key: newKey }) {
 	const current = resolveNested(resolved);
 	if (current == null) return;
-	currentPageComponent = current.route.component;
-	currentPageProps = current.props;
-	key = current.route.path + JSON.stringify(Object.fromEntries(current.props));
+	currentPageComponent.value = current.route.component;
+	currentPageProps.value = current.props;
+	key.value = current.route.path + JSON.stringify(Object.fromEntries(current.props));
 }
 
 router.addListener('change', onChange);
diff --git a/packages/frontend/src/pages/_error_.vue b/packages/frontend/src/pages/_error_.vue
index 4821687ac3..72a12e3c7b 100644
--- a/packages/frontend/src/pages/_error_.vue
+++ b/packages/frontend/src/pages/_error_.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import { version } from '@/config.js';
@@ -42,29 +42,29 @@ const props = withDefaults(defineProps<{
 }>(), {
 });
 
-let loaded = $ref(false);
-let serverIsDead = $ref(false);
-let meta = $ref<Misskey.entities.MetaResponse | null>(null);
+const loaded = ref(false);
+const serverIsDead = ref(false);
+const meta = ref<Misskey.entities.MetaResponse | null>(null);
 
 os.api('meta', {
 	detail: false,
 }).then(res => {
-	loaded = true;
-	serverIsDead = false;
-	meta = res;
+	loaded.value = true;
+	serverIsDead.value = false;
+	meta.value = res;
 	miLocalStorage.setItem('v', res.version);
 }, () => {
-	loaded = true;
-	serverIsDead = true;
+	loaded.value = true;
+	serverIsDead.value = true;
 });
 
 function reload() {
 	unisonReload();
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.error,
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index b106806135..c245b9b6cb 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -123,7 +123,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { nextTick, onBeforeUnmount } from 'vue';
+import { nextTick, onBeforeUnmount, ref, shallowRef, computed } from 'vue';
 import { version } from '@/config.js';
 import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
@@ -310,18 +310,18 @@ const patrons = [
 	'SHO SEKIGUCHI',
 ];
 
-let thereIsTreasure = $ref($i && !claimedAchievements.includes('foundTreasure'));
+const thereIsTreasure = ref($i && !claimedAchievements.includes('foundTreasure'));
 
 let easterEggReady = false;
-let easterEggEmojis = $ref([]);
-let easterEggEngine = $ref(null);
-const containerEl = $shallowRef<HTMLElement>();
+const easterEggEmojis = ref([]);
+const easterEggEngine = ref(null);
+const containerEl = shallowRef<HTMLElement>();
 
 function iconLoaded() {
 	const emojis = defaultStore.state.reactions;
-	const containerWidth = containerEl.offsetWidth;
+	const containerWidth = containerEl.value.offsetWidth;
 	for (let i = 0; i < 32; i++) {
-		easterEggEmojis.push({
+		easterEggEmojis.value.push({
 			id: i.toString(),
 			top: -(128 + (Math.random() * 256)),
 			left: (Math.random() * containerWidth),
@@ -337,7 +337,7 @@ function iconLoaded() {
 function gravity() {
 	if (!easterEggReady) return;
 	easterEggReady = false;
-	easterEggEngine = physics(containerEl);
+	easterEggEngine.value = physics(containerEl.value);
 }
 
 function iLoveMisskey() {
@@ -348,19 +348,19 @@ function iLoveMisskey() {
 }
 
 function getTreasure() {
-	thereIsTreasure = false;
+	thereIsTreasure.value = false;
 	claimAchievement('foundTreasure');
 }
 
 onBeforeUnmount(() => {
-	if (easterEggEngine) {
-		easterEggEngine.stop();
+	if (easterEggEngine.value) {
+		easterEggEngine.value.stop();
 	}
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.aboutMisskey,
diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue
index 4ae460f763..60b515be3c 100644
--- a/packages/frontend/src/pages/about.emojis.vue
+++ b/packages/frontend/src/pages/about.emojis.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import XEmoji from './emojis.emoji.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -47,44 +47,44 @@ import { i18n } from '@/i18n.js';
 import { $i } from '@/account.js';
 
 const customEmojiTags = getCustomEmojiTags();
-let q = $ref('');
-let searchEmojis = $ref<Misskey.entities.EmojiSimple[]>(null);
-let selectedTags = $ref(new Set());
+const q = ref('');
+const searchEmojis = ref<Misskey.entities.EmojiSimple[]>(null);
+const selectedTags = ref(new Set());
 
 function search() {
-	if ((q === '' || q == null) && selectedTags.size === 0) {
-		searchEmojis = null;
+	if ((q.value === '' || q.value == null) && selectedTags.value.size === 0) {
+		searchEmojis.value = null;
 		return;
 	}
 
-	if (selectedTags.size === 0) {
-		const queryarry = q.match(/\:([a-z0-9_]*)\:/g);
+	if (selectedTags.value.size === 0) {
+		const queryarry = q.value.match(/\:([a-z0-9_]*)\:/g);
 
 		if (queryarry) {
-			searchEmojis = customEmojis.value.filter(emoji =>
+			searchEmojis.value = customEmojis.value.filter(emoji =>
 				queryarry.includes(`:${emoji.name}:`),
 			);
 		} else {
-			searchEmojis = customEmojis.value.filter(emoji => emoji.name.includes(q) || emoji.aliases.includes(q));
+			searchEmojis.value = customEmojis.value.filter(emoji => emoji.name.includes(q.value) || emoji.aliases.includes(q.value));
 		}
 	} else {
-		searchEmojis = customEmojis.value.filter(emoji => (emoji.name.includes(q) || emoji.aliases.includes(q)) && [...selectedTags].every(t => emoji.aliases.includes(t)));
+		searchEmojis.value = customEmojis.value.filter(emoji => (emoji.name.includes(q.value) || emoji.aliases.includes(q.value)) && [...selectedTags.value].every(t => emoji.aliases.includes(t)));
 	}
 }
 
 function toggleTag(tag) {
-	if (selectedTags.has(tag)) {
-		selectedTags.delete(tag);
+	if (selectedTags.value.has(tag)) {
+		selectedTags.value.delete(tag);
 	} else {
-		selectedTags.add(tag);
+		selectedTags.value.add(tag);
 	}
 }
 
-watch($$(q), () => {
+watch(q, () => {
 	search();
 });
 
-watch($$(selectedTags), () => {
+watch(selectedTags, () => {
 	search();
 }, { deep: true });
 </script>
diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue
index 47fe9c4279..e01c5f7542 100644
--- a/packages/frontend/src/pages/about.federation.vue
+++ b/packages/frontend/src/pages/about.federation.vue
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
@@ -59,25 +59,25 @@ import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
 import FormSplit from '@/components/form/split.vue';
 import { i18n } from '@/i18n.js';
 
-let host = $ref('');
-let state = $ref('federating');
-let sort = $ref('+pubSub');
+const host = ref('');
+const state = ref('federating');
+const sort = ref('+pubSub');
 const pagination = {
 	endpoint: 'federation/instances' as const,
 	limit: 10,
 	displayLimit: 50,
 	offsetMode: true,
 	params: computed(() => ({
-		sort: sort,
-		host: host !== '' ? host : null,
+		sort: sort.value,
+		host: host.value !== '' ? host.value : null,
 		...(
-			state === 'federating' ? { federating: true } :
-			state === 'subscribing' ? { subscribing: true } :
-			state === 'publishing' ? { publishing: true } :
-			state === 'suspended' ? { suspended: true } :
-			state === 'blocked' ? { blocked: true } :
-			state === 'silenced' ? { silenced: true } :
-			state === 'notResponding' ? { notResponding: true } :
+			state.value === 'federating' ? { federating: true } :
+			state.value === 'subscribing' ? { subscribing: true } :
+			state.value === 'publishing' ? { publishing: true } :
+			state.value === 'suspended' ? { suspended: true } :
+			state.value === 'blocked' ? { blocked: true } :
+			state.value === 'silenced' ? { silenced: true } :
+			state.value === 'notResponding' ? { notResponding: true } :
 			{}),
 	})),
 } as Paging;
diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue
index 4fa409ff4b..f463caecb6 100644
--- a/packages/frontend/src/pages/about.vue
+++ b/packages/frontend/src/pages/about.vue
@@ -102,7 +102,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref } from 'vue';
 import XEmojis from './about.emojis.vue';
 import XFederation from './about.federation.vue';
 import { version, host } from '@/config.js';
@@ -126,23 +126,23 @@ const props = withDefaults(defineProps<{
 	initialTab: 'overview',
 });
 
-let stats = $ref(null);
-let tab = $ref(props.initialTab);
+const stats = ref(null);
+const tab = ref(props.initialTab);
 
-watch($$(tab), () => {
-	if (tab === 'charts') {
+watch(tab, () => {
+	if (tab.value === 'charts') {
 		claimAchievement('viewInstanceChart');
 	}
 });
 
 const initStats = () => os.api('stats', {
 }).then((res) => {
-	stats = res;
+	stats.value = res;
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'overview',
 	title: i18n.ts.overview,
 }, {
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue
index 4ce0f2936c..aefff69c60 100644
--- a/packages/frontend/src/pages/admin-file.vue
+++ b/packages/frontend/src/pages/admin-file.vue
@@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkObjectView from '@/components/MkObjectView.vue';
@@ -82,19 +82,19 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { iAmAdmin, iAmModerator } from '@/account.js';
 
-let tab = $ref('overview');
-let file: any = $ref(null);
-let info: any = $ref(null);
-let isSensitive: boolean = $ref(false);
+const tab = ref('overview');
+const file = ref<any>(null);
+const info = ref<any>(null);
+const isSensitive = ref<boolean>(false);
 
 const props = defineProps<{
 	fileId: string,
 }>();
 
 async function fetch() {
-	file = await os.api('drive/files/show', { fileId: props.fileId });
-	info = await os.api('admin/drive/show-file', { fileId: props.fileId });
-	isSensitive = file.isSensitive;
+	file.value = await os.api('drive/files/show', { fileId: props.fileId });
+	info.value = await os.api('admin/drive/show-file', { fileId: props.fileId });
+	isSensitive.value = file.value.isSensitive;
 }
 
 fetch();
@@ -102,29 +102,29 @@ fetch();
 async function del() {
 	const { canceled } = await os.confirm({
 		type: 'warning',
-		text: i18n.t('removeAreYouSure', { x: file.name }),
+		text: i18n.t('removeAreYouSure', { x: file.value.name }),
 	});
 	if (canceled) return;
 
 	os.apiWithDialog('drive/files/delete', {
-		fileId: file.id,
+		fileId: file.value.id,
 	});
 }
 
 async function toggleIsSensitive(v) {
 	await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v });
-	isSensitive = v;
+	isSensitive.value = v;
 }
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	text: i18n.ts.openInNewTab,
 	icon: 'ti ti-external-link',
 	handler: () => {
-		window.open(file.url, '_blank');
+		window.open(file.value.url, '_blank');
 	},
 }]);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'overview',
 	title: i18n.ts.overview,
 	icon: 'ti ti-info-circle',
@@ -139,7 +139,7 @@ const headerTabs = $computed(() => [{
 }]);
 
 definePageMetadata(computed(() => ({
-	title: file ? i18n.ts.file + ': ' + file.name : i18n.ts.file,
+	title: file.value ? i18n.ts.file + ': ' + file.value.name : i18n.ts.file,
 	icon: 'ti ti-file',
 })));
 </script>
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 87ebedc296..fd839b4369 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -203,7 +203,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, defineAsyncComponent, watch } from 'vue';
+import { computed, defineAsyncComponent, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkChart from '@/components/MkChart.vue';
 import MkObjectView from '@/components/MkObjectView.vue';
@@ -234,17 +234,17 @@ const props = withDefaults(defineProps<{
 	initialTab: 'overview',
 });
 
-let tab = $ref(props.initialTab);
-let chartSrc = $ref('per-user-notes');
-let user = $ref<null | Misskey.entities.UserDetailed>();
-let init = $ref<ReturnType<typeof createFetcher>>();
-let info = $ref();
-let ips = $ref(null);
-let ap = $ref(null);
-let moderator = $ref(false);
-let silenced = $ref(false);
-let suspended = $ref(false);
-let moderationNote = $ref('');
+const tab = ref(props.initialTab);
+const chartSrc = ref('per-user-notes');
+const user = ref<null | Misskey.entities.UserDetailed>();
+const init = ref<ReturnType<typeof createFetcher>>();
+const info = ref();
+const ips = ref(null);
+const ap = ref(null);
+const moderator = ref(false);
+const silenced = ref(false);
+const suspended = ref(false);
+const moderationNote = ref('');
 const filesPagination = {
 	endpoint: 'admin/drive/files' as const,
 	limit: 10,
@@ -259,7 +259,7 @@ const announcementsPagination = {
 		userId: props.userId,
 	})),
 };
-let expandedRoles = $ref([]);
+const expandedRoles = ref([]);
 
 function createFetcher() {
 	return () => Promise.all([os.api('users/show', {
@@ -269,27 +269,27 @@ function createFetcher() {
 	}), iAmAdmin ? os.api('admin/get-user-ips', {
 		userId: props.userId,
 	}) : Promise.resolve(null)]).then(([_user, _info, _ips]) => {
-		user = _user;
-		info = _info;
-		ips = _ips;
-		moderator = info.isModerator;
-		silenced = info.isSilenced;
-		suspended = info.isSuspended;
-		moderationNote = info.moderationNote;
+		user.value = _user;
+		info.value = _info;
+		ips.value = _ips;
+		moderator.value = info.value.isModerator;
+		silenced.value = info.value.isSilenced;
+		suspended.value = info.value.isSuspended;
+		moderationNote.value = info.value.moderationNote;
 
-		watch($$(moderationNote), async () => {
-			await os.api('admin/update-user-note', { userId: user.id, text: moderationNote });
+		watch(moderationNote, async () => {
+			await os.api('admin/update-user-note', { userId: user.value.id, text: moderationNote.value });
 			await refreshUser();
 		});
 	});
 }
 
 function refreshUser() {
-	init = createFetcher();
+	init.value = createFetcher();
 }
 
 async function updateRemoteUser() {
-	await os.apiWithDialog('federation/update-remote-user', { userId: user.id });
+	await os.apiWithDialog('federation/update-remote-user', { userId: user.value.id });
 	refreshUser();
 }
 
@@ -302,7 +302,7 @@ async function resetPassword() {
 		return;
 	} else {
 		const { password } = await os.api('admin/reset-password', {
-			userId: user.id,
+			userId: user.value.id,
 		});
 		os.alert({
 			type: 'success',
@@ -317,9 +317,9 @@ async function toggleSuspend(v) {
 		text: v ? i18n.ts.suspendConfirm : i18n.ts.unsuspendConfirm,
 	});
 	if (confirm.canceled) {
-		suspended = !v;
+		suspended.value = !v;
 	} else {
-		await os.api(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: user.id });
+		await os.api(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: user.value.id });
 		await refreshUser();
 	}
 }
@@ -331,7 +331,7 @@ async function unsetUserAvatar() {
   });
   if (confirm.canceled) return;
   const process = async () => {
-    await os.api('admin/unset-user-avatar', { userId: user.id });
+    await os.api('admin/unset-user-avatar', { userId: user.value.id });
     os.success();
   };
   await process().catch(err => {
@@ -350,7 +350,7 @@ async function unsetUserBanner() {
   });
   if (confirm.canceled) return;
   const process = async () => {
-    await os.api('admin/unset-user-banner', { userId: user.id });
+    await os.api('admin/unset-user-banner', { userId: user.value.id });
     os.success();
   };
   await process().catch(err => {
@@ -369,7 +369,7 @@ async function deleteAllFiles() {
 	});
 	if (confirm.canceled) return;
 	const process = async () => {
-		await os.api('admin/delete-all-files-of-a-user', { userId: user.id });
+		await os.api('admin/delete-all-files-of-a-user', { userId: user.value.id });
 		os.success();
 	};
 	await process().catch(err => {
@@ -389,13 +389,13 @@ async function deleteAccount() {
 	if (confirm.canceled) return;
 
 	const typed = await os.inputText({
-		text: i18n.t('typeToConfirm', { x: user?.username }),
+		text: i18n.t('typeToConfirm', { x: user.value?.username }),
 	});
 	if (typed.canceled) return;
 
-	if (typed.result === user?.username) {
+	if (typed.result === user.value?.username) {
 		await os.apiWithDialog('admin/delete-account', {
-			userId: user.id,
+			userId: user.value.id,
 		});
 	} else {
 		os.alert({
@@ -438,7 +438,7 @@ async function assignRole() {
 		: period === 'oneMonth' ? Date.now() + (1000 * 60 * 60 * 24 * 30)
 		: null;
 
-	await os.apiWithDialog('admin/roles/assign', { roleId, userId: user.id, expiresAt });
+	await os.apiWithDialog('admin/roles/assign', { roleId, userId: user.value.id, expiresAt });
 	refreshUser();
 }
 
@@ -448,50 +448,50 @@ async function unassignRole(role, ev) {
 		icon: 'ti ti-x',
 		danger: true,
 		action: async () => {
-			await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: user.id });
+			await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: user.value.id });
 			refreshUser();
 		},
 	}], ev.currentTarget ?? ev.target);
 }
 
 function toggleRoleItem(role) {
-	if (expandedRoles.includes(role.id)) {
-		expandedRoles = expandedRoles.filter(x => x !== role.id);
+	if (expandedRoles.value.includes(role.id)) {
+		expandedRoles.value = expandedRoles.value.filter(x => x !== role.id);
 	} else {
-		expandedRoles.push(role.id);
+		expandedRoles.value.push(role.id);
 	}
 }
 
 function createAnnouncement() {
 	os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
-		user,
+		user: user.value,
 	}, {}, 'closed');
 }
 
 function editAnnouncement(announcement) {
 	os.popup(defineAsyncComponent(() => import('@/components/MkUserAnnouncementEditDialog.vue')), {
-		user,
+		user: user.value,
 		announcement,
 	}, {}, 'closed');
 }
 
 watch(() => props.userId, () => {
-	init = createFetcher();
+	init.value = createFetcher();
 }, {
 	immediate: true,
 });
 
-watch($$(user), () => {
+watch(user, () => {
 	os.api('ap/get', {
-		uri: user.uri ?? `${url}/users/${user.id}`,
+		uri: user.value.uri ?? `${url}/users/${user.value.id}`,
 	}).then(res => {
-		ap = res;
+		ap.value = res;
 	});
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'overview',
 	title: i18n.ts.overview,
 	icon: 'ti ti-info-circle',
@@ -518,7 +518,7 @@ const headerTabs = $computed(() => [{
 }]);
 
 definePageMetadata(computed(() => ({
-	title: user ? acct(user) : i18n.ts.userInfo,
+	title: user.value ? acct(user.value) : i18n.ts.userInfo,
 	icon: 'ti ti-user-exclamation',
 })));
 </script>
diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue
index 503fb2af9c..03f28b5219 100644
--- a/packages/frontend/src/pages/admin/_header_.vue
+++ b/packages/frontend/src/pages/admin/_header_.vue
@@ -69,7 +69,7 @@ const metadata = injectPageMetadata();
 
 const el = shallowRef<HTMLElement>(null);
 const tabRefs = {};
-const tabHighlightEl = $shallowRef<HTMLElement | null>(null);
+const tabHighlightEl = shallowRef<HTMLElement | null>(null);
 const bg = ref(null);
 const height = ref(0);
 const hasTabs = computed(() => {
@@ -131,13 +131,13 @@ onMounted(() => {
 	watch(() => [props.tab, props.tabs], () => {
 		nextTick(() => {
 			const tabEl = tabRefs[props.tab];
-			if (tabEl && tabHighlightEl) {
+			if (tabEl && tabHighlightEl.value) {
 				// offsetWidth や offsetLeft は少数を丸めてしまうため getBoundingClientRect を使う必要がある
 				// https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/offsetWidth#%E5%80%A4
 				const parentRect = tabEl.parentElement.getBoundingClientRect();
 				const rect = tabEl.getBoundingClientRect();
-				tabHighlightEl.style.width = rect.width + 'px';
-				tabHighlightEl.style.left = (rect.left - parentRect.left) + 'px';
+				tabHighlightEl.value.style.width = rect.width + 'px';
+				tabHighlightEl.value.style.left = (rect.left - parentRect.left) + 'px';
 			}
 		});
 	}, {
diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue
index 875702ee7e..3613189548 100644
--- a/packages/frontend/src/pages/admin/abuses.vue
+++ b/packages/frontend/src/pages/admin/abuses.vue
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, shallowRef, ref } from 'vue';
 
 import XHeader from './_header_.vue';
 import MkSelect from '@/components/MkSelect.vue';
@@ -61,31 +61,31 @@ import XAbuseReport from '@/components/MkAbuseReport.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let reports = $shallowRef<InstanceType<typeof MkPagination>>();
+const reports = shallowRef<InstanceType<typeof MkPagination>>();
 
-let state = $ref('unresolved');
-let reporterOrigin = $ref('combined');
-let targetUserOrigin = $ref('combined');
-let searchUsername = $ref('');
-let searchHost = $ref('');
+const state = ref('unresolved');
+const reporterOrigin = ref('combined');
+const targetUserOrigin = ref('combined');
+const searchUsername = ref('');
+const searchHost = ref('');
 
 const pagination = {
 	endpoint: 'admin/abuse-user-reports' as const,
 	limit: 10,
 	params: computed(() => ({
-		state,
-		reporterOrigin,
-		targetUserOrigin,
+		state: state.value,
+		reporterOrigin: reporterOrigin.value,
+		targetUserOrigin: targetUserOrigin.value,
 	})),
 };
 
 function resolved(reportId) {
-	reports.removeItem(reportId);
+	reports.value.removeItem(reportId);
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.abuseReports,
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 1c15e32552..f0cf786556 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -85,7 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -98,7 +98,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let ads: any[] = $ref([]);
+const ads = ref<any[]>([]);
 
 // ISO形式はTZがUTCになってしまうので、TZ分ずらして時間を初期化
 const localTime = new Date();
@@ -109,7 +109,7 @@ let publishing: boolean | null = null;
 
 os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
 	if (adsResponse != null) {
-		ads = adsResponse.map(r => {
+		ads.value = adsResponse.map(r => {
 			const exdate = new Date(r.expiresAt);
 			const stdate = new Date(r.startsAt);
 			exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
@@ -141,7 +141,7 @@ function toggleDayOfWeek(ad, index) {
 }
 
 function add() {
-	ads.unshift({
+	ads.value.unshift({
 		id: null,
 		memo: '',
 		place: 'square',
@@ -161,7 +161,7 @@ function remove(ad) {
 		text: i18n.t('removeAreYouSure', { x: ad.url }),
 	}).then(({ canceled }) => {
 		if (canceled) return;
-		ads = ads.filter(x => x !== ad);
+		ads.value = ads.value.filter(x => x !== ad);
 		if (ad.id == null) return;
 		os.apiWithDialog('admin/ad/delete', {
 			id: ad.id,
@@ -209,9 +209,9 @@ function save(ad) {
 }
 
 function more() {
-	os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
+	os.api('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
 		if (adsResponse == null) return;
-		ads = ads.concat(adsResponse.map(r => {
+		ads.value = ads.value.concat(adsResponse.map(r => {
 			const exdate = new Date(r.expiresAt);
 			const stdate = new Date(r.startsAt);
 			exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
@@ -228,7 +228,7 @@ function more() {
 function refresh() {
 	os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
 		if (adsResponse == null) return;
-		ads = adsResponse.map(r => {
+		ads.value = adsResponse.map(r => {
 			const exdate = new Date(r.expiresAt);
 			const stdate = new Date(r.startsAt);
 			exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
@@ -244,14 +244,14 @@ function refresh() {
 
 refresh();
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	asFullButton: true,
 	icon: 'ti ti-plus',
 	text: i18n.ts.add,
 	handler: add,
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.ads,
diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue
index 5785fb118c..92070dc6c6 100644
--- a/packages/frontend/src/pages/admin/announcements.vue
+++ b/packages/frontend/src/pages/admin/announcements.vue
@@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -84,14 +84,14 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkFolder from '@/components/MkFolder.vue';
 
-let announcements: any[] = $ref([]);
+const announcements = ref<any[]>([]);
 
 os.api('admin/announcements/list').then(announcementResponse => {
-	announcements = announcementResponse;
+	announcements.value = announcementResponse;
 });
 
 function add() {
-	announcements.unshift({
+	announcements.value.unshift({
 		_id: Math.random().toString(36),
 		id: null,
 		title: 'New announcement',
@@ -111,7 +111,7 @@ function del(announcement) {
 		text: i18n.t('deleteAreYouSure', { x: announcement.title }),
 	}).then(({ canceled }) => {
 		if (canceled) return;
-		announcements = announcements.filter(x => x !== announcement);
+		announcements.value = announcements.value.filter(x => x !== announcement);
 		os.api('admin/announcements/delete', announcement);
 	});
 }
@@ -134,27 +134,27 @@ async function save(announcement) {
 }
 
 function more() {
-	os.api('admin/announcements/list', { untilId: announcements.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => {
-		announcements = announcements.concat(announcementResponse);
+	os.api('admin/announcements/list', { untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => {
+		announcements.value = announcements.value.concat(announcementResponse);
 	});
 }
 
 function refresh() {
 	os.api('admin/announcements/list').then(announcementResponse => {
-		announcements = announcementResponse;
+		announcements.value = announcementResponse;
 	});
 }
 
 refresh();
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	asFullButton: true,
 	icon: 'ti ti-plus',
 	text: i18n.ts.add,
 	handler: add,
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.announcements,
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index 7f5709feb9..367ae38b5a 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, ref } from 'vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -76,37 +76,37 @@ import { i18n } from '@/i18n.js';
 
 const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
 
-let provider = $ref(null);
-let hcaptchaSiteKey: string | null = $ref(null);
-let hcaptchaSecretKey: string | null = $ref(null);
-let recaptchaSiteKey: string | null = $ref(null);
-let recaptchaSecretKey: string | null = $ref(null);
-let turnstileSiteKey: string | null = $ref(null);
-let turnstileSecretKey: string | null = $ref(null);
+const provider = ref(null);
+const hcaptchaSiteKey = ref<string | null>(null);
+const hcaptchaSecretKey = ref<string | null>(null);
+const recaptchaSiteKey = ref<string | null>(null);
+const recaptchaSecretKey = ref<string | null>(null);
+const turnstileSiteKey = ref<string | null>(null);
+const turnstileSecretKey = ref<string | null>(null);
 
 async function init() {
 	const meta = await os.api('admin/meta');
-	hcaptchaSiteKey = meta.hcaptchaSiteKey;
-	hcaptchaSecretKey = meta.hcaptchaSecretKey;
-	recaptchaSiteKey = meta.recaptchaSiteKey;
-	recaptchaSecretKey = meta.recaptchaSecretKey;
-	turnstileSiteKey = meta.turnstileSiteKey;
-	turnstileSecretKey = meta.turnstileSecretKey;
+	hcaptchaSiteKey.value = meta.hcaptchaSiteKey;
+	hcaptchaSecretKey.value = meta.hcaptchaSecretKey;
+	recaptchaSiteKey.value = meta.recaptchaSiteKey;
+	recaptchaSecretKey.value = meta.recaptchaSecretKey;
+	turnstileSiteKey.value = meta.turnstileSiteKey;
+	turnstileSecretKey.value = meta.turnstileSecretKey;
 
-	provider = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : meta.enableTurnstile ? 'turnstile' : null;
+	provider.value = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : meta.enableTurnstile ? 'turnstile' : null;
 }
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		enableHcaptcha: provider === 'hcaptcha',
-		hcaptchaSiteKey,
-		hcaptchaSecretKey,
-		enableRecaptcha: provider === 'recaptcha',
-		recaptchaSiteKey,
-		recaptchaSecretKey,
-		enableTurnstile: provider === 'turnstile',
-		turnstileSiteKey,
-		turnstileSecretKey,
+		enableHcaptcha: provider.value === 'hcaptcha',
+		hcaptchaSiteKey: hcaptchaSiteKey.value,
+		hcaptchaSecretKey: hcaptchaSecretKey.value,
+		enableRecaptcha: provider.value === 'recaptcha',
+		recaptchaSiteKey: recaptchaSiteKey.value,
+		recaptchaSecretKey: recaptchaSecretKey.value,
+		enableTurnstile: provider.value === 'turnstile',
+		turnstileSiteKey: turnstileSiteKey.value,
+		turnstileSecretKey: turnstileSecretKey.value,
 	}).then(() => {
 		fetchInstance();
 	});
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index c6c71d46cf..28109cfd2d 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -94,7 +94,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import JSON5 from 'json5';
 import XHeader from './_header_.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
@@ -111,55 +111,55 @@ import MkButton from '@/components/MkButton.vue';
 import MkColorInput from '@/components/MkColorInput.vue';
 import { host } from '@/config.js';
 
-let iconUrl: string | null = $ref(null);
-let app192IconUrl: string | null = $ref(null);
-let app512IconUrl: string | null = $ref(null);
-let bannerUrl: string | null = $ref(null);
-let backgroundImageUrl: string | null = $ref(null);
-let themeColor: any = $ref(null);
-let defaultLightTheme: any = $ref(null);
-let defaultDarkTheme: any = $ref(null);
-let serverErrorImageUrl: string | null = $ref(null);
-let infoImageUrl: string | null = $ref(null);
-let notFoundImageUrl: string | null = $ref(null);
-let manifestJsonOverride: string = $ref('{}');
+const iconUrl = ref<string | null>(null);
+const app192IconUrl = ref<string | null>(null);
+const app512IconUrl = ref<string | null>(null);
+const bannerUrl = ref<string | null>(null);
+const backgroundImageUrl = ref<string | null>(null);
+const themeColor = ref<any>(null);
+const defaultLightTheme = ref<any>(null);
+const defaultDarkTheme = ref<any>(null);
+const serverErrorImageUrl = ref<string | null>(null);
+const infoImageUrl = ref<string | null>(null);
+const notFoundImageUrl = ref<string | null>(null);
+const manifestJsonOverride = ref<string>('{}');
 
 async function init() {
 	const meta = await os.api('admin/meta');
-	iconUrl = meta.iconUrl;
-	app192IconUrl = meta.app192IconUrl;
-	app512IconUrl = meta.app512IconUrl;
-	bannerUrl = meta.bannerUrl;
-	backgroundImageUrl = meta.backgroundImageUrl;
-	themeColor = meta.themeColor;
-	defaultLightTheme = meta.defaultLightTheme;
-	defaultDarkTheme = meta.defaultDarkTheme;
-	serverErrorImageUrl = meta.serverErrorImageUrl;
-	infoImageUrl = meta.infoImageUrl;
-	notFoundImageUrl = meta.notFoundImageUrl;
-	manifestJsonOverride = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
+	iconUrl.value = meta.iconUrl;
+	app192IconUrl.value = meta.app192IconUrl;
+	app512IconUrl.value = meta.app512IconUrl;
+	bannerUrl.value = meta.bannerUrl;
+	backgroundImageUrl.value = meta.backgroundImageUrl;
+	themeColor.value = meta.themeColor;
+	defaultLightTheme.value = meta.defaultLightTheme;
+	defaultDarkTheme.value = meta.defaultDarkTheme;
+	serverErrorImageUrl.value = meta.serverErrorImageUrl;
+	infoImageUrl.value = meta.infoImageUrl;
+	notFoundImageUrl.value = meta.notFoundImageUrl;
+	manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t');
 }
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		iconUrl,
-		app192IconUrl,
-		app512IconUrl,
-		bannerUrl,
-		backgroundImageUrl,
-		themeColor: themeColor === '' ? null : themeColor,
-		defaultLightTheme: defaultLightTheme === '' ? null : defaultLightTheme,
-		defaultDarkTheme: defaultDarkTheme === '' ? null : defaultDarkTheme,
-		infoImageUrl,
-		notFoundImageUrl,
-		serverErrorImageUrl,
-		manifestJsonOverride: manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride)),
+		iconUrl: iconUrl.value,
+		app192IconUrl: app192IconUrl.value,
+		app512IconUrl: app512IconUrl.value,
+		bannerUrl: bannerUrl.value,
+		backgroundImageUrl: backgroundImageUrl.value,
+		themeColor: themeColor.value === '' ? null : themeColor.value,
+		defaultLightTheme: defaultLightTheme.value === '' ? null : defaultLightTheme.value,
+		defaultDarkTheme: defaultDarkTheme.value === '' ? null : defaultDarkTheme.value,
+		infoImageUrl: infoImageUrl.value,
+		notFoundImageUrl: notFoundImageUrl.value,
+		serverErrorImageUrl: serverErrorImageUrl.value,
+		manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)),
 	}).then(() => {
 		fetchInstance();
 	});
 }
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.branding,
diff --git a/packages/frontend/src/pages/admin/database.vue b/packages/frontend/src/pages/admin/database.vue
index bba03deb4b..53f556bb64 100644
--- a/packages/frontend/src/pages/admin/database.vue
+++ b/packages/frontend/src/pages/admin/database.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { computed } from 'vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import * as os from '@/os.js';
@@ -29,9 +29,9 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size));
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.database,
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index 32294847bb..c93a19387c 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -78,23 +78,23 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 
-let enableEmail: boolean = $ref(false);
-let email: any = $ref(null);
-let smtpSecure: boolean = $ref(false);
-let smtpHost: string = $ref('');
-let smtpPort: number = $ref(0);
-let smtpUser: string = $ref('');
-let smtpPass: string = $ref('');
+const enableEmail = ref<boolean>(false);
+const email = ref<any>(null);
+const smtpSecure = ref<boolean>(false);
+const smtpHost = ref<string>('');
+const smtpPort = ref<number>(0);
+const smtpUser = ref<string>('');
+const smtpPass = ref<string>('');
 
 async function init() {
 	const meta = await os.api('admin/meta');
-	enableEmail = meta.enableEmail;
-	email = meta.email;
-	smtpSecure = meta.smtpSecure;
-	smtpHost = meta.smtpHost;
-	smtpPort = meta.smtpPort;
-	smtpUser = meta.smtpUser;
-	smtpPass = meta.smtpPass;
+	enableEmail.value = meta.enableEmail;
+	email.value = meta.email;
+	smtpSecure.value = meta.smtpSecure;
+	smtpHost.value = meta.smtpHost;
+	smtpPort.value = meta.smtpPort;
+	smtpUser.value = meta.smtpUser;
+	smtpPass.value = meta.smtpPass;
 }
 
 async function testEmail() {
@@ -115,19 +115,19 @@ async function testEmail() {
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		enableEmail,
-		email,
-		smtpSecure,
-		smtpHost,
-		smtpPort,
-		smtpUser,
-		smtpPass,
+		enableEmail: enableEmail.value,
+		email: email.value,
+		smtpSecure: smtpSecure.value,
+		smtpHost: smtpHost.value,
+		smtpPort: smtpPort.value,
+		smtpUser: smtpUser.value,
+		smtpPass: smtpPass.value,
 	}).then(() => {
 		fetchInstance();
 	});
 }
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.emailServer,
diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue
index e614bfeb1b..22dc115fda 100644
--- a/packages/frontend/src/pages/admin/external-services.vue
+++ b/packages/frontend/src/pages/admin/external-services.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -46,27 +46,27 @@ import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let deeplAuthKey: string = $ref('');
-let deeplIsPro: boolean = $ref(false);
+const deeplAuthKey = ref<string>('');
+const deeplIsPro = ref<boolean>(false);
 
 async function init() {
 	const meta = await os.api('admin/meta');
-	deeplAuthKey = meta.deeplAuthKey;
-	deeplIsPro = meta.deeplIsPro;
+	deeplAuthKey.value = meta.deeplAuthKey;
+	deeplIsPro.value = meta.deeplIsPro;
 }
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		deeplAuthKey,
-		deeplIsPro,
+		deeplAuthKey: deeplAuthKey.value,
+		deeplIsPro: deeplIsPro.value,
 	}).then(() => {
 		fetchInstance();
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.externalServices,
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index 41849894ea..bfe9a8c570 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
@@ -68,24 +68,24 @@ import FormSplit from '@/components/form/split.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let host = $ref('');
-let state = $ref('federating');
-let sort = $ref('+pubSub');
+const host = ref('');
+const state = ref('federating');
+const sort = ref('+pubSub');
 const pagination = {
 	endpoint: 'federation/instances' as const,
 	limit: 10,
 	offsetMode: true,
 	params: computed(() => ({
-		sort: sort,
-		host: host !== '' ? host : null,
+		sort: sort.value,
+		host: host.value !== '' ? host.value : null,
 		...(
-			state === 'federating' ? { federating: true } :
-			state === 'subscribing' ? { subscribing: true } :
-			state === 'publishing' ? { publishing: true } :
-			state === 'suspended' ? { suspended: true } :
-			state === 'blocked' ? { blocked: true } :
-			state === 'silenced' ? { silenced: true } :
-			state === 'notResponding' ? { notResponding: true } :
+			state.value === 'federating' ? { federating: true } :
+			state.value === 'subscribing' ? { subscribing: true } :
+			state.value === 'publishing' ? { publishing: true } :
+			state.value === 'suspended' ? { suspended: true } :
+			state.value === 'blocked' ? { blocked: true } :
+			state.value === 'silenced' ? { silenced: true } :
+			state.value === 'notResponding' ? { notResponding: true } :
 			{}),
 	})),
 };
@@ -98,9 +98,9 @@ function getStatus(instance) {
 	return 'Alive';
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(computed(() => ({
 	title: i18n.ts.federation,
diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue
index 6fb6ef40f9..9d26925993 100644
--- a/packages/frontend/src/pages/admin/files.vue
+++ b/packages/frontend/src/pages/admin/files.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
@@ -45,19 +45,19 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let origin = $ref('local');
-let type = $ref(null);
-let searchHost = $ref('');
-let userId = $ref('');
-let viewMode = $ref('grid');
+const origin = ref('local');
+const type = ref(null);
+const searchHost = ref('');
+const userId = ref('');
+const viewMode = ref('grid');
 const pagination = {
 	endpoint: 'admin/drive/files' as const,
 	limit: 10,
 	params: computed(() => ({
-		type: (type && type !== '') ? type : null,
-		userId: (userId && userId !== '') ? userId : null,
-		origin: origin,
-		hostname: (searchHost && searchHost !== '') ? searchHost : null,
+		type: (type.value && type.value !== '') ? type.value : null,
+		userId: (userId.value && userId.value !== '') ? userId.value : null,
+		origin: origin.value,
+		hostname: (searchHost.value && searchHost.value !== '') ? searchHost.value : null,
 	})),
 };
 
@@ -95,7 +95,7 @@ async function find() {
 	});
 }
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	text: i18n.ts.lookup,
 	icon: 'ti ti-search',
 	handler: find,
@@ -105,7 +105,7 @@ const headerActions = $computed(() => [{
 	handler: clear,
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(computed(() => ({
 	title: i18n.ts.files,
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index cc0cdf7466..414889125c 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onActivated, onMounted, onUnmounted, provide, watch } from 'vue';
+import { onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue';
 import { i18n } from '@/i18n.js';
 import MkSuperMenu from '@/components/MkSuperMenu.vue';
 import MkInfo from '@/components/MkInfo.vue';
@@ -50,32 +50,32 @@ const indexInfo = {
 
 provide('shouldOmitHeaderTitle', false);
 
-let INFO = $ref(indexInfo);
-let childInfo = $ref(null);
-let narrow = $ref(false);
-let view = $ref(null);
-let el = $ref(null);
-let pageProps = $ref({});
+const INFO = ref(indexInfo);
+const childInfo = ref(null);
+const narrow = ref(false);
+const view = ref(null);
+const el = ref(null);
+const pageProps = ref({});
 let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
 let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile;
 let noEmailServer = !instance.enableEmail;
-let thereIsUnresolvedAbuseReport = $ref(false);
-let currentPage = $computed(() => router.currentRef.value.child);
+const thereIsUnresolvedAbuseReport = ref(false);
+const currentPage = computed(() => router.currentRef.value.child);
 
 os.api('admin/abuse-user-reports', {
 	state: 'unresolved',
 	limit: 1,
 }).then(reports => {
-	if (reports.length > 0) thereIsUnresolvedAbuseReport = true;
+	if (reports.length > 0) thereIsUnresolvedAbuseReport.value = true;
 });
 
 const NARROW_THRESHOLD = 600;
 const ro = new ResizeObserver((entries, observer) => {
 	if (entries.length === 0) return;
-	narrow = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
+	narrow.value = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
 });
 
-const menuDef = $computed(() => [{
+const menuDef = computed(() => [{
 	title: i18n.ts.quickAction,
 	items: [{
 		type: 'button',
@@ -94,67 +94,67 @@ const menuDef = $computed(() => [{
 		icon: 'ti ti-dashboard',
 		text: i18n.ts.dashboard,
 		to: '/admin/overview',
-		active: currentPage?.route.name === 'overview',
+		active: currentPage.value?.route.name === 'overview',
 	}, {
 		icon: 'ti ti-users',
 		text: i18n.ts.users,
 		to: '/admin/users',
-		active: currentPage?.route.name === 'users',
+		active: currentPage.value?.route.name === 'users',
 	}, {
 		icon: 'ti ti-user-plus',
 		text: i18n.ts.invite,
 		to: '/admin/invites',
-		active: currentPage?.route.name === 'invites',
+		active: currentPage.value?.route.name === 'invites',
 	}, {
 		icon: 'ti ti-badges',
 		text: i18n.ts.roles,
 		to: '/admin/roles',
-		active: currentPage?.route.name === 'roles',
+		active: currentPage.value?.route.name === 'roles',
 	}, {
 		icon: 'ti ti-icons',
 		text: i18n.ts.customEmojis,
 		to: '/admin/emojis',
-		active: currentPage?.route.name === 'emojis',
+		active: currentPage.value?.route.name === 'emojis',
 	}, {
 		icon: 'ti ti-sparkles',
 		text: i18n.ts.avatarDecorations,
 		to: '/admin/avatar-decorations',
-		active: currentPage?.route.name === 'avatarDecorations',
+		active: currentPage.value?.route.name === 'avatarDecorations',
 	}, {
 		icon: 'ti ti-whirl',
 		text: i18n.ts.federation,
 		to: '/admin/federation',
-		active: currentPage?.route.name === 'federation',
+		active: currentPage.value?.route.name === 'federation',
 	}, {
 		icon: 'ti ti-clock-play',
 		text: i18n.ts.jobQueue,
 		to: '/admin/queue',
-		active: currentPage?.route.name === 'queue',
+		active: currentPage.value?.route.name === 'queue',
 	}, {
 		icon: 'ti ti-cloud',
 		text: i18n.ts.files,
 		to: '/admin/files',
-		active: currentPage?.route.name === 'files',
+		active: currentPage.value?.route.name === 'files',
 	}, {
 		icon: 'ti ti-speakerphone',
 		text: i18n.ts.announcements,
 		to: '/admin/announcements',
-		active: currentPage?.route.name === 'announcements',
+		active: currentPage.value?.route.name === 'announcements',
 	}, {
 		icon: 'ti ti-ad',
 		text: i18n.ts.ads,
 		to: '/admin/ads',
-		active: currentPage?.route.name === 'ads',
+		active: currentPage.value?.route.name === 'ads',
 	}, {
 		icon: 'ti ti-exclamation-circle',
 		text: i18n.ts.abuseReports,
 		to: '/admin/abuses',
-		active: currentPage?.route.name === 'abuses',
+		active: currentPage.value?.route.name === 'abuses',
 	}, {
 		icon: 'ti ti-list-search',
 		text: i18n.ts.moderationLogs,
 		to: '/admin/modlog',
-		active: currentPage?.route.name === 'modlog',
+		active: currentPage.value?.route.name === 'modlog',
 	}],
 }, {
 	title: i18n.ts.settings,
@@ -162,57 +162,57 @@ const menuDef = $computed(() => [{
 		icon: 'ti ti-settings',
 		text: i18n.ts.general,
 		to: '/admin/settings',
-		active: currentPage?.route.name === 'settings',
+		active: currentPage.value?.route.name === 'settings',
 	}, {
 		icon: 'ti ti-paint',
 		text: i18n.ts.branding,
 		to: '/admin/branding',
-		active: currentPage?.route.name === 'branding',
+		active: currentPage.value?.route.name === 'branding',
 	}, {
 		icon: 'ti ti-shield',
 		text: i18n.ts.moderation,
 		to: '/admin/moderation',
-		active: currentPage?.route.name === 'moderation',
+		active: currentPage.value?.route.name === 'moderation',
 	}, {
 		icon: 'ti ti-mail',
 		text: i18n.ts.emailServer,
 		to: '/admin/email-settings',
-		active: currentPage?.route.name === 'email-settings',
+		active: currentPage.value?.route.name === 'email-settings',
 	}, {
 		icon: 'ti ti-cloud',
 		text: i18n.ts.objectStorage,
 		to: '/admin/object-storage',
-		active: currentPage?.route.name === 'object-storage',
+		active: currentPage.value?.route.name === 'object-storage',
 	}, {
 		icon: 'ti ti-lock',
 		text: i18n.ts.security,
 		to: '/admin/security',
-		active: currentPage?.route.name === 'security',
+		active: currentPage.value?.route.name === 'security',
 	}, {
 		icon: 'ti ti-planet',
 		text: i18n.ts.relays,
 		to: '/admin/relays',
-		active: currentPage?.route.name === 'relays',
+		active: currentPage.value?.route.name === 'relays',
 	}, {
 		icon: 'ti ti-ban',
 		text: i18n.ts.instanceBlocking,
 		to: '/admin/instance-block',
-		active: currentPage?.route.name === 'instance-block',
+		active: currentPage.value?.route.name === 'instance-block',
 	}, {
 		icon: 'ti ti-ghost',
 		text: i18n.ts.proxyAccount,
 		to: '/admin/proxy-account',
-		active: currentPage?.route.name === 'proxy-account',
+		active: currentPage.value?.route.name === 'proxy-account',
 	}, {
 		icon: 'ti ti-link',
 		text: i18n.ts.externalServices,
 		to: '/admin/external-services',
-		active: currentPage?.route.name === 'external-services',
+		active: currentPage.value?.route.name === 'external-services',
 	}, {
 		icon: 'ti ti-adjustments',
 		text: i18n.ts.other,
 		to: '/admin/other-settings',
-		active: currentPage?.route.name === 'other-settings',
+		active: currentPage.value?.route.name === 'other-settings',
 	}],
 }, {
 	title: i18n.ts.info,
@@ -220,28 +220,28 @@ const menuDef = $computed(() => [{
 		icon: 'ti ti-database',
 		text: i18n.ts.database,
 		to: '/admin/database',
-		active: currentPage?.route.name === 'database',
+		active: currentPage.value?.route.name === 'database',
 	}],
 }]);
 
-watch(narrow, () => {
-	if (currentPage?.route.name == null && !narrow) {
+watch(narrow.value, () => {
+	if (currentPage.value?.route.name == null && !narrow.value) {
 		router.push('/admin/overview');
 	}
 });
 
 onMounted(() => {
-	ro.observe(el);
+	ro.observe(el.value);
 
-	narrow = el.offsetWidth < NARROW_THRESHOLD;
-	if (currentPage?.route.name == null && !narrow) {
+	narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
+	if (currentPage.value?.route.name == null && !narrow.value) {
 		router.push('/admin/overview');
 	}
 });
 
 onActivated(() => {
-	narrow = el.offsetWidth < NARROW_THRESHOLD;
-	if (currentPage?.route.name == null && !narrow) {
+	narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
+	if (currentPage.value?.route.name == null && !narrow.value) {
 		router.push('/admin/overview');
 	}
 });
@@ -251,16 +251,16 @@ onUnmounted(() => {
 });
 
 watch(router.currentRef, (to) => {
-	if (to.route.path === '/admin' && to.child?.route.name == null && !narrow) {
+	if (to.route.path === '/admin' && to.child?.route.name == null && !narrow.value) {
 		router.replace('/admin/overview');
 	}
 });
 
 provideMetadataReceiver((info) => {
 	if (info == null) {
-		childInfo = null;
+		childInfo.value = null;
 	} else {
-		childInfo = info;
+		childInfo.value = info;
 	}
 });
 
@@ -312,11 +312,11 @@ function lookup(ev: MouseEvent) {
 	}], ev.currentTarget ?? ev.target);
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(INFO);
+definePageMetadata(INFO.value);
 
 defineExpose({
 	header: {
diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue
index 259354b3d0..356eca2af6 100644
--- a/packages/frontend/src/pages/admin/instance-block.vue
+++ b/packages/frontend/src/pages/admin/instance-block.vue
@@ -23,6 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
@@ -32,29 +33,29 @@ import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let blockedHosts: string = $ref('');
-let silencedHosts: string = $ref('');
-let tab = $ref('block');
+const blockedHosts = ref<string>('');
+const silencedHosts = ref<string>('');
+const tab = ref('block');
 
 async function init() {
 	const meta = await os.api('admin/meta');
-	blockedHosts = meta.blockedHosts.join('\n');
-	silencedHosts = meta.silencedHosts.join('\n');
+	blockedHosts.value = meta.blockedHosts.join('\n');
+	silencedHosts.value = meta.silencedHosts.join('\n');
 }
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		blockedHosts: blockedHosts.split('\n') || [],
-		silencedHosts: silencedHosts.split('\n') || [],
+		blockedHosts: blockedHosts.value.split('\n') || [],
+		silencedHosts: silencedHosts.value.split('\n') || [],
 
 	}).then(() => {
 		fetchInstance();
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'block',
 	title: i18n.ts.block,
 	icon: 'ti ti-ban',
diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue
index 74dc2e4c36..838ef52b14 100644
--- a/packages/frontend/src/pages/admin/invites.vue
+++ b/packages/frontend/src/pages/admin/invites.vue
@@ -70,8 +70,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
 
-let type = ref('all');
-let sort = ref('+createdAt');
+const type = ref('all');
+const sort = ref('+createdAt');
 
 const pagination: Paging = {
 	endpoint: 'admin/invite/list' as const,
@@ -109,8 +109,8 @@ function deleted(id: string) {
 	}
 }
 
-const headerActions = $computed(() => []);
-const headerTabs = $computed(() => []);
+const headerActions = computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.invite,
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index 47f46fe6cf..a64dad3164 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -59,7 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -74,40 +74,40 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 import FormLink from '@/components/form/link.vue';
 
-let enableRegistration: boolean = $ref(false);
-let emailRequiredForSignup: boolean = $ref(false);
-let sensitiveWords: string = $ref('');
-let hiddenTags: string = $ref('');
-let preservedUsernames: string = $ref('');
-let tosUrl: string | null = $ref(null);
-let privacyPolicyUrl: string | null = $ref(null);
+const enableRegistration = ref<boolean>(false);
+const emailRequiredForSignup = ref<boolean>(false);
+const sensitiveWords = ref<string>('');
+const hiddenTags = ref<string>('');
+const preservedUsernames = ref<string>('');
+const tosUrl = ref<string | null>(null);
+const privacyPolicyUrl = ref<string | null>(null);
 
 async function init() {
 	const meta = await os.api('admin/meta');
-	enableRegistration = !meta.disableRegistration;
-	emailRequiredForSignup = meta.emailRequiredForSignup;
-	sensitiveWords = meta.sensitiveWords.join('\n');
-	hiddenTags = meta.hiddenTags.join('\n');
-	preservedUsernames = meta.preservedUsernames.join('\n');
-	tosUrl = meta.tosUrl;
-	privacyPolicyUrl = meta.privacyPolicyUrl;
+	enableRegistration.value = !meta.disableRegistration;
+	emailRequiredForSignup.value = meta.emailRequiredForSignup;
+	sensitiveWords.value = meta.sensitiveWords.join('\n');
+	hiddenTags.value = meta.hiddenTags.join('\n');
+	preservedUsernames.value = meta.preservedUsernames.join('\n');
+	tosUrl.value = meta.tosUrl;
+	privacyPolicyUrl.value = meta.privacyPolicyUrl;
 }
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		disableRegistration: !enableRegistration,
-		emailRequiredForSignup,
-		tosUrl,
-		privacyPolicyUrl,
-		sensitiveWords: sensitiveWords.split('\n'),
-		hiddenTags: hiddenTags.split('\n'),
-		preservedUsernames: preservedUsernames.split('\n'),
+		disableRegistration: !enableRegistration.value,
+		emailRequiredForSignup: emailRequiredForSignup.value,
+		tosUrl: tosUrl.value,
+		privacyPolicyUrl: privacyPolicyUrl.value,
+		sensitiveWords: sensitiveWords.value.split('\n'),
+		hiddenTags: hiddenTags.value.split('\n'),
+		preservedUsernames: preservedUsernames.value.split('\n'),
 	}).then(() => {
 		fetchInstance();
 	});
 }
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.moderation,
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
index 4b5ef5f771..7daf9acc29 100644
--- a/packages/frontend/src/pages/admin/modlog.vue
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, shallowRef, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import XHeader from './_header_.vue';
 import XModLog from './modlog.ModLog.vue';
@@ -40,25 +40,25 @@ import MkPagination from '@/components/MkPagination.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let logs = $shallowRef<InstanceType<typeof MkPagination>>();
+const logs = shallowRef<InstanceType<typeof MkPagination>>();
 
-let type = $ref(null);
-let moderatorId = $ref('');
+const type = ref(null);
+const moderatorId = ref('');
 
 const pagination = {
 	endpoint: 'admin/show-moderation-logs' as const,
 	limit: 30,
 	params: computed(() => ({
-		type,
-		userId: moderatorId === '' ? null : moderatorId,
+		type: type.value,
+		userId: moderatorId.value === '' ? null : moderatorId.value,
 	})),
 };
 
 console.log(Misskey);
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.moderationLogs,
diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue
index 8d27c31068..7019971e90 100644
--- a/packages/frontend/src/pages/admin/object-storage.vue
+++ b/packages/frontend/src/pages/admin/object-storage.vue
@@ -83,7 +83,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -95,58 +95,58 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 
-let useObjectStorage: boolean = $ref(false);
-let objectStorageBaseUrl: string | null = $ref(null);
-let objectStorageBucket: string | null = $ref(null);
-let objectStoragePrefix: string | null = $ref(null);
-let objectStorageEndpoint: string | null = $ref(null);
-let objectStorageRegion: string | null = $ref(null);
-let objectStoragePort: number | null = $ref(null);
-let objectStorageAccessKey: string | null = $ref(null);
-let objectStorageSecretKey: string | null = $ref(null);
-let objectStorageUseSSL: boolean = $ref(false);
-let objectStorageUseProxy: boolean = $ref(false);
-let objectStorageSetPublicRead: boolean = $ref(false);
-let objectStorageS3ForcePathStyle: boolean = $ref(true);
+const useObjectStorage = ref<boolean>(false);
+const objectStorageBaseUrl = ref<string | null>(null);
+const objectStorageBucket = ref<string | null>(null);
+const objectStoragePrefix = ref<string | null>(null);
+const objectStorageEndpoint = ref<string | null>(null);
+const objectStorageRegion = ref<string | null>(null);
+const objectStoragePort = ref<number | null>(null);
+const objectStorageAccessKey = ref<string | null>(null);
+const objectStorageSecretKey = ref<string | null>(null);
+const objectStorageUseSSL = ref<boolean>(false);
+const objectStorageUseProxy = ref<boolean>(false);
+const objectStorageSetPublicRead = ref<boolean>(false);
+const objectStorageS3ForcePathStyle = ref<boolean>(true);
 
 async function init() {
 	const meta = await os.api('admin/meta');
-	useObjectStorage = meta.useObjectStorage;
-	objectStorageBaseUrl = meta.objectStorageBaseUrl;
-	objectStorageBucket = meta.objectStorageBucket;
-	objectStoragePrefix = meta.objectStoragePrefix;
-	objectStorageEndpoint = meta.objectStorageEndpoint;
-	objectStorageRegion = meta.objectStorageRegion;
-	objectStoragePort = meta.objectStoragePort;
-	objectStorageAccessKey = meta.objectStorageAccessKey;
-	objectStorageSecretKey = meta.objectStorageSecretKey;
-	objectStorageUseSSL = meta.objectStorageUseSSL;
-	objectStorageUseProxy = meta.objectStorageUseProxy;
-	objectStorageSetPublicRead = meta.objectStorageSetPublicRead;
-	objectStorageS3ForcePathStyle = meta.objectStorageS3ForcePathStyle;
+	useObjectStorage.value = meta.useObjectStorage;
+	objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
+	objectStorageBucket.value = meta.objectStorageBucket;
+	objectStoragePrefix.value = meta.objectStoragePrefix;
+	objectStorageEndpoint.value = meta.objectStorageEndpoint;
+	objectStorageRegion.value = meta.objectStorageRegion;
+	objectStoragePort.value = meta.objectStoragePort;
+	objectStorageAccessKey.value = meta.objectStorageAccessKey;
+	objectStorageSecretKey.value = meta.objectStorageSecretKey;
+	objectStorageUseSSL.value = meta.objectStorageUseSSL;
+	objectStorageUseProxy.value = meta.objectStorageUseProxy;
+	objectStorageSetPublicRead.value = meta.objectStorageSetPublicRead;
+	objectStorageS3ForcePathStyle.value = meta.objectStorageS3ForcePathStyle;
 }
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		useObjectStorage,
-		objectStorageBaseUrl,
-		objectStorageBucket,
-		objectStoragePrefix,
-		objectStorageEndpoint,
-		objectStorageRegion,
-		objectStoragePort,
-		objectStorageAccessKey,
-		objectStorageSecretKey,
-		objectStorageUseSSL,
-		objectStorageUseProxy,
-		objectStorageSetPublicRead,
-		objectStorageS3ForcePathStyle,
+		useObjectStorage: useObjectStorage.value,
+		objectStorageBaseUrl: objectStorageBaseUrl.value,
+		objectStorageBucket: objectStorageBucket.value,
+		objectStoragePrefix: objectStoragePrefix.value,
+		objectStorageEndpoint: objectStorageEndpoint.value,
+		objectStorageRegion: objectStorageRegion.value,
+		objectStoragePort: objectStoragePort.value,
+		objectStorageAccessKey: objectStorageAccessKey.value,
+		objectStorageSecretKey: objectStorageSecretKey.value,
+		objectStorageUseSSL: objectStorageUseSSL.value,
+		objectStorageUseProxy: objectStorageUseProxy.value,
+		objectStorageSetPublicRead: objectStorageSetPublicRead.value,
+		objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value,
 	}).then(() => {
 		fetchInstance();
 	});
 }
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.objectStorage,
diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue
index 7574c9d7d9..5bb328ac92 100644
--- a/packages/frontend/src/pages/admin/other-settings.vue
+++ b/packages/frontend/src/pages/admin/other-settings.vue
@@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os.js';
@@ -52,38 +52,38 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkSwitch from '@/components/MkSwitch.vue';
 
-let enableServerMachineStats: boolean = $ref(false);
-let enableIdenticonGeneration: boolean = $ref(false);
-let enableChartsForRemoteUser: boolean = $ref(false);
-let enableChartsForFederatedInstances: boolean = $ref(false);
+const enableServerMachineStats = ref<boolean>(false);
+const enableIdenticonGeneration = ref<boolean>(false);
+const enableChartsForRemoteUser = ref<boolean>(false);
+const enableChartsForFederatedInstances = ref<boolean>(false);
 
 async function init() {
 	const meta = await os.api('admin/meta');
-	enableServerMachineStats = meta.enableServerMachineStats;
-	enableIdenticonGeneration = meta.enableIdenticonGeneration;
-	enableChartsForRemoteUser = meta.enableChartsForRemoteUser;
-	enableChartsForFederatedInstances = meta.enableChartsForFederatedInstances;
+	enableServerMachineStats.value = meta.enableServerMachineStats;
+	enableIdenticonGeneration.value = meta.enableIdenticonGeneration;
+	enableChartsForRemoteUser.value = meta.enableChartsForRemoteUser;
+	enableChartsForFederatedInstances.value = meta.enableChartsForFederatedInstances;
 }
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		enableServerMachineStats,
-		enableIdenticonGeneration,
-		enableChartsForRemoteUser,
-		enableChartsForFederatedInstances,
+		enableServerMachineStats: enableServerMachineStats.value,
+		enableIdenticonGeneration: enableIdenticonGeneration.value,
+		enableChartsForRemoteUser: enableChartsForRemoteUser.value,
+		enableChartsForFederatedInstances: enableChartsForFederatedInstances.value,
 	}).then(() => {
 		fetchInstance();
 	});
 }
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	asFullButton: true,
 	icon: 'ti ti-check',
 	text: i18n.ts.save,
 	handler: save,
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.other,
diff --git a/packages/frontend/src/pages/admin/overview.active-users.vue b/packages/frontend/src/pages/admin/overview.active-users.vue
index 8426c463d2..5e67370c2b 100644
--- a/packages/frontend/src/pages/admin/overview.active-users.vue
+++ b/packages/frontend/src/pages/admin/overview.active-users.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
 import gradient from 'chartjs-plugin-gradient';
 import * as os from '@/os.js';
@@ -24,11 +24,11 @@ import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
-const chartEl = $shallowRef<HTMLCanvasElement>(null);
+const chartEl = shallowRef<HTMLCanvasElement>(null);
 const now = new Date();
 let chartInstance: Chart = null;
 const chartLimit = 7;
-let fetching = $ref(true);
+const fetching = ref(true);
 
 const { handler: externalTooltipHandler } = useChartTooltip();
 
@@ -61,7 +61,7 @@ async function renderChart() {
 
 	const max = Math.max(...raw.read);
 
-	chartInstance = new Chart(chartEl, {
+	chartInstance = new Chart(chartEl.value, {
 		type: 'bar',
 		data: {
 			datasets: [{
@@ -155,7 +155,7 @@ async function renderChart() {
 		plugins: [chartVLine(vLineColor)],
 	});
 
-	fetching = false;
+	fetching.value = false;
 }
 
 onMounted(async () => {
diff --git a/packages/frontend/src/pages/admin/overview.ap-requests.vue b/packages/frontend/src/pages/admin/overview.ap-requests.vue
index cd54041c34..0de62fadea 100644
--- a/packages/frontend/src/pages/admin/overview.ap-requests.vue
+++ b/packages/frontend/src/pages/admin/overview.ap-requests.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
 import gradient from 'chartjs-plugin-gradient';
 import * as os from '@/os.js';
@@ -33,9 +33,9 @@ import { initChart } from '@/scripts/init-chart.js';
 initChart();
 
 const chartLimit = 50;
-const chartEl = $shallowRef<HTMLCanvasElement>();
-const chartEl2 = $shallowRef<HTMLCanvasElement>();
-let fetching = $ref(true);
+const chartEl = shallowRef<HTMLCanvasElement>();
+const chartEl2 = shallowRef<HTMLCanvasElement>();
+const fetching = ref(true);
 
 const { handler: externalTooltipHandler } = useChartTooltip();
 const { handler: externalTooltipHandler2 } = useChartTooltip();
@@ -74,7 +74,7 @@ onMounted(async () => {
 	const succMax = Math.max(...raw.deliverSucceeded);
 	const failMax = Math.max(...raw.deliverFailed);
 
-	new Chart(chartEl, {
+	new Chart(chartEl.value, {
 		type: 'line',
 		data: {
 			datasets: [{
@@ -178,7 +178,7 @@ onMounted(async () => {
 		plugins: [chartVLine(vLineColor)],
 	});
 
-	new Chart(chartEl2, {
+	new Chart(chartEl2.value, {
 		type: 'bar',
 		data: {
 			datasets: [{
@@ -265,7 +265,7 @@ onMounted(async () => {
 		plugins: [chartVLine(vLineColor)],
 	});
 
-	fetching = false;
+	fetching.value = false;
 });
 </script>
 
diff --git a/packages/frontend/src/pages/admin/overview.federation.vue b/packages/frontend/src/pages/admin/overview.federation.vue
index 346a9c0258..033fc9ad85 100644
--- a/packages/frontend/src/pages/admin/overview.federation.vue
+++ b/packages/frontend/src/pages/admin/overview.federation.vue
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import XPie from './overview.pie.vue';
 import * as os from '@/os.js';
 import number from '@/filters/number.js';
@@ -54,25 +54,25 @@ import MkNumberDiff from '@/components/MkNumberDiff.vue';
 import { i18n } from '@/i18n.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 
-let topSubInstancesForPie: any = $ref(null);
-let topPubInstancesForPie: any = $ref(null);
-let federationPubActive = $ref<number | null>(null);
-let federationPubActiveDiff = $ref<number | null>(null);
-let federationSubActive = $ref<number | null>(null);
-let federationSubActiveDiff = $ref<number | null>(null);
-let fetching = $ref(true);
+const topSubInstancesForPie = ref<any>(null);
+const topPubInstancesForPie = ref<any>(null);
+const federationPubActive = ref<number | null>(null);
+const federationPubActiveDiff = ref<number | null>(null);
+const federationSubActive = ref<number | null>(null);
+const federationSubActiveDiff = ref<number | null>(null);
+const fetching = ref(true);
 
 const { handler: externalTooltipHandler } = useChartTooltip();
 
 onMounted(async () => {
 	const chart = await os.apiGet('charts/federation', { limit: 2, span: 'day' });
-	federationPubActive = chart.pubActive[0];
-	federationPubActiveDiff = chart.pubActive[0] - chart.pubActive[1];
-	federationSubActive = chart.subActive[0];
-	federationSubActiveDiff = chart.subActive[0] - chart.subActive[1];
+	federationPubActive.value = chart.pubActive[0];
+	federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
+	federationSubActive.value = chart.subActive[0];
+	federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
 
 	os.apiGet('federation/stats', { limit: 10 }).then(res => {
-		topSubInstancesForPie = res.topSubInstances.map(x => ({
+		topSubInstancesForPie.value = res.topSubInstances.map(x => ({
 			name: x.host,
 			color: x.themeColor,
 			value: x.followersCount,
@@ -80,7 +80,7 @@ onMounted(async () => {
 				os.pageWindow(`/instance-info/${x.host}`);
 			},
 		})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowersCount }]);
-		topPubInstancesForPie = res.topPubInstances.map(x => ({
+		topPubInstancesForPie.value = res.topPubInstances.map(x => ({
 			name: x.host,
 			color: x.themeColor,
 			value: x.followingCount,
@@ -90,7 +90,7 @@ onMounted(async () => {
 		})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowingCount }]);
 	});
 
-	fetching = false;
+	fetching.value = false;
 });
 </script>
 
diff --git a/packages/frontend/src/pages/admin/overview.heatmap.vue b/packages/frontend/src/pages/admin/overview.heatmap.vue
index 4d09f183bf..8e3c809353 100644
--- a/packages/frontend/src/pages/admin/overview.heatmap.vue
+++ b/packages/frontend/src/pages/admin/overview.heatmap.vue
@@ -17,10 +17,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref } from 'vue';
 import MkHeatmap from '@/components/MkHeatmap.vue';
 import MkSelect from '@/components/MkSelect.vue';
 
-let src = $ref('active-users');
+const src = ref('active-users');
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/admin/overview.moderators.vue b/packages/frontend/src/pages/admin/overview.moderators.vue
index 4086ca51f0..c6e81b4a18 100644
--- a/packages/frontend/src/pages/admin/overview.moderators.vue
+++ b/packages/frontend/src/pages/admin/overview.moderators.vue
@@ -17,21 +17,21 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import * as os from '@/os.js';
 import { defaultStore } from '@/store.js';
 
-let moderators: any = $ref(null);
-let fetching = $ref(true);
+const moderators = ref<any>(null);
+const fetching = ref(true);
 
 onMounted(async () => {
-	moderators = await os.api('admin/show-users', {
+	moderators.value = await os.api('admin/show-users', {
 		sort: '+lastActiveDate',
 		state: 'adminOrModerator',
 		limit: 30,
 	});
 
-	fetching = false;
+	fetching.value = false;
 });
 </script>
 
diff --git a/packages/frontend/src/pages/admin/overview.queue.vue b/packages/frontend/src/pages/admin/overview.queue.vue
index 1af9d89f62..b6b3bf194a 100644
--- a/packages/frontend/src/pages/admin/overview.queue.vue
+++ b/packages/frontend/src/pages/admin/overview.queue.vue
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { markRaw, onMounted, onUnmounted, ref } from 'vue';
+import { markRaw, onMounted, onUnmounted, ref, shallowRef } from 'vue';
 import XChart from './overview.queue.chart.vue';
 import number from '@/filters/number.js';
 import { useStream } from '@/stream.js';
@@ -46,10 +46,10 @@ const activeSincePrevTick = ref(0);
 const active = ref(0);
 const delayed = ref(0);
 const waiting = ref(0);
-let chartProcess = $shallowRef<InstanceType<typeof XChart>>();
-let chartActive = $shallowRef<InstanceType<typeof XChart>>();
-let chartDelayed = $shallowRef<InstanceType<typeof XChart>>();
-let chartWaiting = $shallowRef<InstanceType<typeof XChart>>();
+const chartProcess = shallowRef<InstanceType<typeof XChart>>();
+const chartActive = shallowRef<InstanceType<typeof XChart>>();
+const chartDelayed = shallowRef<InstanceType<typeof XChart>>();
+const chartWaiting = shallowRef<InstanceType<typeof XChart>>();
 
 const props = defineProps<{
 	domain: string;
@@ -61,10 +61,10 @@ const onStats = (stats) => {
 	delayed.value = stats[props.domain].delayed;
 	waiting.value = stats[props.domain].waiting;
 
-	chartProcess.pushData(stats[props.domain].activeSincePrevTick);
-	chartActive.pushData(stats[props.domain].active);
-	chartDelayed.pushData(stats[props.domain].delayed);
-	chartWaiting.pushData(stats[props.domain].waiting);
+	chartProcess.value.pushData(stats[props.domain].activeSincePrevTick);
+	chartActive.value.pushData(stats[props.domain].active);
+	chartDelayed.value.pushData(stats[props.domain].delayed);
+	chartWaiting.value.pushData(stats[props.domain].waiting);
 };
 
 const onStatsLog = (statsLog) => {
@@ -80,10 +80,10 @@ const onStatsLog = (statsLog) => {
 		dataWaiting.push(stats[props.domain].waiting);
 	}
 
-	chartProcess.setData(dataProcess);
-	chartActive.setData(dataActive);
-	chartDelayed.setData(dataDelayed);
-	chartWaiting.setData(dataWaiting);
+	chartProcess.value.setData(dataProcess);
+	chartActive.value.setData(dataActive);
+	chartDelayed.value.setData(dataDelayed);
+	chartWaiting.value.setData(dataWaiting);
 };
 
 onMounted(() => {
diff --git a/packages/frontend/src/pages/admin/overview.stats.vue b/packages/frontend/src/pages/admin/overview.stats.vue
index 5899177efa..ea8cb164cd 100644
--- a/packages/frontend/src/pages/admin/overview.stats.vue
+++ b/packages/frontend/src/pages/admin/overview.stats.vue
@@ -61,7 +61,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import * as os from '@/os.js';
 import MkNumberDiff from '@/components/MkNumberDiff.vue';
 import MkNumber from '@/components/MkNumber.vue';
@@ -69,29 +69,29 @@ import { i18n } from '@/i18n.js';
 import { customEmojis } from '@/custom-emojis.js';
 import { defaultStore } from '@/store.js';
 
-let stats: any = $ref(null);
-let usersComparedToThePrevDay = $ref<number>();
-let notesComparedToThePrevDay = $ref<number>();
-let onlineUsersCount = $ref(0);
-let fetching = $ref(true);
+const stats = ref<any>(null);
+const usersComparedToThePrevDay = ref<number>();
+const notesComparedToThePrevDay = ref<number>();
+const onlineUsersCount = ref(0);
+const fetching = ref(true);
 
 onMounted(async () => {
 	const [_stats, _onlineUsersCount] = await Promise.all([
 		os.api('stats', {}),
 		os.apiGet('get-online-users-count').then(res => res.count),
 	]);
-	stats = _stats;
-	onlineUsersCount = _onlineUsersCount;
+	stats.value = _stats;
+	onlineUsersCount.value = _onlineUsersCount;
 
 	os.apiGet('charts/users', { limit: 2, span: 'day' }).then(chart => {
-		usersComparedToThePrevDay = stats.originalUsersCount - chart.local.total[1];
+		usersComparedToThePrevDay.value = stats.value.originalUsersCount - chart.local.total[1];
 	});
 
 	os.apiGet('charts/notes', { limit: 2, span: 'day' }).then(chart => {
-		notesComparedToThePrevDay = stats.originalNotesCount - chart.local.total[1];
+		notesComparedToThePrevDay.value = stats.value.originalNotesCount - chart.local.total[1];
 	});
 
-	fetching = false;
+	fetching.value = false;
 });
 </script>
 
diff --git a/packages/frontend/src/pages/admin/overview.users.vue b/packages/frontend/src/pages/admin/overview.users.vue
index 6ee83c51e7..6b8dd90747 100644
--- a/packages/frontend/src/pages/admin/overview.users.vue
+++ b/packages/frontend/src/pages/admin/overview.users.vue
@@ -17,13 +17,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref } from 'vue';
 import * as os from '@/os.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import { defaultStore } from '@/store.js';
 
-let newUsers = $ref(null);
-let fetching = $ref(true);
+const newUsers = ref(null);
+const fetching = ref(true);
 
 const fetch = async () => {
 	const _newUsers = await os.api('admin/show-users', {
@@ -31,8 +32,8 @@ const fetch = async () => {
 		sort: '+createdAt',
 		origin: 'local',
 	});
-	newUsers = _newUsers;
-	fetching = false;
+	newUsers.value = _newUsers;
+	fetching.value = false;
 };
 
 useInterval(fetch, 1000 * 60, {
diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue
index 170dc0d212..8b7cad004f 100644
--- a/packages/frontend/src/pages/admin/overview.vue
+++ b/packages/frontend/src/pages/admin/overview.vue
@@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { markRaw, onMounted, onBeforeUnmount, nextTick } from 'vue';
+import { markRaw, onMounted, onBeforeUnmount, nextTick, shallowRef, ref, computed } from 'vue';
 import XFederation from './overview.federation.vue';
 import XInstances from './overview.instances.vue';
 import XQueue from './overview.queue.vue';
@@ -82,16 +82,16 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 
-const rootEl = $shallowRef<HTMLElement>();
-let serverInfo: any = $ref(null);
-let topSubInstancesForPie: any = $ref(null);
-let topPubInstancesForPie: any = $ref(null);
-let federationPubActive = $ref<number | null>(null);
-let federationPubActiveDiff = $ref<number | null>(null);
-let federationSubActive = $ref<number | null>(null);
-let federationSubActiveDiff = $ref<number | null>(null);
-let newUsers = $ref(null);
-let activeInstances = $shallowRef(null);
+const rootEl = shallowRef<HTMLElement>();
+const serverInfo = ref<any>(null);
+const topSubInstancesForPie = ref<any>(null);
+const topPubInstancesForPie = ref<any>(null);
+const federationPubActive = ref<number | null>(null);
+const federationPubActiveDiff = ref<number | null>(null);
+const federationSubActive = ref<number | null>(null);
+const federationSubActiveDiff = ref<number | null>(null);
+const newUsers = ref(null);
+const activeInstances = shallowRef(null);
 const queueStatsConnection = markRaw(useStream().useChannel('queueStats'));
 const now = new Date();
 const filesPagination = {
@@ -116,14 +116,14 @@ onMounted(async () => {
 	*/
 
 	os.apiGet('charts/federation', { limit: 2, span: 'day' }).then(chart => {
-		federationPubActive = chart.pubActive[0];
-		federationPubActiveDiff = chart.pubActive[0] - chart.pubActive[1];
-		federationSubActive = chart.subActive[0];
-		federationSubActiveDiff = chart.subActive[0] - chart.subActive[1];
+		federationPubActive.value = chart.pubActive[0];
+		federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
+		federationSubActive.value = chart.subActive[0];
+		federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
 	});
 
 	os.apiGet('federation/stats', { limit: 10 }).then(res => {
-		topSubInstancesForPie = res.topSubInstances.map(x => ({
+		topSubInstancesForPie.value = res.topSubInstances.map(x => ({
 			name: x.host,
 			color: x.themeColor,
 			value: x.followersCount,
@@ -131,7 +131,7 @@ onMounted(async () => {
 				os.pageWindow(`/instance-info/${x.host}`);
 			},
 		})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowersCount }]);
-		topPubInstancesForPie = res.topPubInstances.map(x => ({
+		topPubInstancesForPie.value = res.topPubInstances.map(x => ({
 			name: x.host,
 			color: x.themeColor,
 			value: x.followingCount,
@@ -142,21 +142,21 @@ onMounted(async () => {
 	});
 
 	os.api('admin/server-info').then(serverInfoResponse => {
-		serverInfo = serverInfoResponse;
+		serverInfo.value = serverInfoResponse;
 	});
 
 	os.api('admin/show-users', {
 		limit: 5,
 		sort: '+createdAt',
 	}).then(res => {
-		newUsers = res;
+		newUsers.value = res;
 	});
 
 	os.api('federation/instances', {
 		sort: '+latestRequestReceivedAt',
 		limit: 25,
 	}).then(res => {
-		activeInstances = res;
+		activeInstances.value = res;
 	});
 
 	nextTick(() => {
@@ -171,9 +171,9 @@ onBeforeUnmount(() => {
 	queueStatsConnection.dispose();
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.dashboard,
diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue
index 9681215aa1..4fdecbb67e 100644
--- a/packages/frontend/src/pages/admin/proxy-account.vue
+++ b/packages/frontend/src/pages/admin/proxy-account.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
@@ -31,36 +31,36 @@ import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let proxyAccount: any = $ref(null);
-let proxyAccountId: any = $ref(null);
+const proxyAccount = ref<any>(null);
+const proxyAccountId = ref<any>(null);
 
 async function init() {
 	const meta = await os.api('admin/meta');
-	proxyAccountId = meta.proxyAccountId;
-	if (proxyAccountId) {
-		proxyAccount = await os.api('users/show', { userId: proxyAccountId });
+	proxyAccountId.value = meta.proxyAccountId;
+	if (proxyAccountId.value) {
+		proxyAccount.value = await os.api('users/show', { userId: proxyAccountId.value });
 	}
 }
 
 function chooseProxyAccount() {
 	os.selectUser().then(user => {
-		proxyAccount = user;
-		proxyAccountId = user.id;
+		proxyAccount.value = user;
+		proxyAccountId.value = user.id;
 		save();
 	});
 }
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		proxyAccountId: proxyAccountId,
+		proxyAccountId: proxyAccountId.value,
 	}).then(() => {
 		fetchInstance();
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.proxyAccount,
diff --git a/packages/frontend/src/pages/admin/queue.chart.vue b/packages/frontend/src/pages/admin/queue.chart.vue
index d9f4af454d..a8fc2f391c 100644
--- a/packages/frontend/src/pages/admin/queue.chart.vue
+++ b/packages/frontend/src/pages/admin/queue.chart.vue
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { markRaw, onMounted, onUnmounted, ref } from 'vue';
+import { markRaw, onMounted, onUnmounted, ref, shallowRef } from 'vue';
 import XChart from './queue.chart.chart.vue';
 import number from '@/filters/number.js';
 import * as os from '@/os.js';
@@ -63,10 +63,10 @@ const active = ref(0);
 const delayed = ref(0);
 const waiting = ref(0);
 const jobs = ref([]);
-let chartProcess = $shallowRef<InstanceType<typeof XChart>>();
-let chartActive = $shallowRef<InstanceType<typeof XChart>>();
-let chartDelayed = $shallowRef<InstanceType<typeof XChart>>();
-let chartWaiting = $shallowRef<InstanceType<typeof XChart>>();
+const chartProcess = shallowRef<InstanceType<typeof XChart>>();
+const chartActive = shallowRef<InstanceType<typeof XChart>>();
+const chartDelayed = shallowRef<InstanceType<typeof XChart>>();
+const chartWaiting = shallowRef<InstanceType<typeof XChart>>();
 
 const props = defineProps<{
 	domain: string;
@@ -78,10 +78,10 @@ const onStats = (stats) => {
 	delayed.value = stats[props.domain].delayed;
 	waiting.value = stats[props.domain].waiting;
 
-	chartProcess.pushData(stats[props.domain].activeSincePrevTick);
-	chartActive.pushData(stats[props.domain].active);
-	chartDelayed.pushData(stats[props.domain].delayed);
-	chartWaiting.pushData(stats[props.domain].waiting);
+	chartProcess.value.pushData(stats[props.domain].activeSincePrevTick);
+	chartActive.value.pushData(stats[props.domain].active);
+	chartDelayed.value.pushData(stats[props.domain].delayed);
+	chartWaiting.value.pushData(stats[props.domain].waiting);
 };
 
 const onStatsLog = (statsLog) => {
@@ -97,10 +97,10 @@ const onStatsLog = (statsLog) => {
 		dataWaiting.push(stats[props.domain].waiting);
 	}
 
-	chartProcess.setData(dataProcess);
-	chartActive.setData(dataActive);
-	chartDelayed.setData(dataDelayed);
-	chartWaiting.setData(dataWaiting);
+	chartProcess.value.setData(dataProcess);
+	chartActive.value.setData(dataActive);
+	chartDelayed.value.setData(dataDelayed);
+	chartWaiting.value.setData(dataWaiting);
 };
 
 onMounted(() => {
diff --git a/packages/frontend/src/pages/admin/queue.vue b/packages/frontend/src/pages/admin/queue.vue
index ece54ab12b..f07fba8d15 100644
--- a/packages/frontend/src/pages/admin/queue.vue
+++ b/packages/frontend/src/pages/admin/queue.vue
@@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref, computed } from 'vue';
 import XQueue from './queue.chart.vue';
 import XHeader from './_header_.vue';
 import * as os from '@/os.js';
@@ -24,7 +25,7 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 
-let tab = $ref('deliver');
+const tab = ref('deliver');
 
 function clear() {
 	os.confirm({
@@ -46,11 +47,11 @@ function promoteAllQueues() {
 	}).then(({ canceled }) => {
 		if (canceled) return;
 
-		os.apiWithDialog('admin/queue/promote', { type: tab });
+		os.apiWithDialog('admin/queue/promote', { type: tab.value });
 	});
 }
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	asFullButton: true,
 	icon: 'ti ti-external-link',
 	text: i18n.ts.dashboard,
@@ -59,7 +60,7 @@ const headerActions = $computed(() => [{
 	},
 }]);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'deliver',
 	title: 'Deliver',
 }, {
diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue
index 8100d8188b..b97eca33d2 100644
--- a/packages/frontend/src/pages/admin/relays.vue
+++ b/packages/frontend/src/pages/admin/relays.vue
@@ -24,14 +24,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let relays: any[] = $ref([]);
+const relays = ref<any[]>([]);
 
 async function addRelay() {
 	const { canceled, result: inbox } = await os.inputText({
@@ -67,20 +67,20 @@ function remove(inbox: string) {
 
 function refresh() {
 	os.api('admin/relays/list').then((relayList: any) => {
-		relays = relayList;
+		relays.value = relayList;
 	});
 }
 
 refresh();
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	asFullButton: true,
 	icon: 'ti ti-plus',
 	text: i18n.ts.addRelay,
 	handler: addRelay,
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.relays,
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index 29726e805b..16db8403ed 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import { v4 as uuid } from 'uuid';
 import XHeader from './_header_.vue';
 import XEditor from './roles.editor.vue';
@@ -39,17 +39,17 @@ const props = defineProps<{
 	id?: string;
 }>();
 
-let role = $ref(null);
-let data = $ref(null);
+const role = ref(null);
+const data = ref(null);
 
 if (props.id) {
-	role = await os.api('admin/roles/show', {
+	role.value = await os.api('admin/roles/show', {
 		roleId: props.id,
 	});
 
-	data = role;
+	data.value = role.value;
 } else {
-	data = {
+	data.value = {
 		name: 'New Role',
 		description: '',
 		isAdministrator: false,
@@ -69,24 +69,24 @@ if (props.id) {
 
 async function save() {
 	rolesCache.delete();
-	if (role) {
+	if (role.value) {
 		os.apiWithDialog('admin/roles/update', {
-			roleId: role.id,
-			...data,
+			roleId: role.value.id,
+			...data.value,
 		});
-		router.push('/admin/roles/' + role.id);
+		router.push('/admin/roles/' + role.value.id);
 	} else {
 		const created = await os.apiWithDialog('admin/roles/create', {
-			...data,
+			...data.value,
 		});
 		router.push('/admin/roles/' + created.id);
 	}
 }
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => role ? {
-	title: i18n.ts._role.edit + ': ' + role.name,
+definePageMetadata(computed(() => role.value ? {
+	title: i18n.ts._role.edit + ': ' + role.value.name,
 	icon: 'ti ti-badge',
 } : {
 	title: i18n.ts._role.new,
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index 1db99e61f4..a8e0e8bbd1 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -278,7 +278,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					</MkRange>
 				</div>
 			</MkFolder>
-			
+
 			<MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])">
 				<template #label>{{ i18n.ts._role._options.canSearchNotes }}</template>
 				<template #suffix>
@@ -537,7 +537,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, ref, computed } from 'vue';
 import { throttle } from 'throttle-debounce';
 import RolesEditorFormula from './RolesEditorFormula.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -562,12 +562,12 @@ const props = defineProps<{
 	readonly?: boolean;
 }>();
 
-let role = $ref(deepClone(props.modelValue));
+const role = ref(deepClone(props.modelValue));
 
 // fill missing policy
 for (const ROLE_POLICY of ROLE_POLICIES) {
-	if (role.policies[ROLE_POLICY] == null) {
-		role.policies[ROLE_POLICY] = {
+	if (role.value.policies[ROLE_POLICY] == null) {
+		role.value.policies[ROLE_POLICY] = {
 			useDefault: true,
 			priority: 0,
 			value: instance.policies[ROLE_POLICY],
@@ -575,15 +575,15 @@ for (const ROLE_POLICY of ROLE_POLICIES) {
 	}
 }
 
-let rolePermission = $computed({
-	get: () => role.isAdministrator ? 'administrator' : role.isModerator ? 'moderator' : 'normal',
+const rolePermission = computed({
+	get: () => role.value.isAdministrator ? 'administrator' : role.value.isModerator ? 'moderator' : 'normal',
 	set: (val) => {
-		role.isAdministrator = val === 'administrator';
-		role.isModerator = val === 'moderator';
+		role.value.isAdministrator = val === 'administrator';
+		role.value.isModerator = val === 'moderator';
 	},
 });
 
-let q = $ref('');
+const q = ref('');
 
 function getPriorityIcon(option) {
 	if (option.priority === 2) return 'ti ti-arrows-up';
@@ -592,32 +592,32 @@ function getPriorityIcon(option) {
 }
 
 function matchQuery(keywords: string[]): boolean {
-	if (q.trim().length === 0) return true;
-	return keywords.some(keyword => keyword.toLowerCase().includes(q.toLowerCase()));
+	if (q.value.trim().length === 0) return true;
+	return keywords.some(keyword => keyword.toLowerCase().includes(q.value.toLowerCase()));
 }
 
 const save = throttle(100, () => {
 	const data = {
-		name: role.name,
-		description: role.description,
-		color: role.color === '' ? null : role.color,
-		iconUrl: role.iconUrl === '' ? null : role.iconUrl,
-		displayOrder: role.displayOrder,
-		target: role.target,
-		condFormula: role.condFormula,
-		isAdministrator: role.isAdministrator,
-		isModerator: role.isModerator,
-		isPublic: role.isPublic,
-		isExplorable: role.isExplorable,
-		asBadge: role.asBadge,
-		canEditMembersByModerator: role.canEditMembersByModerator,
-		policies: role.policies,
+		name: role.value.name,
+		description: role.value.description,
+		color: role.value.color === '' ? null : role.value.color,
+		iconUrl: role.value.iconUrl === '' ? null : role.value.iconUrl,
+		displayOrder: role.value.displayOrder,
+		target: role.value.target,
+		condFormula: role.value.condFormula,
+		isAdministrator: role.value.isAdministrator,
+		isModerator: role.value.isModerator,
+		isPublic: role.value.isPublic,
+		isExplorable: role.value.isExplorable,
+		asBadge: role.value.asBadge,
+		canEditMembersByModerator: role.value.canEditMembersByModerator,
+		policies: role.value.policies,
 	};
 
 	emit('update:modelValue', data);
 });
 
-watch($$(role), save, { deep: true });
+watch(role, save, { deep: true });
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 0b02f419bc..c11cc24b4f 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, reactive } from 'vue';
+import { computed, reactive, ref } from 'vue';
 import XHeader from './_header_.vue';
 import XEditor from './roles.editor.vue';
 import MkFolder from '@/components/MkFolder.vue';
@@ -90,7 +90,7 @@ const usersPagination = {
 	})),
 };
 
-let expandedItems = $ref([]);
+const expandedItems = ref([]);
 
 const role = reactive(await os.api('admin/roles/show', {
 	roleId: props.id,
@@ -160,16 +160,16 @@ async function unassign(user, ev) {
 }
 
 async function toggleItem(item) {
-	if (expandedItems.includes(item.id)) {
-		expandedItems = expandedItems.filter(x => x !== item.id);
+	if (expandedItems.value.includes(item.id)) {
+		expandedItems.value = expandedItems.value.filter(x => x !== item.id);
 	} else {
-		expandedItems.push(item.id);
+		expandedItems.value.push(item.id);
 	}
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(computed(() => ({
 	title: i18n.ts.role + ': ' + role.name,
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 91cd86485f..db4595b150 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -258,9 +258,9 @@ function create() {
 	router.push('/admin/roles/new');
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(computed(() => ({
 	title: i18n.ts.roles,
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index f7f76d910a..9835591fa8 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -114,7 +114,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import XBotProtection from './bot-protection.vue';
 import XHeader from './_header_.vue';
 import MkFolder from '@/components/MkFolder.vue';
@@ -129,65 +129,65 @@ import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let summalyProxy: string = $ref('');
-let enableHcaptcha: boolean = $ref(false);
-let enableRecaptcha: boolean = $ref(false);
-let enableTurnstile: boolean = $ref(false);
-let sensitiveMediaDetection: string = $ref('none');
-let sensitiveMediaDetectionSensitivity: number = $ref(0);
-let setSensitiveFlagAutomatically: boolean = $ref(false);
-let enableSensitiveMediaDetectionForVideos: boolean = $ref(false);
-let enableIpLogging: boolean = $ref(false);
-let enableActiveEmailValidation: boolean = $ref(false);
-let enableVerifymailApi: boolean = $ref(false);
-let verifymailAuthKey: string | null = $ref(null);
+const summalyProxy = ref<string>('');
+const enableHcaptcha = ref<boolean>(false);
+const enableRecaptcha = ref<boolean>(false);
+const enableTurnstile = ref<boolean>(false);
+const sensitiveMediaDetection = ref<string>('none');
+const sensitiveMediaDetectionSensitivity = ref<number>(0);
+const setSensitiveFlagAutomatically = ref<boolean>(false);
+const enableSensitiveMediaDetectionForVideos = ref<boolean>(false);
+const enableIpLogging = ref<boolean>(false);
+const enableActiveEmailValidation = ref<boolean>(false);
+const enableVerifymailApi = ref<boolean>(false);
+const verifymailAuthKey = ref<string | null>(null);
 
 async function init() {
 	const meta = await os.api('admin/meta');
-	summalyProxy = meta.summalyProxy;
-	enableHcaptcha = meta.enableHcaptcha;
-	enableRecaptcha = meta.enableRecaptcha;
-	enableTurnstile = meta.enableTurnstile;
-	sensitiveMediaDetection = meta.sensitiveMediaDetection;
-	sensitiveMediaDetectionSensitivity =
+	summalyProxy.value = meta.summalyProxy;
+	enableHcaptcha.value = meta.enableHcaptcha;
+	enableRecaptcha.value = meta.enableRecaptcha;
+	enableTurnstile.value = meta.enableTurnstile;
+	sensitiveMediaDetection.value = meta.sensitiveMediaDetection;
+	sensitiveMediaDetectionSensitivity.value =
 		meta.sensitiveMediaDetectionSensitivity === 'veryLow' ? 0 :
 		meta.sensitiveMediaDetectionSensitivity === 'low' ? 1 :
 		meta.sensitiveMediaDetectionSensitivity === 'medium' ? 2 :
 		meta.sensitiveMediaDetectionSensitivity === 'high' ? 3 :
 		meta.sensitiveMediaDetectionSensitivity === 'veryHigh' ? 4 : 0;
-	setSensitiveFlagAutomatically = meta.setSensitiveFlagAutomatically;
-	enableSensitiveMediaDetectionForVideos = meta.enableSensitiveMediaDetectionForVideos;
-	enableIpLogging = meta.enableIpLogging;
-	enableActiveEmailValidation = meta.enableActiveEmailValidation;
-	enableVerifymailApi = meta.enableVerifymailApi;
-	verifymailAuthKey = meta.verifymailAuthKey;
+	setSensitiveFlagAutomatically.value = meta.setSensitiveFlagAutomatically;
+	enableSensitiveMediaDetectionForVideos.value = meta.enableSensitiveMediaDetectionForVideos;
+	enableIpLogging.value = meta.enableIpLogging;
+	enableActiveEmailValidation.value = meta.enableActiveEmailValidation;
+	enableVerifymailApi.value = meta.enableVerifymailApi;
+	verifymailAuthKey.value = meta.verifymailAuthKey;
 }
 
 function save() {
 	os.apiWithDialog('admin/update-meta', {
-		summalyProxy,
-		sensitiveMediaDetection,
+		summalyProxy: summalyProxy.value,
+		sensitiveMediaDetection: sensitiveMediaDetection.value,
 		sensitiveMediaDetectionSensitivity:
-			sensitiveMediaDetectionSensitivity === 0 ? 'veryLow' :
-			sensitiveMediaDetectionSensitivity === 1 ? 'low' :
-			sensitiveMediaDetectionSensitivity === 2 ? 'medium' :
-			sensitiveMediaDetectionSensitivity === 3 ? 'high' :
-			sensitiveMediaDetectionSensitivity === 4 ? 'veryHigh' :
+			sensitiveMediaDetectionSensitivity.value === 0 ? 'veryLow' :
+			sensitiveMediaDetectionSensitivity.value === 1 ? 'low' :
+			sensitiveMediaDetectionSensitivity.value === 2 ? 'medium' :
+			sensitiveMediaDetectionSensitivity.value === 3 ? 'high' :
+			sensitiveMediaDetectionSensitivity.value === 4 ? 'veryHigh' :
 			0,
-		setSensitiveFlagAutomatically,
-		enableSensitiveMediaDetectionForVideos,
-		enableIpLogging,
-		enableActiveEmailValidation,
-		enableVerifymailApi,
-		verifymailAuthKey,
+		setSensitiveFlagAutomatically: setSensitiveFlagAutomatically.value,
+		enableSensitiveMediaDetectionForVideos: enableSensitiveMediaDetectionForVideos.value,
+		enableIpLogging: enableIpLogging.value,
+		enableActiveEmailValidation: enableActiveEmailValidation.value,
+		enableVerifymailApi: enableVerifymailApi.value,
+		verifymailAuthKey: verifymailAuthKey.value,
 	}).then(() => {
 		fetchInstance();
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.security,
diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue
index 4cd1f6cbec..231f4ba56f 100644
--- a/packages/frontend/src/pages/admin/server-rules.vue
+++ b/packages/frontend/src/pages/admin/server-rules.vue
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import * as os from '@/os.js';
 import { fetchInstance, instance } from '@/instance.js';
@@ -52,20 +52,20 @@ import MkInput from '@/components/MkInput.vue';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
-let serverRules: string[] = $ref(instance.serverRules);
+const serverRules = ref<string[]>(instance.serverRules);
 
 const save = async () => {
 	await os.apiWithDialog('admin/update-meta', {
-		serverRules,
+		serverRules: serverRules.value,
 	});
 	fetchInstance();
 };
 
 const remove = (index: number): void => {
-	serverRules.splice(index, 1);
+	serverRules.value.splice(index, 1);
 };
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.serverRules,
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index 86fbfa0827..f17284efa9 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -148,7 +148,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -163,76 +163,76 @@ import { i18n } from '@/i18n.js';
 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);
-let impressumUrl: string | null = $ref(null);
-let pinnedUsers: string = $ref('');
-let cacheRemoteFiles: boolean = $ref(false);
-let cacheRemoteSensitiveFiles: boolean = $ref(false);
-let enableServiceWorker: boolean = $ref(false);
-let swPublicKey: any = $ref(null);
-let swPrivateKey: any = $ref(null);
-let enableFanoutTimeline: boolean = $ref(false);
-let enableFanoutTimelineDbFallback: boolean = $ref(false);
-let perLocalUserUserTimelineCacheMax: number = $ref(0);
-let perRemoteUserUserTimelineCacheMax: number = $ref(0);
-let perUserHomeTimelineCacheMax: number = $ref(0);
-let perUserListTimelineCacheMax: number = $ref(0);
-let notesPerOneAd: number = $ref(0);
+const name = ref<string | null>(null);
+const shortName = ref<string | null>(null);
+const description = ref<string | null>(null);
+const maintainerName = ref<string | null>(null);
+const maintainerEmail = ref<string | null>(null);
+const impressumUrl = ref<string | null>(null);
+const pinnedUsers = ref<string>('');
+const cacheRemoteFiles = ref<boolean>(false);
+const cacheRemoteSensitiveFiles = ref<boolean>(false);
+const enableServiceWorker = ref<boolean>(false);
+const swPublicKey = ref<any>(null);
+const swPrivateKey = ref<any>(null);
+const enableFanoutTimeline = ref<boolean>(false);
+const enableFanoutTimelineDbFallback = ref<boolean>(false);
+const perLocalUserUserTimelineCacheMax = ref<number>(0);
+const perRemoteUserUserTimelineCacheMax = ref<number>(0);
+const perUserHomeTimelineCacheMax = ref<number>(0);
+const perUserListTimelineCacheMax = ref<number>(0);
+const notesPerOneAd = ref<number>(0);
 
 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;
-	impressumUrl = meta.impressumUrl;
-	pinnedUsers = meta.pinnedUsers.join('\n');
-	cacheRemoteFiles = meta.cacheRemoteFiles;
-	cacheRemoteSensitiveFiles = meta.cacheRemoteSensitiveFiles;
-	enableServiceWorker = meta.enableServiceWorker;
-	swPublicKey = meta.swPublickey;
-	swPrivateKey = meta.swPrivateKey;
-	enableFanoutTimeline = meta.enableFanoutTimeline;
-	enableFanoutTimelineDbFallback = meta.enableFanoutTimelineDbFallback;
-	perLocalUserUserTimelineCacheMax = meta.perLocalUserUserTimelineCacheMax;
-	perRemoteUserUserTimelineCacheMax = meta.perRemoteUserUserTimelineCacheMax;
-	perUserHomeTimelineCacheMax = meta.perUserHomeTimelineCacheMax;
-	perUserListTimelineCacheMax = meta.perUserListTimelineCacheMax;
-	notesPerOneAd = meta.notesPerOneAd;
+	name.value = meta.name;
+	shortName.value = meta.shortName;
+	description.value = meta.description;
+	maintainerName.value = meta.maintainerName;
+	maintainerEmail.value = meta.maintainerEmail;
+	impressumUrl.value = meta.impressumUrl;
+	pinnedUsers.value = meta.pinnedUsers.join('\n');
+	cacheRemoteFiles.value = meta.cacheRemoteFiles;
+	cacheRemoteSensitiveFiles.value = meta.cacheRemoteSensitiveFiles;
+	enableServiceWorker.value = meta.enableServiceWorker;
+	swPublicKey.value = meta.swPublickey;
+	swPrivateKey.value = meta.swPrivateKey;
+	enableFanoutTimeline.value = meta.enableFanoutTimeline;
+	enableFanoutTimelineDbFallback.value = meta.enableFanoutTimelineDbFallback;
+	perLocalUserUserTimelineCacheMax.value = meta.perLocalUserUserTimelineCacheMax;
+	perRemoteUserUserTimelineCacheMax.value = meta.perRemoteUserUserTimelineCacheMax;
+	perUserHomeTimelineCacheMax.value = meta.perUserHomeTimelineCacheMax;
+	perUserListTimelineCacheMax.value = meta.perUserListTimelineCacheMax;
+	notesPerOneAd.value = meta.notesPerOneAd;
 }
 
 async function save(): void {
 	await os.apiWithDialog('admin/update-meta', {
-		name,
-		shortName: shortName === '' ? null : shortName,
-		description,
-		maintainerName,
-		maintainerEmail,
-		impressumUrl,
-		pinnedUsers: pinnedUsers.split('\n'),
-		cacheRemoteFiles,
-		cacheRemoteSensitiveFiles,
-		enableServiceWorker,
-		swPublicKey,
-		swPrivateKey,
-		enableFanoutTimeline,
-		enableFanoutTimelineDbFallback,
-		perLocalUserUserTimelineCacheMax,
-		perRemoteUserUserTimelineCacheMax,
-		perUserHomeTimelineCacheMax,
-		perUserListTimelineCacheMax,
-		notesPerOneAd,
+		name: name.value,
+		shortName: shortName.value === '' ? null : shortName.value,
+		description: description.value,
+		maintainerName: maintainerName.value,
+		maintainerEmail: maintainerEmail.value,
+		impressumUrl: impressumUrl.value,
+		pinnedUsers: pinnedUsers.value.split('\n'),
+		cacheRemoteFiles: cacheRemoteFiles.value,
+		cacheRemoteSensitiveFiles: cacheRemoteSensitiveFiles.value,
+		enableServiceWorker: enableServiceWorker.value,
+		swPublicKey: swPublicKey.value,
+		swPrivateKey: swPrivateKey.value,
+		enableFanoutTimeline: enableFanoutTimeline.value,
+		enableFanoutTimelineDbFallback: enableFanoutTimelineDbFallback.value,
+		perLocalUserUserTimelineCacheMax: perLocalUserUserTimelineCacheMax.value,
+		perRemoteUserUserTimelineCacheMax: perRemoteUserUserTimelineCacheMax.value,
+		perUserHomeTimelineCacheMax: perUserHomeTimelineCacheMax.value,
+		perUserListTimelineCacheMax: perUserListTimelineCacheMax.value,
+		notesPerOneAd: notesPerOneAd.value,
 	});
 
 	fetchInstance();
 }
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.general,
diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue
index fcf41de734..ea4c231af2 100644
--- a/packages/frontend/src/pages/admin/users.vue
+++ b/packages/frontend/src/pages/admin/users.vue
@@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, shallowRef, ref } from 'vue';
 import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
@@ -69,22 +69,22 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import { dateString } from '@/filters/date.js';
 
-let paginationComponent = $shallowRef<InstanceType<typeof MkPagination>>();
+const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
 
-let sort = $ref('+createdAt');
-let state = $ref('all');
-let origin = $ref('local');
-let searchUsername = $ref('');
-let searchHost = $ref('');
+const sort = ref('+createdAt');
+const state = ref('all');
+const origin = ref('local');
+const searchUsername = ref('');
+const searchHost = ref('');
 const pagination = {
 	endpoint: 'admin/show-users' as const,
 	limit: 10,
 	params: computed(() => ({
-		sort: sort,
-		state: state,
-		origin: origin,
-		username: searchUsername,
-		hostname: searchHost,
+		sort: sort.value,
+		state: state.value,
+		origin: origin.value,
+		username: searchUsername.value,
+		hostname: searchHost.value,
 	})),
 	offsetMode: true,
 };
@@ -111,7 +111,7 @@ async function addUser() {
 		username: username,
 		password: password,
 	}).then(res => {
-		paginationComponent.reload();
+		paginationComponent.value.reload();
 	});
 }
 
@@ -119,7 +119,7 @@ function show(user) {
 	os.pageWindow(`/admin/user/${user.id}`);
 }
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	icon: 'ti ti-search',
 	text: i18n.ts.search,
 	handler: searchUser,
@@ -135,7 +135,7 @@ const headerActions = $computed(() => [{
 	handler: lookupUser,
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(computed(() => ({
 	title: i18n.ts.users,
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index afc6a98281..8eca403707 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
@@ -90,9 +90,9 @@ async function read(announcement) {
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'current',
 	title: i18n.ts.currentAnnouncements,
 	icon: 'ti ti-flare',
diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue
index 4ea91796d5..3d7ecdacf6 100644
--- a/packages/frontend/src/pages/antenna-timeline.vue
+++ b/packages/frontend/src/pages/antenna-timeline.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref, shallowRef } from 'vue';
 import MkTimeline from '@/components/MkTimeline.vue';
 import { scroll } from '@/scripts/scroll.js';
 import * as os from '@/os.js';
@@ -38,20 +38,20 @@ const props = defineProps<{
 	antennaId: string;
 }>();
 
-let antenna = $ref(null);
-let queue = $ref(0);
-let rootEl = $shallowRef<HTMLElement>();
-let tlEl = $shallowRef<InstanceType<typeof MkTimeline>>();
-const keymap = $computed(() => ({
+const antenna = ref(null);
+const queue = ref(0);
+const rootEl = shallowRef<HTMLElement>();
+const tlEl = shallowRef<InstanceType<typeof MkTimeline>>();
+const keymap = computed(() => ({
 	't': focus,
 }));
 
 function queueUpdated(q) {
-	queue = q;
+	queue.value = q;
 }
 
 function top() {
-	scroll(rootEl, { top: 0 });
+	scroll(rootEl.value, { top: 0 });
 }
 
 async function timetravel() {
@@ -60,7 +60,7 @@ async function timetravel() {
 	});
 	if (canceled) return;
 
-	tlEl.timetravel(date);
+	tlEl.value.timetravel(date);
 }
 
 function settings() {
@@ -68,16 +68,16 @@ function settings() {
 }
 
 function focus() {
-	tlEl.focus();
+	tlEl.value.focus();
 }
 
 watch(() => props.antennaId, async () => {
-	antenna = await os.api('antennas/show', {
+	antenna.value = await os.api('antennas/show', {
 		antennaId: props.antennaId,
 	});
 }, { immediate: true });
 
-const headerActions = $computed(() => antenna ? [{
+const headerActions = computed(() => antenna.value ? [{
 	icon: 'ti ti-calendar-time',
 	text: i18n.ts.jumpToSpecifiedDate,
 	handler: timetravel,
@@ -87,10 +87,10 @@ const headerActions = $computed(() => antenna ? [{
 	handler: settings,
 }] : []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => antenna ? {
-	title: antenna.name,
+definePageMetadata(computed(() => antenna.value ? {
+	title: antenna.value.name,
 	icon: 'ti ti-antenna',
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/api-console.vue b/packages/frontend/src/pages/api-console.vue
index 01657c4c2a..5374c220de 100644
--- a/packages/frontend/src/pages/api-console.vue
+++ b/packages/frontend/src/pages/api-console.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 import JSON5 from 'json5';
 import { Endpoints } from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
@@ -83,9 +83,9 @@ function onEndpointChange() {
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: 'API console',
diff --git a/packages/frontend/src/pages/auth.form.vue b/packages/frontend/src/pages/auth.form.vue
index 9d39a1e603..8a17e5895d 100644
--- a/packages/frontend/src/pages/auth.form.vue
+++ b/packages/frontend/src/pages/auth.form.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
@@ -35,11 +35,11 @@ const emit = defineEmits<{
 	(event: 'denied'): void;
 }>();
 
-const app = $computed(() => props.session.app);
+const app = computed(() => props.session.app);
 
-const name = $computed(() => {
+const name = computed(() => {
 	const el = document.createElement('div');
-	el.textContent = app.name;
+	el.textContent = app.value.name;
 	return el.innerHTML;
 });
 
diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue
index bcd54a6ed5..1b342647fb 100644
--- a/packages/frontend/src/pages/auth.vue
+++ b/packages/frontend/src/pages/auth.vue
@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import XForm from './auth.form.vue';
 import MkSignin from '@/components/MkSignin.vue';
@@ -55,15 +55,15 @@ const props = defineProps<{
 	token: string;
 }>();
 
-let state = $ref<'waiting' | 'accepted' | 'fetch-session-error' | 'denied' | null>(null);
-let session = $ref<Misskey.entities.AuthSessionShowResponse | null>(null);
+const state = ref<'waiting' | 'accepted' | 'fetch-session-error' | 'denied' | null>(null);
+const session = ref<Misskey.entities.AuthSessionShowResponse | null>(null);
 
 function accepted() {
-	state = 'accepted';
-	if (session && session.app.callbackUrl) {
-		const url = new URL(session.app.callbackUrl);
+	state.value = 'accepted';
+	if (session.value && session.value.app.callbackUrl) {
+		const url = new URL(session.value.app.callbackUrl);
 		if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(url.protocol)) throw new Error('invalid url');
-		location.href = `${session.app.callbackUrl}?token=${session.token}`;
+		location.href = `${session.value.app.callbackUrl}?token=${session.value.token}`;
 	}
 }
 
@@ -75,27 +75,27 @@ onMounted(async () => {
 	if (!$i) return;
 
 	try {
-		session = await os.api('auth/session/show', {
+		session.value = await os.api('auth/session/show', {
 			token: props.token,
 		});
 
 		// 既に連携していた場合
-		if (session.app.isAuthorized) {
+		if (session.value.app.isAuthorized) {
 			await os.api('auth/accept', {
-				token: session.token,
+				token: session.value.token,
 			});
 			accepted();
 		} else {
-			state = 'waiting';
+			state.value = 'waiting';
 		}
 	} catch (err) {
-		state = 'fetch-session-error';
+		state.value = 'fetch-session-error';
 	}
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts._auth.shareAccessTitle,
diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue
index 715f234493..b9edb18d10 100644
--- a/packages/frontend/src/pages/avatar-decorations.vue
+++ b/packages/frontend/src/pages/avatar-decorations.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
@@ -46,10 +46,10 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkFolder from '@/components/MkFolder.vue';
 
-let avatarDecorations: any[] = $ref([]);
+const avatarDecorations = ref<any[]>([]);
 
 function add() {
-	avatarDecorations.unshift({
+	avatarDecorations.value.unshift({
 		_id: Math.random().toString(36),
 		id: null,
 		name: '',
@@ -64,7 +64,7 @@ function del(avatarDecoration) {
 		text: i18n.t('deleteAreYouSure', { x: avatarDecoration.name }),
 	}).then(({ canceled }) => {
 		if (canceled) return;
-		avatarDecorations = avatarDecorations.filter(x => x !== avatarDecoration);
+		avatarDecorations.value = avatarDecorations.value.filter(x => x !== avatarDecoration);
 		os.api('admin/avatar-decorations/delete', avatarDecoration);
 	});
 }
@@ -80,20 +80,20 @@ async function save(avatarDecoration) {
 
 function load() {
 	os.api('admin/avatar-decorations/list').then(_avatarDecorations => {
-		avatarDecorations = _avatarDecorations;
+		avatarDecorations.value = _avatarDecorations;
 	});
 }
 
 load();
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	asFullButton: true,
 	icon: 'ti ti-plus',
 	text: i18n.ts.add,
 	handler: add,
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.avatarDecorations,
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index 5256ea4f11..af382bb137 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -90,22 +90,22 @@ const props = defineProps<{
 	channelId?: string;
 }>();
 
-let channel = $ref(null);
-let name = $ref(null);
-let description = $ref(null);
-let bannerUrl = $ref<string | null>(null);
-let bannerId = $ref<string | null>(null);
-let color = $ref('#000');
-let isSensitive = $ref(false);
-let allowRenoteToExternal = $ref(true);
+const channel = ref(null);
+const name = ref(null);
+const description = ref(null);
+const bannerUrl = ref<string | null>(null);
+const bannerId = ref<string | null>(null);
+const color = ref('#000');
+const isSensitive = ref(false);
+const allowRenoteToExternal = ref(true);
 const pinnedNotes = ref([]);
 
-watch(() => bannerId, async () => {
-	if (bannerId == null) {
-		bannerUrl = null;
+watch(() => bannerId.value, async () => {
+	if (bannerId.value == null) {
+		bannerUrl.value = null;
 	} else {
-		bannerUrl = (await os.api('drive/files/show', {
-			fileId: bannerId,
+		bannerUrl.value = (await os.api('drive/files/show', {
+			fileId: bannerId.value,
 		})).url;
 	}
 });
@@ -113,20 +113,20 @@ watch(() => bannerId, async () => {
 async function fetchChannel() {
 	if (props.channelId == null) return;
 
-	channel = await os.api('channels/show', {
+	channel.value = await os.api('channels/show', {
 		channelId: props.channelId,
 	});
 
-	name = channel.name;
-	description = channel.description;
-	bannerId = channel.bannerId;
-	bannerUrl = channel.bannerUrl;
-	isSensitive = channel.isSensitive;
-	pinnedNotes.value = channel.pinnedNoteIds.map(id => ({
+	name.value = channel.value.name;
+	description.value = channel.value.description;
+	bannerId.value = channel.value.bannerId;
+	bannerUrl.value = channel.value.bannerUrl;
+	isSensitive.value = channel.value.isSensitive;
+	pinnedNotes.value = channel.value.pinnedNoteIds.map(id => ({
 		id,
 	}));
-	color = channel.color;
-	allowRenoteToExternal = channel.allowRenoteToExternal;
+	color.value = channel.value.color;
+	allowRenoteToExternal.value = channel.value.allowRenoteToExternal;
 }
 
 fetchChannel();
@@ -150,13 +150,13 @@ function removePinnedNote(index: number) {
 
 function save() {
 	const params = {
-		name: name,
-		description: description,
-		bannerId: bannerId,
+		name: name.value,
+		description: description.value,
+		bannerId: bannerId.value,
 		pinnedNoteIds: pinnedNotes.value.map(x => x.id),
-		color: color,
-		isSensitive: isSensitive,
-		allowRenoteToExternal: allowRenoteToExternal,
+		color: color.value,
+		isSensitive: isSensitive.value,
+		allowRenoteToExternal: allowRenoteToExternal.value,
 	};
 
 	if (props.channelId) {
@@ -172,7 +172,7 @@ function save() {
 async function archive() {
 	const { canceled } = await os.confirm({
 		type: 'warning',
-		title: i18n.t('channelArchiveConfirmTitle', { name: name }),
+		title: i18n.t('channelArchiveConfirmTitle', { name: name.value }),
 		text: i18n.ts.channelArchiveConfirmDescription,
 	});
 
@@ -188,17 +188,17 @@ async function archive() {
 
 function setBannerImage(evt) {
 	selectFile(evt.currentTarget ?? evt.target, null).then(file => {
-		bannerId = file.id;
+		bannerId.value = file.id;
 	});
 }
 
 function removeBannerImage() {
-	bannerId = null;
+	bannerId.value = null;
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(computed(() => props.channelId ? {
 	title: i18n.ts._channel.edit,
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index dc374e2925..698f7fa383 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -68,7 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref } from 'vue';
 import MkPostForm from '@/components/MkPostForm.vue';
 import MkTimeline from '@/components/MkTimeline.vue';
 import XChannelFollowButton from '@/components/MkChannelFollowButton.vue';
@@ -96,13 +96,13 @@ const props = defineProps<{
 	channelId: string;
 }>();
 
-let tab = $ref('overview');
-let channel = $ref(null);
-let favorited = $ref(false);
-let searchQuery = $ref('');
-let searchPagination = $ref();
-let searchKey = $ref('');
-const featuredPagination = $computed(() => ({
+const tab = ref('overview');
+const channel = ref(null);
+const favorited = ref(false);
+const searchQuery = ref('');
+const searchPagination = ref();
+const searchKey = ref('');
+const featuredPagination = computed(() => ({
 	endpoint: 'notes/featured' as const,
 	limit: 10,
 	params: {
@@ -111,30 +111,30 @@ const featuredPagination = $computed(() => ({
 }));
 
 watch(() => props.channelId, async () => {
-	channel = await os.api('channels/show', {
+	channel.value = await os.api('channels/show', {
 		channelId: props.channelId,
 	});
-	favorited = channel.isFavorited;
-	if (favorited || channel.isFollowing) {
-		tab = 'timeline';
+	favorited.value = channel.value.isFavorited;
+	if (favorited.value || channel.value.isFollowing) {
+		tab.value = 'timeline';
 	}
 }, { immediate: true });
 
 function edit() {
-	router.push(`/channels/${channel.id}/edit`);
+	router.push(`/channels/${channel.value.id}/edit`);
 }
 
 function openPostForm() {
 	os.post({
-		channel,
+		channel: channel.value,
 	});
 }
 
 function favorite() {
 	os.apiWithDialog('channels/favorite', {
-		channelId: channel.id,
+		channelId: channel.value.id,
 	}).then(() => {
-		favorited = true;
+		favorited.value = true;
 	});
 }
 
@@ -145,38 +145,38 @@ async function unfavorite() {
 	});
 	if (confirm.canceled) return;
 	os.apiWithDialog('channels/unfavorite', {
-		channelId: channel.id,
+		channelId: channel.value.id,
 	}).then(() => {
-		favorited = false;
+		favorited.value = false;
 	});
 }
 
 async function search() {
-	const query = searchQuery.toString().trim();
+	const query = searchQuery.value.toString().trim();
 
 	if (query == null) return;
 
-	searchPagination = {
+	searchPagination.value = {
 		endpoint: 'notes/search',
 		limit: 10,
 		params: {
 			query: query,
-			channelId: channel.id,
+			channelId: channel.value.id,
 		},
 	};
 
-	searchKey = query;
+	searchKey.value = query;
 }
 
-const headerActions = $computed(() => {
-	if (channel && channel.userId) {
+const headerActions = computed(() => {
+	if (channel.value && channel.value.userId) {
 		const headerItems: PageHeaderItem[] = [];
 
 		headerItems.push({
 			icon: 'ti ti-link',
 			text: i18n.ts.copyUrl,
 			handler: async (): Promise<void> => {
-				copyToClipboard(`${url}/channels/${channel.id}`);
+				copyToClipboard(`${url}/channels/${channel.value.id}`);
 				os.success();
 			},
 		});
@@ -187,15 +187,15 @@ const headerActions = $computed(() => {
 				text: i18n.ts.share,
 				handler: async (): Promise<void> => {
 					navigator.share({
-						title: channel.name,
-						text: channel.description,
-						url: `${url}/channels/${channel.id}`,
+						title: channel.value.name,
+						text: channel.value.description,
+						url: `${url}/channels/${channel.value.id}`,
 					});
 				},
 			});
 		}
 
-		if (($i && $i.id === channel.userId) || iAmModerator) {
+		if (($i && $i.id === channel.value.userId) || iAmModerator) {
 			headerItems.push({
 				icon: 'ti ti-settings',
 				text: i18n.ts.edit,
@@ -209,7 +209,7 @@ const headerActions = $computed(() => {
 	}
 });
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'overview',
 	title: i18n.ts.overview,
 	icon: 'ti ti-info-circle',
@@ -227,8 +227,8 @@ const headerTabs = $computed(() => [{
 	icon: 'ti ti-search',
 }]);
 
-definePageMetadata(computed(() => channel ? {
-	title: channel.name,
+definePageMetadata(computed(() => channel.value ? {
+	title: channel.value.name,
 	icon: 'ti ti-device-tv',
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue
index 8219a947f2..e58c89bb77 100644
--- a/packages/frontend/src/pages/channels.vue
+++ b/packages/frontend/src/pages/channels.vue
@@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, onMounted } from 'vue';
+import { computed, onMounted, ref } from 'vue';
 import MkChannelPreview from '@/components/MkChannelPreview.vue';
 import MkChannelList from '@/components/MkChannelList.vue';
 import MkPagination from '@/components/MkPagination.vue';
@@ -69,15 +69,15 @@ const props = defineProps<{
 	type?: string;
 }>();
 
-let key = $ref('');
-let tab = $ref('featured');
-let searchQuery = $ref('');
-let searchType = $ref('nameAndDescription');
-let channelPagination = $ref();
+const key = ref('');
+const tab = ref('featured');
+const searchQuery = ref('');
+const searchType = ref('nameAndDescription');
+const channelPagination = ref();
 
 onMounted(() => {
-	searchQuery = props.query ?? '';
-	searchType = props.type ?? 'nameAndDescription';
+	searchQuery.value = props.query ?? '';
+	searchType.value = props.type ?? 'nameAndDescription';
 });
 
 const featuredPagination = {
@@ -99,35 +99,35 @@ const ownedPagination = {
 };
 
 async function search() {
-	const query = searchQuery.toString().trim();
+	const query = searchQuery.value.toString().trim();
 
 	if (query == null) return;
 
-	const type = searchType.toString().trim();
+	const type = searchType.value.toString().trim();
 
-	channelPagination = {
+	channelPagination.value = {
 		endpoint: 'channels/search',
 		limit: 10,
 		params: {
-			query: searchQuery,
+			query: searchQuery.value,
 			type: type,
 		},
 	};
 
-	key = query + type;
+	key.value = query + type;
 }
 
 function create() {
 	router.push('/channels/new');
 }
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	icon: 'ti ti-plus',
 	text: i18n.ts.create,
 	handler: create,
 }]);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'search',
 	title: i18n.ts.search,
 	icon: 'ti ti-search',
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index b32c8a3864..2ea0312c7e 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch, provide } from 'vue';
+import { computed, watch, provide, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkNotes from '@/components/MkNotes.vue';
 import { $i } from '@/account.js';
@@ -43,8 +43,8 @@ const props = defineProps<{
 	clipId: string,
 }>();
 
-let clip: Misskey.entities.Clip = $ref<Misskey.entities.Clip>();
-let favorited = $ref(false);
+const clip = ref<Misskey.entities.Clip | null>(null);
+const favorited = ref(false);
 const pagination = {
 	endpoint: 'clips/notes' as const,
 	limit: 10,
@@ -53,24 +53,24 @@ const pagination = {
 	})),
 };
 
-const isOwned: boolean | null = $computed<boolean | null>(() => $i && clip && ($i.id === clip.userId));
+const isOwned = computed<boolean | null>(() => $i && clip.value && ($i.id === clip.value.userId));
 
 watch(() => props.clipId, async () => {
-	clip = await os.api('clips/show', {
+	clip.value = await os.api('clips/show', {
 		clipId: props.clipId,
 	});
-	favorited = clip.isFavorited;
+	favorited.value = clip.value.isFavorited;
 }, {
 	immediate: true,
 });
 
-provide('currentClip', $$(clip));
+provide('currentClip', clip);
 
 function favorite() {
 	os.apiWithDialog('clips/favorite', {
 		clipId: props.clipId,
 	}).then(() => {
-		favorited = true;
+		favorited.value = true;
 	});
 }
 
@@ -83,57 +83,57 @@ async function unfavorite() {
 	os.apiWithDialog('clips/unfavorite', {
 		clipId: props.clipId,
 	}).then(() => {
-		favorited = false;
+		favorited.value = false;
 	});
 }
 
-const headerActions = $computed(() => clip && isOwned ? [{
+const headerActions = computed(() => clip.value && isOwned.value ? [{
 	icon: 'ti ti-pencil',
 	text: i18n.ts.edit,
 	handler: async (): Promise<void> => {
-		const { canceled, result } = await os.form(clip.name, {
+		const { canceled, result } = await os.form(clip.value.name, {
 			name: {
 				type: 'string',
 				label: i18n.ts.name,
-				default: clip.name,
+				default: clip.value.name,
 			},
 			description: {
 				type: 'string',
 				required: false,
 				multiline: true,
 				label: i18n.ts.description,
-				default: clip.description,
+				default: clip.value.description,
 			},
 			isPublic: {
 				type: 'boolean',
 				label: i18n.ts.public,
-				default: clip.isPublic,
+				default: clip.value.isPublic,
 			},
 		});
 		if (canceled) return;
 
 		os.apiWithDialog('clips/update', {
-			clipId: clip.id,
+			clipId: clip.value.id,
 			...result,
 		});
 
 		clipsCache.delete();
 	},
-}, ...(clip.isPublic ? [{
+}, ...(clip.value.isPublic ? [{
 	icon: 'ti ti-link',
 	text: i18n.ts.copyUrl,
 	handler: async (): Promise<void> => {
-		copyToClipboard(`${url}/clips/${clip.id}`);
+		copyToClipboard(`${url}/clips/${clip.value.id}`);
 		os.success();
 	},
-}] : []), ...(clip.isPublic && isSupportShare() ? [{
+}] : []), ...(clip.value.isPublic && isSupportShare() ? [{
 	icon: 'ti ti-share',
 	text: i18n.ts.share,
 	handler: async (): Promise<void> => {
 		navigator.share({
-			title: clip.name,
-			text: clip.description,
-			url: `${url}/clips/${clip.id}`,
+			title: clip.value.name,
+			text: clip.value.description,
+			url: `${url}/clips/${clip.value.id}`,
 		});
 	},
 }] : []), {
@@ -143,20 +143,20 @@ const headerActions = $computed(() => clip && isOwned ? [{
 	handler: async (): Promise<void> => {
 		const { canceled } = await os.confirm({
 			type: 'warning',
-			text: i18n.t('deleteAreYouSure', { x: clip.name }),
+			text: i18n.t('deleteAreYouSure', { x: clip.value.name }),
 		});
 		if (canceled) return;
 
 		await os.apiWithDialog('clips/delete', {
-			clipId: clip.id,
+			clipId: clip.value.id,
 		});
 
 		clipsCache.delete();
 	},
 }] : null);
 
-definePageMetadata(computed(() => clip ? {
-	title: clip.name,
+definePageMetadata(computed(() => clip.value ? {
+	title: clip.value.name,
 	icon: 'ti ti-paperclip',
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 316dbaa3aa..fa92424fa0 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -286,7 +286,7 @@ const delBulk = async () => {
 	emojisPaginationComponent.value.reload();
 };
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	asFullButton: true,
 	icon: 'ti ti-plus',
 	text: i18n.ts.addEmoji,
@@ -296,7 +296,7 @@ const headerActions = $computed(() => [{
 	handler: menu,
 }]);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'local',
 	title: i18n.ts.local,
 }, {
diff --git a/packages/frontend/src/pages/drive.vue b/packages/frontend/src/pages/drive.vue
index 54fb83fc1d..64fbd16971 100644
--- a/packages/frontend/src/pages/drive.vue
+++ b/packages/frontend/src/pages/drive.vue
@@ -10,19 +10,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import XDrive from '@/components/MkDrive.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let folder = $ref(null);
+const folder = ref(null);
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(computed(() => ({
-	title: folder ? folder.name : i18n.ts.drive,
+	title: folder.value ? folder.value.name : i18n.ts.drive,
 	icon: 'ti ti-cloud',
 	hideHeader: true,
 })));
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index 5bce74aa94..8119150df9 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -74,7 +74,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -92,22 +92,22 @@ const props = defineProps<{
 	emoji?: any,
 }>();
 
-let dialog = $ref(null);
-let name: string = $ref(props.emoji ? props.emoji.name : '');
-let category: string = $ref(props.emoji ? props.emoji.category : '');
-let aliases: string = $ref(props.emoji ? props.emoji.aliases.join(' ') : '');
-let license: string = $ref(props.emoji ? (props.emoji.license ?? '') : '');
-let isSensitive = $ref(props.emoji ? props.emoji.isSensitive : false);
-let localOnly = $ref(props.emoji ? props.emoji.localOnly : false);
-let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []);
-let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]);
-let file = $ref<Misskey.entities.DriveFile>();
+const dialog = ref(null);
+const name = ref<string>(props.emoji ? props.emoji.name : '');
+const category = ref<string>(props.emoji ? props.emoji.category : '');
+const aliases = ref<string>(props.emoji ? props.emoji.aliases.join(' ') : '');
+const license = ref<string>(props.emoji ? (props.emoji.license ?? '') : '');
+const isSensitive = ref(props.emoji ? props.emoji.isSensitive : false);
+const localOnly = ref(props.emoji ? props.emoji.localOnly : false);
+const roleIdsThatCanBeUsedThisEmojiAsReaction = ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []);
+const rolesThatCanBeUsedThisEmojiAsReaction = ref([]);
+const file = ref<Misskey.entities.DriveFile>();
 
-watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => {
-	rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null);
+watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => {
+	rolesThatCanBeUsedThisEmojiAsReaction.value = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.value.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null);
 }, { immediate: true });
 
-const imgUrl = computed(() => file ? file.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null);
+const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null);
 
 const emit = defineEmits<{
 	(ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void,
@@ -115,42 +115,42 @@ const emit = defineEmits<{
 }>();
 
 async function changeImage(ev) {
-	file = await selectFile(ev.currentTarget ?? ev.target, null);
-	const candidate = file.name.replace(/\.(.+)$/, '');
+	file.value = await selectFile(ev.currentTarget ?? ev.target, null);
+	const candidate = file.value.name.replace(/\.(.+)$/, '');
 	if (candidate.match(/^[a-z0-9_]+$/)) {
-		name = candidate;
+		name.value = candidate;
 	}
 }
 
 async function addRole() {
 	const roles = await os.api('admin/roles/list');
-	const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id);
+	const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id);
 
 	const { canceled, result: role } = await os.select({
 		items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })),
 	});
 	if (canceled) return;
 
-	rolesThatCanBeUsedThisEmojiAsReaction.push(role);
+	rolesThatCanBeUsedThisEmojiAsReaction.value.push(role);
 }
 
 async function removeRole(role, ev) {
-	rolesThatCanBeUsedThisEmojiAsReaction = rolesThatCanBeUsedThisEmojiAsReaction.filter(x => x.id !== role.id);
+	rolesThatCanBeUsedThisEmojiAsReaction.value = rolesThatCanBeUsedThisEmojiAsReaction.value.filter(x => x.id !== role.id);
 }
 
 async function done() {
 	const params = {
-		name,
-		category: category === '' ? null : category,
-		aliases: aliases.split(' ').filter(x => x !== ''),
-		license: license === '' ? null : license,
-		isSensitive,
-		localOnly,
-		roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id),
+		name: name.value,
+		category: category.value === '' ? null : category.value,
+		aliases: aliases.value.split(' ').filter(x => x !== ''),
+		license: license.value === '' ? null : license.value,
+		isSensitive: isSensitive.value,
+		localOnly: localOnly.value,
+		roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id),
 	};
 
-	if (file) {
-		params.fileId = file.id;
+	if (file.value) {
+		params.fileId = file.value.id;
 	}
 
 	if (props.emoji) {
@@ -166,7 +166,7 @@ async function done() {
 			},
 		});
 
-		dialog.close();
+		dialog.value.close();
 	} else {
 		const created = await os.apiWithDialog('admin/emoji/add', params);
 
@@ -174,14 +174,14 @@ async function done() {
 			created: created,
 		});
 
-		dialog.close();
+		dialog.value.close();
 	}
 }
 
 async function del() {
 	const { canceled } = await os.confirm({
 		type: 'warning',
-		text: i18n.t('removeAreYouSure', { x: name }),
+		text: i18n.t('removeAreYouSure', { x: name.value }),
 	});
 	if (canceled) return;
 
@@ -191,7 +191,7 @@ async function del() {
 		emit('done', {
 			deleted: true,
 		});
-		dialog.close();
+		dialog.value.close();
 	});
 }
 </script>
diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue
index a36d1b3bda..000371528e 100644
--- a/packages/frontend/src/pages/explore.featured.vue
+++ b/packages/frontend/src/pages/explore.featured.vue
@@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref } from 'vue';
 import MkNotes from '@/components/MkNotes.vue';
 import MkTab from '@/components/MkTab.vue';
 import { i18n } from '@/i18n.js';
@@ -30,5 +31,5 @@ const paginationForPolls = {
 	offsetMode: true,
 };
 
-let tab = $ref('notes');
+const tab = ref('notes');
 </script>
diff --git a/packages/frontend/src/pages/explore.roles.vue b/packages/frontend/src/pages/explore.roles.vue
index 995ccd777c..929da19426 100644
--- a/packages/frontend/src/pages/explore.roles.vue
+++ b/packages/frontend/src/pages/explore.roles.vue
@@ -12,14 +12,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import MkRolePreview from '@/components/MkRolePreview.vue';
 import * as os from '@/os.js';
 
-let roles = $ref();
+const roles = ref();
 
 os.api('roles/list').then(res => {
-	roles = res.filter(x => x.target === 'manual').sort((a, b) => b.displayOrder - a.displayOrder);
+	roles.value = res.filter(x => x.target === 'manual').sort((a, b) => b.displayOrder - a.displayOrder);
 });
 </script>
 
diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue
index 1f187d6b8a..ffebd4cd6c 100644
--- a/packages/frontend/src/pages/explore.users.vue
+++ b/packages/frontend/src/pages/explore.users.vue
@@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, ref, shallowRef, computed } from 'vue';
 import MkUserList from '@/components/MkUserList.vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import MkTab from '@/components/MkTab.vue';
@@ -74,16 +74,16 @@ const props = defineProps<{
 	tag?: string;
 }>();
 
-let origin = $ref('local');
-let tagsEl = $shallowRef<InstanceType<typeof MkFoldableSection>>();
-let tagsLocal = $ref([]);
-let tagsRemote = $ref([]);
+const origin = ref('local');
+const tagsEl = shallowRef<InstanceType<typeof MkFoldableSection>>();
+const tagsLocal = ref([]);
+const tagsRemote = ref([]);
 
 watch(() => props.tag, () => {
-	if (tagsEl) tagsEl.toggleContent(props.tag == null);
+	if (tagsEl.value) tagsEl.value.toggleContent(props.tag == null);
 });
 
-const tagUsers = $computed(() => ({
+const tagUsers = computed(() => ({
 	endpoint: 'hashtags/users' as const,
 	limit: 30,
 	params: {
@@ -127,13 +127,13 @@ os.api('hashtags/list', {
 	attachedToLocalUserOnly: true,
 	limit: 30,
 }).then(tags => {
-	tagsLocal = tags;
+	tagsLocal.value = tags;
 });
 os.api('hashtags/list', {
 	sort: '+attachedRemoteUsers',
 	attachedToRemoteUserOnly: true,
 	limit: 30,
 }).then(tags => {
-	tagsRemote = tags;
+	tagsRemote.value = tags;
 });
 </script>
diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue
index fd846d979a..f068de8880 100644
--- a/packages/frontend/src/pages/explore.vue
+++ b/packages/frontend/src/pages/explore.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref, shallowRef } from 'vue';
 import XFeatured from './explore.featured.vue';
 import XUsers from './explore.users.vue';
 import XRoles from './explore.roles.vue';
@@ -36,16 +36,16 @@ const props = withDefaults(defineProps<{
 	initialTab: 'featured',
 });
 
-let tab = $ref(props.initialTab);
-let tagsEl = $shallowRef<InstanceType<typeof MkFoldableSection>>();
+const tab = ref(props.initialTab);
+const tagsEl = shallowRef<InstanceType<typeof MkFoldableSection>>();
 
 watch(() => props.tag, () => {
-	if (tagsEl) tagsEl.toggleContent(props.tag == null);
+	if (tagsEl.value) tagsEl.value.toggleContent(props.tag == null);
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'featured',
 	icon: 'ti ti-bolt',
 	title: i18n.ts.featured,
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index f494218c18..cfda4d6556 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
@@ -363,79 +363,79 @@ const props = defineProps<{
 	id?: string;
 }>();
 
-let flash = $ref(null);
-let visibility = $ref('public');
+const flash = ref(null);
+const visibility = ref('public');
 
 if (props.id) {
-	flash = await os.api('flash/show', {
+	flash.value = await os.api('flash/show', {
 		flashId: props.id,
 	});
 }
 
-let title = $ref(flash?.title ?? 'New Play');
-let summary = $ref(flash?.summary ?? '');
-let permissions = $ref(flash?.permissions ?? []);
-let script = $ref(flash?.script ?? PRESET_DEFAULT);
+const title = ref(flash.value?.title ?? 'New Play');
+const summary = ref(flash.value?.summary ?? '');
+const permissions = ref(flash.value?.permissions ?? []);
+const script = ref(flash.value?.script ?? PRESET_DEFAULT);
 
 function selectPreset(ev: MouseEvent) {
 	os.popupMenu([{
 		text: 'Omikuji',
 		action: () => {
-			script = PRESET_OMIKUJI;
+			script.value = PRESET_OMIKUJI;
 		},
 	}, {
 		text: 'Shuffle',
 		action: () => {
-			script = PRESET_SHUFFLE;
+			script.value = PRESET_SHUFFLE;
 		},
 	}, {
 		text: 'Quiz',
 		action: () => {
-			script = PRESET_QUIZ;
+			script.value = PRESET_QUIZ;
 		},
 	}, {
 		text: 'Timeline viewer',
 		action: () => {
-			script = PRESET_TIMELINE;
+			script.value = PRESET_TIMELINE;
 		},
 	}], ev.currentTarget ?? ev.target);
 }
 
 async function save() {
-	if (flash) {
+	if (flash.value) {
 		os.apiWithDialog('flash/update', {
 			flashId: props.id,
-			title,
-			summary,
-			permissions,
-			script,
-			visibility,
+			title: title.value,
+			summary: summary.value,
+			permissions: permissions.value,
+			script: script.value,
+			visibility: visibility.value,
 		});
 	} else {
 		const created = await os.apiWithDialog('flash/create', {
-			title,
-			summary,
-			permissions,
-			script,
+			title: title.value,
+			summary: summary.value,
+			permissions: permissions.value,
+			script: script.value,
 		});
 		router.push('/play/' + created.id + '/edit');
 	}
 }
 
 function show() {
-	if (flash == null) {
+	if (flash.value == null) {
 		os.alert({
 			text: 'Please save',
 		});
 	} else {
-		os.pageWindow(`/play/${flash.id}`);
+		os.pageWindow(`/play/${flash.value.id}`);
 	}
 }
 
 async function del() {
 	const { canceled } = await os.confirm({
 		type: 'warning',
-		text: i18n.t('deleteAreYouSure', { x: flash.title }),
+		text: i18n.t('deleteAreYouSure', { x: flash.value.title }),
 	});
 	if (canceled) return;
 
@@ -445,12 +445,12 @@ async function del() {
 	router.push('/play');
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => flash ? {
-	title: i18n.ts._play.edit + ': ' + flash.title,
+definePageMetadata(computed(() => flash.value ? {
+	title: i18n.ts._play.edit + ': ' + flash.value.title,
 } : {
 	title: i18n.ts._play.new,
 }));
diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue
index 4a686efd45..e0b9f87d46 100644
--- a/packages/frontend/src/pages/flash/flash-index.vue
+++ b/packages/frontend/src/pages/flash/flash-index.vue
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import MkFlashPreview from '@/components/MkFlashPreview.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -48,7 +48,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const router = useRouter();
 
-let tab = $ref('featured');
+const tab = ref('featured');
 
 const featuredFlashsPagination = {
 	endpoint: 'flash/featured' as const,
@@ -67,13 +67,13 @@ function create() {
 	router.push('/play/new');
 }
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	icon: 'ti ti-plus',
 	text: i18n.ts.create,
 	handler: create,
 }]);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'featured',
 	title: i18n.ts._play.featured,
 	icon: 'ti ti-flare',
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 4755eb5062..0ac95ca282 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -57,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, onDeactivated, onUnmounted, Ref, ref, watch } from 'vue';
+import { computed, onDeactivated, onUnmounted, Ref, ref, watch, shallowRef } from 'vue';
 import { Interpreter, Parser, values } from '@syuilo/aiscript';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
@@ -78,45 +78,45 @@ const props = defineProps<{
 	id: string;
 }>();
 
-let flash = $ref(null);
-let error = $ref(null);
+const flash = ref(null);
+const error = ref(null);
 
 function fetchFlash() {
-	flash = null;
+	flash.value = null;
 	os.api('flash/show', {
 		flashId: props.id,
 	}).then(_flash => {
-		flash = _flash;
+		flash.value = _flash;
 	}).catch(err => {
-		error = err;
+		error.value = err;
 	});
 }
 
 function copyLink() {
-	copyToClipboard(`${url}/play/${flash.id}`);
+	copyToClipboard(`${url}/play/${flash.value.id}`);
 	os.success();
 }
 
 function share() {
 	navigator.share({
-		title: flash.title,
-		text: flash.summary,
-		url: `${url}/play/${flash.id}`,
+		title: flash.value.title,
+		text: flash.value.summary,
+		url: `${url}/play/${flash.value.id}`,
 	});
 }
 
 function shareWithNote() {
 	os.post({
-		initialText: `${flash.title} ${url}/play/${flash.id}`,
+		initialText: `${flash.value.title} ${url}/play/${flash.value.id}`,
 	});
 }
 
 function like() {
 	os.apiWithDialog('flash/like', {
-		flashId: flash.id,
+		flashId: flash.value.id,
 	}).then(() => {
-		flash.isLiked = true;
-		flash.likedCount++;
+		flash.value.isLiked = true;
+		flash.value.likedCount++;
 	});
 }
 
@@ -127,10 +127,10 @@ async function unlike() {
 	});
 	if (confirm.canceled) return;
 	os.apiWithDialog('flash/unlike', {
-		flashId: flash.id,
+		flashId: flash.value.id,
 	}).then(() => {
-		flash.isLiked = false;
-		flash.likedCount--;
+		flash.value.isLiked = false;
+		flash.value.likedCount--;
 	});
 }
 
@@ -138,28 +138,28 @@ watch(() => props.id, fetchFlash, { immediate: true });
 
 const parser = new Parser();
 
-let started = $ref(false);
-let aiscript = $shallowRef<Interpreter | null>(null);
+const started = ref(false);
+const aiscript = shallowRef<Interpreter | null>(null);
 const root = ref<AsUiRoot>();
-const components: Ref<AsUiComponent>[] = $ref([]);
+const components = ref<Ref<AsUiComponent>[]>([]);
 
 function start() {
-	started = true;
+	started.value = true;
 	run();
 }
 
 async function run() {
-	if (aiscript) aiscript.abort();
+	if (aiscript.value) aiscript.value.abort();
 
-	aiscript = new Interpreter({
+	aiscript.value = new Interpreter({
 		...createAiScriptEnv({
-			storageKey: 'flash:' + flash.id,
+			storageKey: 'flash:' + flash.value.id,
 		}),
-		...registerAsUiLib(components, (_root) => {
+		...registerAsUiLib(components.value, (_root) => {
 			root.value = _root.value;
 		}),
-		THIS_ID: values.STR(flash.id),
-		THIS_URL: values.STR(`${url}/play/${flash.id}`),
+		THIS_ID: values.STR(flash.value.id),
+		THIS_URL: values.STR(`${url}/play/${flash.value.id}`),
 	}, {
 		in: (q) => {
 			return new Promise(ok => {
@@ -184,7 +184,7 @@ async function run() {
 
 	let ast;
 	try {
-		ast = parser.parse(flash.script);
+		ast = parser.parse(flash.value.script);
 	} catch (err) {
 		os.alert({
 			type: 'error',
@@ -193,7 +193,7 @@ async function run() {
 		return;
 	}
 	try {
-		await aiscript.exec(ast);
+		await aiscript.value.exec(ast);
 	} catch (err) {
 		os.alert({
 			type: 'error',
@@ -204,24 +204,24 @@ async function run() {
 }
 
 onDeactivated(() => {
-	if (aiscript) aiscript.abort();
+	if (aiscript.value) aiscript.value.abort();
 });
 
 onUnmounted(() => {
-	if (aiscript) aiscript.abort();
+	if (aiscript.value) aiscript.value.abort();
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => flash ? {
-	title: flash.title,
-	avatar: flash.user,
-	path: `/play/${flash.id}`,
+definePageMetadata(computed(() => flash.value ? {
+	title: flash.value.title,
+	avatar: flash.value.user,
+	path: `/play/${flash.value.id}`,
 	share: {
-		title: flash.title,
-		text: flash.summary,
+		title: flash.value.title,
+		text: flash.value.summary,
 	},
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue
index 4f7fdef0ba..51f31b1ca5 100644
--- a/packages/frontend/src/pages/follow-requests.vue
+++ b/packages/frontend/src/pages/follow-requests.vue
@@ -65,9 +65,9 @@ function reject(user) {
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(computed(() => ({
 	title: i18n.ts.followRequests,
diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue
index f3cbd4947c..5761e8e32c 100644
--- a/packages/frontend/src/pages/gallery/edit.vue
+++ b/packages/frontend/src/pages/gallery/edit.vue
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
@@ -56,38 +56,38 @@ const props = defineProps<{
 	postId?: string;
 }>();
 
-let init = $ref(null);
-let files = $ref([]);
-let description = $ref(null);
-let title = $ref(null);
-let isSensitive = $ref(false);
+const init = ref(null);
+const files = ref([]);
+const description = ref(null);
+const title = ref(null);
+const isSensitive = ref(false);
 
 function selectFile(evt) {
 	selectFiles(evt.currentTarget ?? evt.target, null).then(selected => {
-		files = files.concat(selected);
+		files.value = files.value.concat(selected);
 	});
 }
 
 function remove(file) {
-	files = files.filter(f => f.id !== file.id);
+	files.value = files.value.filter(f => f.id !== file.id);
 }
 
 async function save() {
 	if (props.postId) {
 		await os.apiWithDialog('gallery/posts/update', {
 			postId: props.postId,
-			title: title,
-			description: description,
-			fileIds: files.map(file => file.id),
-			isSensitive: isSensitive,
+			title: title.value,
+			description: description.value,
+			fileIds: files.value.map(file => file.id),
+			isSensitive: isSensitive.value,
 		});
 		router.push(`/gallery/${props.postId}`);
 	} else {
 		const created = await os.apiWithDialog('gallery/posts/create', {
-			title: title,
-			description: description,
-			fileIds: files.map(file => file.id),
-			isSensitive: isSensitive,
+			title: title.value,
+			description: description.value,
+			fileIds: files.value.map(file => file.id),
+			isSensitive: isSensitive.value,
 		});
 		router.push(`/gallery/${created.id}`);
 	}
@@ -106,19 +106,19 @@ async function del() {
 }
 
 watch(() => props.postId, () => {
-	init = () => props.postId ? os.api('gallery/posts/show', {
+	init.value = () => props.postId ? os.api('gallery/posts/show', {
 		postId: props.postId,
 	}).then(post => {
-		files = post.files;
-		title = post.title;
-		description = post.description;
-		isSensitive = post.isSensitive;
+		files.value = post.files;
+		title.value = post.title;
+		description.value = post.description;
+		isSensitive.value = post.isSensitive;
 	}) : Promise.resolve(null);
 }, { immediate: true });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(computed(() => props.postId ? {
 	title: i18n.ts.edit,
diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue
index 43bb7c496d..8d9ac07805 100644
--- a/packages/frontend/src/pages/gallery/index.vue
+++ b/packages/frontend/src/pages/gallery/index.vue
@@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, ref, computed } from 'vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
@@ -61,9 +61,9 @@ const props = defineProps<{
 	tag?: string;
 }>();
 
-let tab = $ref('explore');
-let tags = $ref([]);
-let tagsRef = $ref();
+const tab = ref('explore');
+const tags = ref([]);
+const tagsRef = ref();
 
 const recentPostsPagination = {
 	endpoint: 'gallery/posts' as const,
@@ -82,7 +82,7 @@ const likedPostsPagination = {
 	limit: 5,
 };
 
-const tagUsersPagination = $computed(() => ({
+const tagUsersPagination = computed(() => ({
 	endpoint: 'hashtags/users' as const,
 	limit: 30,
 	params: {
@@ -93,10 +93,10 @@ const tagUsersPagination = $computed(() => ({
 }));
 
 watch(() => props.tag, () => {
-	if (tagsRef) tagsRef.tags.toggleContent(props.tag == null);
+	if (tagsRef.value) tagsRef.value.tags.toggleContent(props.tag == null);
 });
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	icon: 'ti ti-plus',
 	text: i18n.ts.create,
 	handler: () => {
@@ -104,7 +104,7 @@ const headerActions = $computed(() => [{
 	},
 }]);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'explore',
 	title: i18n.ts.gallery,
 	icon: 'ti ti-icons',
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 5b551f75b5..3dd04ccb55 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
@@ -84,43 +84,43 @@ const props = defineProps<{
 	postId: string;
 }>();
 
-let post = $ref(null);
-let error = $ref(null);
+const post = ref(null);
+const error = ref(null);
 const otherPostsPagination = {
 	endpoint: 'users/gallery/posts' as const,
 	limit: 6,
 	params: computed(() => ({
-		userId: post.user.id,
+		userId: post.value.user.id,
 	})),
 };
 
 function fetchPost() {
-	post = null;
+	post.value = null;
 	os.api('gallery/posts/show', {
 		postId: props.postId,
 	}).then(_post => {
-		post = _post;
+		post.value = _post;
 	}).catch(_error => {
-		error = _error;
+		error.value = _error;
 	});
 }
 
 function copyLink() {
-	copyToClipboard(`${url}/gallery/${post.id}`);
+	copyToClipboard(`${url}/gallery/${post.value.id}`);
 	os.success();
 }
 
 function share() {
 	navigator.share({
-		title: post.title,
-		text: post.description,
-		url: `${url}/gallery/${post.id}`,
+		title: post.value.title,
+		text: post.value.description,
+		url: `${url}/gallery/${post.value.id}`,
 	});
 }
 
 function shareWithNote() {
 	os.post({
-		initialText: `${post.title} ${url}/gallery/${post.id}`,
+		initialText: `${post.value.title} ${url}/gallery/${post.value.id}`,
 	});
 }
 
@@ -128,8 +128,8 @@ function like() {
 	os.apiWithDialog('gallery/posts/like', {
 		postId: props.postId,
 	}).then(() => {
-		post.isLiked = true;
-		post.likedCount++;
+		post.value.isLiked = true;
+		post.value.likedCount++;
 	});
 }
 
@@ -142,28 +142,28 @@ async function unlike() {
 	os.apiWithDialog('gallery/posts/unlike', {
 		postId: props.postId,
 	}).then(() => {
-		post.isLiked = false;
-		post.likedCount--;
+		post.value.isLiked = false;
+		post.value.likedCount--;
 	});
 }
 
 function edit() {
-	router.push(`/gallery/${post.id}/edit`);
+	router.push(`/gallery/${post.value.id}/edit`);
 }
 
 watch(() => props.postId, fetchPost, { immediate: true });
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	icon: 'ti ti-pencil',
 	text: i18n.ts.edit,
 	handler: edit,
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => post ? {
-	title: post.title,
-	avatar: post.user,
+definePageMetadata(computed(() => post.value ? {
+	title: post.value.title,
+	avatar: post.value.user,
 } : null));
 </script>
 
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 8706228fd1..93d74fb42e 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -117,7 +117,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkChart from '@/components/MkChart.vue';
 import MkObjectView from '@/components/MkObjectView.vue';
@@ -142,14 +142,14 @@ const props = defineProps<{
 	host: string;
 }>();
 
-let tab = $ref('overview');
-let chartSrc = $ref('instance-requests');
-let meta = $ref<Misskey.entities.AdminMetaResponse | null>(null);
-let instance = $ref<Misskey.entities.FederationInstance | null>(null);
-let suspended = $ref(false);
-let isBlocked = $ref(false);
-let isSilenced = $ref(false);
-let faviconUrl = $ref<string | null>(null);
+const tab = ref('overview');
+const chartSrc = ref('instance-requests');
+const meta = ref<Misskey.entities.AdminMetaResponse | null>(null);
+const instance = ref<Misskey.entities.FederationInstance | null>(null);
+const suspended = ref(false);
+const isBlocked = ref(false);
+const isSilenced = ref(false);
+const faviconUrl = ref<string | null>(null);
 
 const usersPagination = {
 	endpoint: iAmModerator ? 'admin/show-users' : 'users' as const,
@@ -164,48 +164,48 @@ const usersPagination = {
 
 async function fetch(): Promise<void> {
 	if (iAmAdmin) {
-		meta = await os.api('admin/meta');
+		meta.value = await os.api('admin/meta');
 	}
-	instance = await os.api('federation/show-instance', {
+	instance.value = await os.api('federation/show-instance', {
 		host: props.host,
 	});
-	suspended = instance?.isSuspended ?? false;
-	isBlocked = instance?.isBlocked ?? false;
-	isSilenced = instance?.isSilenced ?? false;
-	faviconUrl = getProxiedImageUrlNullable(instance?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance?.iconUrl, 'preview');
+	suspended.value = instance.value?.isSuspended ?? false;
+	isBlocked.value = instance.value?.isBlocked ?? false;
+	isSilenced.value = instance.value?.isSilenced ?? false;
+	faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview');
 }
 
 async function toggleBlock(): Promise<void> {
-	if (!meta) throw new Error('No meta?');
-	if (!instance) throw new Error('No instance?');
-	const { host } = instance;
+	if (!meta.value) throw new Error('No meta?');
+	if (!instance.value) throw new Error('No instance?');
+	const { host } = instance.value;
 	await os.api('admin/update-meta', {
-		blockedHosts: isBlocked ? meta.blockedHosts.concat([host]) : meta.blockedHosts.filter(x => x !== host),
+		blockedHosts: isBlocked.value ? meta.value.blockedHosts.concat([host]) : meta.value.blockedHosts.filter(x => x !== host),
 	});
 }
 
 async function toggleSilenced(): Promise<void> {
-	if (!meta) throw new Error('No meta?');
-	if (!instance) throw new Error('No instance?');
-	const { host } = instance;
-	const silencedHosts = meta.silencedHosts ?? [];
+	if (!meta.value) throw new Error('No meta?');
+	if (!instance.value) throw new Error('No instance?');
+	const { host } = instance.value;
+	const silencedHosts = meta.value.silencedHosts ?? [];
 	await os.api('admin/update-meta', {
-		silencedHosts: isSilenced ? silencedHosts.concat([host]) : silencedHosts.filter(x => x !== host),
+		silencedHosts: isSilenced.value ? silencedHosts.concat([host]) : silencedHosts.filter(x => x !== host),
 	});
 }
 
 async function toggleSuspend(): Promise<void> {
-	if (!instance) throw new Error('No instance?');
+	if (!instance.value) throw new Error('No instance?');
 	await os.api('admin/federation/update-instance', {
-		host: instance.host,
-		isSuspended: suspended,
+		host: instance.value.host,
+		isSuspended: suspended.value,
 	});
 }
 
 function refreshMetadata(): void {
-	if (!instance) throw new Error('No instance?');
+	if (!instance.value) throw new Error('No instance?');
 	os.api('admin/federation/refresh-remote-instance-metadata', {
-		host: instance.host,
+		host: instance.value.host,
 	});
 	os.alert({
 		text: 'Refresh requested',
@@ -214,7 +214,7 @@ function refreshMetadata(): void {
 
 fetch();
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	text: `https://${props.host}`,
 	icon: 'ti ti-external-link',
 	handler: () => {
@@ -222,7 +222,7 @@ const headerActions = $computed(() => [{
 	},
 }]);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'overview',
 	title: i18n.ts.overview,
 	icon: 'ti ti-info-circle',
diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue
index db0b201b73..d951e8ce07 100644
--- a/packages/frontend/src/pages/list.vue
+++ b/packages/frontend/src/pages/list.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch, computed } from 'vue';
+import { watch, computed, ref } from 'vue';
 import * as os from '@/os.js';
 import { userPage } from '@/filters/user.js';
 import { i18n } from '@/i18n.js';
@@ -47,41 +47,41 @@ const props = defineProps<{
 	listId: string;
 }>();
 
-let list = $ref(null);
-let error = $ref();
-let users = $ref([]);
+const list = ref(null);
+const error = ref();
+const users = ref([]);
 
 function fetchList(): void {
 	os.api('users/lists/show', {
 		listId: props.listId,
 		forPublic: true,
 	}).then(_list => {
-		list = _list;
+		list.value = _list;
 		os.api('users/show', {
-			userIds: list.userIds,
+			userIds: list.value.userIds,
 		}).then(_users => {
-			users = _users;
+			users.value = _users;
 		});
 	}).catch(err => {
-		error = err;
+		error.value = err;
 	});
 }
 
 function like() {
 	os.apiWithDialog('users/lists/favorite', {
-		listId: list.id,
+		listId: list.value.id,
 	}).then(() => {
-		list.isLiked = true;
-		list.likedCount++;
+		list.value.isLiked = true;
+		list.value.likedCount++;
 	});
 }
 
 function unlike() {
 	os.apiWithDialog('users/lists/unfavorite', {
-		listId: list.id,
+		listId: list.value.id,
 	}).then(() => {
-		list.isLiked = false;
-		list.likedCount--;
+		list.value.isLiked = false;
+		list.value.likedCount--;
 	});
 }
 
@@ -90,17 +90,17 @@ async function create() {
 		title: i18n.ts.enterListName,
 	});
 	if (canceled) return;
-	await os.apiWithDialog('users/lists/create-from-public', { name: name, listId: list.id });
+	await os.apiWithDialog('users/lists/create-from-public', { name: name, listId: list.value.id });
 }
 
 watch(() => props.listId, fetchList, { immediate: true });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => list ? {
-	title: list.name,
+definePageMetadata(computed(() => list.value ? {
+	title: list.value.name,
 	icon: 'ti ti-list',
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue
index 1572d27aab..ad9bea4548 100644
--- a/packages/frontend/src/pages/miauth.vue
+++ b/packages/frontend/src/pages/miauth.vue
@@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import MkSignin from '@/components/MkSignin.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
@@ -61,10 +61,10 @@ const props = defineProps<{
 
 const _permissions = props.permission ? props.permission.split(',') : [];
 
-let state = $ref<string | null>(null);
+const state = ref<string | null>(null);
 
 async function accept(): Promise<void> {
-	state = 'waiting';
+	state.value = 'waiting';
 	await os.api('miauth/gen-token', {
 		session: props.session,
 		name: props.name,
@@ -72,7 +72,7 @@ async function accept(): Promise<void> {
 		permission: _permissions,
 	});
 
-	state = 'accepted';
+	state.value = 'accepted';
 	if (props.callback) {
 		const cbUrl = new URL(props.callback);
 		if (['javascript:', 'file:', 'data:', 'mailto:', 'tel:'].includes(cbUrl.protocol)) throw new Error('invalid url');
@@ -82,16 +82,16 @@ async function accept(): Promise<void> {
 }
 
 function deny(): void {
-	state = 'denied';
+	state.value = 'denied';
 }
 
 function onLogin(res): void {
 	login(res.i);
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: 'MiAuth',
diff --git a/packages/frontend/src/pages/my-antennas/create.vue b/packages/frontend/src/pages/my-antennas/create.vue
index 6c963cdb5d..c5b1b54222 100644
--- a/packages/frontend/src/pages/my-antennas/create.vue
+++ b/packages/frontend/src/pages/my-antennas/create.vue
@@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref } from 'vue';
 import XAntenna from './editor.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -18,7 +19,7 @@ import { antennasCache } from '@/cache.js';
 
 const router = useRouter();
 
-let draft = $ref({
+const draft = ref({
 	name: '',
 	src: 'all',
 	userListId: null,
diff --git a/packages/frontend/src/pages/my-antennas/edit.vue b/packages/frontend/src/pages/my-antennas/edit.vue
index 6600ebcc64..896e61f289 100644
--- a/packages/frontend/src/pages/my-antennas/edit.vue
+++ b/packages/frontend/src/pages/my-antennas/edit.vue
@@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref } from 'vue';
 import XAntenna from './editor.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
@@ -19,7 +20,7 @@ import { antennasCache } from '@/cache';
 
 const router = useRouter();
 
-let antenna: any = $ref(null);
+const antenna = ref<any>(null);
 
 const props = defineProps<{
 	antennaId: string
@@ -31,7 +32,7 @@ function onAntennaUpdated() {
 }
 
 os.api('antennas/show', { antennaId: props.antennaId }).then((antennaResponse) => {
-	antenna = antennaResponse;
+	antenna.value = antennaResponse;
 });
 
 definePageMetadata({
diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue
index 16b8b848fd..388096c7df 100644
--- a/packages/frontend/src/pages/my-antennas/editor.vue
+++ b/packages/frontend/src/pages/my-antennas/editor.vue
@@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -69,38 +69,38 @@ const emit = defineEmits<{
 	(ev: 'deleted'): void,
 }>();
 
-let name: string = $ref(props.antenna.name);
-let src: string = $ref(props.antenna.src);
-let userListId: any = $ref(props.antenna.userListId);
-let users: string = $ref(props.antenna.users.join('\n'));
-let keywords: string = $ref(props.antenna.keywords.map(x => x.join(' ')).join('\n'));
-let excludeKeywords: string = $ref(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n'));
-let caseSensitive: boolean = $ref(props.antenna.caseSensitive);
-let localOnly: boolean = $ref(props.antenna.localOnly);
-let withReplies: boolean = $ref(props.antenna.withReplies);
-let withFile: boolean = $ref(props.antenna.withFile);
-let notify: boolean = $ref(props.antenna.notify);
-let userLists: any = $ref(null);
+const name = ref<string>(props.antenna.name);
+const src = ref<string>(props.antenna.src);
+const userListId = ref<any>(props.antenna.userListId);
+const users = ref<string>(props.antenna.users.join('\n'));
+const keywords = ref<string>(props.antenna.keywords.map(x => x.join(' ')).join('\n'));
+const excludeKeywords = ref<string>(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n'));
+const caseSensitive = ref<boolean>(props.antenna.caseSensitive);
+const localOnly = ref<boolean>(props.antenna.localOnly);
+const withReplies = ref<boolean>(props.antenna.withReplies);
+const withFile = ref<boolean>(props.antenna.withFile);
+const notify = ref<boolean>(props.antenna.notify);
+const userLists = ref<any>(null);
 
-watch(() => src, async () => {
-	if (src === 'list' && userLists === null) {
-		userLists = await os.api('users/lists/list');
+watch(() => src.value, async () => {
+	if (src.value === 'list' && userLists.value === null) {
+		userLists.value = await os.api('users/lists/list');
 	}
 });
 
 async function saveAntenna() {
 	const antennaData = {
-		name,
-		src,
-		userListId,
-		withReplies,
-		withFile,
-		notify,
-		caseSensitive,
-		localOnly,
-		users: users.trim().split('\n').map(x => x.trim()),
-		keywords: keywords.trim().split('\n').map(x => x.trim().split(' ')),
-		excludeKeywords: excludeKeywords.trim().split('\n').map(x => x.trim().split(' ')),
+		name: name.value,
+		src: src.value,
+		userListId: userListId.value,
+		withReplies: withReplies.value,
+		withFile: withFile.value,
+		notify: notify.value,
+		caseSensitive: caseSensitive.value,
+		localOnly: localOnly.value,
+		users: users.value.trim().split('\n').map(x => x.trim()),
+		keywords: keywords.value.trim().split('\n').map(x => x.trim().split(' ')),
+		excludeKeywords: excludeKeywords.value.trim().split('\n').map(x => x.trim().split(' ')),
 	};
 
 	if (props.antenna.id == null) {
@@ -130,9 +130,9 @@ async function deleteAntenna() {
 
 function addUser() {
 	os.selectUser().then(user => {
-		users = users.trim();
-		users += '\n@' + Misskey.acct.toString(user as any);
-		users = users.trim();
+		users.value = users.value.trim();
+		users.value += '\n@' + Misskey.acct.toString(user as any);
+		users.value = users.value.trim();
 	});
 }
 </script>
diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue
index 2d9cd05b45..4b2a3b548d 100644
--- a/packages/frontend/src/pages/my-antennas/index.vue
+++ b/packages/frontend/src/pages/my-antennas/index.vue
@@ -28,14 +28,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onActivated } from 'vue';
+import { onActivated, computed } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { antennasCache } from '@/cache';
 import { infoImageUrl } from '@/instance.js';
 
-const antennas = $computed(() => antennasCache.value.value ?? []);
+const antennas = computed(() => antennasCache.value.value ?? []);
 
 function fetch() {
 	antennasCache.fetch();
@@ -43,7 +43,7 @@ function fetch() {
 
 fetch();
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	asFullButton: true,
 	icon: 'ti ti-refresh',
 	text: i18n.ts.reload,
@@ -53,7 +53,7 @@ const headerActions = $computed(() => [{
 	},
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.manageAntennas,
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index 8289d65a4b..2390617954 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, ref, shallowRef, computed } from 'vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkClipPreview from '@/components/MkClipPreview.vue';
@@ -41,13 +41,13 @@ const pagination = {
 	limit: 10,
 };
 
-let tab = $ref('my');
-let favorites = $ref();
+const tab = ref('my');
+const favorites = ref();
 
-const pagingComponent = $shallowRef<InstanceType<typeof MkPagination>>();
+const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
 
-watch($$(tab), async () => {
-	favorites = await os.api('clips/my-favorites');
+watch(tab, async () => {
+	favorites.value = await os.api('clips/my-favorites');
 });
 
 async function create() {
@@ -74,20 +74,20 @@ async function create() {
 
 	clipsCache.delete();
 
-	pagingComponent.reload();
+	pagingComponent.value.reload();
 }
 
 function onClipCreated() {
-	pagingComponent.reload();
+	pagingComponent.value.reload();
 }
 
 function onClipDeleted() {
-	pagingComponent.reload();
+	pagingComponent.value.reload();
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'my',
 	title: i18n.ts.myClips,
 	icon: 'ti ti-paperclip',
diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue
index 3e7efb5a7c..ff360fccfe 100644
--- a/packages/frontend/src/pages/my-lists/index.vue
+++ b/packages/frontend/src/pages/my-lists/index.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onActivated } from 'vue';
+import { onActivated, computed } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkAvatars from '@/components/MkAvatars.vue';
 import * as os from '@/os.js';
@@ -39,7 +39,7 @@ import { userListsCache } from '@/cache';
 import { infoImageUrl } from '@/instance.js';
 import { $i } from '@/account.js';
 
-const items = $computed(() => userListsCache.value.value ?? []);
+const items = computed(() => userListsCache.value.value ?? []);
 
 function fetch() {
 	userListsCache.fetch();
@@ -57,7 +57,7 @@ async function create() {
 	fetch();
 }
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	asFullButton: true,
 	icon: 'ti ti-refresh',
 	text: i18n.ts.reload,
@@ -67,7 +67,7 @@ const headerActions = $computed(() => [{
 	},
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.manageLists,
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index b600f99fbc..3c6b0750de 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -79,7 +79,7 @@ const props = defineProps<{
 }>();
 
 const paginationEl = ref<InstanceType<typeof MkPagination>>();
-let list = $ref<Misskey.entities.UserList | null>(null);
+const list = ref<Misskey.entities.UserList | null>(null);
 const isPublic = ref(false);
 const name = ref('');
 const membershipsPagination = {
@@ -94,17 +94,17 @@ function fetchList() {
 	os.api('users/lists/show', {
 		listId: props.listId,
 	}).then(_list => {
-		list = _list;
-		name.value = list.name;
-		isPublic.value = list.isPublic;
+		list.value = _list;
+		name.value = list.value.name;
+		isPublic.value = list.value.isPublic;
 	});
 }
 
 function addUser() {
 	os.selectUser().then(user => {
-		if (!list) return;
+		if (!list.value) return;
 		os.apiWithDialog('users/lists/push', {
-			listId: list.id,
+			listId: list.value.id,
 			userId: user.id,
 		}).then(() => {
 			paginationEl.value.reload();
@@ -118,9 +118,9 @@ async function removeUser(item, ev) {
 		icon: 'ti ti-x',
 		danger: true,
 		action: async () => {
-			if (!list) return;
+			if (!list.value) return;
 			os.api('users/lists/pull', {
-				listId: list.id,
+				listId: list.value.id,
 				userId: item.userId,
 			}).then(() => {
 				paginationEl.value.removeItem(item.id);
@@ -135,7 +135,7 @@ async function showMembershipMenu(item, ev) {
 		icon: item.withReplies ? 'ti ti-messages-off' : 'ti ti-messages',
 		action: async () => {
 			os.api('users/lists/update-membership', {
-				listId: list.id,
+				listId: list.value.id,
 				userId: item.userId,
 				withReplies: !item.withReplies,
 			}).then(() => {
@@ -149,42 +149,42 @@ async function showMembershipMenu(item, ev) {
 }
 
 async function deleteList() {
-	if (!list) return;
+	if (!list.value) return;
 	const { canceled } = await os.confirm({
 		type: 'warning',
-		text: i18n.t('removeAreYouSure', { x: list.name }),
+		text: i18n.t('removeAreYouSure', { x: list.value.name }),
 	});
 	if (canceled) return;
 
 	await os.apiWithDialog('users/lists/delete', {
-		listId: list.id,
+		listId: list.value.id,
 	});
 	userListsCache.delete();
 	mainRouter.push('/my/lists');
 }
 
 async function updateSettings() {
-	if (!list) return;
+	if (!list.value) return;
 	await os.apiWithDialog('users/lists/update', {
-		listId: list.id,
+		listId: list.value.id,
 		name: name.value,
 		isPublic: isPublic.value,
 	});
 
 	userListsCache.delete();
 
-	list.name = name.value;
-	list.isPublic = isPublic.value;
+	list.value.name = name.value;
+	list.value.isPublic = isPublic.value;
 }
 
 watch(() => props.listId, fetchList, { immediate: true });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => list ? {
-	title: list.name,
+definePageMetadata(computed(() => list.value ? {
+	title: list.value.name,
 	icon: 'ti ti-list',
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/not-found.vue b/packages/frontend/src/pages/not-found.vue
index b3d40e3ef8..2245147873 100644
--- a/packages/frontend/src/pages/not-found.vue
+++ b/packages/frontend/src/pages/not-found.vue
@@ -13,6 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { computed } from 'vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { pleaseLogin } from '@/scripts/please-login.js';
@@ -26,9 +27,9 @@ if (props.showLoginPopup) {
 	pleaseLogin('/');
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.notFound,
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index 066a3042ba..eee6dbfbb8 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
 import MkNotes from '@/components/MkNotes.vue';
@@ -61,18 +61,18 @@ const props = defineProps<{
 	noteId: string;
 }>();
 
-let note = $ref<null | Misskey.entities.Note>();
-let clips = $ref();
-let showPrev = $ref(false);
-let showNext = $ref(false);
-let error = $ref();
+const note = ref<null | Misskey.entities.Note>();
+const clips = ref();
+const showPrev = ref(false);
+const showNext = ref(false);
+const error = ref();
 
 const prevPagination = {
 	endpoint: 'users/notes' as const,
 	limit: 10,
-	params: computed(() => note ? ({
-		userId: note.userId,
-		untilId: note.id,
+	params: computed(() => note.value ? ({
+		userId: note.value.userId,
+		untilId: note.value.id,
 	}) : null),
 };
 
@@ -80,30 +80,30 @@ const nextPagination = {
 	reversed: true,
 	endpoint: 'users/notes' as const,
 	limit: 10,
-	params: computed(() => note ? ({
-		userId: note.userId,
-		sinceId: note.id,
+	params: computed(() => note.value ? ({
+		userId: note.value.userId,
+		sinceId: note.value.id,
 	}) : null),
 };
 
 function fetchNote() {
-	showPrev = false;
-	showNext = false;
-	note = null;
+	showPrev.value = false;
+	showNext.value = false;
+	note.value = null;
 	os.api('notes/show', {
 		noteId: props.noteId,
 	}).then(res => {
-		note = res;
+		note.value = res;
 		// 古いノートは被クリップ数をカウントしていないので、2023-10-01以前のものは強制的にnotes/clipsを叩く
-		if (note.clippedCount > 0 || new Date(note.createdAt).getTime() < new Date('2023-10-01').getTime()) {
+		if (note.value.clippedCount > 0 || new Date(note.value.createdAt).getTime() < new Date('2023-10-01').getTime()) {
 			os.api('notes/clips', {
-				noteId: note.id,
+				noteId: note.value.id,
 			}).then((_clips) => {
-				clips = _clips;
+				clips.value = _clips;
 			});
 		}
 	}).catch(err => {
-		error = err;
+		error.value = err;
 	});
 }
 
@@ -111,18 +111,18 @@ watch(() => props.noteId, fetchNote, {
 	immediate: true,
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => note ? {
+definePageMetadata(computed(() => note.value ? {
 	title: i18n.ts.note,
-	subtitle: dateString(note.createdAt),
-	avatar: note.user,
-	path: `/notes/${note.id}`,
+	subtitle: dateString(note.value.createdAt),
+	avatar: note.value.user,
+	path: `/notes/${note.value.id}`,
 	share: {
-		title: i18n.t('noteOf', { user: note.user.name }),
-		text: note.text,
+		title: i18n.t('noteOf', { user: note.value.user.name }),
+		text: note.value.text,
 	},
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue
index 8d2475b085..71ce7c353b 100644
--- a/packages/frontend/src/pages/notifications.vue
+++ b/packages/frontend/src/pages/notifications.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import XNotifications from '@/components/MkNotifications.vue';
 import MkNotes from '@/components/MkNotes.vue';
 import * as os from '@/os.js';
@@ -29,9 +29,9 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { notificationTypes } from '@/const.js';
 
-let tab = $ref('all');
-let includeTypes = $ref<string[] | null>(null);
-const excludeTypes = $computed(() => includeTypes ? notificationTypes.filter(t => !includeTypes.includes(t)) : null);
+const tab = ref('all');
+const includeTypes = ref<string[] | null>(null);
+const excludeTypes = computed(() => includeTypes.value ? notificationTypes.filter(t => !includeTypes.value.includes(t)) : null);
 
 const mentionsPagination = {
 	endpoint: 'notes/mentions' as const,
@@ -49,27 +49,27 @@ const directNotesPagination = {
 function setFilter(ev) {
 	const typeItems = notificationTypes.map(t => ({
 		text: i18n.t(`_notification._types.${t}`),
-		active: includeTypes && includeTypes.includes(t),
+		active: includeTypes.value && includeTypes.value.includes(t),
 		action: () => {
-			includeTypes = [t];
+			includeTypes.value = [t];
 		},
 	}));
-	const items = includeTypes != null ? [{
+	const items = includeTypes.value != null ? [{
 		icon: 'ti ti-x',
 		text: i18n.ts.clear,
 		action: () => {
-			includeTypes = null;
+			includeTypes.value = null;
 		},
 	}, null, ...typeItems] : typeItems;
 	os.popupMenu(items, ev.currentTarget ?? ev.target);
 }
 
-const headerActions = $computed(() => [tab === 'all' ? {
+const headerActions = computed(() => [tab.value === 'all' ? {
 	text: i18n.ts.filter,
 	icon: 'ti ti-filter',
-	highlighted: includeTypes != null,
+	highlighted: includeTypes.value != null,
 	handler: setFilter,
-} : undefined, tab === 'all' ? {
+} : undefined, tab.value === 'all' ? {
 	text: i18n.ts.markAllAsRead,
 	icon: 'ti ti-check',
 	handler: () => {
@@ -77,7 +77,7 @@ const headerActions = $computed(() => [tab === 'all' ? {
 	},
 } : undefined].filter(x => x !== undefined));
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'all',
 	title: i18n.ts.all,
 	icon: 'ti ti-point',
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
index 4fffd311b2..e3f116dc6c 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 /* eslint-disable vue/no-mutating-props */
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import XContainer from '../page-editor.container.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
 import * as os from '@/os.js';
@@ -35,14 +35,14 @@ const emit = defineEmits<{
 	(ev: 'update:modelValue', value: any): void;
 }>();
 
-let file: any = $ref(null);
+const file = ref<any>(null);
 
 async function choose() {
 	os.selectDriveFile(false).then((fileResponse) => {
-		file = fileResponse[0];
+		file.value = fileResponse[0];
 		emit('update:modelValue', {
 			...props.modelValue,
-			fileId: file.id,
+			fileId: file.value.id,
 		});
 	});
 }
@@ -54,7 +54,7 @@ onMounted(async () => {
 		os.api('drive/files/show', {
 			fileId: props.modelValue.fileId,
 		}).then(fileResponse => {
-			file = fileResponse;
+			file.value = fileResponse;
 		});
 	}
 });
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
index fc436aad75..ce3980ac8d 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 /* eslint-disable vue/no-mutating-props */
-import { watch } from 'vue';
+import { watch, ref } from 'vue';
 import XContainer from '../page-editor.container.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
@@ -40,19 +40,19 @@ const emit = defineEmits<{
 	(ev: 'update:modelValue', value: any): void;
 }>();
 
-let id: any = $ref(props.modelValue.note);
-let note: any = $ref(null);
+const id = ref<any>(props.modelValue.note);
+const note = ref<any>(null);
 
-watch($$(id), async () => {
-	if (id && (id.startsWith('http://') || id.startsWith('https://'))) {
-		id = (id.endsWith('/') ? id.slice(0, -1) : id).split('/').pop();
+watch(id, async () => {
+	if (id.value && (id.value.startsWith('http://') || id.value.startsWith('https://'))) {
+		id.value = (id.value.endsWith('/') ? id.value.slice(0, -1) : id.value).split('/').pop();
 	}
 
 	emit('update:modelValue', {
 		...props.modelValue,
-		note: id,
+		note: id.value,
 	});
-	note = await os.api('notes/show', { noteId: id });
+	note.value = await os.api('notes/show', { noteId: id.value });
 }, {
 	immediate: true,
 });
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue
index 31c2c96d7c..1220ca29a7 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 /* eslint-disable vue/no-mutating-props */
-import { defineAsyncComponent, inject, onMounted, watch } from 'vue';
+import { defineAsyncComponent, inject, onMounted, watch, ref } from 'vue';
 import { v4 as uuid } from 'uuid';
 import XContainer from '../page-editor.container.vue';
 import * as os from '@/os.js';
@@ -42,12 +42,12 @@ const emit = defineEmits<{
 	(ev: 'update:modelValue', value: any): void;
 }>();
 
-const children = $ref(deepClone(props.modelValue.children ?? []));
+const children = ref(deepClone(props.modelValue.children ?? []));
 
-watch($$(children), () => {
+watch(children, () => {
 	emit('update:modelValue', {
 		...props.modelValue,
-		children,
+		children: children.value,
 	});
 }, {
 	deep: true,
@@ -75,7 +75,7 @@ async function add() {
 	if (canceled) return;
 
 	const id = uuid();
-	children.push({ id, type });
+	children.value.push({ id, type });
 }
 
 onMounted(() => {
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
index 2ecf5790b8..4f47a77bdd 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 /* eslint-disable vue/no-mutating-props */
-import { watch } from 'vue';
+import { watch, ref } from 'vue';
 import XContainer from '../page-editor.container.vue';
 import { i18n } from '@/i18n.js';
 
@@ -28,12 +28,12 @@ const emit = defineEmits<{
 	(ev: 'update:modelValue', value: any): void;
 }>();
 
-const text = $ref(props.modelValue.text ?? '');
+const text = ref(props.modelValue.text ?? '');
 
-watch($$(text), () => {
+watch(text, () => {
 	emit('update:modelValue', {
 		...props.modelValue,
-		text,
+		text: text.value,
 	});
 });
 </script>
diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue
index dc749c292e..e95dd1f39e 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.vue
@@ -61,7 +61,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, provide, watch } from 'vue';
+import { computed, provide, watch, ref } from 'vue';
 import { v4 as uuid } from 'uuid';
 import XBlocks from './page-editor.blocks.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -82,47 +82,47 @@ const props = defineProps<{
 	initUser?: string;
 }>();
 
-let tab = $ref('settings');
-let author = $ref($i);
-let readonly = $ref(false);
-let page = $ref(null);
-let pageId = $ref(null);
-let currentName = $ref(null);
-let title = $ref('');
-let summary = $ref(null);
-let name = $ref(Date.now().toString());
-let eyeCatchingImage = $ref(null);
-let eyeCatchingImageId = $ref(null);
-let font = $ref('sans-serif');
-let content = $ref([]);
-let alignCenter = $ref(false);
-let hideTitleWhenPinned = $ref(false);
+const tab = ref('settings');
+const author = ref($i);
+const readonly = ref(false);
+const page = ref(null);
+const pageId = ref(null);
+const currentName = ref(null);
+const title = ref('');
+const summary = ref(null);
+const name = ref(Date.now().toString());
+const eyeCatchingImage = ref(null);
+const eyeCatchingImageId = ref(null);
+const font = ref('sans-serif');
+const content = ref([]);
+const alignCenter = ref(false);
+const hideTitleWhenPinned = ref(false);
 
-provide('readonly', readonly);
+provide('readonly', readonly.value);
 provide('getPageBlockList', getPageBlockList);
 
-watch($$(eyeCatchingImageId), async () => {
-	if (eyeCatchingImageId == null) {
-		eyeCatchingImage = null;
+watch(eyeCatchingImageId, async () => {
+	if (eyeCatchingImageId.value == null) {
+		eyeCatchingImage.value = null;
 	} else {
-		eyeCatchingImage = await os.api('drive/files/show', {
-			fileId: eyeCatchingImageId,
+		eyeCatchingImage.value = await os.api('drive/files/show', {
+			fileId: eyeCatchingImageId.value,
 		});
 	}
 });
 
 function getSaveOptions() {
 	return {
-		title: title.trim(),
-		name: name.trim(),
-		summary: summary,
-		font: font,
+		title: title.value.trim(),
+		name: name.value.trim(),
+		summary: summary.value,
+		font: font.value,
 		script: '',
-		hideTitleWhenPinned: hideTitleWhenPinned,
-		alignCenter: alignCenter,
-		content: content,
+		hideTitleWhenPinned: hideTitleWhenPinned.value,
+		alignCenter: alignCenter.value,
+		content: content.value,
 		variables: [],
-		eyeCatchingImageId: eyeCatchingImageId,
+		eyeCatchingImageId: eyeCatchingImageId.value,
 	};
 }
 
@@ -146,11 +146,11 @@ function save() {
 		}
 	};
 
-	if (pageId) {
-		options.pageId = pageId;
+	if (pageId.value) {
+		options.pageId = pageId.value;
 		os.api('pages/update', options)
 			.then(page => {
-				currentName = name.trim();
+				currentName.value = name.value.trim();
 				os.alert({
 					type: 'success',
 					text: i18n.ts._pages.updated,
@@ -159,13 +159,13 @@ function save() {
 	} else {
 		os.api('pages/create', options)
 			.then(created => {
-				pageId = created.id;
-				currentName = name.trim();
+				pageId.value = created.id;
+				currentName.value = name.value.trim();
 				os.alert({
 					type: 'success',
 					text: i18n.ts._pages.created,
 				});
-				mainRouter.push(`/pages/edit/${pageId}`);
+				mainRouter.push(`/pages/edit/${pageId.value}`);
 			}).catch(onError);
 	}
 }
@@ -173,11 +173,11 @@ function save() {
 function del() {
 	os.confirm({
 		type: 'warning',
-		text: i18n.t('removeAreYouSure', { x: title.trim() }),
+		text: i18n.t('removeAreYouSure', { x: title.value.trim() }),
 	}).then(({ canceled }) => {
 		if (canceled) return;
 		os.api('pages/delete', {
-			pageId: pageId,
+			pageId: pageId.value,
 		}).then(() => {
 			os.alert({
 				type: 'success',
@@ -189,16 +189,16 @@ function del() {
 }
 
 function duplicate() {
-	title = title + ' - copy';
-	name = name + '-copy';
+	title.value = title.value + ' - copy';
+	name.value = name.value + '-copy';
 	os.api('pages/create', getSaveOptions()).then(created => {
-		pageId = created.id;
-		currentName = name.trim();
+		pageId.value = created.id;
+		currentName.value = name.value.trim();
 		os.alert({
 			type: 'success',
 			text: i18n.ts._pages.created,
 		});
-		mainRouter.push(`/pages/edit/${pageId}`);
+		mainRouter.push(`/pages/edit/${pageId.value}`);
 	});
 }
 
@@ -211,7 +211,7 @@ async function add() {
 	if (canceled) return;
 
 	const id = uuid();
-	content.push({ id, type });
+	content.value.push({ id, type });
 }
 
 function getPageBlockList() {
@@ -225,42 +225,42 @@ function getPageBlockList() {
 
 function setEyeCatchingImage(img) {
 	selectFile(img.currentTarget ?? img.target, null).then(file => {
-		eyeCatchingImageId = file.id;
+		eyeCatchingImageId.value = file.id;
 	});
 }
 
 function removeEyeCatchingImage() {
-	eyeCatchingImageId = null;
+	eyeCatchingImageId.value = null;
 }
 
 async function init() {
 	if (props.initPageId) {
-		page = await os.api('pages/show', {
+		page.value = await os.api('pages/show', {
 			pageId: props.initPageId,
 		});
 	} else if (props.initPageName && props.initUser) {
-		page = await os.api('pages/show', {
+		page.value = await os.api('pages/show', {
 			name: props.initPageName,
 			username: props.initUser,
 		});
-		readonly = true;
+		readonly.value = true;
 	}
 
-	if (page) {
-		author = page.user;
-		pageId = page.id;
-		title = page.title;
-		name = page.name;
-		currentName = page.name;
-		summary = page.summary;
-		font = page.font;
-		hideTitleWhenPinned = page.hideTitleWhenPinned;
-		alignCenter = page.alignCenter;
-		content = page.content;
-		eyeCatchingImageId = page.eyeCatchingImageId;
+	if (page.value) {
+		author.value = page.value.user;
+		pageId.value = page.value.id;
+		title.value = page.value.title;
+		name.value = page.value.name;
+		currentName.value = page.value.name;
+		summary.value = page.value.summary;
+		font.value = page.value.font;
+		hideTitleWhenPinned.value = page.value.hideTitleWhenPinned;
+		alignCenter.value = page.value.alignCenter;
+		content.value = page.value.content;
+		eyeCatchingImageId.value = page.value.eyeCatchingImageId;
 	} else {
 		const id = uuid();
-		content = [{
+		content.value = [{
 			id,
 			type: 'text',
 			text: 'Hello World!',
@@ -270,9 +270,9 @@ async function init() {
 
 init();
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'settings',
 	title: i18n.ts._pages.pageSetting,
 	icon: 'ti ti-settings',
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 2bc053ccfe..a342dff41f 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref } from 'vue';
 import XPage from '@/components/page/page.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
@@ -99,24 +99,24 @@ const props = defineProps<{
 	username: string;
 }>();
 
-let page = $ref(null);
-let error = $ref(null);
+const page = ref(null);
+const error = ref(null);
 const otherPostsPagination = {
 	endpoint: 'users/pages' as const,
 	limit: 6,
 	params: computed(() => ({
-		userId: page.user.id,
+		userId: page.value.user.id,
 	})),
 };
-const path = $computed(() => props.username + '/' + props.pageName);
+const path = computed(() => props.username + '/' + props.pageName);
 
 function fetchPage() {
-	page = null;
+	page.value = null;
 	os.api('pages/show', {
 		name: props.pageName,
 		username: props.username,
 	}).then(async _page => {
-		page = _page;
+		page.value = _page;
 
 		// plugin
 		if (pageViewInterruptors.length > 0) {
@@ -124,38 +124,38 @@ function fetchPage() {
 			for (const interruptor of pageViewInterruptors) {
 				result = await interruptor.handler(result);
 			}
-			page = result;
+			page.value = result;
 		}
 	}).catch(err => {
-		error = err;
+		error.value = err;
 	});
 }
 
 function share() {
 	navigator.share({
-		title: page.title ?? page.name,
-		text: page.summary,
-		url: `${url}/@${page.user.username}/pages/${page.name}`,
+		title: page.value.title ?? page.value.name,
+		text: page.value.summary,
+		url: `${url}/@${page.value.user.username}/pages/${page.value.name}`,
 	});
 }
 
 function copyLink() {
-	copyToClipboard(`${url}/@${page.user.username}/pages/${page.name}`);
+	copyToClipboard(`${url}/@${page.value.user.username}/pages/${page.value.name}`);
 	os.success();
 }
 
 function shareWithNote() {
 	os.post({
-		initialText: `${page.title || page.name} ${url}/@${page.user.username}/pages/${page.name}`,
+		initialText: `${page.value.title || page.value.name} ${url}/@${page.value.user.username}/pages/${page.value.name}`,
 	});
 }
 
 function like() {
 	os.apiWithDialog('pages/like', {
-		pageId: page.id,
+		pageId: page.value.id,
 	}).then(() => {
-		page.isLiked = true;
-		page.likedCount++;
+		page.value.isLiked = true;
+		page.value.likedCount++;
 	});
 }
 
@@ -166,32 +166,32 @@ async function unlike() {
 	});
 	if (confirm.canceled) return;
 	os.apiWithDialog('pages/unlike', {
-		pageId: page.id,
+		pageId: page.value.id,
 	}).then(() => {
-		page.isLiked = false;
-		page.likedCount--;
+		page.value.isLiked = false;
+		page.value.likedCount--;
 	});
 }
 
 function pin(pin) {
 	os.apiWithDialog('i/update', {
-		pinnedPageId: pin ? page.id : null,
+		pinnedPageId: pin ? page.value.id : null,
 	});
 }
 
-watch(() => path, fetchPage, { immediate: true });
+watch(() => path.value, fetchPage, { immediate: true });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => page ? {
-	title: page.title || page.name,
-	avatar: page.user,
-	path: `/@${page.user.username}/pages/${page.name}`,
+definePageMetadata(computed(() => page.value ? {
+	title: page.value.title || page.value.name,
+	avatar: page.value.user,
+	path: `/@${page.value.user.username}/pages/${page.value.name}`,
 	share: {
-		title: page.title || page.name,
-		text: page.summary,
+		title: page.value.title || page.value.name,
+		text: page.value.summary,
 	},
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/pages.vue b/packages/frontend/src/pages/pages.vue
index 6f40b24771..bc51b55c7f 100644
--- a/packages/frontend/src/pages/pages.vue
+++ b/packages/frontend/src/pages/pages.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import MkPagePreview from '@/components/MkPagePreview.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -46,7 +46,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const router = useRouter();
 
-let tab = $ref('featured');
+const tab = ref('featured');
 
 const featuredPagesPagination = {
 	endpoint: 'pages/featured' as const,
@@ -65,13 +65,13 @@ function create() {
 	router.push('/pages/new');
 }
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	icon: 'ti ti-plus',
 	text: i18n.ts.create,
 	handler: create,
 }]);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'featured',
 	title: i18n.ts._pages.featured,
 	icon: 'ti ti-flare',
diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue
index 387cb2f1f7..49a8642fc4 100644
--- a/packages/frontend/src/pages/registry.keys.vue
+++ b/packages/frontend/src/pages/registry.keys.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, computed, ref } from 'vue';
 import JSON5 from 'json5';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
@@ -49,16 +49,16 @@ const props = defineProps<{
 	domain: string;
 }>();
 
-const scope = $computed(() => props.path ? props.path.split('/') : []);
+const scope = computed(() => props.path ? props.path.split('/') : []);
 
-let keys = $ref(null);
+const keys = ref(null);
 
 function fetchKeys() {
 	os.api('i/registry/keys-with-type', {
-		scope: scope,
+		scope: scope.value,
 		domain: props.domain === '@' ? null : props.domain,
 	}).then(res => {
-		keys = Object.entries(res).sort((a, b) => a[0].localeCompare(b[0]));
+		keys.value = Object.entries(res).sort((a, b) => a[0].localeCompare(b[0]));
 	});
 }
 
@@ -76,7 +76,7 @@ async function createKey() {
 		scope: {
 			type: 'string',
 			label: i18n.ts._registry.scope,
-			default: scope.join('/'),
+			default: scope.value.join('/'),
 		},
 	});
 	if (canceled) return;
@@ -91,9 +91,9 @@ async function createKey() {
 
 watch(() => props.path, fetchKeys, { immediate: true });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.registry,
diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue
index 68d6c8c1a0..29406ec83c 100644
--- a/packages/frontend/src/pages/registry.value.vue
+++ b/packages/frontend/src/pages/registry.value.vue
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, computed, ref } from 'vue';
 import JSON5 from 'json5';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
@@ -61,26 +61,26 @@ const props = defineProps<{
 	domain: string;
 }>();
 
-const scope = $computed(() => props.path.split('/').slice(0, -1));
-const key = $computed(() => props.path.split('/').at(-1));
+const scope = computed(() => props.path.split('/').slice(0, -1));
+const key = computed(() => props.path.split('/').at(-1));
 
-let value = $ref(null);
-let valueForEditor = $ref(null);
+const value = ref(null);
+const valueForEditor = ref(null);
 
 function fetchValue() {
 	os.api('i/registry/get-detail', {
-		scope,
-		key,
+		scope: scope.value,
+		key: key.value,
 		domain: props.domain === '@' ? null : props.domain,
 	}).then(res => {
-		value = res;
-		valueForEditor = JSON5.stringify(res.value, null, '\t');
+		value.value = res;
+		valueForEditor.value = JSON5.stringify(res.value, null, '\t');
 	});
 }
 
 async function save() {
 	try {
-		JSON5.parse(valueForEditor);
+		JSON5.parse(valueForEditor.value);
 	} catch (err) {
 		os.alert({
 			type: 'error',
@@ -94,9 +94,9 @@ async function save() {
 	}).then(({ canceled }) => {
 		if (canceled) return;
 		os.apiWithDialog('i/registry/set', {
-			scope,
-			key,
-			value: JSON5.parse(valueForEditor),
+			scope: scope.value,
+			key: key.value,
+			value: JSON5.parse(valueForEditor.value),
 			domain: props.domain === '@' ? null : props.domain,
 		});
 	});
@@ -109,8 +109,8 @@ function del() {
 	}).then(({ canceled }) => {
 		if (canceled) return;
 		os.apiWithDialog('i/registry/remove', {
-			scope,
-			key,
+			scope: scope.value,
+			key: key.value,
 			domain: props.domain === '@' ? null : props.domain,
 		});
 	});
@@ -118,9 +118,9 @@ function del() {
 
 watch(() => props.path, fetchValue, { immediate: true });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.registry,
diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue
index d0a3df5deb..e8bd006373 100644
--- a/packages/frontend/src/pages/registry.vue
+++ b/packages/frontend/src/pages/registry.vue
@@ -22,6 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref, computed } from 'vue';
 import JSON5 from 'json5';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
@@ -30,11 +31,11 @@ import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
 import MkButton from '@/components/MkButton.vue';
 
-let scopesWithDomain = $ref(null);
+const scopesWithDomain = ref(null);
 
 function fetchScopes() {
 	os.api('i/registry/scopes-with-domain').then(res => {
-		scopesWithDomain = res;
+		scopesWithDomain.value = res;
 	});
 }
 
@@ -66,9 +67,9 @@ async function createKey() {
 
 fetchScopes();
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.registry,
diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue
index 718ca7d773..c9d193b787 100644
--- a/packages/frontend/src/pages/reset-password.vue
+++ b/packages/frontend/src/pages/reset-password.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, onMounted } from 'vue';
+import { defineAsyncComponent, onMounted, ref, computed } from 'vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
@@ -32,12 +32,12 @@ const props = defineProps<{
 	token?: string;
 }>();
 
-let password = $ref('');
+const password = ref('');
 
 async function save() {
 	await os.apiWithDialog('reset-password', {
 		token: props.token,
-		password: password,
+		password: password.value,
 	});
 	mainRouter.push('/');
 }
@@ -49,9 +49,9 @@ onMounted(() => {
 	}
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.resetPassword,
diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue
index 1e3db42758..7d8785218f 100644
--- a/packages/frontend/src/pages/role.vue
+++ b/packages/frontend/src/pages/role.vue
@@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref } from 'vue';
 import * as os from '@/os.js';
 import MkUserList from '@/components/MkUserList.vue';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -52,29 +52,29 @@ const props = withDefaults(defineProps<{
 	initialTab: 'users',
 });
 
-let tab = $ref(props.initialTab);
-let role = $ref();
-let error = $ref();
-let visible = $ref(false);
+const tab = ref(props.initialTab);
+const role = ref();
+const error = ref();
+const visible = ref(false);
 
 watch(() => props.role, () => {
 	os.api('roles/show', {
 		roleId: props.role,
 	}).then(res => {
-		role = res;
-		document.title = `${role?.name} | ${instanceName}`;
-		visible = res.isExplorable && res.isPublic;
+		role.value = res;
+		document.title = `${role.value?.name} | ${instanceName}`;
+		visible.value = res.isExplorable && res.isPublic;
 	}).catch((err) => {
 		if (err.code === 'NO_SUCH_ROLE') {
-			error = i18n.ts.noRole;
+			error.value = i18n.ts.noRole;
 		} else {
-			error = i18n.ts.somethingHappened;
+			error.value = i18n.ts.somethingHappened;
 		}
-		document.title = `${error} | ${instanceName}`;
+		document.title = `${error.value} | ${instanceName}`;
 	});
 }, { immediate: true });
 
-const users = $computed(() => ({
+const users = computed(() => ({
 	endpoint: 'roles/users' as const,
 	limit: 30,
 	params: {
@@ -82,7 +82,7 @@ const users = $computed(() => ({
 	},
 }));
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'users',
 	icon: 'ti ti-users',
 	title: i18n.ts.users,
@@ -93,7 +93,7 @@ const headerTabs = $computed(() => [{
 }]);
 
 definePageMetadata(computed(() => ({
-	title: role?.name,
+	title: role.value?.name,
 	icon: 'ti ti-badge',
 })));
 </script>
diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue
index f8d3187bd4..1453bc1658 100644
--- a/packages/frontend/src/pages/scratchpad.vue
+++ b/packages/frontend/src/pages/scratchpad.vue
@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onDeactivated, onUnmounted, Ref, ref, watch } from 'vue';
+import { onDeactivated, onUnmounted, Ref, ref, watch, computed } from 'vue';
 import { Interpreter, Parser, utils } from '@syuilo/aiscript';
 import MkContainer from '@/components/MkContainer.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -59,8 +59,8 @@ let aiscript: Interpreter;
 const code = ref('');
 const logs = ref<any[]>([]);
 const root = ref<AsUiRoot>();
-let components: Ref<AsUiComponent>[] = $ref([]);
-let uiKey = $ref(0);
+const components = ref<Ref<AsUiComponent>[]>([]);
+const uiKey = ref(0);
 
 const saved = miLocalStorage.getItem('scratchpad');
 if (saved) {
@@ -74,15 +74,15 @@ watch(code, () => {
 async function run() {
 	if (aiscript) aiscript.abort();
 	root.value = undefined;
-	components = [];
-	uiKey++;
+	components.value = [];
+	uiKey.value++;
 	logs.value = [];
 	aiscript = new Interpreter(({
 		...createAiScriptEnv({
 			storageKey: 'widget',
 			token: $i?.token,
 		}),
-		...registerAsUiLib(components, (_root) => {
+		...registerAsUiLib(components.value, (_root) => {
 			root.value = _root.value;
 		}),
 	}), {
@@ -160,9 +160,9 @@ onUnmounted(() => {
 	if (aiscript) aiscript.abort();
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.scratchpad,
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index c1692d8be2..3e74a6f591 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, onMounted } from 'vue';
+import { computed, onMounted, ref } from 'vue';
 import MkNotes from '@/components/MkNotes.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkRadios from '@/components/MkRadios.vue';
@@ -59,21 +59,21 @@ import MkFolder from '@/components/MkFolder.vue';
 
 const router = useRouter();
 
-let key = $ref(0);
-let searchQuery = $ref('');
-let searchOrigin = $ref('combined');
-let notePagination = $ref();
-let user = $ref(null);
-let isLocalOnly = $ref(false);
+const key = ref(0);
+const searchQuery = ref('');
+const searchOrigin = ref('combined');
+const notePagination = ref();
+const user = ref(null);
+const isLocalOnly = ref(false);
 
 function selectUser() {
 	os.selectUser().then(_user => {
-		user = _user;
+		user.value = _user;
 	});
 }
 
 async function search() {
-	const query = searchQuery.toString().trim();
+	const query = searchQuery.value.toString().trim();
 
 	if (query == null || query === '') return;
 
@@ -95,17 +95,17 @@ async function search() {
 		return;
 	}
 
-	notePagination = {
+	notePagination.value = {
 		endpoint: 'notes/search',
 		limit: 10,
 		params: {
-			query: searchQuery,
-			userId: user ? user.id : null,
+			query: searchQuery.value,
+			userId: user.value ? user.value.id : null,
 		},
 	};
 
-	if (isLocalOnly) notePagination.params.host = '.';
+	if (isLocalOnly.value) notePagination.value.params.host = '.';
 
-	key++;
+	key.value++;
 }
 </script>
diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue
index 1a7501adc6..39707e634c 100644
--- a/packages/frontend/src/pages/search.user.vue
+++ b/packages/frontend/src/pages/search.user.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, defineAsyncComponent, onMounted } from 'vue';
+import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
 import MkUserList from '@/components/MkUserList.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkRadios from '@/components/MkRadios.vue';
@@ -40,13 +40,13 @@ import { useRouter } from '@/router.js';
 
 const router = useRouter();
 
-let key = $ref('');
-let searchQuery = $ref('');
-let searchOrigin = $ref('combined');
-let userPagination = $ref();
+const key = ref('');
+const searchQuery = ref('');
+const searchOrigin = ref('combined');
+const userPagination = ref();
 
 async function search() {
-	const query = searchQuery.toString().trim();
+	const query = searchQuery.value.toString().trim();
 
 	if (query == null || query === '') return;
 
@@ -68,15 +68,15 @@ async function search() {
 		return;
 	}
 
-	userPagination = {
+	userPagination.value = {
 		endpoint: 'users/search',
 		limit: 10,
 		params: {
 			query: query,
-			origin: searchOrigin,
+			origin: searchOrigin.value,
 		},
 	};
 
-	key = query;
+	key.value = query;
 }
 </script>
diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue
index e205fe850f..c47414e573 100644
--- a/packages/frontend/src/pages/search.vue
+++ b/packages/frontend/src/pages/search.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, defineAsyncComponent, onMounted } from 'vue';
+import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import * as os from '@/os.js';
@@ -34,13 +34,13 @@ import MkInfo from '@/components/MkInfo.vue';
 const XNote = defineAsyncComponent(() => import('./search.note.vue'));
 const XUser = defineAsyncComponent(() => import('./search.user.vue'));
 
-let tab = $ref('note');
+const tab = ref('note');
 
 const notesSearchAvailable = (($i == null && instance.policies.canSearchNotes) || ($i != null && $i.policies.canSearchNotes));
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => [{
+const headerTabs = computed(() => [{
 	key: 'note',
 	title: i18n.ts.notes,
 	icon: 'ti ti-pencil',
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index 8a89a3a86d..d9a59cdc35 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -72,7 +72,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref, defineAsyncComponent } from 'vue';
+import { ref, defineAsyncComponent, computed } from 'vue';
 import { supported as webAuthnSupported, create as webAuthnCreate, parseCreationOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
@@ -91,7 +91,7 @@ withDefaults(defineProps<{
 	first: false,
 });
 
-const usePasswordLessLogin = $computed(() => $i?.usePasswordLessLogin ?? false);
+const usePasswordLessLogin = computed(() => $i?.usePasswordLessLogin ?? false);
 
 async function registerTOTP(): Promise<void> {
 	const auth = await os.authenticateDialog();
diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue
index df8c7b440a..6ed04ecf9e 100644
--- a/packages/frontend/src/pages/settings/accounts.vue
+++ b/packages/frontend/src/pages/settings/accounts.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, ref } from 'vue';
+import { defineAsyncComponent, ref, computed } from 'vue';
 import type * as Misskey from 'misskey-js';
 import FormSuspense from '@/components/form/suspense.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -101,9 +101,9 @@ function switchAccountWithToken(token: string) {
 	login(token);
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.accounts,
diff --git a/packages/frontend/src/pages/settings/api.vue b/packages/frontend/src/pages/settings/api.vue
index e0266bccba..eee7884aaa 100644
--- a/packages/frontend/src/pages/settings/api.vue
+++ b/packages/frontend/src/pages/settings/api.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, ref } from 'vue';
+import { defineAsyncComponent, ref, computed } from 'vue';
 import FormLink from '@/components/form/link.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
@@ -40,9 +40,9 @@ function generateToken() {
 	}, 'closed');
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: 'API',
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index 7fd4ed61c9..f461271f0b 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 import FormPagination from '@/components/MkPagination.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
@@ -71,9 +71,9 @@ function revoke(token) {
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.installedApps,
diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue
index d58f959fc5..e33e778246 100644
--- a/packages/frontend/src/pages/settings/custom-css.vue
+++ b/packages/frontend/src/pages/settings/custom-css.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref, watch } from 'vue';
+import { ref, watch, computed } from 'vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import FormInfo from '@/components/MkInfo.vue';
 import * as os from '@/os.js';
@@ -41,9 +41,9 @@ watch(localCustomCss, async () => {
 	await apply();
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.customCss,
diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue
index 9fecc65d6d..b681e0d159 100644
--- a/packages/frontend/src/pages/settings/deck.vue
+++ b/packages/frontend/src/pages/settings/deck.vue
@@ -32,9 +32,9 @@ const useSimpleUiForNonRootPages = computed(deckStore.makeGetterSetter('useSimpl
 const alwaysShowMainColumn = computed(deckStore.makeGetterSetter('alwaysShowMainColumn'));
 const columnAlign = computed(deckStore.makeGetterSetter('columnAlign'));
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.deck,
diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue
index 899d824bf9..8da60ef504 100644
--- a/packages/frontend/src/pages/settings/drive-cleaner.vue
+++ b/packages/frontend/src/pages/settings/drive-cleaner.vue
@@ -60,7 +60,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkSelect from '@/components/MkSelect.vue';
 import { getDriveFileMenu } from '@/scripts/get-drive-file-menu.js';
 
-let sortMode = ref('+size');
+const sortMode = ref('+size');
 const pagination = {
 	endpoint: 'drive/files' as const,
 	limit: 10,
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index 01a0711682..8f5c313d16 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -76,8 +76,8 @@ const fetching = ref(true);
 const usage = ref<any>(null);
 const capacity = ref<any>(null);
 const uploadFolder = ref<any>(null);
-let alwaysMarkNsfw = $ref($i.alwaysMarkNsfw);
-let autoSensitive = $ref($i.autoSensitive);
+const alwaysMarkNsfw = ref($i.alwaysMarkNsfw);
+const autoSensitive = ref($i.autoSensitive);
 
 const meterStyle = computed(() => {
 	return {
@@ -122,21 +122,21 @@ function chooseUploadFolder() {
 
 function saveProfile() {
 	os.api('i/update', {
-		alwaysMarkNsfw: !!alwaysMarkNsfw,
-		autoSensitive: !!autoSensitive,
+		alwaysMarkNsfw: !!alwaysMarkNsfw.value,
+		autoSensitive: !!autoSensitive.value,
 	}).catch(err => {
 		os.alert({
 			type: 'error',
 			title: i18n.ts.error,
 			text: err.message,
 		});
-		alwaysMarkNsfw = true;
+		alwaysMarkNsfw.value = true;
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.drive,
diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue
index 82b7f0ae4c..309e025ada 100644
--- a/packages/frontend/src/pages/settings/email.vue
+++ b/packages/frontend/src/pages/settings/email.vue
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, ref, watch } from 'vue';
+import { onMounted, ref, watch, computed } from 'vue';
 import FormSection from '@/components/form/section.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -106,9 +106,9 @@ onMounted(() => {
 	});
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.email,
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 717021abd0..f108a0c64e 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -427,9 +427,9 @@ watch(dataSaver, (to) => {
 	deep: true,
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.general,
diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue
index 0f01fda26f..858983a214 100644
--- a/packages/frontend/src/pages/settings/import-export.vue
+++ b/packages/frontend/src/pages/settings/import-export.vue
@@ -111,7 +111,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
@@ -208,9 +208,9 @@ const importAntennas = async (ev) => {
 	os.api('i/import-antennas', { fileId: file.id }).then(onImportSuccess).catch(onError);
 };
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.importAndExport,
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index 5a1a9aedb3..49290e7c22 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -50,14 +50,14 @@ const childInfo = ref(null);
 
 const router = useRouter();
 
-let narrow = $ref(false);
+const narrow = ref(false);
 const NARROW_THRESHOLD = 600;
 
-let currentPage = $computed(() => router.currentRef.value.child);
+const currentPage = computed(() => router.currentRef.value.child);
 
 const ro = new ResizeObserver((entries, observer) => {
 	if (entries.length === 0) return;
-	narrow = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
+	narrow.value = entries[0].borderBoxSize[0].inlineSize < NARROW_THRESHOLD;
 });
 
 const menuDef = computed(() => [{
@@ -66,37 +66,37 @@ const menuDef = computed(() => [{
 		icon: 'ti ti-user',
 		text: i18n.ts.profile,
 		to: '/settings/profile',
-		active: currentPage?.route.name === 'profile',
+		active: currentPage.value?.route.name === 'profile',
 	}, {
 		icon: 'ti ti-lock-open',
 		text: i18n.ts.privacy,
 		to: '/settings/privacy',
-		active: currentPage?.route.name === 'privacy',
+		active: currentPage.value?.route.name === 'privacy',
 	}, {
 		icon: 'ti ti-mood-happy',
 		text: i18n.ts.reaction,
 		to: '/settings/reaction',
-		active: currentPage?.route.name === 'reaction',
+		active: currentPage.value?.route.name === 'reaction',
 	}, {
 		icon: 'ti ti-cloud',
 		text: i18n.ts.drive,
 		to: '/settings/drive',
-		active: currentPage?.route.name === 'drive',
+		active: currentPage.value?.route.name === 'drive',
 	}, {
 		icon: 'ti ti-bell',
 		text: i18n.ts.notifications,
 		to: '/settings/notifications',
-		active: currentPage?.route.name === 'notifications',
+		active: currentPage.value?.route.name === 'notifications',
 	}, {
 		icon: 'ti ti-mail',
 		text: i18n.ts.email,
 		to: '/settings/email',
-		active: currentPage?.route.name === 'email',
+		active: currentPage.value?.route.name === 'email',
 	}, {
 		icon: 'ti ti-lock',
 		text: i18n.ts.security,
 		to: '/settings/security',
-		active: currentPage?.route.name === 'security',
+		active: currentPage.value?.route.name === 'security',
 	}],
 }, {
 	title: i18n.ts.clientSettings,
@@ -104,32 +104,32 @@ const menuDef = computed(() => [{
 		icon: 'ti ti-adjustments',
 		text: i18n.ts.general,
 		to: '/settings/general',
-		active: currentPage?.route.name === 'general',
+		active: currentPage.value?.route.name === 'general',
 	}, {
 		icon: 'ti ti-palette',
 		text: i18n.ts.theme,
 		to: '/settings/theme',
-		active: currentPage?.route.name === 'theme',
+		active: currentPage.value?.route.name === 'theme',
 	}, {
 		icon: 'ti ti-menu-2',
 		text: i18n.ts.navbar,
 		to: '/settings/navbar',
-		active: currentPage?.route.name === 'navbar',
+		active: currentPage.value?.route.name === 'navbar',
 	}, {
 		icon: 'ti ti-equal-double',
 		text: i18n.ts.statusbar,
 		to: '/settings/statusbar',
-		active: currentPage?.route.name === 'statusbar',
+		active: currentPage.value?.route.name === 'statusbar',
 	}, {
 		icon: 'ti ti-music',
 		text: i18n.ts.sounds,
 		to: '/settings/sounds',
-		active: currentPage?.route.name === 'sounds',
+		active: currentPage.value?.route.name === 'sounds',
 	}, {
 		icon: 'ti ti-plug',
 		text: i18n.ts.plugins,
 		to: '/settings/plugin',
-		active: currentPage?.route.name === 'plugin',
+		active: currentPage.value?.route.name === 'plugin',
 	}],
 }, {
 	title: i18n.ts.otherSettings,
@@ -137,44 +137,44 @@ const menuDef = computed(() => [{
 		icon: 'ti ti-badges',
 		text: i18n.ts.roles,
 		to: '/settings/roles',
-		active: currentPage?.route.name === 'roles',
+		active: currentPage.value?.route.name === 'roles',
 	}, {
 		icon: 'ti ti-ban',
 		text: i18n.ts.muteAndBlock,
 		to: '/settings/mute-block',
-		active: currentPage?.route.name === 'mute-block',
+		active: currentPage.value?.route.name === 'mute-block',
 	}, {
 		icon: 'ti ti-api',
 		text: 'API',
 		to: '/settings/api',
-		active: currentPage?.route.name === 'api',
+		active: currentPage.value?.route.name === 'api',
 	}, {
 		icon: 'ti ti-webhook',
 		text: 'Webhook',
 		to: '/settings/webhook',
-		active: currentPage?.route.name === 'webhook',
+		active: currentPage.value?.route.name === 'webhook',
 	}, {
 		icon: 'ti ti-package',
 		text: i18n.ts.importAndExport,
 		to: '/settings/import-export',
-		active: currentPage?.route.name === 'import-export',
+		active: currentPage.value?.route.name === 'import-export',
 	}, {
 		icon: 'ti ti-plane',
 		text: `${i18n.ts.accountMigration}`,
 		to: '/settings/migration',
-		active: currentPage?.route.name === 'migration',
+		active: currentPage.value?.route.name === 'migration',
 	}, {
 		icon: 'ti ti-dots',
 		text: i18n.ts.other,
 		to: '/settings/other',
-		active: currentPage?.route.name === 'other',
+		active: currentPage.value?.route.name === 'other',
 	}],
 }, {
 	items: [{
 		icon: 'ti ti-device-floppy',
 		text: i18n.ts.preferencesBackups,
 		to: '/settings/preferences-backups',
-		active: currentPage?.route.name === 'preferences-backups',
+		active: currentPage.value?.route.name === 'preferences-backups',
 	}, {
 		type: 'button',
 		icon: 'ti ti-trash',
@@ -198,23 +198,23 @@ const menuDef = computed(() => [{
 	}],
 }]);
 
-watch($$(narrow), () => {
+watch(narrow, () => {
 });
 
 onMounted(() => {
 	ro.observe(el.value);
 
-	narrow = el.value.offsetWidth < NARROW_THRESHOLD;
+	narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
 
-	if (!narrow && currentPage?.route.name == null) {
+	if (!narrow.value && currentPage.value?.route.name == null) {
 		router.replace('/settings/profile');
 	}
 });
 
 onActivated(() => {
-	narrow = el.value.offsetWidth < NARROW_THRESHOLD;
+	narrow.value = el.value.offsetWidth < NARROW_THRESHOLD;
 
-	if (!narrow && currentPage?.route.name == null) {
+	if (!narrow.value && currentPage.value?.route.name == null) {
 		router.replace('/settings/profile');
 	}
 });
@@ -224,7 +224,7 @@ onUnmounted(() => {
 });
 
 watch(router.currentRef, (to) => {
-	if (to.route.name === 'settings' && to.child?.route.name == null && !narrow) {
+	if (to.route.name === 'settings' && to.child?.route.name == null && !narrow.value) {
 		router.replace('/settings/profile');
 	}
 });
@@ -239,9 +239,9 @@ provideMetadataReceiver((info) => {
 	}
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(INFO);
 // w 890
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index 4883ca0df4..83f7baf428 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -126,7 +126,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import XInstanceMute from './mute-block.instance-mute.vue';
 import XWordMute from './mute-block.word-mute.vue';
 import MkPagination from '@/components/MkPagination.vue';
@@ -154,9 +154,9 @@ const blockingPagination = {
 	limit: 10,
 };
 
-let expandedRenoteMuteItems = $ref([]);
-let expandedMuteItems = $ref([]);
-let expandedBlockItems = $ref([]);
+const expandedRenoteMuteItems = ref([]);
+const expandedMuteItems = ref([]);
+const expandedBlockItems = ref([]);
 
 async function unrenoteMute(user, ev) {
 	os.popupMenu([{
@@ -192,26 +192,26 @@ async function unblock(user, ev) {
 }
 
 async function toggleRenoteMuteItem(item) {
-	if (expandedRenoteMuteItems.includes(item.id)) {
-		expandedRenoteMuteItems = expandedRenoteMuteItems.filter(x => x !== item.id);
+	if (expandedRenoteMuteItems.value.includes(item.id)) {
+		expandedRenoteMuteItems.value = expandedRenoteMuteItems.value.filter(x => x !== item.id);
 	} else {
-		expandedRenoteMuteItems.push(item.id);
+		expandedRenoteMuteItems.value.push(item.id);
 	}
 }
 
 async function toggleMuteItem(item) {
-	if (expandedMuteItems.includes(item.id)) {
-		expandedMuteItems = expandedMuteItems.filter(x => x !== item.id);
+	if (expandedMuteItems.value.includes(item.id)) {
+		expandedMuteItems.value = expandedMuteItems.value.filter(x => x !== item.id);
 	} else {
-		expandedMuteItems.push(item.id);
+		expandedMuteItems.value.push(item.id);
 	}
 }
 
 async function toggleBlockItem(item) {
-	if (expandedBlockItems.includes(item.id)) {
-		expandedBlockItems = expandedBlockItems.filter(x => x !== item.id);
+	if (expandedBlockItems.value.includes(item.id)) {
+		expandedBlockItems.value = expandedBlockItems.value.filter(x => x !== item.id);
 	} else {
-		expandedBlockItems.push(item.id);
+		expandedBlockItems.value.push(item.id);
 	}
 }
 
@@ -223,9 +223,9 @@ async function saveHardMutedWords(hardMutedWords: (string | string[])[]) {
 	await os.api('i/update', { hardMutedWords });
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.muteAndBlock,
diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue
index c9cccd7dcb..66477a86ca 100644
--- a/packages/frontend/src/pages/settings/navbar.vue
+++ b/packages/frontend/src/pages/settings/navbar.vue
@@ -115,9 +115,9 @@ watch(menuDisplay, async () => {
 	await reloadAsk();
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.navbar,
diff --git a/packages/frontend/src/pages/settings/notifications.notification-config.vue b/packages/frontend/src/pages/settings/notifications.notification-config.vue
index c1f107c2b8..5c8378e1dc 100644
--- a/packages/frontend/src/pages/settings/notifications.notification-config.vue
+++ b/packages/frontend/src/pages/settings/notifications.notification-config.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkSelect from '@/components/MkSelect.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -41,10 +41,10 @@ const emit = defineEmits<{
 	(ev: 'update', result: any): void;
 }>();
 
-let type = $ref(props.value.type);
-let userListId = $ref(props.value.userListId);
+const type = ref(props.value.type);
+const userListId = ref(props.value.userListId);
 
 function save() {
-	emit('update', { type, userListId });
+	emit('update', { type: type.value, userListId: userListId.value });
 }
 </script>
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index 7b09c6c900..394e428eda 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, shallowRef, computed } from 'vue';
 import XNotificationConfig from './notifications.notification-config.vue';
 import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
@@ -70,9 +70,9 @@ import { notificationTypes } from '@/const.js';
 
 const nonConfigurableNotificationTypes = ['note'];
 
-let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
-let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer);
-let sendReadMessage = $computed(() => pushRegistrationInServer?.sendReadMessage || false);
+const allowButton = shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
+const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrationInServer);
+const sendReadMessage = computed(() => pushRegistrationInServer.value?.sendReadMessage || false);
 const userLists = await os.api('users/lists/list');
 
 async function readAllUnreadNotes() {
@@ -95,14 +95,14 @@ async function updateReceiveConfig(type, value) {
 }
 
 function onChangeSendReadMessage(v: boolean) {
-	if (!pushRegistrationInServer) return;
+	if (!pushRegistrationInServer.value) return;
 
 	os.apiWithDialog('sw/update-registration', {
-		endpoint: pushRegistrationInServer.endpoint,
+		endpoint: pushRegistrationInServer.value.endpoint,
 		sendReadMessage: v,
 	}).then(res => {
-		if (!allowButton)	return;
-		allowButton.pushRegistrationInServer = res;
+		if (!allowButton.value)	return;
+		allowButton.value.pushRegistrationInServer = res;
 	});
 }
 
@@ -110,9 +110,9 @@ function testNotification(): void {
 	os.api('notifications/test-notification');
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.notifications,
diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue
index a921e0cea9..340a9550b4 100644
--- a/packages/frontend/src/pages/settings/other.vue
+++ b/packages/frontend/src/pages/settings/other.vue
@@ -163,9 +163,9 @@ watch([
 	await reloadAsk();
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.other,
diff --git a/packages/frontend/src/pages/settings/plugin.install.vue b/packages/frontend/src/pages/settings/plugin.install.vue
index 693e02d0ed..f304d777a5 100644
--- a/packages/frontend/src/pages/settings/plugin.install.vue
+++ b/packages/frontend/src/pages/settings/plugin.install.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { nextTick, ref } from 'vue';
+import { nextTick, ref, computed } from 'vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkButton from '@/components/MkButton.vue';
 import FormInfo from '@/components/MkInfo.vue';
@@ -49,9 +49,9 @@ async function install() {
 	}
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts._plugin.install,
diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue
index 5ebd74ef7a..bf760e623f 100644
--- a/packages/frontend/src/pages/settings/plugin.vue
+++ b/packages/frontend/src/pages/settings/plugin.vue
@@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { nextTick, ref } from 'vue';
+import { nextTick, ref, computed } from 'vue';
 import FormLink from '@/components/form/link.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import FormSection from '@/components/form/section.vue';
@@ -121,9 +121,9 @@ function changeActive(plugin, active) {
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.plugins,
diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue
index d195b4142c..971881ea24 100644
--- a/packages/frontend/src/pages/settings/privacy.vue
+++ b/packages/frontend/src/pages/settings/privacy.vue
@@ -66,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import FormSection from '@/components/form/section.vue';
@@ -77,36 +77,36 @@ import { i18n } from '@/i18n.js';
 import { $i } from '@/account.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let isLocked = $ref($i.isLocked);
-let autoAcceptFollowed = $ref($i.autoAcceptFollowed);
-let noCrawle = $ref($i.noCrawle);
-let preventAiLearning = $ref($i.preventAiLearning);
-let isExplorable = $ref($i.isExplorable);
-let hideOnlineStatus = $ref($i.hideOnlineStatus);
-let publicReactions = $ref($i.publicReactions);
-let ffVisibility = $ref($i.ffVisibility);
+const isLocked = ref($i.isLocked);
+const autoAcceptFollowed = ref($i.autoAcceptFollowed);
+const noCrawle = ref($i.noCrawle);
+const preventAiLearning = ref($i.preventAiLearning);
+const isExplorable = ref($i.isExplorable);
+const hideOnlineStatus = ref($i.hideOnlineStatus);
+const publicReactions = ref($i.publicReactions);
+const ffVisibility = ref($i.ffVisibility);
 
-let defaultNoteVisibility = $computed(defaultStore.makeGetterSetter('defaultNoteVisibility'));
-let defaultNoteLocalOnly = $computed(defaultStore.makeGetterSetter('defaultNoteLocalOnly'));
-let rememberNoteVisibility = $computed(defaultStore.makeGetterSetter('rememberNoteVisibility'));
-let keepCw = $computed(defaultStore.makeGetterSetter('keepCw'));
+const defaultNoteVisibility = computed(defaultStore.makeGetterSetter('defaultNoteVisibility'));
+const defaultNoteLocalOnly = computed(defaultStore.makeGetterSetter('defaultNoteLocalOnly'));
+const rememberNoteVisibility = computed(defaultStore.makeGetterSetter('rememberNoteVisibility'));
+const keepCw = computed(defaultStore.makeGetterSetter('keepCw'));
 
 function save() {
 	os.api('i/update', {
-		isLocked: !!isLocked,
-		autoAcceptFollowed: !!autoAcceptFollowed,
-		noCrawle: !!noCrawle,
-		preventAiLearning: !!preventAiLearning,
-		isExplorable: !!isExplorable,
-		hideOnlineStatus: !!hideOnlineStatus,
-		publicReactions: !!publicReactions,
-		ffVisibility: ffVisibility,
+		isLocked: !!isLocked.value,
+		autoAcceptFollowed: !!autoAcceptFollowed.value,
+		noCrawle: !!noCrawle.value,
+		preventAiLearning: !!preventAiLearning.value,
+		isExplorable: !!isExplorable.value,
+		hideOnlineStatus: !!hideOnlineStatus.value,
+		publicReactions: !!publicReactions.value,
+		ffVisibility: ffVisibility.value,
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.privacy,
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index f6e387da52..ba75b539e1 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -144,7 +144,7 @@ import MkInfo from '@/components/MkInfo.vue';
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
 const reactionAcceptance = computed(defaultStore.makeGetterSetter('reactionAcceptance'));
-let avatarDecorations: any[] = $ref([]);
+const avatarDecorations = ref<any[]>([]);
 
 const profile = reactive({
 	name: $i.name,
@@ -166,7 +166,7 @@ const fields = ref($i?.fields.map(field => ({ id: Math.random().toString(), name
 const fieldEditMode = ref(false);
 
 os.api('get-avatar-decorations').then(_avatarDecorations => {
-	avatarDecorations = _avatarDecorations;
+	avatarDecorations.value = _avatarDecorations;
 });
 
 function addField() {
@@ -273,9 +273,9 @@ function openDecoration(avatarDecoration) {
 	}, {}, 'closed');
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.profile,
diff --git a/packages/frontend/src/pages/settings/reaction.vue b/packages/frontend/src/pages/settings/reaction.vue
index fb0f975212..fe5d9fc443 100644
--- a/packages/frontend/src/pages/settings/reaction.vue
+++ b/packages/frontend/src/pages/settings/reaction.vue
@@ -60,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, watch } from 'vue';
+import { defineAsyncComponent, watch, ref, computed } from 'vue';
 import Sortable from 'vuedraggable';
 import MkRadios from '@/components/MkRadios.vue';
 import FromSlot from '@/components/form/slot.vue';
@@ -73,22 +73,22 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { deepClone } from '@/scripts/clone.js';
 
-let reactions = $ref(deepClone(defaultStore.state.reactions));
+const reactions = ref(deepClone(defaultStore.state.reactions));
 
-const reactionPickerSize = $computed(defaultStore.makeGetterSetter('reactionPickerSize'));
-const reactionPickerWidth = $computed(defaultStore.makeGetterSetter('reactionPickerWidth'));
-const reactionPickerHeight = $computed(defaultStore.makeGetterSetter('reactionPickerHeight'));
-const reactionPickerUseDrawerForMobile = $computed(defaultStore.makeGetterSetter('reactionPickerUseDrawerForMobile'));
+const reactionPickerSize = computed(defaultStore.makeGetterSetter('reactionPickerSize'));
+const reactionPickerWidth = computed(defaultStore.makeGetterSetter('reactionPickerWidth'));
+const reactionPickerHeight = computed(defaultStore.makeGetterSetter('reactionPickerHeight'));
+const reactionPickerUseDrawerForMobile = computed(defaultStore.makeGetterSetter('reactionPickerUseDrawerForMobile'));
 
 function save() {
-	defaultStore.set('reactions', reactions);
+	defaultStore.set('reactions', reactions.value);
 }
 
 function remove(reaction, ev: MouseEvent) {
 	os.popupMenu([{
 		text: i18n.ts.remove,
 		action: () => {
-			reactions = reactions.filter(x => x !== reaction);
+			reactions.value = reactions.value.filter(x => x !== reaction);
 		},
 	}], ev.currentTarget ?? ev.target);
 }
@@ -107,28 +107,28 @@ async function setDefault() {
 	});
 	if (canceled) return;
 
-	reactions = deepClone(defaultStore.def.reactions.default);
+	reactions.value = deepClone(defaultStore.def.reactions.default);
 }
 
 function chooseEmoji(ev: MouseEvent) {
 	os.pickEmoji(ev.currentTarget ?? ev.target, {
 		showPinned: false,
 	}).then(emoji => {
-		if (!reactions.includes(emoji)) {
-			reactions.push(emoji);
+		if (!reactions.value.includes(emoji)) {
+			reactions.value.push(emoji);
 		}
 	});
 }
 
-watch($$(reactions), () => {
+watch(reactions, () => {
 	save();
 }, {
 	deep: true,
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.reaction,
diff --git a/packages/frontend/src/pages/settings/roles.vue b/packages/frontend/src/pages/settings/roles.vue
index 71238af72e..0f6c30dae9 100644
--- a/packages/frontend/src/pages/settings/roles.vue
+++ b/packages/frontend/src/pages/settings/roles.vue
@@ -46,9 +46,9 @@ function save() {
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.roles,
diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue
index eacd34778d..3f85f27e47 100644
--- a/packages/frontend/src/pages/settings/security.vue
+++ b/packages/frontend/src/pages/settings/security.vue
@@ -40,6 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { computed } from 'vue';
 import X2fa from './2fa.vue';
 import FormSection from '@/components/form/section.vue';
 import FormSlot from '@/components/form/slot.vue';
@@ -97,9 +98,9 @@ async function regenerateToken() {
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.security,
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index e549901f05..9fbcce2286 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -90,9 +90,9 @@ function reset() {
 	}
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.sounds,
diff --git a/packages/frontend/src/pages/settings/statusbar.vue b/packages/frontend/src/pages/settings/statusbar.vue
index 7103c2582a..b341e8488e 100644
--- a/packages/frontend/src/pages/settings/statusbar.vue
+++ b/packages/frontend/src/pages/settings/statusbar.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref, computed } from 'vue';
 import { v4 as uuid } from 'uuid';
 import XStatusbar from './statusbar.statusbar.vue';
 import MkFolder from '@/components/MkFolder.vue';
@@ -27,11 +27,11 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const statusbars = defaultStore.reactiveState.statusbars;
 
-let userLists = $ref();
+const userLists = ref();
 
 onMounted(() => {
 	os.api('users/lists/list').then(res => {
-		userLists = res;
+		userLists.value = res;
 	});
 });
 
@@ -45,9 +45,9 @@ async function add() {
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.statusbar,
diff --git a/packages/frontend/src/pages/settings/theme.install.vue b/packages/frontend/src/pages/settings/theme.install.vue
index 7fa7b23e44..c2ca53c743 100644
--- a/packages/frontend/src/pages/settings/theme.install.vue
+++ b/packages/frontend/src/pages/settings/theme.install.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkButton from '@/components/MkButton.vue';
 import { parseThemeCode, previewTheme, installTheme } from '@/scripts/install-theme.js';
@@ -25,7 +25,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let installThemeCode = $ref(null);
+const installThemeCode = ref(null);
 
 async function install(code: string): Promise<void> {
 	try {
@@ -55,9 +55,9 @@ async function install(code: string): Promise<void> {
 	}
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts._theme.install,
diff --git a/packages/frontend/src/pages/settings/theme.manage.vue b/packages/frontend/src/pages/settings/theme.manage.vue
index 8c90c175f0..2a2dd5e764 100644
--- a/packages/frontend/src/pages/settings/theme.manage.vue
+++ b/packages/frontend/src/pages/settings/theme.manage.vue
@@ -72,9 +72,9 @@ function uninstall() {
 	os.success();
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts._theme.manage,
diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue
index 9e55a8fd8d..ad2fc6efe9 100644
--- a/packages/frontend/src/pages/settings/theme.vue
+++ b/packages/frontend/src/pages/settings/theme.vue
@@ -160,9 +160,9 @@ function setWallpaper(event) {
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.theme,
diff --git a/packages/frontend/src/pages/settings/webhook.edit.vue b/packages/frontend/src/pages/settings/webhook.edit.vue
index 3301732c88..c1695dc6a5 100644
--- a/packages/frontend/src/pages/settings/webhook.edit.vue
+++ b/packages/frontend/src/pages/settings/webhook.edit.vue
@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import MkInput from '@/components/MkInput.vue';
 import FormSection from '@/components/form/section.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
@@ -62,36 +62,36 @@ const webhook = await os.api('i/webhooks/show', {
 	webhookId: props.webhookId,
 });
 
-let name = $ref(webhook.name);
-let url = $ref(webhook.url);
-let secret = $ref(webhook.secret);
-let active = $ref(webhook.active);
+const name = ref(webhook.name);
+const url = ref(webhook.url);
+const secret = ref(webhook.secret);
+const active = ref(webhook.active);
 
-let event_follow = $ref(webhook.on.includes('follow'));
-let event_followed = $ref(webhook.on.includes('followed'));
-let event_note = $ref(webhook.on.includes('note'));
-let event_reply = $ref(webhook.on.includes('reply'));
-let event_renote = $ref(webhook.on.includes('renote'));
-let event_reaction = $ref(webhook.on.includes('reaction'));
-let event_mention = $ref(webhook.on.includes('mention'));
+const event_follow = ref(webhook.on.includes('follow'));
+const event_followed = ref(webhook.on.includes('followed'));
+const event_note = ref(webhook.on.includes('note'));
+const event_reply = ref(webhook.on.includes('reply'));
+const event_renote = ref(webhook.on.includes('renote'));
+const event_reaction = ref(webhook.on.includes('reaction'));
+const event_mention = ref(webhook.on.includes('mention'));
 
 async function save(): Promise<void> {
 	const events = [];
-	if (event_follow) events.push('follow');
-	if (event_followed) events.push('followed');
-	if (event_note) events.push('note');
-	if (event_reply) events.push('reply');
-	if (event_renote) events.push('renote');
-	if (event_reaction) events.push('reaction');
-	if (event_mention) events.push('mention');
+	if (event_follow.value) events.push('follow');
+	if (event_followed.value) events.push('followed');
+	if (event_note.value) events.push('note');
+	if (event_reply.value) events.push('reply');
+	if (event_renote.value) events.push('renote');
+	if (event_reaction.value) events.push('reaction');
+	if (event_mention.value) events.push('mention');
 
 	os.apiWithDialog('i/webhooks/update', {
-		name,
-		url,
-		secret,
+		name: name.value,
+		url: url.value,
+		secret: secret.value,
 		webhookId: props.webhookId,
 		on: events,
-		active,
+		active: active.value,
 	});
 }
 
@@ -109,9 +109,9 @@ async function del(): Promise<void> {
 	router.push('/settings/webhook');
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: 'Edit webhook',
diff --git a/packages/frontend/src/pages/settings/webhook.new.vue b/packages/frontend/src/pages/settings/webhook.new.vue
index ed56126548..8a4f03431c 100644
--- a/packages/frontend/src/pages/settings/webhook.new.vue
+++ b/packages/frontend/src/pages/settings/webhook.new.vue
@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref, computed } from 'vue';
 import MkInput from '@/components/MkInput.vue';
 import FormSection from '@/components/form/section.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
@@ -48,39 +48,39 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let name = $ref('');
-let url = $ref('');
-let secret = $ref('');
+const name = ref('');
+const url = ref('');
+const secret = ref('');
 
-let event_follow = $ref(true);
-let event_followed = $ref(true);
-let event_note = $ref(true);
-let event_reply = $ref(true);
-let event_renote = $ref(true);
-let event_reaction = $ref(true);
-let event_mention = $ref(true);
+const event_follow = ref(true);
+const event_followed = ref(true);
+const event_note = ref(true);
+const event_reply = ref(true);
+const event_renote = ref(true);
+const event_reaction = ref(true);
+const event_mention = ref(true);
 
 async function create(): Promise<void> {
 	const events = [];
-	if (event_follow) events.push('follow');
-	if (event_followed) events.push('followed');
-	if (event_note) events.push('note');
-	if (event_reply) events.push('reply');
-	if (event_renote) events.push('renote');
-	if (event_reaction) events.push('reaction');
-	if (event_mention) events.push('mention');
+	if (event_follow.value) events.push('follow');
+	if (event_followed.value) events.push('followed');
+	if (event_note.value) events.push('note');
+	if (event_reply.value) events.push('reply');
+	if (event_renote.value) events.push('renote');
+	if (event_reaction.value) events.push('reaction');
+	if (event_mention.value) events.push('mention');
 
 	os.apiWithDialog('i/webhooks/create', {
-		name,
-		url,
-		secret,
+		name: name.value,
+		url: url.value,
+		secret: secret.value,
 		on: events,
 	});
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: 'Create new webhook',
diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue
index 33841d7f8d..334e5e841b 100644
--- a/packages/frontend/src/pages/settings/webhook.vue
+++ b/packages/frontend/src/pages/settings/webhook.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { computed } from 'vue';
 import MkPagination from '@/components/MkPagination.vue';
 import FormSection from '@/components/form/section.vue';
 import FormLink from '@/components/form/link.vue';
@@ -46,9 +46,9 @@ const pagination = {
 	noPaging: true,
 };
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: 'Webhook',
diff --git a/packages/frontend/src/pages/share.vue b/packages/frontend/src/pages/share.vue
index 1d77e5931d..3e9cac9858 100644
--- a/packages/frontend/src/pages/share.vue
+++ b/packages/frontend/src/pages/share.vue
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 // SPECIFICATION: https://misskey-hub.net/docs/features/share-form.html
 
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import MkPostForm from '@/components/MkPostForm.vue';
@@ -46,27 +46,27 @@ const localOnlyQuery = urlParams.get('localOnly');
 const visibilityQuery = urlParams.get('visibility') as typeof Misskey.noteVisibilities[number];
 
 const state = ref<'fetching' | 'writing' | 'posted'>('fetching');
-let title = $ref(urlParams.get('title'));
+const title = ref(urlParams.get('title'));
 const text = urlParams.get('text');
 const url = urlParams.get('url');
-let initialText = $ref<string | undefined>();
-let reply = $ref<Misskey.entities.Note | undefined>();
-let renote = $ref<Misskey.entities.Note | undefined>();
-let visibility = $ref(Misskey.noteVisibilities.includes(visibilityQuery) ? visibilityQuery : undefined);
-let localOnly = $ref(localOnlyQuery === '0' ? false : localOnlyQuery === '1' ? true : undefined);
-let files = $ref([] as Misskey.entities.DriveFile[]);
-let visibleUsers = $ref([] as Misskey.entities.User[]);
+const initialText = ref<string | undefined>();
+const reply = ref<Misskey.entities.Note | undefined>();
+const renote = ref<Misskey.entities.Note | undefined>();
+const visibility = ref(Misskey.noteVisibilities.includes(visibilityQuery) ? visibilityQuery : undefined);
+const localOnly = ref(localOnlyQuery === '0' ? false : localOnlyQuery === '1' ? true : undefined);
+const files = ref([] as Misskey.entities.DriveFile[]);
+const visibleUsers = ref([] as Misskey.entities.User[]);
 
 async function init() {
 	let noteText = '';
-	if (title) noteText += `[ ${title} ]\n`;
+	if (title.value) noteText += `[ ${title.value} ]\n`;
 	// Googleニュース対策
-	if (text?.startsWith(`${title}.\n`)) noteText += text.replace(`${title}.\n`, '');
-	else if (text && title !== text) noteText += `${text}\n`;
+	if (text?.startsWith(`${title.value}.\n`)) noteText += text.replace(`${title.value}.\n`, '');
+	else if (text && title.value !== text) noteText += `${text}\n`;
 	if (url) noteText += `${url}`;
-	initialText = noteText.trim();
+	initialText.value = noteText.trim();
 
-	if (visibility === 'specified') {
+	if (visibility.value === 'specified') {
 		const visibleUserIds = urlParams.get('visibleUserIds');
 		const visibleAccts = urlParams.get('visibleAccts');
 		await Promise.all(
@@ -78,7 +78,7 @@ async function init() {
 				.map(q => 'username' in q ? { username: q.username, host: q.host === null ? undefined : q.host } : q)
 				.map(q => os.api('users/show', q)
 					.then(user => {
-						visibleUsers.push(user);
+						visibleUsers.value.push(user);
 					}, () => {
 						console.error(`Invalid user query: ${JSON.stringify(q)}`);
 					}),
@@ -91,7 +91,7 @@ async function init() {
 		const replyId = urlParams.get('replyId');
 		const replyUri = urlParams.get('replyUri');
 		if (replyId) {
-			reply = await os.api('notes/show', {
+			reply.value = await os.api('notes/show', {
 				noteId: replyId,
 			});
 		} else if (replyUri) {
@@ -99,7 +99,7 @@ async function init() {
 				uri: replyUri,
 			});
 			if (obj.type === 'Note') {
-				reply = obj.object;
+				reply.value = obj.object;
 			}
 		}
 		//#endregion
@@ -108,7 +108,7 @@ async function init() {
 		const renoteId = urlParams.get('renoteId');
 		const renoteUri = urlParams.get('renoteUri');
 		if (renoteId) {
-			renote = await os.api('notes/show', {
+			renote.value = await os.api('notes/show', {
 				noteId: renoteId,
 			});
 		} else if (renoteUri) {
@@ -116,7 +116,7 @@ async function init() {
 				uri: renoteUri,
 			});
 			if (obj.type === 'Note') {
-				renote = obj.object;
+				renote.value = obj.object;
 			}
 		}
 		//#endregion
@@ -128,7 +128,7 @@ async function init() {
 				fileIds.split(',')
 					.map(fileId => os.api('drive/files/show', { fileId })
 						.then(file => {
-							files.push(file);
+							files.value.push(file);
 						}, () => {
 							console.error(`Failed to fetch a file ${fileId}`);
 						}),
@@ -167,9 +167,9 @@ function onPosted(): void {
 	postMessageToParentWindow('misskey:shareForm:shareCompleted');
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.share,
diff --git a/packages/frontend/src/pages/signup-complete.vue b/packages/frontend/src/pages/signup-complete.vue
index d9a730851d..638c7e8773 100644
--- a/packages/frontend/src/pages/signup-complete.vue
+++ b/packages/frontend/src/pages/signup-complete.vue
@@ -25,29 +25,29 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkAnimBg from '@/components/MkAnimBg.vue';
 import { login } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
 
-let submitting = $ref(false);
+const submitting = ref(false);
 
 const props = defineProps<{
 	code: string;
 }>();
 
 function submit() {
-	if (submitting) return;
-	submitting = true;
+	if (submitting.value) return;
+	submitting.value = true;
 
 	os.api('signup-pending', {
 		code: props.code,
 	}).then(res => {
 		return login(res.i, '/');
 	}).catch(() => {
-		submitting = false;
+		submitting.value = false;
 
 		os.alert({
 			type: 'error',
diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue
index 85c571ecd6..797ab796d2 100644
--- a/packages/frontend/src/pages/tag.vue
+++ b/packages/frontend/src/pages/tag.vue
@@ -51,9 +51,9 @@ async function post() {
 	notes.value?.pagingComponent?.reload();
 }
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(computed(() => ({
 	title: props.tag,
diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue
index 740fd5c4eb..0d137137fc 100644
--- a/packages/frontend/src/pages/theme-editor.vue
+++ b/packages/frontend/src/pages/theme-editor.vue
@@ -73,7 +73,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, ref, computed } from 'vue';
 import { toUnicode } from 'punycode/';
 import tinycolor from 'tinycolor2';
 import { v4 as uuid } from 'uuid';
@@ -124,57 +124,57 @@ const fgColors = [
 	{ color: 'pink', forLight: '#84667d', forDark: '#e4d1e0', forPreview: '#b12390' },
 ];
 
-let theme = $ref<Partial<Theme>>({
+const theme = ref<Partial<Theme>>({
 	base: 'light',
 	props: lightTheme.props,
 });
-let description = $ref<string | null>(null);
-let themeCode = $ref<string | null>(null);
-let changed = $ref(false);
+const description = ref<string | null>(null);
+const themeCode = ref<string | null>(null);
+const changed = ref(false);
 
-useLeaveGuard($$(changed));
+useLeaveGuard(changed);
 
 function showPreview() {
 	os.pageWindow('/preview');
 }
 
 function setBgColor(color: typeof bgColors[number]) {
-	if (theme.base !== color.kind) {
+	if (theme.value.base !== color.kind) {
 		const base = color.kind === 'dark' ? darkTheme : lightTheme;
 		for (const prop of Object.keys(base.props)) {
 			if (prop === 'accent') continue;
 			if (prop === 'fg') continue;
-			theme.props[prop] = base.props[prop];
+			theme.value.props[prop] = base.props[prop];
 		}
 	}
-	theme.base = color.kind;
-	theme.props.bg = color.color;
+	theme.value.base = color.kind;
+	theme.value.props.bg = color.color;
 
-	if (theme.props.fg) {
-		const matchedFgColor = fgColors.find(x => [tinycolor(x.forLight).toRgbString(), tinycolor(x.forDark).toRgbString()].includes(tinycolor(theme.props.fg).toRgbString()));
+	if (theme.value.props.fg) {
+		const matchedFgColor = fgColors.find(x => [tinycolor(x.forLight).toRgbString(), tinycolor(x.forDark).toRgbString()].includes(tinycolor(theme.value.props.fg).toRgbString()));
 		if (matchedFgColor) setFgColor(matchedFgColor);
 	}
 }
 
 function setAccentColor(color) {
-	theme.props.accent = color;
+	theme.value.props.accent = color;
 }
 
 function setFgColor(color) {
-	theme.props.fg = theme.base === 'light' ? color.forLight : color.forDark;
+	theme.value.props.fg = theme.value.base === 'light' ? color.forLight : color.forDark;
 }
 
 function apply() {
-	themeCode = JSON5.stringify(theme, null, '\t');
-	applyTheme(theme, false);
-	changed = true;
+	themeCode.value = JSON5.stringify(theme.value, null, '\t');
+	applyTheme(theme.value, false);
+	changed.value = true;
 }
 
 function applyThemeCode() {
 	let parsed;
 
 	try {
-		parsed = JSON5.parse(themeCode);
+		parsed = JSON5.parse(themeCode.value);
 	} catch (err) {
 		os.alert({
 			type: 'error',
@@ -183,7 +183,7 @@ function applyThemeCode() {
 		return;
 	}
 
-	theme = parsed;
+	theme.value = parsed;
 }
 
 async function saveAs() {
@@ -193,27 +193,27 @@ async function saveAs() {
 	});
 	if (canceled) return;
 
-	theme.id = uuid();
-	theme.name = name;
-	theme.author = `@${$i.username}@${toUnicode(host)}`;
-	if (description) theme.desc = description;
-	await addTheme(theme);
-	applyTheme(theme);
+	theme.value.id = uuid();
+	theme.value.name = name;
+	theme.value.author = `@${$i.username}@${toUnicode(host)}`;
+	if (description.value) theme.value.desc = description.value;
+	await addTheme(theme.value);
+	applyTheme(theme.value);
 	if (defaultStore.state.darkMode) {
-		ColdDeviceStorage.set('darkTheme', theme);
+		ColdDeviceStorage.set('darkTheme', theme.value);
 	} else {
-		ColdDeviceStorage.set('lightTheme', theme);
+		ColdDeviceStorage.set('lightTheme', theme.value);
 	}
-	changed = false;
+	changed.value = false;
 	os.alert({
 		type: 'success',
-		text: i18n.t('_theme.installed', { name: theme.name }),
+		text: i18n.t('_theme.installed', { name: theme.value.name }),
 	});
 }
 
-watch($$(theme), apply, { deep: true });
+watch(theme, apply, { deep: true });
 
-const headerActions = $computed(() => [{
+const headerActions = computed(() => [{
 	asFullButton: true,
 	icon: 'ti ti-eye',
 	text: i18n.ts.preview,
@@ -225,7 +225,7 @@ const headerActions = $computed(() => [{
 	handler: saveAs,
 }]);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.themeEditor,
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index b390d1931d..942061efe3 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch, provide } from 'vue';
+import { computed, watch, provide, shallowRef, ref } from 'vue';
 import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
 import MkTimeline from '@/components/MkTimeline.vue';
 import MkInfo from '@/components/MkInfo.vue';
@@ -57,28 +57,28 @@ const keymap = {
 	't': focus,
 };
 
-const tlComponent = $shallowRef<InstanceType<typeof MkTimeline>>();
-const rootEl = $shallowRef<HTMLElement>();
+const tlComponent = shallowRef<InstanceType<typeof MkTimeline>>();
+const rootEl = shallowRef<HTMLElement>();
 
-let queue = $ref(0);
-let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global');
-const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) });
-const withRenotes = $ref(true);
-const withReplies = $ref($i ? defaultStore.state.tlWithReplies : false);
-const onlyFiles = $ref(false);
+const queue = ref(0);
+const srcWhenNotSignin = ref(isLocalTimelineAvailable ? 'local' : 'global');
+const src = computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin.value), set: (x) => saveSrc(x) });
+const withRenotes = ref(true);
+const withReplies = ref($i ? defaultStore.state.tlWithReplies : false);
+const onlyFiles = ref(false);
 
-watch($$(src), () => queue = 0);
+watch(src, () => queue.value = 0);
 
-watch($$(withReplies), (x) => {
+watch(withReplies, (x) => {
 	if ($i) defaultStore.set('tlWithReplies', x);
 });
 
 function queueUpdated(q: number): void {
-	queue = q;
+	queue.value = q;
 }
 
 function top(): void {
-	if (rootEl) scroll(rootEl, { top: 0 });
+	if (rootEl.value) scroll(rootEl.value, { top: 0 });
 }
 
 async function chooseList(ev: MouseEvent): Promise<void> {
@@ -125,7 +125,7 @@ function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global' | `list:${string
 		src: newSrc,
 		userList,
 	});
-	srcWhenNotSignin = newSrc;
+	srcWhenNotSignin.value = newSrc;
 }
 
 async function timetravel(): Promise<void> {
@@ -134,21 +134,21 @@ async function timetravel(): Promise<void> {
 	});
 	if (canceled) return;
 
-	tlComponent.timetravel(date);
+	tlComponent.value.timetravel(date);
 }
 
 function focus(): void {
-	tlComponent.focus();
+	tlComponent.value.focus();
 }
 
 function closeTutorial(): void {
-	if (!['home', 'local', 'social', 'global'].includes(src)) return;
+	if (!['home', 'local', 'social', 'global'].includes(src.value)) return;
 	const before = defaultStore.state.timelineTutorials;
-	before[src] = true;
+	before[src.value] = true;
 	defaultStore.set('timelineTutorials', before);
 }
 
-const headerActions = $computed(() => {
+const headerActions = computed(() => {
 	const tmp = [
 		{
 			icon: 'ti ti-dots',
@@ -157,17 +157,17 @@ const headerActions = $computed(() => {
 				os.popupMenu([{
 					type: 'switch',
 					text: i18n.ts.showRenotes,
-					ref: $$(withRenotes),
-				}, src === 'local' || src === 'social' ? {
+					ref: withRenotes,
+				}, src.value === 'local' || src.value === 'social' ? {
 					type: 'switch',
 					text: i18n.ts.showRepliesToOthersInTimeline,
-					ref: $$(withReplies),
-					disabled: $$(onlyFiles),
+					ref: withReplies,
+					disabled: onlyFiles,
 				} : undefined, {
 					type: 'switch',
 					text: i18n.ts.fileAttachedOnly,
-					ref: $$(onlyFiles),
-					disabled: src === 'local' || src === 'social' ? $$(withReplies) : false,
+					ref: onlyFiles,
+					disabled: src.value === 'local' || src.value === 'social' ? withReplies : false,
 				}], ev.currentTarget ?? ev.target);
 			},
 		},
@@ -178,14 +178,14 @@ const headerActions = $computed(() => {
 			text: i18n.ts.reload,
 			handler: (ev: Event) => {
 				console.log('called');
-				tlComponent.reloadTimeline();
+				tlComponent.value.reloadTimeline();
 			},
 		});
 	}
 	return tmp;
 });
 
-const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLists.value.map(l => ({
+const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserLists.value.map(l => ({
 	key: 'list:' + l.id,
 	title: l.name,
 	icon: 'ti ti-star',
@@ -227,7 +227,7 @@ const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLis
 	onClick: chooseChannel,
 }] as Tab[]);
 
-const headerTabsWhenNotLogin = $computed(() => [
+const headerTabsWhenNotLogin = computed(() => [
 	...(isLocalTimelineAvailable ? [{
 		key: 'local',
 		title: i18n.ts._timelines.local,
@@ -244,7 +244,7 @@ const headerTabsWhenNotLogin = $computed(() => [
 
 definePageMetadata(computed(() => ({
 	title: i18n.ts.timeline,
-	icon: src === 'local' ? 'ti ti-planet' : src === 'social' ? 'ti ti-universe' : src === 'global' ? 'ti ti-whirl' : 'ti ti-home',
+	icon: src.value === 'local' ? 'ti ti-planet' : src.value === 'social' ? 'ti ti-universe' : src.value === 'global' ? 'ti ti-whirl' : 'ti ti-home',
 })));
 </script>
 
diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue
index 0fc7b62d82..5804a7e7da 100644
--- a/packages/frontend/src/pages/user-list-timeline.vue
+++ b/packages/frontend/src/pages/user-list-timeline.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref, shallowRef } from 'vue';
 import MkTimeline from '@/components/MkTimeline.vue';
 import { scroll } from '@/scripts/scroll.js';
 import * as os from '@/os.js';
@@ -38,39 +38,39 @@ const props = defineProps<{
 	listId: string;
 }>();
 
-let list = $ref(null);
-let queue = $ref(0);
-let tlEl = $shallowRef<InstanceType<typeof MkTimeline>>();
-let rootEl = $shallowRef<HTMLElement>();
+const list = ref(null);
+const queue = ref(0);
+const tlEl = shallowRef<InstanceType<typeof MkTimeline>>();
+const rootEl = shallowRef<HTMLElement>();
 
 watch(() => props.listId, async () => {
-	list = await os.api('users/lists/show', {
+	list.value = await os.api('users/lists/show', {
 		listId: props.listId,
 	});
 }, { immediate: true });
 
 function queueUpdated(q) {
-	queue = q;
+	queue.value = q;
 }
 
 function top() {
-	scroll(rootEl, { top: 0 });
+	scroll(rootEl.value, { top: 0 });
 }
 
 function settings() {
 	router.push(`/my/lists/${props.listId}`);
 }
 
-const headerActions = $computed(() => list ? [{
+const headerActions = computed(() => list.value ? [{
 	icon: 'ti ti-settings',
 	text: i18n.ts.settings,
 	handler: settings,
 }] : []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => list ? {
-	title: list.name,
+definePageMetadata(computed(() => list.value ? {
+	title: list.value.name,
 	icon: 'ti ti-list',
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/user-tag.vue b/packages/frontend/src/pages/user-tag.vue
index 71f8d31924..06269ec9a9 100644
--- a/packages/frontend/src/pages/user-tag.vue
+++ b/packages/frontend/src/pages/user-tag.vue
@@ -25,7 +25,7 @@ const props = defineProps<{
 	tag: string;
 }>();
 
-const tagUsers = $computed(() => ({
+const tagUsers = computed(() => ({
 	endpoint: 'hashtags/users' as const,
 	limit: 30,
 	params: {
diff --git a/packages/frontend/src/pages/user/activity.following.vue b/packages/frontend/src/pages/user/activity.following.vue
index bc6bf77168..bd1159cb32 100644
--- a/packages/frontend/src/pages/user/activity.following.vue
+++ b/packages/frontend/src/pages/user/activity.following.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef, ref } from 'vue';
 import { Chart, ChartDataset } from 'chart.js';
 import * as Misskey from 'misskey-js';
 import gradient from 'chartjs-plugin-gradient';
@@ -32,12 +32,12 @@ const props = defineProps<{
 	user: Misskey.entities.User;
 }>();
 
-const chartEl = $shallowRef<HTMLCanvasElement>(null);
-let legendEl = $shallowRef<InstanceType<typeof MkChartLegend>>();
+const chartEl = shallowRef<HTMLCanvasElement>(null);
+const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
 const now = new Date();
 let chartInstance: Chart = null;
 const chartLimit = 30;
-let fetching = $ref(true);
+const fetching = ref(true);
 
 const { handler: externalTooltipHandler } = useChartTooltip();
 
@@ -88,7 +88,7 @@ async function renderChart() {
 		}, extra);
 	}
 
-	chartInstance = new Chart(chartEl, {
+	chartInstance = new Chart(chartEl.value, {
 		type: 'bar',
 		data: {
 			datasets: [
@@ -162,10 +162,10 @@ async function renderChart() {
 				gradient,
 			},
 		},
-		plugins: [chartVLine(vLineColor), chartLegend(legendEl)],
+		plugins: [chartVLine(vLineColor), chartLegend(legendEl.value)],
 	});
 
-	fetching = false;
+	fetching.value = false;
 }
 
 onMounted(async () => {
diff --git a/packages/frontend/src/pages/user/activity.heatmap.vue b/packages/frontend/src/pages/user/activity.heatmap.vue
index 73bec2487e..ff46db9653 100644
--- a/packages/frontend/src/pages/user/activity.heatmap.vue
+++ b/packages/frontend/src/pages/user/activity.heatmap.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, nextTick, watch } from 'vue';
+import { onMounted, nextTick, watch, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
@@ -29,11 +29,11 @@ const props = defineProps<{
 	user: Misskey.entities.User;
 }>();
 
-const rootEl = $shallowRef<HTMLDivElement>(null);
-const chartEl = $shallowRef<HTMLCanvasElement>(null);
+const rootEl = shallowRef<HTMLDivElement>(null);
+const chartEl = shallowRef<HTMLCanvasElement>(null);
 const now = new Date();
 let chartInstance: Chart = null;
-let fetching = $ref(true);
+const fetching = ref(true);
 
 const { handler: externalTooltipHandler } = useChartTooltip({
 	position: 'middle',
@@ -44,8 +44,8 @@ async function renderChart() {
 		chartInstance.destroy();
 	}
 
-	const wide = rootEl.offsetWidth > 700;
-	const narrow = rootEl.offsetWidth < 400;
+	const wide = rootEl.value.offsetWidth > 700;
+	const narrow = rootEl.value.offsetWidth < 400;
 
 	const weeks = wide ? 50 : narrow ? 10 : 25;
 	const chartLimit = 7 * weeks;
@@ -78,7 +78,7 @@ async function renderChart() {
 		values = raw.inc;
 	}
 
-	fetching = false;
+	fetching.value = false;
 
 	await nextTick();
 
@@ -91,7 +91,7 @@ async function renderChart() {
 
 	const marginEachCell = 4;
 
-	chartInstance = new Chart(chartEl, {
+	chartInstance = new Chart(chartEl.value, {
 		type: 'matrix',
 		data: {
 			datasets: [{
@@ -203,7 +203,7 @@ async function renderChart() {
 }
 
 watch(() => props.src, () => {
-	fetching = true;
+	fetching.value = true;
 	renderChart();
 });
 
diff --git a/packages/frontend/src/pages/user/activity.notes.vue b/packages/frontend/src/pages/user/activity.notes.vue
index 5ba4af7ca1..dd035641d8 100644
--- a/packages/frontend/src/pages/user/activity.notes.vue
+++ b/packages/frontend/src/pages/user/activity.notes.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef, ref } from 'vue';
 import { Chart, ChartDataset } from 'chart.js';
 import * as Misskey from 'misskey-js';
 import gradient from 'chartjs-plugin-gradient';
@@ -32,12 +32,12 @@ const props = defineProps<{
 	user: Misskey.entities.User;
 }>();
 
-const chartEl = $shallowRef<HTMLCanvasElement>(null);
-let legendEl = $shallowRef<InstanceType<typeof MkChartLegend>>();
+const chartEl = shallowRef<HTMLCanvasElement>(null);
+const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
 const now = new Date();
 let chartInstance: Chart = null;
 const chartLimit = 50;
-let fetching = $ref(true);
+const fetching = ref(true);
 
 const { handler: externalTooltipHandler } = useChartTooltip();
 
@@ -87,7 +87,7 @@ async function renderChart() {
 		}, extra);
 	}
 
-	chartInstance = new Chart(chartEl, {
+	chartInstance = new Chart(chartEl.value, {
 		type: 'bar',
 		data: {
 			datasets: [
@@ -161,10 +161,10 @@ async function renderChart() {
 				gradient,
 			},
 		},
-		plugins: [chartVLine(vLineColor), chartLegend(legendEl)],
+		plugins: [chartVLine(vLineColor), chartLegend(legendEl.value)],
 	});
 
-	fetching = false;
+	fetching.value = false;
 }
 
 onMounted(async () => {
diff --git a/packages/frontend/src/pages/user/activity.pv.vue b/packages/frontend/src/pages/user/activity.pv.vue
index 54d1d0c1be..2dd9a1570f 100644
--- a/packages/frontend/src/pages/user/activity.pv.vue
+++ b/packages/frontend/src/pages/user/activity.pv.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef, ref } from 'vue';
 import { Chart, ChartDataset } from 'chart.js';
 import * as Misskey from 'misskey-js';
 import gradient from 'chartjs-plugin-gradient';
@@ -32,12 +32,12 @@ const props = defineProps<{
 	user: Misskey.entities.User;
 }>();
 
-const chartEl = $shallowRef<HTMLCanvasElement>(null);
-let legendEl = $shallowRef<InstanceType<typeof MkChartLegend>>();
+const chartEl = shallowRef<HTMLCanvasElement>(null);
+const legendEl = shallowRef<InstanceType<typeof MkChartLegend>>();
 const now = new Date();
 let chartInstance: Chart = null;
 const chartLimit = 30;
-let fetching = $ref(true);
+const fetching = ref(true);
 
 const { handler: externalTooltipHandler } = useChartTooltip();
 
@@ -88,7 +88,7 @@ async function renderChart() {
 		}, extra);
 	}
 
-	chartInstance = new Chart(chartEl, {
+	chartInstance = new Chart(chartEl.value, {
 		type: 'bar',
 		data: {
 			datasets: [
@@ -172,10 +172,10 @@ async function renderChart() {
 				gradient,
 			},
 		},
-		plugins: [chartVLine(vLineColor), chartLegend(legendEl)],
+		plugins: [chartVLine(vLineColor), chartLegend(legendEl.value)],
 	});
 
-	fetching = false;
+	fetching.value = false;
 }
 
 onMounted(async () => {
diff --git a/packages/frontend/src/pages/user/followers.vue b/packages/frontend/src/pages/user/followers.vue
index b744f6aeec..7e893ae849 100644
--- a/packages/frontend/src/pages/user/followers.vue
+++ b/packages/frontend/src/pages/user/followers.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import XFollowList from './follow-list.vue';
 import * as os from '@/os.js';
@@ -31,16 +31,16 @@ const props = withDefaults(defineProps<{
 }>(), {
 });
 
-let user = $ref<null | Misskey.entities.UserDetailed>(null);
-let error = $ref(null);
+const user = ref<null | Misskey.entities.UserDetailed>(null);
+const error = ref(null);
 
 function fetchUser(): void {
 	if (props.acct == null) return;
-	user = null;
+	user.value = null;
 	os.api('users/show', Misskey.acct.parse(props.acct)).then(u => {
-		user = u;
+		user.value = u;
 	}).catch(err => {
-		error = err;
+		error.value = err;
 	});
 }
 
@@ -48,15 +48,15 @@ watch(() => props.acct, fetchUser, {
 	immediate: true,
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => user ? {
+definePageMetadata(computed(() => user.value ? {
 	icon: 'ti ti-user',
-	title: user.name ? `${user.name} (@${user.username})` : `@${user.username}`,
+	title: user.value.name ? `${user.value.name} (@${user.value.username})` : `@${user.value.username}`,
 	subtitle: i18n.ts.followers,
-	userName: user,
-	avatar: user,
+	userName: user.value,
+	avatar: user.value,
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/user/following.vue b/packages/frontend/src/pages/user/following.vue
index 52f5207119..c5f51712f6 100644
--- a/packages/frontend/src/pages/user/following.vue
+++ b/packages/frontend/src/pages/user/following.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
+import { computed, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import XFollowList from './follow-list.vue';
 import * as os from '@/os.js';
@@ -31,16 +31,16 @@ const props = withDefaults(defineProps<{
 }>(), {
 });
 
-let user = $ref<null | Misskey.entities.UserDetailed>(null);
-let error = $ref(null);
+const user = ref<null | Misskey.entities.UserDetailed>(null);
+const error = ref(null);
 
 function fetchUser(): void {
 	if (props.acct == null) return;
-	user = null;
+	user.value = null;
 	os.api('users/show', Misskey.acct.parse(props.acct)).then(u => {
-		user = u;
+		user.value = u;
 	}).catch(err => {
-		error = err;
+		error.value = err;
 	});
 }
 
@@ -48,15 +48,15 @@ watch(() => props.acct, fetchUser, {
 	immediate: true,
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
-definePageMetadata(computed(() => user ? {
+definePageMetadata(computed(() => user.value ? {
 	icon: 'ti ti-user',
-	title: user.name ? `${user.name} (@${user.username})` : `@${user.username}`,
+	title: user.value.name ? `${user.value.name} (@${user.value.username})` : `@${user.value.username}`,
 	subtitle: i18n.ts.following,
-	userName: user,
-	avatar: user,
+	userName: user.value,
+	avatar: user.value,
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 2e5dd705d0..e2b205a401 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -146,7 +146,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch } from 'vue';
+import { defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkNote from '@/components/MkNote.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
@@ -198,19 +198,19 @@ const props = withDefaults(defineProps<{
 
 const router = useRouter();
 
-let user = $ref(props.user);
-let parallaxAnimationId = $ref<null | number>(null);
-let narrow = $ref<null | boolean>(null);
-let rootEl = $ref<null | HTMLElement>(null);
-let bannerEl = $ref<null | HTMLElement>(null);
-let memoTextareaEl = $ref<null | HTMLElement>(null);
-let memoDraft = $ref(props.user.memo);
-let isEditingMemo = $ref(false);
-let moderationNote = $ref(props.user.moderationNote);
-let editModerationNote = $ref(false);
+const user = ref(props.user);
+const parallaxAnimationId = ref<null | number>(null);
+const narrow = ref<null | boolean>(null);
+const rootEl = ref<null | HTMLElement>(null);
+const bannerEl = ref<null | HTMLElement>(null);
+const memoTextareaEl = ref<null | HTMLElement>(null);
+const memoDraft = ref(props.user.memo);
+const isEditingMemo = ref(false);
+const moderationNote = ref(props.user.moderationNote);
+const editModerationNote = ref(false);
 
-watch($$(moderationNote), async () => {
-	await os.api('admin/update-user-note', { userId: props.user.id, text: moderationNote });
+watch(moderationNote, async () => {
+	await os.api('admin/update-user-note', { userId: props.user.id, text: moderationNote.value });
 });
 
 const pagination = {
@@ -221,32 +221,32 @@ const pagination = {
 	})),
 };
 
-const style = $computed(() => {
+const style = computed(() => {
 	if (props.user.bannerUrl == null) return {};
 	return {
 		backgroundImage: `url(${ props.user.bannerUrl })`,
 	};
 });
 
-const age = $computed(() => {
+const age = computed(() => {
 	return calcAge(props.user.birthday);
 });
 
 function menu(ev) {
-	const { menu, cleanup } = getUserMenu(user, router);
+	const { menu, cleanup } = getUserMenu(user.value, router);
 	os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup);
 }
 
 function parallaxLoop() {
-	parallaxAnimationId = window.requestAnimationFrame(parallaxLoop);
+	parallaxAnimationId.value = window.requestAnimationFrame(parallaxLoop);
 	parallax();
 }
 
 function parallax() {
-	const banner = bannerEl as any;
+	const banner = bannerEl.value as any;
 	if (banner == null) return;
 
-	const top = getScrollPosition(rootEl);
+	const top = getScrollPosition(rootEl.value);
 
 	if (top < 0) return;
 
@@ -256,33 +256,33 @@ function parallax() {
 }
 
 function showMemoTextarea() {
-	isEditingMemo = true;
+	isEditingMemo.value = true;
 	nextTick(() => {
-		memoTextareaEl?.focus();
+		memoTextareaEl.value?.focus();
 	});
 }
 
 function adjustMemoTextarea() {
-	if (!memoTextareaEl) return;
-	memoTextareaEl.style.height = '0px';
-	memoTextareaEl.style.height = `${memoTextareaEl.scrollHeight}px`;
+	if (!memoTextareaEl.value) return;
+	memoTextareaEl.value.style.height = '0px';
+	memoTextareaEl.value.style.height = `${memoTextareaEl.value.scrollHeight}px`;
 }
 
 async function updateMemo() {
 	await api('users/update-memo', {
-		memo: memoDraft,
+		memo: memoDraft.value,
 		userId: props.user.id,
 	});
-	isEditingMemo = false;
+	isEditingMemo.value = false;
 }
 
 watch([props.user], () => {
-	memoDraft = props.user.memo;
+	memoDraft.value = props.user.memo;
 });
 
 onMounted(() => {
 	window.requestAnimationFrame(parallaxLoop);
-	narrow = rootEl!.clientWidth < 1000;
+	narrow.value = rootEl.value!.clientWidth < 1000;
 
 	if (props.user.birthday) {
 		const m = new Date().getMonth() + 1;
@@ -301,8 +301,8 @@ onMounted(() => {
 });
 
 onUnmounted(() => {
-	if (parallaxAnimationId) {
-		window.cancelAnimationFrame(parallaxAnimationId);
+	if (parallaxAnimationId.value) {
+		window.cancelAnimationFrame(parallaxAnimationId.value);
 	}
 });
 </script>
diff --git a/packages/frontend/src/pages/user/index.activity.vue b/packages/frontend/src/pages/user/index.activity.vue
index c531bda598..fe17de9061 100644
--- a/packages/frontend/src/pages/user/index.activity.vue
+++ b/packages/frontend/src/pages/user/index.activity.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkChart from '@/components/MkChart.vue';
@@ -34,20 +34,20 @@ const props = withDefaults(defineProps<{
 	limit: 50,
 });
 
-let chartSrc = $ref('per-user-notes');
+const chartSrc = ref('per-user-notes');
 
 function showMenu(ev: MouseEvent) {
 	os.popupMenu([{
 		text: i18n.ts.notes,
-		active: chartSrc === 'per-user-notes',
+		active: chartSrc.value === 'per-user-notes',
 		action: () => {
-			chartSrc = 'per-user-notes';
+			chartSrc.value = 'per-user-notes';
 		},
 	}, {
 		text: i18n.ts.numberOfProfileView,
-		active: chartSrc === 'per-user-pv',
+		active: chartSrc.value === 'per-user-pv',
 		action: () => {
-			chartSrc = 'per-user-pv';
+			chartSrc.value = 'per-user-pv';
 		},
 	}, /*, {
 		text: i18n.ts.following,
diff --git a/packages/frontend/src/pages/user/index.files.vue b/packages/frontend/src/pages/user/index.files.vue
index 72f95ec1a5..32561e6b0b 100644
--- a/packages/frontend/src/pages/user/index.files.vue
+++ b/packages/frontend/src/pages/user/index.files.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { getStaticImageUrl } from '@/scripts/media-proxy.js';
 import { notePage } from '@/filters/note.js';
@@ -47,12 +47,12 @@ const props = defineProps<{
 	user: Misskey.entities.UserDetailed;
 }>();
 
-let fetching = $ref(true);
-let files = $ref<{
+const fetching = ref(true);
+const files = ref<{
 	note: Misskey.entities.Note;
 	file: Misskey.entities.DriveFile;
 }[]>([]);
-let showingFiles = $ref<string[]>([]);
+const showingFiles = ref<string[]>([]);
 
 function thumbnail(image: Misskey.entities.DriveFile): string {
 	return defaultStore.state.disableShowingAnimatedImages
@@ -68,13 +68,13 @@ onMounted(() => {
 	}).then(notes => {
 		for (const note of notes) {
 			for (const file of note.files) {
-				files.push({
+				files.value.push({
 					note,
 					file,
 				});
 			}
 		}
-		fetching = false;
+		fetching.value = false;
 	});
 });
 </script>
diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue
index 50cc9a3311..4725aedeee 100644
--- a/packages/frontend/src/pages/user/index.vue
+++ b/packages/frontend/src/pages/user/index.vue
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, computed, watch } from 'vue';
+import { defineAsyncComponent, computed, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { acct as getAcct } from '@/filters/user.js';
 import * as os from '@/os.js';
@@ -54,17 +54,17 @@ const props = withDefaults(defineProps<{
 	page: 'home',
 });
 
-let tab = $ref(props.page);
-let user = $ref<null | Misskey.entities.UserDetailed>(null);
-let error = $ref(null);
+const tab = ref(props.page);
+const user = ref<null | Misskey.entities.UserDetailed>(null);
+const error = ref(null);
 
 function fetchUser(): void {
 	if (props.acct == null) return;
-	user = null;
+	user.value = null;
 	os.api('users/show', Misskey.acct.parse(props.acct)).then(u => {
-		user = u;
+		user.value = u;
 	}).catch(err => {
-		error = err;
+		error.value = err;
 	});
 }
 
@@ -72,9 +72,9 @@ watch(() => props.acct, fetchUser, {
 	immediate: true,
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => user ? [{
+const headerTabs = computed(() => user.value ? [{
 	key: 'home',
 	title: i18n.ts.overview,
 	icon: 'ti ti-home',
@@ -86,11 +86,11 @@ const headerTabs = $computed(() => user ? [{
 	key: 'activity',
 	title: i18n.ts.activity,
 	icon: 'ti ti-chart-line',
-}, ...(user.host == null ? [{
+}, ...(user.value.host == null ? [{
 	key: 'achievements',
 	title: i18n.ts.achievements,
 	icon: 'ti ti-medal',
-}] : []), ...($i && ($i.id === user.id)) || user.publicReactions ? [{
+}] : []), ...($i && ($i.id === user.value.id)) || user.value.publicReactions ? [{
 	key: 'reactions',
 	title: i18n.ts.reaction,
 	icon: 'ti ti-mood-happy',
@@ -120,15 +120,15 @@ const headerTabs = $computed(() => user ? [{
 	icon: 'ti ti-code',
 }] : []);
 
-definePageMetadata(computed(() => user ? {
+definePageMetadata(computed(() => user.value ? {
 	icon: 'ti ti-user',
-	title: user.name ? `${user.name} (@${user.username})` : `@${user.username}`,
-	subtitle: `@${getAcct(user)}`,
-	userName: user,
-	avatar: user,
-	path: `/@${user.username}`,
+	title: user.value.name ? `${user.value.name} (@${user.value.username})` : `@${user.value.username}`,
+	subtitle: `@${getAcct(user.value)}`,
+	userName: user.value,
+	avatar: user.value,
+	path: `/@${user.value.username}`,
 	share: {
-		title: user.name,
+		title: user.value.name,
 	},
 } : null));
 </script>
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index 89d0eb9a83..a8ce4537e5 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import XTimeline from './welcome.timeline.vue';
 import MarqueeText from '@/components/MkMarquee.vue';
@@ -48,8 +48,8 @@ import MkNumber from '@/components/MkNumber.vue';
 import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
 import { getProxiedImageUrl } from '@/scripts/media-proxy.js';
 
-let meta = $ref<Misskey.entities.MetaResponse>();
-let instances = $ref<Misskey.entities.FederationInstance[]>();
+const meta = ref<Misskey.entities.MetaResponse>();
+const instances = ref<Misskey.entities.FederationInstance[]>();
 
 function getInstanceIcon(instance: Misskey.entities.FederationInstance): string {
 	if (!instance.iconUrl) {
@@ -60,14 +60,14 @@ function getInstanceIcon(instance: Misskey.entities.FederationInstance): string
 }
 
 os.api('meta', { detail: true }).then(_meta => {
-	meta = _meta;
+	meta.value = _meta;
 });
 
 os.apiGet('federation/instances', {
 	sort: '+pubSub',
 	limit: 20,
 }).then(_instances => {
-	instances = _instances;
+	instances.value = _instances;
 });
 </script>
 
diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue
index 0e6ce61781..61b86f993d 100644
--- a/packages/frontend/src/pages/welcome.setup.vue
+++ b/packages/frontend/src/pages/welcome.setup.vue
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import { host, version } from '@/config.js';
@@ -44,21 +44,21 @@ import { login } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import MkAnimBg from '@/components/MkAnimBg.vue';
 
-let username = $ref('');
-let password = $ref('');
-let submitting = $ref(false);
+const username = ref('');
+const password = ref('');
+const submitting = ref(false);
 
 function submit() {
-	if (submitting) return;
-	submitting = true;
+	if (submitting.value) return;
+	submitting.value = true;
 
 	os.api('admin/accounts/create', {
-		username: username,
-		password: password,
+		username: username.value,
+		password: password.value,
 	}).then(res => {
 		return login(res.token);
 	}).catch(() => {
-		submitting = false;
+		submitting.value = false;
 
 		os.alert({
 			type: 'error',
diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue
index 8e2192074d..129131ce4a 100644
--- a/packages/frontend/src/pages/welcome.timeline.vue
+++ b/packages/frontend/src/pages/welcome.timeline.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
-import { onUpdated } from 'vue';
+import { onUpdated, ref, shallowRef } from 'vue';
 import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
 import MkMediaList from '@/components/MkMediaList.vue';
 import MkPoll from '@/components/MkPoll.vue';
@@ -36,20 +36,20 @@ import * as os from '@/os.js';
 import { getScrollContainer } from '@/scripts/scroll.js';
 import { $i } from '@/account.js';
 
-let notes = $ref<Misskey.entities.Note[]>([]);
-let isScrolling = $ref(false);
-let scrollEl = $shallowRef<HTMLElement>();
+const notes = ref<Misskey.entities.Note[]>([]);
+const isScrolling = ref(false);
+const scrollEl = shallowRef<HTMLElement>();
 
 os.apiGet('notes/featured').then(_notes => {
-	notes = _notes;
+	notes.value = _notes;
 });
 
 onUpdated(() => {
-	if (!scrollEl) return;
-	const container = getScrollContainer(scrollEl);
+	if (!scrollEl.value) return;
+	const container = getScrollContainer(scrollEl.value);
 	const containerHeight = container ? container.clientHeight : window.innerHeight;
-	if (scrollEl.offsetHeight > containerHeight) {
-		isScrolling = true;
+	if (scrollEl.value.offsetHeight > containerHeight) {
+		isScrolling.value = true;
 	}
 });
 </script>
diff --git a/packages/frontend/src/pages/welcome.vue b/packages/frontend/src/pages/welcome.vue
index 2e6bb8b38f..f7d262cc8a 100644
--- a/packages/frontend/src/pages/welcome.vue
+++ b/packages/frontend/src/pages/welcome.vue
@@ -11,22 +11,22 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref } from 'vue';
 import XSetup from './welcome.setup.vue';
 import XEntrance from './welcome.entrance.a.vue';
 import { instanceName } from '@/config.js';
 import * as os from '@/os.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-let meta = $ref(null);
+const meta = ref(null);
 
 os.api('meta', { detail: true }).then(res => {
-	meta = res;
+	meta.value = res;
 });
 
-const headerActions = $computed(() => []);
+const headerActions = computed(() => []);
 
-const headerTabs = $computed(() => []);
+const headerTabs = computed(() => []);
 
 definePageMetadata(computed(() => ({
 	title: instanceName,
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 7f8556d8d2..6b69e1accf 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -63,7 +63,7 @@ const XUpload = defineAsyncComponent(() => import('./upload.vue'));
 
 const dev = _DEV_;
 
-let notifications = $ref<Misskey.entities.Notification[]>([]);
+const notifications = ref<Misskey.entities.Notification[]>([]);
 
 function onNotification(notification: Misskey.entities.Notification, isClient = false) {
 	if (document.visibilityState === 'visible') {
@@ -72,13 +72,13 @@ function onNotification(notification: Misskey.entities.Notification, isClient =
 			useStream().send('readNotification');
 		}
 
-		notifications.unshift(notification);
+		notifications.value.unshift(notification);
 		window.setTimeout(() => {
-			if (notifications.length > 3) notifications.pop();
+			if (notifications.value.length > 3) notifications.value.pop();
 		}, 500);
 
 		window.setTimeout(() => {
-			notifications = notifications.filter(x => x.id !== notification.id);
+			notifications.value = notifications.value.filter(x => x.id !== notification.id);
 		}, 6000);
 	}
 
diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue
index a4ea916d23..c92695afed 100644
--- a/packages/frontend/src/ui/_common_/statusbar-federation.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue
@@ -49,7 +49,7 @@ const props = defineProps<{
 
 const instances = ref<Misskey.entities.FederationInstance[]>([]);
 const fetching = ref(true);
-let key = $ref(0);
+const key = ref(0);
 
 const tick = () => {
 	os.api('federation/instances', {
@@ -58,7 +58,7 @@ const tick = () => {
 	}).then(res => {
 		instances.value = res;
 		fetching.value = false;
-		key++;
+		key.value++;
 	});
 };
 
diff --git a/packages/frontend/src/ui/_common_/statusbar-rss.vue b/packages/frontend/src/ui/_common_/statusbar-rss.vue
index c8e5b3a8af..58e109ad7f 100644
--- a/packages/frontend/src/ui/_common_/statusbar-rss.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-rss.vue
@@ -44,7 +44,7 @@ const props = defineProps<{
 
 const items = ref([]);
 const fetching = ref(true);
-let key = $ref(0);
+const key = ref(0);
 
 const tick = () => {
 	window.fetch(`/api/fetch-rss?url=${props.url}`, {}).then(res => {
@@ -54,7 +54,7 @@ const tick = () => {
 			}
 			items.value = feed.items;
 			fetching.value = false;
-			key++;
+			key.value++;
 		});
 	});
 };
diff --git a/packages/frontend/src/ui/_common_/statusbar-user-list.vue b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
index f1fcd315d0..6057174ba8 100644
--- a/packages/frontend/src/ui/_common_/statusbar-user-list.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
@@ -50,7 +50,7 @@ const props = defineProps<{
 
 const notes = ref<Misskey.entities.Note[]>([]);
 const fetching = ref(true);
-let key = $ref(0);
+const key = ref(0);
 
 const tick = () => {
 	if (props.userListId == null) return;
@@ -59,7 +59,7 @@ const tick = () => {
 	}).then(res => {
 		notes.value = res;
 		fetching.value = false;
-		key++;
+		key.value++;
 	});
 };
 
diff --git a/packages/frontend/src/ui/_common_/stream-indicator.vue b/packages/frontend/src/ui/_common_/stream-indicator.vue
index b09221f5d2..be6a4959ec 100644
--- a/packages/frontend/src/ui/_common_/stream-indicator.vue
+++ b/packages/frontend/src/ui/_common_/stream-indicator.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onUnmounted } from 'vue';
+import { onUnmounted, ref } from 'vue';
 import { useStream } from '@/stream.js';
 import { i18n } from '@/i18n.js';
 import MkButton from '@/components/MkButton.vue';
@@ -23,14 +23,14 @@ import { defaultStore } from '@/store.js';
 
 const zIndex = os.claimZIndex('high');
 
-let hasDisconnected = $ref(false);
+const hasDisconnected = ref(false);
 
 function onDisconnected() {
-	hasDisconnected = true;
+	hasDisconnected.value = true;
 }
 
 function resetDisconnected() {
-	hasDisconnected = false;
+	hasDisconnected.value = false;
 }
 
 function reload() {
diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue
index 3b78aa93cd..aa9f908cec 100644
--- a/packages/frontend/src/ui/classic.header.vue
+++ b/packages/frontend/src/ui/classic.header.vue
@@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, defineAsyncComponent, onMounted } from 'vue';
+import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
 import { openInstanceMenu } from './_common_/common';
 import * as os from '@/os.js';
 import { navbarItemDef } from '@/navbar';
@@ -59,12 +59,12 @@ import { i18n } from '@/i18n.js';
 
 const WINDOW_THRESHOLD = 1400;
 
-let settingsWindowed = $ref(window.innerWidth > WINDOW_THRESHOLD);
-let menu = $ref(defaultStore.state.menu);
+const settingsWindowed = ref(window.innerWidth > WINDOW_THRESHOLD);
+const menu = ref(defaultStore.state.menu);
 // const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay'));
-let otherNavItemIndicated = computed<boolean>(() => {
+const otherNavItemIndicated = computed<boolean>(() => {
 	for (const def in navbarItemDef) {
-		if (menu.includes(def)) continue;
+		if (menu.value.includes(def)) continue;
 		if (navbarItemDef[def].indicated) return true;
 	}
 	return false;
@@ -86,7 +86,7 @@ function openAccountMenu(ev: MouseEvent) {
 
 onMounted(() => {
 	window.addEventListener('resize', () => {
-		settingsWindowed = (window.innerWidth >= WINDOW_THRESHOLD);
+		settingsWindowed.value = (window.innerWidth >= WINDOW_THRESHOLD);
 	}, { passive: true });
 });
 
diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue
index 01dbf1c9c0..402ab1efee 100644
--- a/packages/frontend/src/ui/classic.sidebar.vue
+++ b/packages/frontend/src/ui/classic.sidebar.vue
@@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, onMounted, computed, watch, nextTick } from 'vue';
+import { defineAsyncComponent, onMounted, computed, watch, nextTick, ref, shallowRef } from 'vue';
 import { openInstanceMenu } from './_common_/common.js';
 // import { host } from '@/config.js';
 import * as os from '@/os.js';
@@ -65,24 +65,24 @@ import { i18n } from '@/i18n.js';
 
 const WINDOW_THRESHOLD = 1400;
 
-const menu = $ref(defaultStore.state.menu);
+const menu = ref(defaultStore.state.menu);
 const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay'));
 const otherNavItemIndicated = computed<boolean>(() => {
 	for (const def in navbarItemDef) {
-		if (menu.includes(def)) continue;
+		if (menu.value.includes(def)) continue;
 		if (navbarItemDef[def].indicated) return true;
 	}
 	return false;
 });
-let el = $shallowRef<HTMLElement>();
+const el = shallowRef<HTMLElement>();
 // let accounts = $ref([]);
 // let connection = $ref(null);
-let iconOnly = $ref(false);
-let settingsWindowed = $ref(false);
+const iconOnly = ref(false);
+const settingsWindowed = ref(false);
 
 function calcViewState() {
-	iconOnly = (window.innerWidth <= WINDOW_THRESHOLD) || (menuDisplay.value === 'sideIcon');
-	settingsWindowed = (window.innerWidth > WINDOW_THRESHOLD);
+	iconOnly.value = (window.innerWidth <= WINDOW_THRESHOLD) || (menuDisplay.value === 'sideIcon');
+	settingsWindowed.value = (window.innerWidth > WINDOW_THRESHOLD);
 }
 
 function more(ev: MouseEvent) {
diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue
index 3323e813bb..1a9f939c83 100644
--- a/packages/frontend/src/ui/classic.vue
+++ b/packages/frontend/src/ui/classic.vue
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, ComputedRef, onMounted, provide } from 'vue';
+import { defineAsyncComponent, ComputedRef, onMounted, provide, ref, computed, shallowRef } from 'vue';
 import XSidebar from './classic.sidebar.vue';
 import XCommon from './_common_/common.vue';
 import { instanceName } from '@/config.js';
@@ -62,26 +62,26 @@ const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
 
 const DESKTOP_THRESHOLD = 1100;
 
-let isDesktop = $ref(window.innerWidth >= DESKTOP_THRESHOLD);
+const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
 
-let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
-let widgetsShowing = $ref(false);
-let fullView = $ref(false);
-let globalHeaderHeight = $ref(0);
+const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
+const widgetsShowing = ref(false);
+const fullView = ref(false);
+const globalHeaderHeight = ref(0);
 const wallpaper = miLocalStorage.getItem('wallpaper') != null;
-const showMenuOnTop = $computed(() => defaultStore.state.menuDisplay === 'top');
-let live2d = $shallowRef<HTMLIFrameElement>();
-let widgetsLeft = $ref();
-let widgetsRight = $ref();
+const showMenuOnTop = computed(() => defaultStore.state.menuDisplay === 'top');
+const live2d = shallowRef<HTMLIFrameElement>();
+const widgetsLeft = ref();
+const widgetsRight = ref();
 
 provide('router', mainRouter);
 provideMetadataReceiver((info) => {
-	pageMetadata = info;
-	if (pageMetadata.value) {
-		document.title = `${pageMetadata.value.title} | ${instanceName}`;
+	pageMetadata.value = info;
+	if (pageMetadata.value.value) {
+		document.title = `${pageMetadata.value.value.title} | ${instanceName}`;
 	}
 });
-provide('shouldHeaderThin', showMenuOnTop);
+provide('shouldHeaderThin', showMenuOnTop.value);
 provide('forceSpacerMin', true);
 
 function attachSticky(el) {
@@ -110,10 +110,10 @@ function onContextmenu(ev: MouseEvent) {
 		type: 'label',
 		text: path,
 	}, {
-		icon: fullView ? 'ti ti-minimize' : 'ti ti-maximize',
-		text: fullView ? i18n.ts.quitFullView : i18n.ts.fullView,
+		icon: fullView.value ? 'ti ti-minimize' : 'ti ti-maximize',
+		text: fullView.value ? i18n.ts.quitFullView : i18n.ts.fullView,
 		action: () => {
-			fullView = !fullView;
+			fullView.value = !fullView.value;
 		},
 	}, {
 		icon: 'ti ti-window-maximize',
@@ -154,13 +154,13 @@ defaultStore.loaded.then(() => {
 
 onMounted(() => {
 	window.addEventListener('resize', () => {
-		isDesktop = (window.innerWidth >= DESKTOP_THRESHOLD);
+		isDesktop.value = (window.innerWidth >= DESKTOP_THRESHOLD);
 	}, { passive: true });
 
 	if (defaultStore.state.aiChanMode) {
-		const iframeRect = live2d.getBoundingClientRect();
+		const iframeRect = live2d.value.getBoundingClientRect();
 		window.addEventListener('mousemove', ev => {
-			live2d.contentWindow.postMessage({
+			live2d.value.contentWindow.postMessage({
 				type: 'moveCursor',
 				body: {
 					x: ev.clientX - iframeRect.left,
@@ -169,7 +169,7 @@ onMounted(() => {
 			}, '*');
 		}, { passive: true });
 		window.addEventListener('touchmove', ev => {
-			live2d.contentWindow.postMessage({
+			live2d.value.contentWindow.postMessage({
 				type: 'moveCursor',
 				body: {
 					x: ev.touches[0].clientX - iframeRect.left,
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index 1d51e08f78..10a073243b 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -92,7 +92,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, defineAsyncComponent, ref, watch } from 'vue';
+import { computed, defineAsyncComponent, ref, watch, shallowRef } from 'vue';
 import { v4 as uuid } from 'uuid';
 import XCommon from './_common_/common.vue';
 import { deckStore, addColumn as addColumnToStore, loadDeck, getProfiles, deleteProfile as deleteProfile_ } from './deck/deck-store.js';
@@ -171,7 +171,7 @@ function showSettings() {
 	os.pageWindow('/settings/deck');
 }
 
-let columnsEl = $shallowRef<HTMLElement>();
+const columnsEl = shallowRef<HTMLElement>();
 
 const addColumn = async (ev) => {
 	const columns = [
@@ -212,7 +212,7 @@ const onContextmenu = (ev) => {
 
 function onWheel(ev: WheelEvent) {
 	if (ev.deltaX === 0) {
-		columnsEl.scrollLeft += ev.deltaY;
+		columnsEl.value.scrollLeft += ev.deltaY;
 	}
 }
 
diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue
index 1f4600d949..fe4d2a809c 100644
--- a/packages/frontend/src/ui/deck/antenna-column.vue
+++ b/packages/frontend/src/ui/deck/antenna-column.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef } from 'vue';
 import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
@@ -26,7 +26,7 @@ const props = defineProps<{
 	isStacked: boolean;
 }>();
 
-let timeline = $shallowRef<InstanceType<typeof MkTimeline>>();
+const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
 
 onMounted(() => {
 	if (props.column.antennaId == null) {
diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index d2d279e5d7..de5d94b4f7 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -19,6 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { shallowRef } from 'vue';
 import * as Misskey from 'misskey-js';
 import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
@@ -32,8 +33,8 @@ const props = defineProps<{
 	isStacked: boolean;
 }>();
 
-let timeline = $shallowRef<InstanceType<typeof MkTimeline>>();
-let channel = $shallowRef<Misskey.entities.Channel>();
+const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
+const channel = shallowRef<Misskey.entities.Channel>();
 
 if (props.column.channelId == null) {
 	setChannel();
@@ -58,14 +59,14 @@ async function setChannel() {
 }
 
 async function post() {
-	if (!channel || channel.id !== props.column.channelId) {
-		channel = await os.api('channels/show', {
+	if (!channel.value || channel.value.id !== props.column.channelId) {
+		channel.value = await os.api('channels/show', {
 			channelId: props.column.channelId,
 		});
 	}
 
 	os.post({
-		channel,
+		channel: channel.value,
 	});
 }
 
diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue
index 1a6b833b45..39a0279dea 100644
--- a/packages/frontend/src/ui/deck/column.vue
+++ b/packages/frontend/src/ui/deck/column.vue
@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onBeforeUnmount, onMounted, provide, watch } from 'vue';
+import { onBeforeUnmount, onMounted, provide, watch, shallowRef, ref, computed } from 'vue';
 import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn, Column } from './deck-store';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
@@ -67,16 +67,16 @@ const emit = defineEmits<{
 	(ev: 'headerWheel', ctx: WheelEvent): void;
 }>();
 
-let body = $shallowRef<HTMLDivElement | null>();
+const body = shallowRef<HTMLDivElement | null>();
 
-let dragging = $ref(false);
-watch($$(dragging), v => os.deckGlobalEvents.emit(v ? 'column.dragStart' : 'column.dragEnd'));
+const dragging = ref(false);
+watch(dragging, v => os.deckGlobalEvents.emit(v ? 'column.dragStart' : 'column.dragEnd'));
 
-let draghover = $ref(false);
-let dropready = $ref(false);
+const draghover = ref(false);
+const dropready = ref(false);
 
-const isMainColumn = $computed(() => props.column.type === 'main');
-const active = $computed(() => props.column.active !== false);
+const isMainColumn = computed(() => props.column.type === 'main');
+const active = computed(() => props.column.active !== false);
 
 onMounted(() => {
 	os.deckGlobalEvents.on('column.dragStart', onOtherDragStart);
@@ -89,11 +89,11 @@ onBeforeUnmount(() => {
 });
 
 function onOtherDragStart() {
-	dropready = true;
+	dropready.value = true;
 }
 
 function onOtherDragEnd() {
-	dropready = false;
+	dropready.value = false;
 }
 
 function toggleActive() {
@@ -208,8 +208,8 @@ function onContextmenu(ev: MouseEvent) {
 }
 
 function goTop() {
-	if (body) {
-		body.scrollTo({
+	if (body.value) {
+		body.value.scrollTo({
 			top: 0,
 			behavior: 'smooth',
 		});
@@ -223,17 +223,17 @@ function onDragstart(ev) {
 	// Chromeのバグで、Dragstartハンドラ内ですぐにDOMを変更する(=リアクティブなプロパティを変更する)とDragが終了してしまう
 	// SEE: https://stackoverflow.com/questions/19639969/html5-dragend-event-firing-immediately
 	window.setTimeout(() => {
-		dragging = true;
+		dragging.value = true;
 	}, 10);
 }
 
 function onDragend(ev) {
-	dragging = false;
+	dragging.value = false;
 }
 
 function onDragover(ev) {
 	// 自分自身がドラッグされている場合
-	if (dragging) {
+	if (dragging.value) {
 		// 自分自身にはドロップさせない
 		ev.dataTransfer.dropEffect = 'none';
 	} else {
@@ -241,16 +241,16 @@ function onDragover(ev) {
 
 		ev.dataTransfer.dropEffect = isDeckColumn ? 'move' : 'none';
 
-		if (isDeckColumn) draghover = true;
+		if (isDeckColumn) draghover.value = true;
 	}
 }
 
 function onDragleave() {
-	draghover = false;
+	draghover.value = false;
 }
 
 function onDrop(ev) {
-	draghover = false;
+	draghover.value = false;
 	os.deckGlobalEvents.emit('column.dragEnd');
 
 	const id = ev.dataTransfer.getData(_DATA_TRANSFER_DECK_COLUMN_);
diff --git a/packages/frontend/src/ui/deck/direct-column.vue b/packages/frontend/src/ui/deck/direct-column.vue
index 40c33ebdfc..fd08623462 100644
--- a/packages/frontend/src/ui/deck/direct-column.vue
+++ b/packages/frontend/src/ui/deck/direct-column.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import XColumn from './column.vue';
 import { Column } from './deck-store.js';
 import MkNotes from '@/components/MkNotes.vue';
@@ -30,11 +30,11 @@ const pagination = {
 	},
 };
 
-const tlComponent: InstanceType<typeof MkNotes> = $ref();
+const tlComponent = ref<InstanceType<typeof MkNotes>>();
 
 function reloadTimeline() {
 	return new Promise<void>((res) => {
-		tlComponent.pagingComponent?.reload().then(() => {
+		tlComponent.value.pagingComponent?.reload().then(() => {
 			res();
 		});
 	});
diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue
index 40e4dcee7e..854c8d453b 100644
--- a/packages/frontend/src/ui/deck/list-column.vue
+++ b/packages/frontend/src/ui/deck/list-column.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { watch } from 'vue';
+import { watch, shallowRef, ref } from 'vue';
 import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store';
 import MkTimeline from '@/components/MkTimeline.vue';
@@ -26,14 +26,14 @@ const props = defineProps<{
 	isStacked: boolean;
 }>();
 
-let timeline = $shallowRef<InstanceType<typeof MkTimeline>>();
-const withRenotes = $ref(props.column.withRenotes ?? true);
+const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
+const withRenotes = ref(props.column.withRenotes ?? true);
 
 if (props.column.listId == null) {
 	setList();
 }
 
-watch($$(withRenotes), v => {
+watch(withRenotes, v => {
 	updateColumn(props.column.id, {
 		withRenotes: v,
 	});
@@ -72,7 +72,7 @@ const menu = [
 	{
 		type: 'switch',
 		text: i18n.ts.showRenotes,
-		ref: $$(withRenotes),
+		ref: withRenotes,
 	},
 ];
 </script>
diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue
index d54368c932..0c52957ec4 100644
--- a/packages/frontend/src/ui/deck/main-column.vue
+++ b/packages/frontend/src/ui/deck/main-column.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ComputedRef, provide, shallowRef } from 'vue';
+import { ComputedRef, provide, shallowRef, ref } from 'vue';
 import XColumn from './column.vue';
 import { deckStore, Column } from '@/ui/deck/deck-store.js';
 import * as os from '@/os.js';
@@ -35,11 +35,11 @@ defineProps<{
 }>();
 
 const contents = shallowRef<HTMLElement>();
-let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
+const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
 
 provide('router', mainRouter);
 provideMetadataReceiver((info) => {
-	pageMetadata = info;
+	pageMetadata.value = info;
 });
 
 /*
diff --git a/packages/frontend/src/ui/deck/mentions-column.vue b/packages/frontend/src/ui/deck/mentions-column.vue
index fc67fa144d..b011ba3ca2 100644
--- a/packages/frontend/src/ui/deck/mentions-column.vue
+++ b/packages/frontend/src/ui/deck/mentions-column.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import XColumn from './column.vue';
 import { Column } from './deck-store.js';
 import MkNotes from '@/components/MkNotes.vue';
@@ -22,11 +22,11 @@ defineProps<{
 	isStacked: boolean;
 }>();
 
-const tlComponent: InstanceType<typeof MkNotes> = $ref();
+const tlComponent = ref<InstanceType<typeof MkNotes>>();
 
 function reloadTimeline() {
 	return new Promise<void>((res) => {
-		tlComponent.pagingComponent?.reload().then(() => {
+		tlComponent.value.pagingComponent?.reload().then(() => {
 			res();
 		});
 	});
diff --git a/packages/frontend/src/ui/deck/notifications-column.vue b/packages/frontend/src/ui/deck/notifications-column.vue
index 770e8ea820..e6729b4d58 100644
--- a/packages/frontend/src/ui/deck/notifications-column.vue
+++ b/packages/frontend/src/ui/deck/notifications-column.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent } from 'vue';
+import { defineAsyncComponent, shallowRef } from 'vue';
 import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
 import XNotifications from '@/components/MkNotifications.vue';
@@ -24,7 +24,7 @@ const props = defineProps<{
 	isStacked: boolean;
 }>();
 
-let notificationsComponent = $shallowRef<InstanceType<typeof XNotifications>>();
+const notificationsComponent = shallowRef<InstanceType<typeof XNotifications>>();
 
 function func() {
 	os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), {
diff --git a/packages/frontend/src/ui/deck/role-timeline-column.vue b/packages/frontend/src/ui/deck/role-timeline-column.vue
index 86d8878820..d9bcf8d95e 100644
--- a/packages/frontend/src/ui/deck/role-timeline-column.vue
+++ b/packages/frontend/src/ui/deck/role-timeline-column.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted } from 'vue';
+import { onMounted, shallowRef } from 'vue';
 import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
@@ -26,7 +26,7 @@ const props = defineProps<{
 	isStacked: boolean;
 }>();
 
-let timeline = $shallowRef<InstanceType<typeof MkTimeline>>();
+const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
 
 onMounted(() => {
 	if (props.column.roleId == null) {
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index 41582bbfe3..7ed0f56d02 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, watch } from 'vue';
+import { onMounted, watch, ref, shallowRef } from 'vue';
 import XColumn from './column.vue';
 import { removeColumn, updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
@@ -47,28 +47,28 @@ const props = defineProps<{
 	isStacked: boolean;
 }>();
 
-let disabled = $ref(false);
-let timeline = $shallowRef<InstanceType<typeof MkTimeline>>();
+const disabled = ref(false);
+const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
 
 const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
 const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable));
-const withRenotes = $ref(props.column.withRenotes ?? true);
-const withReplies = $ref(props.column.withReplies ?? false);
-const onlyFiles = $ref(props.column.onlyFiles ?? false);
+const withRenotes = ref(props.column.withRenotes ?? true);
+const withReplies = ref(props.column.withReplies ?? false);
+const onlyFiles = ref(props.column.onlyFiles ?? false);
 
-watch($$(withRenotes), v => {
+watch(withRenotes, v => {
 	updateColumn(props.column.id, {
 		withRenotes: v,
 	});
 });
 
-watch($$(withReplies), v => {
+watch(withReplies, v => {
 	updateColumn(props.column.id, {
 		withReplies: v,
 	});
 });
 
-watch($$(onlyFiles), v => {
+watch(onlyFiles, v => {
 	updateColumn(props.column.id, {
 		onlyFiles: v,
 	});
@@ -78,7 +78,7 @@ onMounted(() => {
 	if (props.column.tl == null) {
 		setType();
 	} else if ($i) {
-		disabled = (
+		disabled.value = (
 			(!((instance.policies.ltlAvailable) || ($i.policies.ltlAvailable)) && ['local', 'social'].includes(props.column.tl)) ||
 			(!((instance.policies.gtlAvailable) || ($i.policies.gtlAvailable)) && ['global'].includes(props.column.tl)));
 	}
@@ -115,17 +115,17 @@ const menu = [{
 }, {
 	type: 'switch',
 	text: i18n.ts.showRenotes,
-	ref: $$(withRenotes),
+	ref: withRenotes,
 }, props.column.tl === 'local' || props.column.tl === 'social' ? {
 	type: 'switch',
 	text: i18n.ts.showRepliesToOthersInTimeline,
-	ref: $$(withReplies),
-	disabled: $$(onlyFiles),
+	ref: withReplies,
+	disabled: onlyFiles,
 } : undefined, {
 	type: 'switch',
 	text: i18n.ts.fileAttachedOnly,
-	ref: $$(onlyFiles),
-	disabled: props.column.tl === 'local' || props.column.tl === 'social' ? $$(withReplies) : false,
+	ref: onlyFiles,
+	disabled: props.column.tl === 'local' || props.column.tl === 'social' ? withReplies : false,
 }];
 </script>
 
diff --git a/packages/frontend/src/ui/deck/widgets-column.vue b/packages/frontend/src/ui/deck/widgets-column.vue
index 5bd6d73976..ef35d885f3 100644
--- a/packages/frontend/src/ui/deck/widgets-column.vue
+++ b/packages/frontend/src/ui/deck/widgets-column.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { ref } from 'vue';
 import XColumn from './column.vue';
 import { addColumnWidget, Column, removeColumnWidget, setColumnWidgets, updateColumnWidget } from './deck-store.js';
 import XWidgets from '@/components/MkWidgets.vue';
@@ -26,7 +26,7 @@ const props = defineProps<{
 	isStacked: boolean;
 }>();
 
-let edit = $ref(false);
+const edit = ref(false);
 
 function addWidget(widget) {
 	addColumnWidget(props.column.id, widget);
@@ -45,7 +45,7 @@ function updateWidgets(widgets) {
 }
 
 function func() {
-	edit = !edit;
+	edit.value = !edit.value;
 }
 
 const menu = [{
diff --git a/packages/frontend/src/ui/minimum.vue b/packages/frontend/src/ui/minimum.vue
index cc5433f81c..f32f2de3df 100644
--- a/packages/frontend/src/ui/minimum.vue
+++ b/packages/frontend/src/ui/minimum.vue
@@ -14,19 +14,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { provide, ComputedRef } from 'vue';
+import { provide, ComputedRef, ref } from 'vue';
 import XCommon from './_common_/common.vue';
 import { mainRouter } from '@/router.js';
 import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
 import { instanceName } from '@/config.js';
 
-let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
+const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
 
 provide('router', mainRouter);
 provideMetadataReceiver((info) => {
-	pageMetadata = info;
-	if (pageMetadata.value) {
-		document.title = `${pageMetadata.value.title} | ${instanceName}`;
+	pageMetadata.value = info;
+	if (pageMetadata.value.value) {
+		document.title = `${pageMetadata.value.value.title} | ${instanceName}`;
 	}
 });
 
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index 5ed1e76828..4721507f7e 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -127,16 +127,16 @@ window.addEventListener('resize', () => {
 	isMobile.value = deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD;
 });
 
-let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
-const widgetsShowing = $ref(false);
-const navFooter = $shallowRef<HTMLElement>();
+const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
+const widgetsShowing = ref(false);
+const navFooter = shallowRef<HTMLElement>();
 const contents = shallowRef<InstanceType<typeof MkStickyContainer>>();
 
 provide('router', mainRouter);
 provideMetadataReceiver((info) => {
-	pageMetadata = info;
-	if (pageMetadata.value) {
-		document.title = `${pageMetadata.value.title} | ${instanceName}`;
+	pageMetadata.value = info;
+	if (pageMetadata.value.value) {
+		document.title = `${pageMetadata.value.value.title} | ${instanceName}`;
 	}
 });
 
@@ -216,16 +216,16 @@ function top() {
 	});
 }
 
-let navFooterHeight = $ref(0);
-provide<Ref<number>>(CURRENT_STICKY_BOTTOM, $$(navFooterHeight));
+const navFooterHeight = ref(0);
+provide<Ref<number>>(CURRENT_STICKY_BOTTOM, navFooterHeight);
 
-watch($$(navFooter), () => {
-	if (navFooter) {
-		navFooterHeight = navFooter.offsetHeight;
-		document.body.style.setProperty('--stickyBottom', `${navFooterHeight}px`);
+watch(navFooter, () => {
+	if (navFooter.value) {
+		navFooterHeight.value = navFooter.value.offsetHeight;
+		document.body.style.setProperty('--stickyBottom', `${navFooterHeight.value}px`);
 		document.body.style.setProperty('--minBottomSpacing', 'var(--minBottomSpacingMobile)');
 	} else {
-		navFooterHeight = 0;
+		navFooterHeight.value = 0;
 		document.body.style.setProperty('--stickyBottom', '0px');
 		document.body.style.setProperty('--minBottomSpacing', '0px');
 	}
diff --git a/packages/frontend/src/ui/universal.widgets.vue b/packages/frontend/src/ui/universal.widgets.vue
index 44988e6df3..376563bd56 100644
--- a/packages/frontend/src/ui/universal.widgets.vue
+++ b/packages/frontend/src/ui/universal.widgets.vue
@@ -13,10 +13,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts">
-let editMode = $ref(false);
+import { computed, ref } from 'vue';
+const editMode = ref(false);
 </script>
 <script lang="ts" setup>
-import { } from 'vue';
 import XWidgets from '@/components/MkWidgets.vue';
 import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
@@ -30,7 +30,7 @@ const props = withDefaults(defineProps<{
 	place: null,
 });
 
-const widgets = $computed(() => {
+const widgets = computed(() => {
 	if (props.place === null) return defaultStore.reactiveState.widgets.value;
 	if (props.place === 'left') return defaultStore.reactiveState.widgets.value.filter(w => w.place === 'left');
 	return defaultStore.reactiveState.widgets.value.filter(w => w.place !== 'left');
diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue
index b7ad184da1..8bf3a28d55 100644
--- a/packages/frontend/src/ui/visitor.vue
+++ b/packages/frontend/src/ui/visitor.vue
@@ -69,7 +69,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ComputedRef, onMounted, provide } from 'vue';
+import { ComputedRef, onMounted, provide, ref, computed } from 'vue';
 import XCommon from './_common_/common.vue';
 import { host, instanceName } from '@/config.js';
 import * as os from '@/os.js';
@@ -84,13 +84,13 @@ import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
 
 const DESKTOP_THRESHOLD = 1100;
 
-let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
+const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
 
 provide('router', mainRouter);
 provideMetadataReceiver((info) => {
-	pageMetadata = info;
-	if (pageMetadata.value) {
-		document.title = `${pageMetadata.value.title} | ${instanceName}`;
+	pageMetadata.value = info;
+	if (pageMetadata.value.value) {
+		document.title = `${pageMetadata.value.value.title} | ${instanceName}`;
 	}
 });
 
@@ -99,14 +99,14 @@ const announcements = {
 	limit: 10,
 };
 
-const isTimelineAvailable = $ref(instance.policies?.ltlAvailable || instance.policies?.gtlAvailable);
+const isTimelineAvailable = ref(instance.policies?.ltlAvailable || instance.policies?.gtlAvailable);
 
-let showMenu = $ref(false);
-let isDesktop = $ref(window.innerWidth >= DESKTOP_THRESHOLD);
-let narrow = $ref(window.innerWidth < 1280);
-let meta = $ref();
+const showMenu = ref(false);
+const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
+const narrow = ref(window.innerWidth < 1280);
+const meta = ref();
 
-const keymap = $computed(() => {
+const keymap = computed(() => {
 	return {
 		'd': () => {
 			if (ColdDeviceStorage.get('syncDeviceDarkMode')) return;
@@ -118,10 +118,10 @@ const keymap = $computed(() => {
 	};
 });
 
-const root = $computed(() => mainRouter.currentRoute.value.name === 'index');
+const root = computed(() => mainRouter.currentRoute.value.name === 'index');
 
 os.api('meta', { detail: true }).then(res => {
-	meta = res;
+	meta.value = res;
 });
 
 function signin() {
@@ -137,15 +137,15 @@ function signup() {
 }
 
 onMounted(() => {
-	if (!isDesktop) {
+	if (!isDesktop.value) {
 		window.addEventListener('resize', () => {
-			if (window.innerWidth >= DESKTOP_THRESHOLD) isDesktop = true;
+			if (window.innerWidth >= DESKTOP_THRESHOLD) isDesktop.value = true;
 		}, { passive: true });
 	}
 });
 
 defineExpose({
-	showMenu: $$(showMenu),
+	showMenu: showMenu,
 });
 </script>
 
diff --git a/packages/frontend/src/ui/zen.vue b/packages/frontend/src/ui/zen.vue
index 4f0945eb48..b819b6ca0a 100644
--- a/packages/frontend/src/ui/zen.vue
+++ b/packages/frontend/src/ui/zen.vue
@@ -22,22 +22,22 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { provide, ComputedRef } from 'vue';
+import { provide, ComputedRef, ref } from 'vue';
 import XCommon from './_common_/common.vue';
 import { mainRouter } from '@/router.js';
 import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
 import { instanceName, ui } from '@/config.js';
 import { i18n } from '@/i18n.js';
 
-let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
+const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
 
 const showBottom = !(new URLSearchParams(location.search)).has('zen') && ui === 'deck';
 
 provide('router', mainRouter);
 provideMetadataReceiver((info) => {
-	pageMetadata = info;
-	if (pageMetadata.value) {
-		document.title = `${pageMetadata.value.title} | ${instanceName}`;
+	pageMetadata.value = info;
+	if (pageMetadata.value.value) {
+		document.title = `${pageMetadata.value.value.title} | ${instanceName}`;
 	}
 });
 
diff --git a/packages/frontend/src/widgets/WidgetActivity.chart.vue b/packages/frontend/src/widgets/WidgetActivity.chart.vue
index 9cfd845ace..a207071324 100644
--- a/packages/frontend/src/widgets/WidgetActivity.chart.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.chart.vue
@@ -34,18 +34,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref } from 'vue';
 const props = defineProps<{
 	activity: any[]
 }>();
 
-let viewBoxX: number = $ref(147);
-let viewBoxY: number = $ref(60);
-let zoom: number = $ref(1);
-let pos: number = $ref(0);
-let pointsNote: any = $ref(null);
-let pointsReply: any = $ref(null);
-let pointsRenote: any = $ref(null);
-let pointsTotal: any = $ref(null);
+const viewBoxX = ref(147);
+const viewBoxY = ref(60);
+const zoom = ref(1);
+const pos = ref(0);
+const pointsNote = ref<any>(null);
+const pointsReply = ref<any>(null);
+const pointsRenote = ref<any>(null);
+const pointsTotal = ref<any>(null);
 
 function dragListen(fn) {
 	window.addEventListener('mousemove', fn);
@@ -62,17 +63,17 @@ function dragClear(fn) {
 function onMousedown(ev) {
 	const clickX = ev.clientX;
 	const clickY = ev.clientY;
-	const baseZoom = zoom;
-	const basePos = pos;
+	const baseZoom = zoom.value;
+	const basePos = pos.value;
 
 	// 動かした時
 	dragListen(me => {
 		let moveLeft = me.clientX - clickX;
 		let moveTop = me.clientY - clickY;
 
-		zoom = Math.max(1, baseZoom + (-moveTop / 20));
-		pos = Math.min(0, basePos + moveLeft);
-		if (pos < -(((props.activity.length - 1) * zoom) - viewBoxX)) pos = -(((props.activity.length - 1) * zoom) - viewBoxX);
+		zoom.value = Math.max(1, baseZoom + (-moveTop / 20));
+		pos.value = Math.min(0, basePos + moveLeft);
+		if (pos.value < -(((props.activity.length - 1) * zoom.value) - viewBoxX.value)) pos.value = -(((props.activity.length - 1) * zoom.value) - viewBoxX.value);
 
 		render();
 	});
@@ -82,10 +83,10 @@ function render() {
 	const peak = Math.max(...props.activity.map(d => d.total));
 	if (peak !== 0) {
 		const activity = props.activity.slice().reverse();
-		pointsNote = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.notes / peak)) * viewBoxY}`).join(' ');
-		pointsReply = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.replies / peak)) * viewBoxY}`).join(' ');
-		pointsRenote = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.renotes / peak)) * viewBoxY}`).join(' ');
-		pointsTotal = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.total / peak)) * viewBoxY}`).join(' ');
+		pointsNote.value = activity.map((d, i) => `${(i * zoom.value) + pos.value},${(1 - (d.notes / peak)) * viewBoxY.value}`).join(' ');
+		pointsReply.value = activity.map((d, i) => `${(i * zoom.value) + pos.value},${(1 - (d.replies / peak)) * viewBoxY.value}`).join(' ');
+		pointsRenote.value = activity.map((d, i) => `${(i * zoom.value) + pos.value},${(1 - (d.renotes / peak)) * viewBoxY.value}`).join(' ');
+		pointsTotal.value = activity.map((d, i) => `${(i * zoom.value) + pos.value},${(1 - (d.total / peak)) * viewBoxY.value}`).join(' ');
 	}
 }
 </script>
diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
index 53b6020ffc..08037222d0 100644
--- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue
+++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
@@ -52,7 +52,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 const parser = new Parser();
 
 const root = ref<AsUiRoot>();
-const components: Ref<AsUiComponent>[] = $ref([]);
+const components = ref<Ref<AsUiComponent>[]>([]);
 
 async function run() {
 	const aiscript = new Interpreter({
@@ -60,7 +60,7 @@ async function run() {
 			storageKey: 'widget',
 			token: $i?.token,
 		}),
-		...registerAsUiLib(components, (_root) => {
+		...registerAsUiLib(components.value, (_root) => {
 			root.value = _root.value;
 		}),
 	}, {
diff --git a/packages/frontend/src/widgets/WidgetClock.vue b/packages/frontend/src/widgets/WidgetClock.vue
index e4ea2c97dd..ca115cfcf7 100644
--- a/packages/frontend/src/widgets/WidgetClock.vue
+++ b/packages/frontend/src/widgets/WidgetClock.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { computed } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
@@ -134,15 +134,15 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 	emit,
 );
 
-const tzAbbrev = $computed(() => (widgetProps.timezone === null
+const tzAbbrev = computed(() => (widgetProps.timezone === null
 	? timezones.find((tz) => tz.name.toLowerCase() === Intl.DateTimeFormat().resolvedOptions().timeZone.toLowerCase())?.abbrev
 	: timezones.find((tz) => tz.name.toLowerCase() === widgetProps.timezone)?.abbrev) ?? '?');
 
-const tzOffset = $computed(() => widgetProps.timezone === null
+const tzOffset = computed(() => widgetProps.timezone === null
 	? 0 - new Date().getTimezoneOffset()
 	: timezones.find((tz) => tz.name.toLowerCase() === widgetProps.timezone)?.offset ?? 0);
 
-const tzOffsetLabel = $computed(() => (tzOffset >= 0 ? '+' : '-') + Math.floor(tzOffset / 60).toString().padStart(2, '0') + ':' + (tzOffset % 60).toString().padStart(2, '0'));
+const tzOffsetLabel = computed(() => (tzOffset.value >= 0 ? '+' : '-') + Math.floor(tzOffset.value / 60).toString().padStart(2, '0') + ':' + (tzOffset.value % 60).toString().padStart(2, '0'));
 
 defineExpose<WidgetComponentExpose>({
 	name,
diff --git a/packages/frontend/src/widgets/WidgetDigitalClock.vue b/packages/frontend/src/widgets/WidgetDigitalClock.vue
index 9ff5f8dcef..ba7b82aad5 100644
--- a/packages/frontend/src/widgets/WidgetDigitalClock.vue
+++ b/packages/frontend/src/widgets/WidgetDigitalClock.vue
@@ -14,6 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { computed } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import { timezones } from '@/scripts/timezones.js';
@@ -63,15 +64,15 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 	emit,
 );
 
-const tzAbbrev = $computed(() => (widgetProps.timezone === null
+const tzAbbrev = computed(() => (widgetProps.timezone === null
 	? timezones.find((tz) => tz.name.toLowerCase() === Intl.DateTimeFormat().resolvedOptions().timeZone.toLowerCase())?.abbrev
 	: timezones.find((tz) => tz.name.toLowerCase() === widgetProps.timezone)?.abbrev) ?? '?');
 
-const tzOffset = $computed(() => widgetProps.timezone === null
+const tzOffset = computed(() => widgetProps.timezone === null
 	? 0 - new Date().getTimezoneOffset()
 	: timezones.find((tz) => tz.name.toLowerCase() === widgetProps.timezone)?.offset ?? 0);
 
-const tzOffsetLabel = $computed(() => (tzOffset >= 0 ? '+' : '-') + Math.floor(tzOffset / 60).toString().padStart(2, '0') + ':' + (tzOffset % 60).toString().padStart(2, '0'));
+const tzOffsetLabel = computed(() => (tzOffset.value >= 0 ? '+' : '-') + Math.floor(tzOffset.value / 60).toString().padStart(2, '0') + ':' + (tzOffset.value % 60).toString().padStart(2, '0'));
 
 defineExpose<WidgetComponentExpose>({
 	name,
diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
index 4ae77e86fc..16e1a42da2 100644
--- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { shallowRef } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
@@ -47,8 +47,8 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 	emit,
 );
 
-let cloud = $shallowRef<InstanceType<typeof MkTagCloud> | null>();
-let activeInstances = $shallowRef(null);
+const cloud = shallowRef<InstanceType<typeof MkTagCloud> | null>();
+const activeInstances = shallowRef(null);
 
 function onInstanceClick(i) {
 	os.pageWindow(`/instance-info/${i.host}`);
@@ -59,8 +59,8 @@ useInterval(() => {
 		sort: '+latestRequestReceivedAt',
 		limit: 25,
 	}).then(res => {
-		activeInstances = res;
-		if (cloud) cloud.update();
+		activeInstances.value = res;
+		if (cloud.value) cloud.value.update();
 	});
 }, 1000 * 60 * 3, {
 	immediate: true,
diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue
index 5531794569..cca368ec8f 100644
--- a/packages/frontend/src/widgets/WidgetJobQueue.vue
+++ b/packages/frontend/src/widgets/WidgetJobQueue.vue
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onUnmounted, reactive } from 'vue';
+import { onUnmounted, reactive, ref } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import { useStream } from '@/stream.js';
@@ -100,8 +100,8 @@ const current = reactive({
 	},
 });
 const prev = reactive({} as typeof current);
-let jammedAudioBuffer: AudioBuffer | null = $ref(null);
-let jammedSoundNodePlaying: boolean = $ref(false);
+const jammedAudioBuffer = ref<AudioBuffer | null>(null);
+const jammedSoundNodePlaying = ref<boolean>(false);
 
 if (defaultStore.state.sound_masterVolume) {
 	sound.loadAudio({
@@ -109,7 +109,7 @@ if (defaultStore.state.sound_masterVolume) {
 		volume: 1,
 	}).then(buf => {
 		if (!buf) throw new Error('[WidgetJobQueue] Failed to initialize AudioBuffer');
-		jammedAudioBuffer = buf;
+		jammedAudioBuffer.value = buf;
 	});
 }
 
@@ -125,11 +125,11 @@ const onStats = (stats) => {
 		current[domain].waiting = stats[domain].waiting;
 		current[domain].delayed = stats[domain].delayed;
 
-		if (current[domain].waiting > 0 && widgetProps.sound && jammedAudioBuffer && !jammedSoundNodePlaying) {
-			const soundNode = sound.createSourceNode(jammedAudioBuffer, 1);
+		if (current[domain].waiting > 0 && widgetProps.sound && jammedAudioBuffer.value && !jammedSoundNodePlaying.value) {
+			const soundNode = sound.createSourceNode(jammedAudioBuffer.value, 1);
 			if (soundNode) {
-				jammedSoundNodePlaying = true;
-				soundNode.onended = () => jammedSoundNodePlaying = false;
+				jammedSoundNodePlaying.value = true;
+				soundNode.onended = () => jammedSoundNodePlaying.value = false;
 				soundNode.start();
 			}
 		}
diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue
index 5540a09c71..be662e0ed1 100644
--- a/packages/frontend/src/widgets/WidgetRss.vue
+++ b/packages/frontend/src/widgets/WidgetRss.vue
@@ -72,7 +72,7 @@ const fetchEndpoint = computed(() => {
 	url.searchParams.set('url', widgetProps.url);
 	return url;
 });
-let intervalClear = $ref<(() => void) | undefined>();
+const intervalClear = ref<(() => void) | undefined>();
 
 const tick = () => {
 	if (document.visibilityState === 'hidden' && rawItems.value.length !== 0) return;
@@ -87,10 +87,10 @@ const tick = () => {
 
 watch(() => fetchEndpoint, tick);
 watch(() => widgetProps.refreshIntervalSec, () => {
-	if (intervalClear) {
-		intervalClear();
+	if (intervalClear.value) {
+		intervalClear.value();
 	}
-	intervalClear = useInterval(tick, Math.max(10000, widgetProps.refreshIntervalSec * 1000), {
+	intervalClear.value = useInterval(tick, Math.max(10000, widgetProps.refreshIntervalSec * 1000), {
 		immediate: true,
 		afterMounted: true,
 	});
diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue
index 2b2a5233be..07f922bfec 100644
--- a/packages/frontend/src/widgets/WidgetRssTicker.vue
+++ b/packages/frontend/src/widgets/WidgetRssTicker.vue
@@ -101,9 +101,9 @@ const fetchEndpoint = computed(() => {
 	url.searchParams.set('url', widgetProps.url);
 	return url;
 });
-let intervalClear = $ref<(() => void) | undefined>();
+const intervalClear = ref<(() => void) | undefined>();
 
-let key = $ref(0);
+const key = ref(0);
 
 const tick = () => {
 	if (document.visibilityState === 'hidden' && rawItems.value.length !== 0) return;
@@ -113,16 +113,16 @@ const tick = () => {
 		.then(feed => {
 			rawItems.value = feed.items ?? [];
 			fetching.value = false;
-			key++;
+			key.value++;
 		});
 };
 
 watch(() => fetchEndpoint, tick);
 watch(() => widgetProps.refreshIntervalSec, () => {
-	if (intervalClear) {
-		intervalClear();
+	if (intervalClear.value) {
+		intervalClear.value();
 	}
-	intervalClear = useInterval(tick, Math.max(10000, widgetProps.refreshIntervalSec * 1000), {
+	intervalClear.value = useInterval(tick, Math.max(10000, widgetProps.refreshIntervalSec * 1000), {
 		immediate: true,
 		afterMounted: true,
 	});
diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue
index a0d460c704..4f3ce1c8c5 100644
--- a/packages/frontend/src/widgets/WidgetUserList.vue
+++ b/packages/frontend/src/widgets/WidgetUserList.vue
@@ -24,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
+import { ref } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
@@ -57,9 +58,9 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
 	emit,
 );
 
-let list = $ref();
-let users = $ref([]);
-let fetching = $ref(true);
+const list = ref();
+const users = ref([]);
+const fetching = ref(true);
 
 async function chooseList() {
 	const lists = await os.api('users/lists/list');
@@ -79,19 +80,19 @@ async function chooseList() {
 
 const fetch = () => {
 	if (widgetProps.listId == null) {
-		fetching = false;
+		fetching.value = false;
 		return;
 	}
 
 	os.api('users/lists/show', {
 		listId: widgetProps.listId,
 	}).then(_list => {
-		list = _list;
+		list.value = _list;
 		os.api('users/show', {
-			userIds: list.userIds,
+			userIds: list.value.userIds,
 		}).then(_users => {
-			users = _users;
-			fetching = false;
+			users.value = _users;
+			fetching.value = false;
 		});
 	});
 };
diff --git a/packages/frontend/src/widgets/server-metric/cpu-mem.vue b/packages/frontend/src/widgets/server-metric/cpu-mem.vue
index c656d75429..9196ae209f 100644
--- a/packages/frontend/src/widgets/server-metric/cpu-mem.vue
+++ b/packages/frontend/src/widgets/server-metric/cpu-mem.vue
@@ -75,7 +75,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onBeforeUnmount } from 'vue';
+import { onMounted, onBeforeUnmount, ref } from 'vue';
 import { v4 as uuid } from 'uuid';
 
 const props = defineProps<{
@@ -83,23 +83,23 @@ const props = defineProps<{
 	meta: any
 }>();
 
-let viewBoxX: number = $ref(50);
-let viewBoxY: number = $ref(30);
-let stats: any[] = $ref([]);
+const viewBoxX = ref<number>(50);
+const viewBoxY = ref<number>(30);
+const stats = ref<any[]>([]);
 const cpuGradientId = uuid();
 const cpuMaskId = uuid();
 const memGradientId = uuid();
 const memMaskId = uuid();
-let cpuPolylinePoints: string = $ref('');
-let memPolylinePoints: string = $ref('');
-let cpuPolygonPoints: string = $ref('');
-let memPolygonPoints: string = $ref('');
-let cpuHeadX: any = $ref(null);
-let cpuHeadY: any = $ref(null);
-let memHeadX: any = $ref(null);
-let memHeadY: any = $ref(null);
-let cpuP: string = $ref('');
-let memP: string = $ref('');
+const cpuPolylinePoints = ref<string>('');
+const memPolylinePoints = ref<string>('');
+const cpuPolygonPoints = ref<string>('');
+const memPolygonPoints = ref<string>('');
+const cpuHeadX = ref<any>(null);
+const cpuHeadY = ref<any>(null);
+const memHeadX = ref<any>(null);
+const memHeadY = ref<any>(null);
+const cpuP = ref<string>('');
+const memP = ref<string>('');
 
 onMounted(() => {
 	props.connection.on('stats', onStats);
@@ -115,24 +115,24 @@ onBeforeUnmount(() => {
 });
 
 function onStats(connStats) {
-	stats.push(connStats);
-	if (stats.length > 50) stats.shift();
+	stats.value.push(connStats);
+	if (stats.value.length > 50) stats.value.shift();
 
-	let cpuPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - s.cpu) * viewBoxY]);
-	let memPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.mem.active / props.meta.mem.total)) * viewBoxY]);
-	cpuPolylinePoints = cpuPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
-	memPolylinePoints = memPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+	let cpuPolylinePointsStats = stats.value.map((s, i) => [viewBoxX.value - ((stats.value.length - 1) - i), (1 - s.cpu) * viewBoxY.value]);
+	let memPolylinePointsStats = stats.value.map((s, i) => [viewBoxX.value - ((stats.value.length - 1) - i), (1 - (s.mem.active / props.meta.mem.total)) * viewBoxY.value]);
+	cpuPolylinePoints.value = cpuPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+	memPolylinePoints.value = memPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
 
-	cpuPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${cpuPolylinePoints} ${viewBoxX},${viewBoxY}`;
-	memPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${memPolylinePoints} ${viewBoxX},${viewBoxY}`;
+	cpuPolygonPoints.value = `${viewBoxX.value - (stats.value.length - 1)},${viewBoxY.value} ${cpuPolylinePoints.value} ${viewBoxX.value},${viewBoxY.value}`;
+	memPolygonPoints.value = `${viewBoxX.value - (stats.value.length - 1)},${viewBoxY.value} ${memPolylinePoints.value} ${viewBoxX.value},${viewBoxY.value}`;
 
-	cpuHeadX = cpuPolylinePointsStats.at(-1)![0];
-	cpuHeadY = cpuPolylinePointsStats.at(-1)![1];
-	memHeadX = memPolylinePointsStats.at(-1)![0];
-	memHeadY = memPolylinePointsStats.at(-1)![1];
+	cpuHeadX.value = cpuPolylinePointsStats.at(-1)![0];
+	cpuHeadY.value = cpuPolylinePointsStats.at(-1)![1];
+	memHeadX.value = memPolylinePointsStats.at(-1)![0];
+	memHeadY.value = memPolylinePointsStats.at(-1)![1];
 
-	cpuP = (connStats.cpu * 100).toFixed(0);
-	memP = (connStats.mem.active / props.meta.mem.total * 100).toFixed(0);
+	cpuP.value = (connStats.cpu * 100).toFixed(0);
+	memP.value = (connStats.mem.active / props.meta.mem.total * 100).toFixed(0);
 }
 
 function onStatsLog(statsLog) {
diff --git a/packages/frontend/src/widgets/server-metric/cpu.vue b/packages/frontend/src/widgets/server-metric/cpu.vue
index 1c9cce8598..0aeba518c0 100644
--- a/packages/frontend/src/widgets/server-metric/cpu.vue
+++ b/packages/frontend/src/widgets/server-metric/cpu.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onBeforeUnmount } from 'vue';
+import { onMounted, onBeforeUnmount, ref } from 'vue';
 import XPie from './pie.vue';
 
 const props = defineProps<{
@@ -23,10 +23,10 @@ const props = defineProps<{
 	meta: any
 }>();
 
-let usage: number = $ref(0);
+const usage = ref<number>(0);
 
 function onStats(stats) {
-	usage = stats.cpu;
+	usage.value = stats.cpu;
 }
 
 onMounted(() => {
diff --git a/packages/frontend/src/widgets/server-metric/disk.vue b/packages/frontend/src/widgets/server-metric/disk.vue
index 079b326fd6..ef88cae9f6 100644
--- a/packages/frontend/src/widgets/server-metric/disk.vue
+++ b/packages/frontend/src/widgets/server-metric/disk.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { computed } from 'vue';
 import XPie from './pie.vue';
 import bytes from '@/filters/bytes.js';
 
@@ -24,10 +24,10 @@ const props = defineProps<{
 	meta: any; // TODO
 }>();
 
-const usage = $computed(() => props.meta.fs.used / props.meta.fs.total);
-const total = $computed(() => props.meta.fs.total);
-const used = $computed(() => props.meta.fs.used);
-const available = $computed(() => props.meta.fs.total - props.meta.fs.used);
+const usage = computed(() => props.meta.fs.used / props.meta.fs.total);
+const total = computed(() => props.meta.fs.total);
+const used = computed(() => props.meta.fs.used);
+const available = computed(() => props.meta.fs.total - props.meta.fs.used);
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/frontend/src/widgets/server-metric/mem.vue b/packages/frontend/src/widgets/server-metric/mem.vue
index 47f5a70a7e..11d0c156c1 100644
--- a/packages/frontend/src/widgets/server-metric/mem.vue
+++ b/packages/frontend/src/widgets/server-metric/mem.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onBeforeUnmount } from 'vue';
+import { onMounted, onBeforeUnmount, ref } from 'vue';
 import XPie from './pie.vue';
 import bytes from '@/filters/bytes.js';
 
@@ -25,16 +25,16 @@ const props = defineProps<{
 	meta: any
 }>();
 
-let usage: number = $ref(0);
-let total: number = $ref(0);
-let used: number = $ref(0);
-let free: number = $ref(0);
+const usage = ref<number>(0);
+const total = ref<number>(0);
+const used = ref<number>(0);
+const free = ref<number>(0);
 
 function onStats(stats) {
-	usage = stats.mem.active / props.meta.mem.total;
-	total = props.meta.mem.total;
-	used = stats.mem.active;
-	free = total - used;
+	usage.value = stats.mem.active / props.meta.mem.total;
+	total.value = props.meta.mem.total;
+	used.value = stats.mem.active;
+	free.value = total.value - used.value;
 }
 
 onMounted(() => {
diff --git a/packages/frontend/src/widgets/server-metric/net.vue b/packages/frontend/src/widgets/server-metric/net.vue
index 5593128660..e6a8bfc22a 100644
--- a/packages/frontend/src/widgets/server-metric/net.vue
+++ b/packages/frontend/src/widgets/server-metric/net.vue
@@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onBeforeUnmount } from 'vue';
+import { onMounted, onBeforeUnmount, ref } from 'vue';
 import bytes from '@/filters/bytes.js';
 
 const props = defineProps<{
@@ -57,19 +57,19 @@ const props = defineProps<{
 	meta: any
 }>();
 
-let viewBoxX: number = $ref(50);
-let viewBoxY: number = $ref(30);
-let stats: any[] = $ref([]);
-let inPolylinePoints: string = $ref('');
-let outPolylinePoints: string = $ref('');
-let inPolygonPoints: string = $ref('');
-let outPolygonPoints: string = $ref('');
-let inHeadX: any = $ref(null);
-let inHeadY: any = $ref(null);
-let outHeadX: any = $ref(null);
-let outHeadY: any = $ref(null);
-let inRecent: number = $ref(0);
-let outRecent: number = $ref(0);
+const viewBoxX = ref<number>(50);
+const viewBoxY = ref<number>(30);
+const stats = ref<any[]>([]);
+const inPolylinePoints = ref<string>('');
+const outPolylinePoints = ref<string>('');
+const inPolygonPoints = ref<string>('');
+const outPolygonPoints = ref<string>('');
+const inHeadX = ref<any>(null);
+const inHeadY = ref<any>(null);
+const outHeadX = ref<any>(null);
+const outHeadY = ref<any>(null);
+const inRecent = ref<number>(0);
+const outRecent = ref<number>(0);
 
 onMounted(() => {
 	props.connection.on('stats', onStats);
@@ -85,27 +85,27 @@ onBeforeUnmount(() => {
 });
 
 function onStats(connStats) {
-	stats.push(connStats);
-	if (stats.length > 50) stats.shift();
+	stats.value.push(connStats);
+	if (stats.value.length > 50) stats.value.shift();
 
-	const inPeak = Math.max(1024 * 64, Math.max(...stats.map(s => s.net.rx)));
-	const outPeak = Math.max(1024 * 64, Math.max(...stats.map(s => s.net.tx)));
+	const inPeak = Math.max(1024 * 64, Math.max(...stats.value.map(s => s.net.rx)));
+	const outPeak = Math.max(1024 * 64, Math.max(...stats.value.map(s => s.net.tx)));
 
-	let inPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.net.rx / inPeak)) * viewBoxY]);
-	let outPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.net.tx / outPeak)) * viewBoxY]);
-	inPolylinePoints = inPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
-	outPolylinePoints = outPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+	let inPolylinePointsStats = stats.value.map((s, i) => [viewBoxX.value - ((stats.value.length - 1) - i), (1 - (s.net.rx / inPeak)) * viewBoxY.value]);
+	let outPolylinePointsStats = stats.value.map((s, i) => [viewBoxX.value - ((stats.value.length - 1) - i), (1 - (s.net.tx / outPeak)) * viewBoxY.value]);
+	inPolylinePoints.value = inPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
+	outPolylinePoints.value = outPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' ');
 
-	inPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${inPolylinePoints} ${viewBoxX},${viewBoxY}`;
-	outPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${outPolylinePoints} ${viewBoxX},${viewBoxY}`;
+	inPolygonPoints.value = `${viewBoxX.value - (stats.value.length - 1)},${viewBoxY.value} ${inPolylinePoints.value} ${viewBoxX.value},${viewBoxY.value}`;
+	outPolygonPoints.value = `${viewBoxX.value - (stats.value.length - 1)},${viewBoxY.value} ${outPolylinePoints.value} ${viewBoxX.value},${viewBoxY.value}`;
 
-	inHeadX = inPolylinePointsStats.at(-1)![0];
-	inHeadY = inPolylinePointsStats.at(-1)![1];
-	outHeadX = outPolylinePointsStats.at(-1)![0];
-	outHeadY = outPolylinePointsStats.at(-1)![1];
+	inHeadX.value = inPolylinePointsStats.at(-1)![0];
+	inHeadY.value = inPolylinePointsStats.at(-1)![1];
+	outHeadX.value = outPolylinePointsStats.at(-1)![0];
+	outHeadY.value = outPolylinePointsStats.at(-1)![1];
 
-	inRecent = connStats.net.rx;
-	outRecent = connStats.net.tx;
+	inRecent.value = connStats.net.rx;
+	outRecent.value = connStats.net.tx;
 }
 
 function onStatsLog(statsLog) {
diff --git a/packages/frontend/src/widgets/server-metric/pie.vue b/packages/frontend/src/widgets/server-metric/pie.vue
index c8a1496101..fd18a6a4f2 100644
--- a/packages/frontend/src/widgets/server-metric/pie.vue
+++ b/packages/frontend/src/widgets/server-metric/pie.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { } from 'vue';
+import { computed } from 'vue';
 
 const props = defineProps<{
 	value: number;
@@ -36,8 +36,8 @@ const props = defineProps<{
 
 const r = 0.45;
 
-const color = $computed(() => `hsl(${180 - (props.value * 180)}, 80%, 70%)`);
-const strokeDashoffset = $computed(() => (1 - props.value) * (Math.PI * (r * 2)));
+const color = computed(() => `hsl(${180 - (props.value * 180)}, 80%, 70%)`);
+const strokeDashoffset = computed(() => (1 - props.value) * (Math.PI * (r * 2)));
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json
index ad8fd773b0..5d451c878c 100644
--- a/packages/frontend/tsconfig.json
+++ b/packages/frontend/tsconfig.json
@@ -33,7 +33,6 @@
 		],
 		"types": [
 			"vite/client",
-			"reactivity-transform/macros-global"
 		],
 		"lib": [
 			"esnext",
diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts
index 4d2bb00e33..8ff3eb1562 100644
--- a/packages/frontend/vite.config.ts
+++ b/packages/frontend/vite.config.ts
@@ -2,8 +2,6 @@ import path from 'path';
 import pluginReplace from '@rollup/plugin-replace';
 import pluginVue from '@vitejs/plugin-vue';
 import { type UserConfig, defineConfig } from 'vite';
-// @ts-expect-error https://github.com/sxzz/unplugin-vue-macros/issues/257#issuecomment-1410752890
-import ReactivityTransform from '@vue-macros/reactivity-transform/vite';
 
 import locales from '../../locales';
 import meta from '../../package.json';
@@ -50,10 +48,7 @@ export function getConfig(): UserConfig {
 		},
 
 		plugins: [
-			pluginVue({
-				reactivityTransform: true,
-			}),
-			ReactivityTransform(),
+			pluginVue(),
 			pluginUnwindCssModuleClassName(),
 			pluginJson5(),
 			...process.env.NODE_ENV === 'production'
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ea1ea6cd18..707deb9f10 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -676,9 +676,6 @@ importers:
       '@vitejs/plugin-vue':
         specifier: 4.5.1
         version: 4.5.1(vite@5.0.5)(vue@3.3.9)
-      '@vue-macros/reactivity-transform':
-        specifier: 0.4.0
-        version: 0.4.0(rollup@4.6.1)(vue@3.3.9)
       '@vue/compiler-sfc':
         specifier: 3.3.9
         version: 3.3.9
@@ -1869,14 +1866,14 @@ packages:
     resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.3
+      '@babel/types': 7.23.5
     dev: true
 
   /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15:
     resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.3
+      '@babel/types': 7.23.5
     dev: true
 
   /@babel/helper-compilation-targets@7.22.10:
@@ -1991,7 +1988,7 @@ packages:
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/template': 7.22.5
-      '@babel/types': 7.23.3
+      '@babel/types': 7.22.17
     dev: true
 
   /@babel/helper-function-name@7.23.0:
@@ -2006,21 +2003,21 @@ packages:
     resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.3
+      '@babel/types': 7.22.17
     dev: true
 
   /@babel/helper-member-expression-to-functions@7.22.5:
     resolution: {integrity: sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.3
+      '@babel/types': 7.23.5
     dev: true
 
   /@babel/helper-member-expression-to-functions@7.23.0:
     resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.3
+      '@babel/types': 7.23.5
     dev: true
 
   /@babel/helper-module-imports@7.22.15:
@@ -2069,7 +2066,7 @@ packages:
     resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.3
+      '@babel/types': 7.23.5
     dev: true
 
   /@babel/helper-plugin-utils@7.22.5:
@@ -2124,7 +2121,7 @@ packages:
     resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/types': 7.23.3
+      '@babel/types': 7.23.5
     dev: true
 
   /@babel/helper-split-export-declaration@7.22.6:
@@ -2150,6 +2147,7 @@ packages:
   /@babel/helper-validator-identifier@7.22.20:
     resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
     engines: {node: '>=6.9.0'}
+    dev: true
 
   /@babel/helper-validator-identifier@7.22.5:
     resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
@@ -2172,7 +2170,7 @@ packages:
     dependencies:
       '@babel/helper-function-name': 7.22.5
       '@babel/template': 7.22.15
-      '@babel/types': 7.23.3
+      '@babel/types': 7.23.5
     dev: true
 
   /@babel/helpers@7.22.11:
@@ -3324,7 +3322,7 @@ packages:
     dependencies:
       '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
-      '@babel/types': 7.23.3
+      '@babel/types': 7.23.5
       esutils: 2.0.3
     dev: true
 
@@ -3401,7 +3399,7 @@ packages:
       '@babel/helper-hoist-variables': 7.22.5
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/parser': 7.23.3
-      '@babel/types': 7.23.3
+      '@babel/types': 7.22.17
       debug: 4.3.4(supports-color@5.5.0)
       globals: 11.12.0
     transitivePeerDependencies:
@@ -3443,14 +3441,6 @@ packages:
       '@babel/helper-validator-identifier': 7.22.15
       to-fast-properties: 2.0.0
 
-  /@babel/types@7.23.3:
-    resolution: {integrity: sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/helper-string-parser': 7.22.5
-      '@babel/helper-validator-identifier': 7.22.20
-      to-fast-properties: 2.0.0
-
   /@babel/types@7.23.5:
     resolution: {integrity: sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==}
     engines: {node: '>=6.9.0'}
@@ -6671,7 +6661,7 @@ packages:
     dependencies:
       '@babel/core': 7.23.5
       '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
-      '@babel/types': 7.23.3
+      '@babel/types': 7.23.5
       '@ndelangen/get-tarball': 3.0.7
       '@storybook/codemod': 7.6.3
       '@storybook/core-common': 7.6.3
@@ -6734,7 +6724,7 @@ packages:
     dependencies:
       '@babel/core': 7.23.5
       '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
-      '@babel/types': 7.23.3
+      '@babel/types': 7.23.5
       '@storybook/csf': 0.1.2
       '@storybook/csf-tools': 7.6.3
       '@storybook/node-logger': 7.6.3
@@ -6901,7 +6891,7 @@ packages:
     resolution: {integrity: sha512-8bMYPsWw2tv+fqZ5H436l4x1KLSB6gIcm6snsjyF916yCHG6WcWm+EI6+wNUoySEtrQY2AiwFJqE37wI5OUJFg==}
     dependencies:
       '@storybook/csf-tools': 7.6.3
-      unplugin: 1.5.1
+      unplugin: 1.4.0
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -6912,7 +6902,7 @@ packages:
       '@babel/generator': 7.23.5
       '@babel/parser': 7.23.3
       '@babel/traverse': 7.23.5
-      '@babel/types': 7.23.3
+      '@babel/types': 7.23.5
       '@storybook/csf': 0.1.2
       '@storybook/types': 7.6.3
       fs-extra: 11.1.1
@@ -7763,7 +7753,7 @@ packages:
     resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==}
     dependencies:
       '@babel/parser': 7.23.3
-      '@babel/types': 7.23.3
+      '@babel/types': 7.22.17
       '@types/babel__generator': 7.6.4
       '@types/babel__template': 7.4.1
       '@types/babel__traverse': 7.20.0
@@ -7772,20 +7762,20 @@ packages:
   /@types/babel__generator@7.6.4:
     resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==}
     dependencies:
-      '@babel/types': 7.23.3
+      '@babel/types': 7.22.17
     dev: true
 
   /@types/babel__template@7.4.1:
     resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==}
     dependencies:
       '@babel/parser': 7.23.3
-      '@babel/types': 7.23.3
+      '@babel/types': 7.22.17
     dev: true
 
   /@types/babel__traverse@7.20.0:
     resolution: {integrity: sha512-TBOjqAGf0hmaqRwpii5LLkJLg7c6OMm4nHLmpsUxwk9bBHtoTC6dAHdVWdGv4TBxj2CZOZY8Xfq8WmfoVi7n4Q==}
     dependencies:
-      '@babel/types': 7.23.3
+      '@babel/types': 7.22.17
     dev: true
 
   /@types/bcryptjs@2.4.6:
@@ -8726,52 +8716,6 @@ packages:
       path-browserify: 1.0.1
     dev: true
 
-  /@vue-macros/common@1.9.0(rollup@4.6.1)(vue@3.3.9):
-    resolution: {integrity: sha512-LbfRHDkceuokkLlVuQW9Wq3ZLmRs6KIDPzCjUvvL14HB4GslWdtvBB1suFfNs6VMvh9Zj30cEKF/EAP7QBCZ6Q==}
-    engines: {node: '>=16.14.0'}
-    peerDependencies:
-      vue: ^2.7.0 || ^3.2.25
-    peerDependenciesMeta:
-      vue:
-        optional: true
-    dependencies:
-      '@babel/types': 7.23.3
-      '@rollup/pluginutils': 5.0.5(rollup@4.6.1)
-      '@vue/compiler-sfc': 3.3.9
-      ast-kit: 0.11.2(rollup@4.6.1)
-      local-pkg: 0.5.0
-      magic-string-ast: 0.3.0
-      vue: 3.3.9(typescript@5.3.2)
-    transitivePeerDependencies:
-      - rollup
-    dev: false
-
-  /@vue-macros/reactivity-transform@0.4.0(rollup@4.6.1)(vue@3.3.9):
-    resolution: {integrity: sha512-3DG+FWkIZe5xZJhIdxyieIYcDKJGC3aUab1JWtEOkS8Q21rLpu6VKUjV6TmB5LNyLSGVp+7de/87Ptd6C6RHOA==}
-    engines: {node: '>=16.14.0'}
-    peerDependencies:
-      vue: ^2.7.0 || ^3.2.25
-    dependencies:
-      '@babel/parser': 7.23.3
-      '@vue-macros/common': 1.9.0(rollup@4.6.1)(vue@3.3.9)
-      '@vue/compiler-core': 3.3.8
-      '@vue/shared': 3.3.8
-      magic-string: 0.30.5
-      unplugin: 1.5.1
-      vue: 3.3.9(typescript@5.3.2)
-    transitivePeerDependencies:
-      - rollup
-    dev: false
-
-  /@vue/compiler-core@3.3.8:
-    resolution: {integrity: sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==}
-    dependencies:
-      '@babel/parser': 7.23.3
-      '@vue/shared': 3.3.8
-      estree-walker: 2.0.2
-      source-map-js: 1.0.2
-    dev: false
-
   /@vue/compiler-core@3.3.9:
     resolution: {integrity: sha512-+/Lf68Vr/nFBA6ol4xOtJrW+BQWv3QWKfRwGSm70jtXwfhZNF4R/eRgyVJYoxFRhdCTk/F6g99BP0ffPgZihfQ==}
     dependencies:
@@ -8862,10 +8806,6 @@ packages:
       '@vue/shared': 3.3.9
       vue: 3.3.9(typescript@5.3.2)
 
-  /@vue/shared@3.3.8:
-    resolution: {integrity: sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw==}
-    dev: false
-
   /@vue/shared@3.3.9:
     resolution: {integrity: sha512-ZE0VTIR0LmYgeyhurPTpy4KzKsuDyQbMSdM49eKkMnT5X4VfFBLysMzjIZhLEFQYjjOVVfbvUDHckwjDFiO2eA==}
 
@@ -9344,17 +9284,6 @@ packages:
     resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
     dev: true
 
-  /ast-kit@0.11.2(rollup@4.6.1):
-    resolution: {integrity: sha512-Q0DjXK4ApbVoIf9GLyCo252tUH44iTnD/hiJ2TQaJeydYWSpKk0sI34+WMel8S9Wt5pbLgG02oJ+gkgX5DV3sQ==}
-    engines: {node: '>=16.14.0'}
-    dependencies:
-      '@babel/parser': 7.23.3
-      '@rollup/pluginutils': 5.0.5(rollup@4.6.1)
-      pathe: 1.1.1
-    transitivePeerDependencies:
-      - rollup
-    dev: false
-
   /ast-types@0.14.2:
     resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==}
     engines: {node: '>=4'}
@@ -9510,7 +9439,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@babel/template': 7.22.5
-      '@babel/types': 7.23.3
+      '@babel/types': 7.22.17
       '@types/babel__core': 7.20.0
       '@types/babel__traverse': 7.20.0
     dev: true
@@ -13658,7 +13587,7 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       '@babel/core': 7.22.11
-      '@babel/parser': 7.23.0
+      '@babel/parser': 7.23.3
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.0
       semver: 6.3.1
@@ -13671,7 +13600,7 @@ packages:
     engines: {node: '>=10'}
     dependencies:
       '@babel/core': 7.22.11
-      '@babel/parser': 7.23.0
+      '@babel/parser': 7.23.3
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.0
       semver: 7.5.4
@@ -14604,14 +14533,6 @@ packages:
     engines: {node: '>=14'}
     dev: true
 
-  /local-pkg@0.5.0:
-    resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
-    engines: {node: '>=14'}
-    dependencies:
-      mlly: 1.4.2
-      pkg-types: 1.0.3
-    dev: false
-
   /locate-path@3.0.0:
     resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
     engines: {node: '>=6'}
@@ -14756,13 +14677,6 @@ packages:
     hasBin: true
     dev: true
 
-  /magic-string-ast@0.3.0:
-    resolution: {integrity: sha512-0shqecEPgdFpnI3AP90epXyxZy9g6CRZ+SZ7BcqFwYmtFEnZ1jpevcV5HoyVnlDS9gCnc1UIg3Rsvp3Ci7r8OA==}
-    engines: {node: '>=16.14.0'}
-    dependencies:
-      magic-string: 0.30.5
-    dev: false
-
   /magic-string@0.27.0:
     resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
     engines: {node: '>=12'}
@@ -15160,15 +15074,7 @@ packages:
       pathe: 1.1.1
       pkg-types: 1.0.3
       ufo: 1.1.2
-
-  /mlly@1.4.2:
-    resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==}
-    dependencies:
-      acorn: 8.11.2
-      pathe: 1.1.1
-      pkg-types: 1.0.3
-      ufo: 1.3.2
-    dev: false
+    dev: true
 
   /mnemonist@0.39.5:
     resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==}
@@ -16075,6 +15981,7 @@ packages:
 
   /pathe@1.1.1:
     resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==}
+    dev: true
 
   /pathval@1.1.1:
     resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
@@ -16272,6 +16179,7 @@ packages:
       jsonc-parser: 3.2.0
       mlly: 1.4.0
       pathe: 1.1.1
+    dev: true
 
   /plimit-lit@1.5.0:
     resolution: {integrity: sha512-Eb/MqCb1Iv/ok4m1FqIXqvUKPISufcjZ605hl3KM/n8GaX8zfhtgdLwZU3vKjuHGh2O9Rjog/bHTq8ofIShdng==}
@@ -17147,7 +17055,7 @@ packages:
     dependencies:
       '@babel/core': 7.22.11
       '@babel/traverse': 7.22.11
-      '@babel/types': 7.23.3
+      '@babel/types': 7.22.17
       '@types/babel__core': 7.20.0
       '@types/babel__traverse': 7.20.0
       '@types/doctrine': 0.0.9
@@ -19250,10 +19158,7 @@ packages:
 
   /ufo@1.1.2:
     resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==}
-
-  /ufo@1.3.2:
-    resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==}
-    dev: false
+    dev: true
 
   /uglify-js@3.17.4:
     resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==}
@@ -19396,13 +19301,14 @@ packages:
     resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
     engines: {node: '>= 0.8'}
 
-  /unplugin@1.5.1:
-    resolution: {integrity: sha512-0QkvG13z6RD+1L1FoibQqnvTwVBXvS4XSPwAyinVgoOCl2jAgwzdUKmEj05o4Lt8xwQI85Hb6mSyYkcAGwZPew==}
+  /unplugin@1.4.0:
+    resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==}
     dependencies:
       acorn: 8.11.2
       chokidar: 3.5.3
       webpack-sources: 3.2.3
-      webpack-virtual-modules: 0.6.0
+      webpack-virtual-modules: 0.5.0
+    dev: true
 
   /untildify@4.0.0:
     resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
@@ -19761,7 +19667,7 @@ packages:
     resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==}
     dependencies:
       '@babel/parser': 7.23.3
-      '@babel/types': 7.23.3
+      '@babel/types': 7.22.17
       '@vue/compiler-dom': 3.3.9
       '@vue/compiler-sfc': 3.3.9
       ast-types: 0.14.2
@@ -19922,9 +19828,11 @@ packages:
   /webpack-sources@3.2.3:
     resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
     engines: {node: '>=10.13.0'}
+    dev: true
 
-  /webpack-virtual-modules@0.6.0:
-    resolution: {integrity: sha512-KnaMTE6EItz/f2q4Gwg5/rmeKVi79OR58NoYnwDJqCk9ywMtTGbBnBcfoBtN4QbYu0lWXvyMoH2Owxuhe4qI6Q==}
+  /webpack-virtual-modules@0.5.0:
+    resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
+    dev: true
 
   /whatwg-encoding@2.0.0:
     resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}

From e926411812f5554520898e37e4876e708049249b Mon Sep 17 00:00:00 2001
From: Ryan He <204075+ryanho@users.noreply.github.com>
Date: Thu, 7 Dec 2023 16:00:34 +0800
Subject: [PATCH 153/435] chore: Add descriptions for "MeiliSearch" and
 "allowedPrivateNetworks" to example.yml (#12594)

* Update example.yml, add descriptions for some items

Add descriptions for "MeiliSearch" and "allowedPrivateNetworks"

* Update docker_example.yml

Add descriptions for "MeiliSearch" and "allowedPrivateNetworks"
---
 .config/docker_example.yml | 7 +++++++
 .config/example.yml        | 6 ++++++
 2 files changed, 13 insertions(+)

diff --git a/.config/docker_example.yml b/.config/docker_example.yml
index 2921746295..d1534486d3 100644
--- a/.config/docker_example.yml
+++ b/.config/docker_example.yml
@@ -106,12 +106,16 @@ redis:
 #   ┌───────────────────────────┐
 #───┘ MeiliSearch configuration └─────────────────────────────
 
+# You can set scope to local (default value) or global 
+# (include notes from remote).
+
 #meilisearch:
 #  host: meilisearch
 #  port: 7700
 #  apiKey: ''
 #  ssl: true
 #  index: ''
+#  scope: local
 
 #   ┌───────────────┐
 #───┘ ID generation └───────────────────────────────────────────
@@ -180,6 +184,9 @@ proxyRemoteFiles: true
 # Sign to ActivityPub GET request (default: true)
 signToActivityPubGet: true
 
+# For security reasons, uploading attachments from the intranet is prohibited,
+# but exceptions can be made from the following settings. Default value is "undefined". 
+# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
 #allowedPrivateNetworks: [
 #  '127.0.0.1/32'
 #]
diff --git a/.config/example.yml b/.config/example.yml
index 0e4f2f5a15..481c615587 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -118,6 +118,9 @@ redis:
 #   ┌───────────────────────────┐
 #───┘ MeiliSearch configuration └─────────────────────────────
 
+# You can set scope to local (default value) or global 
+# (include notes from remote).
+
 #meilisearch:
 #  host: localhost
 #  port: 7700
@@ -210,6 +213,9 @@ proxyRemoteFiles: true
 # Sign to ActivityPub GET request (default: true)
 signToActivityPubGet: true
 
+# For security reasons, uploading attachments from the intranet is prohibited,
+# but exceptions can be made from the following settings. Default value is "undefined". 
+# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
 #allowedPrivateNetworks: [
 #  '127.0.0.1/32'
 #]

From 1d3ef7b42fca601abcdf78f6325ed896f1ebd378 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 7 Dec 2023 17:07:06 +0900
Subject: [PATCH 154/435] fix(backend): pagination with sinceId broken (#12586)

* fix(backend): pagination with sinceId broken

* fix(backend): pagination with sinceId broken for dbFallback
---
 .../src/core/FanoutTimelineEndpointService.ts | 33 ++++++++++++++-----
 1 file changed, 24 insertions(+), 9 deletions(-)

diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index 6775f0051a..a5bf297275 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -60,11 +60,15 @@ export class FanoutTimelineEndpointService {
 		// 呼び出し元と以下の処理をシンプルにするためにdbFallbackを置き換える
 		if (!ps.useDbFallback) ps.dbFallback = () => Promise.resolve([]);
 
+		const shouldPrepend = ps.sinceId && !ps.untilId;
+		const idCompare: (a: string, b: string) => number = shouldPrepend ? (a, b) => a < b ? -1 : 1 : (a, b) => a > b ? -1 : 1;
+
 		const redisResult = await this.fanoutTimelineService.getMulti(ps.redisTimelines, ps.untilId, ps.sinceId);
 
+		// TODO: いい感じにgetMulti内でソート済だからuniqするときにredisResultが全てソート済なのを利用して再ソートを避けたい
 		const redisResultIds = Array.from(new Set(redisResult.flat(1)));
 
-		redisResultIds.sort((a, b) => a > b ? -1 : 1);
+		redisResultIds.sort(idCompare);
 		noteIds = redisResultIds.slice(0, ps.limit);
 
 		shouldFallbackToDb = shouldFallbackToDb || (noteIds.length === 0);
@@ -126,32 +130,43 @@ export class FanoutTimelineEndpointService {
 				const remainingToRead = ps.limit - redisTimeline.length;
 
 				// DBからの取り直しを減らす初回と同じ割合以上で成功すると仮定するが、クエリの長さを考えて三倍まで
-				const countToGet = remainingToRead * Math.ceil(Math.min(1.1 / lastSuccessfulRate, 3));
+				const countToGet = Math.ceil(remainingToRead * Math.min(1.1 / lastSuccessfulRate, 3));
 				noteIds = redisResultIds.slice(readFromRedis, readFromRedis + countToGet);
 
 				readFromRedis += noteIds.length;
 
-				const gotFromDb = await this.getAndFilterFromDb(noteIds, filter);
+				const gotFromDb = await this.getAndFilterFromDb(noteIds, filter, idCompare);
 				redisTimeline.push(...gotFromDb);
 				lastSuccessfulRate = gotFromDb.length / noteIds.length;
 
 				if (ps.allowPartial ? redisTimeline.length !== 0 : redisTimeline.length >= ps.limit) {
 					// 十分Redisからとれた
-					return redisTimeline.slice(0, ps.limit);
+					const result = redisTimeline.slice(0, ps.limit);
+					if (shouldPrepend) result.reverse();
+					return result;
 				}
 			}
 
 			// まだ足りない分はDBにフォールバック
 			const remainingToRead = ps.limit - redisTimeline.length;
-			const gotFromDb = await ps.dbFallback(noteIds[noteIds.length - 1], ps.sinceId, remainingToRead);
-			redisTimeline.push(...gotFromDb);
-			return redisTimeline;
+			let dbUntil: string | null;
+			let dbSince: string | null;
+			if (shouldPrepend) {
+				redisTimeline.reverse();
+				dbUntil = ps.untilId;
+				dbSince = noteIds[noteIds.length - 1];
+			} else {
+				dbUntil = noteIds[noteIds.length - 1];
+				dbSince = ps.sinceId;
+			}
+			const gotFromDb = await ps.dbFallback(dbUntil, dbSince, remainingToRead);
+			return shouldPrepend ? [...gotFromDb, ...redisTimeline] : [...redisTimeline, ...gotFromDb];
 		}
 
 		return await ps.dbFallback(ps.untilId, ps.sinceId, ps.limit);
 	}
 
-	private async getAndFilterFromDb(noteIds: string[], noteFilter: (note: MiNote) => boolean): Promise<MiNote[]> {
+	private async getAndFilterFromDb(noteIds: string[], noteFilter: (note: MiNote) => boolean, idCompare: (a: string, b: string) => number): Promise<MiNote[]> {
 		const query = this.notesRepository.createQueryBuilder('note')
 			.where('note.id IN (:...noteIds)', { noteIds: noteIds })
 			.innerJoinAndSelect('note.user', 'user')
@@ -163,7 +178,7 @@ export class FanoutTimelineEndpointService {
 
 		const notes = (await query.getMany()).filter(noteFilter);
 
-		notes.sort((a, b) => a.id > b.id ? -1 : 1);
+		notes.sort((a, b) => idCompare(a.id, b.id));
 
 		return notes;
 	}

From bcf6b7f5ee73a889a84afd54795f2ae9777074a4 Mon Sep 17 00:00:00 2001
From: KanariKanaru <93921745+kanarikanaru@users.noreply.github.com>
Date: Thu, 7 Dec 2023 17:09:31 +0900
Subject: [PATCH 155/435] =?UTF-8?q?enhance:=20meilisearch=E3=82=92?=
 =?UTF-8?q?=E6=9C=89=E5=8A=B9=E3=81=AB=E3=81=97=E3=81=A6=E3=82=82=E3=83=9F?=
 =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=83=88=E3=82=84=E3=83=96=E3=83=AD=E3=83=83?=
 =?UTF-8?q?=E3=82=AF=E3=82=92=E8=80=83=E6=85=AE=E3=81=99=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#12575)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: meilisearchを有効にしてもミュートやブロックを考慮するように

* Update CHANGELOG.md
---
 CHANGELOG.md                               |  1 +
 packages/backend/src/core/SearchService.ts | 16 +++++++++++++++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0316ab3c1f..cba7a05ae2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
+- Enhance: Meilisearchを有効にした検索で、ユーザーのミュートやブロックを考慮するように
 - Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303
 - Fix: ロールタイムラインが保存されない問題を修正
 - Fix: api.jsonの生成ロジックを改善 #12402
diff --git a/packages/backend/src/core/SearchService.ts b/packages/backend/src/core/SearchService.ts
index b6d2bcabc8..a46d68fd84 100644
--- a/packages/backend/src/core/SearchService.ts
+++ b/packages/backend/src/core/SearchService.ts
@@ -12,6 +12,8 @@ import { MiNote } from '@/models/Note.js';
 import { MiUser } from '@/models/_.js';
 import type { NotesRepository } from '@/models/_.js';
 import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
+import { isUserRelated } from '@/misc/is-user-related.js';
+import { CacheService } from '@/core/CacheService.js';
 import { QueryService } from '@/core/QueryService.js';
 import { IdService } from '@/core/IdService.js';
 import type { Index, MeiliSearch } from 'meilisearch';
@@ -74,6 +76,7 @@ export class SearchService {
 		@Inject(DI.notesRepository)
 		private notesRepository: NotesRepository,
 
+		private cacheService: CacheService,
 		private queryService: QueryService,
 		private idService: IdService,
 	) {
@@ -187,8 +190,19 @@ export class SearchService {
 				limit: pagination.limit,
 			});
 			if (res.hits.length === 0) return [];
-			const notes = await this.notesRepository.findBy({
+			const [
+				userIdsWhoMeMuting,
+				userIdsWhoBlockingMe,
+			] = me ? await Promise.all([
+				this.cacheService.userMutingsCache.fetch(me.id),
+				this.cacheService.userBlockedCache.fetch(me.id),
+			]) : [new Set<string>(), new Set<string>()];
+			const notes = (await this.notesRepository.findBy({
 				id: In(res.hits.map(x => x.id)),
+			})).filter(note => {
+				if (me && isUserRelated(note, userIdsWhoBlockingMe)) return false;
+				if (me && isUserRelated(note, userIdsWhoMeMuting)) return false;
+				return true;
 			});
 			return notes.sort((a, b) => a.id > b.id ? -1 : 1);
 		} else {

From e6d01e33e6b539dca784c5309158ae80836fac27 Mon Sep 17 00:00:00 2001
From: KanariKanaru <93921745+kanarikanaru@users.noreply.github.com>
Date: Thu, 7 Dec 2023 18:15:38 +0900
Subject: [PATCH 156/435] =?UTF-8?q?fix(backend):=20=E3=83=96=E3=83=AD?=
 =?UTF-8?q?=E3=83=83=E3=82=AF=E3=81=97=E3=81=9F=E7=9B=B8=E6=89=8B=E3=81=8B?=
 =?UTF-8?q?=E3=82=89=E8=87=AA=E5=88=86=E3=81=AE=E3=83=8E=E3=83=BC=E3=83=88?=
 =?UTF-8?q?=E3=81=8C=E8=A6=8B=E3=81=88=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB(/users/featured-notes,=20/users/notes)=20(#12511)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: ブロックした相手から自分のノートが見えないように(ユーザー,チャンネル)

* Update CHANGELOG.md

* /users/featured-notesでもブロックを考慮するように

* cacheServiceを使うように

* /channels/timeline.tsで必要のないnoteFilterを持たないように

* Update CHANGELOG.md

* FanoutTimelineEndpointServiceへの対応

- ブロックされている場合は、/users/notesでノートが表示されない
- ミュートしている場合は、ノートが表示される
---
 CHANGELOG.md                                  |  1 +
 .../src/core/FanoutTimelineEndpointService.ts |  3 ++-
 .../api/endpoints/users/featured-notes.ts     | 21 ++++++++++++++++---
 3 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cba7a05ae2..7352db8fab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -62,6 +62,7 @@
 - Fix: ユーザのノート一覧にてインスタンスミュートが効かない問題
 - Fix: チャンネルのノート一覧にてインスタンスミュートが効かない問題
 - Fix: 「みつける」が年越し時に壊れる問題を修正
+- Fix: アカウントをブロックした際に、自身のユーザーのページでノートが相手に表示される問題を修正
 
 ## 2023.11.1
 
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index a5bf297275..11027960f1 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -28,6 +28,7 @@ type TimelineOptions = {
 	redisTimelines: FanoutTimelineName[],
 	noteFilter?: (note: MiNote) => boolean,
 	alwaysIncludeMyNotes?: boolean;
+	ignoreAuthorFromBlock?: boolean;
 	ignoreAuthorFromMute?: boolean;
 	excludeNoFiles?: boolean;
 	excludeReplies?: boolean;
@@ -113,7 +114,7 @@ export class FanoutTimelineEndpointService {
 
 				const parentFilter = filter;
 				filter = (note) => {
-					if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromMute)) return false;
+					if (isUserRelated(note, userIdsWhoBlockingMe, ps.ignoreAuthorFromBlock)) return false;
 					if (isUserRelated(note, userIdsWhoMeMuting, ps.ignoreAuthorFromMute)) return false;
 					if (isPureRenote(note) && isUserRelated(note, userIdsWhoMeMutingRenotes, ps.ignoreAuthorFromMute)) return false;
 					if (isInstanceMuted(note, userMutedInstances)) return false;
diff --git a/packages/backend/src/server/api/endpoints/users/featured-notes.ts b/packages/backend/src/server/api/endpoints/users/featured-notes.ts
index dec0b7a122..f84148d727 100644
--- a/packages/backend/src/server/api/endpoints/users/featured-notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/featured-notes.ts
@@ -9,6 +9,8 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { DI } from '@/di-symbols.js';
 import { FeaturedService } from '@/core/FeaturedService.js';
+import { CacheService } from '@/core/CacheService.js';
+import { isUserRelated } from '@/misc/is-user-related.js';
 
 export const meta = {
 	tags: ['notes'],
@@ -46,6 +48,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 		private noteEntityService: NoteEntityService,
 		private featuredService: FeaturedService,
+		private cacheService: CacheService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			let noteIds = await this.featuredService.getPerUserNotesRanking(ps.userId, 50);
@@ -60,6 +63,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				return [];
 			}
 
+			const [
+				userIdsWhoMeMuting,
+				userIdsWhoBlockingMe,
+			] = me ? await Promise.all([
+				this.cacheService.userMutingsCache.fetch(me.id),
+				this.cacheService.userBlockedCache.fetch(me.id),
+			]) : [new Set<string>(), new Set<string>()];
+
 			const query = this.notesRepository.createQueryBuilder('note')
 				.where('note.id IN (:...noteIds)', { noteIds: noteIds })
 				.innerJoinAndSelect('note.user', 'user')
@@ -69,10 +80,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				.leftJoinAndSelect('renote.user', 'renoteUser')
 				.leftJoinAndSelect('note.channel', 'channel');
 
-			const notes = await query.getMany();
-			notes.sort((a, b) => a.id > b.id ? -1 : 1);
+			const notes = (await query.getMany()).filter(note => {
+				if (me && isUserRelated(note, userIdsWhoBlockingMe, false)) return false;
+				if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false;
 
-			// TODO: ミュート等考慮
+				return true;
+			});
+
+			notes.sort((a, b) => a.id > b.id ? -1 : 1);
 
 			return await this.noteEntityService.packMany(notes, me);
 		});

From b0039f0946b02777ad99ad8c92f6555792aa8996 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Fri, 8 Dec 2023 08:22:08 +0900
Subject: [PATCH 157/435] =?UTF-8?q?chore:=20=E9=96=8B=E7=99=BA=E3=83=A2?=
 =?UTF-8?q?=E3=83=BC=E3=83=89=E3=81=A7=E3=83=95=E3=83=AD=E3=83=B3=E3=83=88?=
 =?UTF-8?q?=E3=82=A8=E3=83=B3=E3=83=89=E3=81=A8=E3=83=90=E3=83=83=E3=82=AF?=
 =?UTF-8?q?=E3=82=A8=E3=83=B3=E3=83=89=E3=82=92=E7=8B=AC=E7=AB=8B=E3=81=97?=
 =?UTF-8?q?=E3=81=A6=E8=B5=B7=E5=8B=95=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B=EF=BC=88=E5=86=8D=EF=BC=89=20(#1259?=
 =?UTF-8?q?3)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* [wip]run standalone vite

* [wip]run standalone vite

* some fix (tabler icons, sw, streaming)

* fix theme

* fix run scripts

* favicon

* client-assets

* cssの読み込み順序とCSP設定の変更

* fix lang change

* fix clientManifest

* baseを相対パスにしてドメイン直下とサブディレクトリ配下両方に対応

* 色々修正

* 色々修正

* 色々修正

* fix

* Revert "client-assets"

This reverts commit 582601e90eb771875bdf8aba263da2316a59d01b.

# Conflicts:
#	packages/frontend/vite.config.ts

* 色々修正

* fix

* fix

* add url and proxy to server proxy

* Update packages/frontend/src/index.html

* wip

* Merge remote-tracking branch 'origin/develop' into feat/launch-standalone-frontend

# Conflicts:
#	packages/frontend/src/pages/welcome.entrance.a.vue

* Merge remote-tracking branch 'origin/develop' into feat/launch-standalone-frontend

# Conflicts:
#	packages/frontend/src/pages/welcome.entrance.a.vue

* fix tabler load

* Apply suggestions from code review

* Update packages/frontend/src/index.html

* fix

* fix vite.config.local-dev.ts

* fix CONTRIBUTING.md

---------

Co-authored-by: FruitRiin <nassii74@gmail.com>
Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
Co-authored-by: 果物リン <fruitriin@riinswork.space>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
Co-authored-by: ozelot <contact@ozelot.dev>
---
 CONTRIBUTING.md                               |  5 ++
 docker-compose.local-db.yml                   | 42 +++++++++++++++
 package.json                                  |  2 +-
 packages/backend/package.json                 |  1 +
 packages/frontend/package.json                |  1 +
 packages/frontend/src/_dev_boot_.ts           | 11 ++++
 packages/frontend/src/boot/common.ts          |  6 +++
 packages/frontend/src/index.html              | 30 +++++++++++
 .../frontend/src/pages/settings/general.vue   |  1 +
 .../frontend/src/pages/welcome.entrance.a.vue |  4 +-
 packages/frontend/vite.config.local-dev.ts    | 53 +++++++++++++++++++
 11 files changed, 153 insertions(+), 3 deletions(-)
 create mode 100644 docker-compose.local-db.yml
 create mode 100644 packages/frontend/src/_dev_boot_.ts
 create mode 100644 packages/frontend/src/index.html
 create mode 100644 packages/frontend/vite.config.local-dev.ts

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 13e0656041..f8b4fe5930 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -138,6 +138,11 @@ After finishing the migration, run the `pnpm dev` command to start the developme
 pnpm dev
 ```
 
+To access Misskey once it's launched, type `http://localhost:[port]` in your browser's address bar.  
+The [port] part will contain the value set in the port item of .config/default.yml (3000 will be entered by default)
+
+caution: If you use a port other than 3000, you need to change the proxy settings in packages/frontend/vite.config.local-dev.ts.
+
 ## Testing
 - Test codes are located in [`/packages/backend/test`](/packages/backend/test).
 
diff --git a/docker-compose.local-db.yml b/docker-compose.local-db.yml
new file mode 100644
index 0000000000..16ba4b49e1
--- /dev/null
+++ b/docker-compose.local-db.yml
@@ -0,0 +1,42 @@
+version: "3"
+
+# このconfigは、 dockerでMisskey本体を起動せず、 redisとpostgresql などだけを起動します
+
+services:
+  redis:
+    restart: always
+    image: redis:7-alpine
+    ports:
+      - "6379:6379"
+    volumes:
+      - ./redis:/data
+    healthcheck:
+      test: "redis-cli ping"
+      interval: 5s
+      retries: 20
+
+  db:
+    restart: always
+    image: postgres:15-alpine
+    ports:
+      - "5432:5432"
+    env_file:
+      - .config/docker.env
+    volumes:
+      - ./db:/var/lib/postgresql/data
+    healthcheck:
+      test: "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
+      interval: 5s
+      retries: 20
+
+#  meilisearch:
+#    restart: always
+#    image: getmeili/meilisearch:v1.3.4
+#    environment:
+#      - MEILI_NO_ANALYTICS=true
+#      - MEILI_ENV=production
+#    env_file:
+#      - .config/meilisearch.env
+#    volumes:
+#      - ./meili_data:/meili_data
+
diff --git a/package.json b/package.json
index e814d71dc0..7e00d43dbe 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,7 @@
 		"check:connect": "cd packages/backend && pnpm check:connect",
 		"migrateandstart": "pnpm migrate && pnpm start",
 		"watch": "pnpm dev",
-		"dev": "node ./scripts/dev.mjs",
+		"dev": "pnpm -r dev",
 		"lint": "pnpm -r lint",
 		"cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
 		"cy:run": "pnpm cypress run",
diff --git a/packages/backend/package.json b/packages/backend/package.json
index fa20458093..25984214eb 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -16,6 +16,7 @@
 		"watch:swc": "swc src -d built -D -w",
 		"build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
 		"watch": "node watch.mjs",
+		"dev": "node ./built/boot/entry.js",
 		"typecheck": "tsc --noEmit",
 		"eslint": "eslint --quiet \"src/**/*.ts\"",
 		"lint": "pnpm typecheck && pnpm eslint",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 9ecb47afba..5946490103 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -4,6 +4,7 @@
 	"type": "module",
 	"scripts": {
 		"watch": "vite",
+		"dev": "vite --config vite.config.local-dev.ts",
 		"build": "vite build",
 		"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
 		"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
diff --git a/packages/frontend/src/_dev_boot_.ts b/packages/frontend/src/_dev_boot_.ts
new file mode 100644
index 0000000000..2e95a03576
--- /dev/null
+++ b/packages/frontend/src/_dev_boot_.ts
@@ -0,0 +1,11 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+// devモードで起動される際(index.htmlを使うとき)はrouterが暴発してしまってうまく読み込めない。
+// よって、devモードとして起動されるときはビルド時に組み込む形としておく。
+// (pnpm start時はpugファイルの中で静的リソースとして読み込むようになっており、この問題は起こっていない)
+import '@tabler/icons-webfont/tabler-icons.scss';
+
+import('@/_boot_.js');
diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index 6e216a78b4..60f2781fdf 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -187,6 +187,12 @@ export async function common(createVue: () => App<Element>) {
 			if (instance.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON.parse(instance.defaultLightTheme));
 			if (instance.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON.parse(instance.defaultDarkTheme));
 			defaultStore.set('themeInitial', false);
+		} else {
+			if (defaultStore.state.darkMode) {
+				applyTheme(darkTheme.value);
+			} else {
+				applyTheme(lightTheme.value);
+			}
 		}
 	});
 
diff --git a/packages/frontend/src/index.html b/packages/frontend/src/index.html
new file mode 100644
index 0000000000..4db52747a8
--- /dev/null
+++ b/packages/frontend/src/index.html
@@ -0,0 +1,30 @@
+<!--
+  SPDX-FileCopyrightText: syuilo and other misskey contributors
+  SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<!--
+  開発モードのviteはこのファイルを起点にサーバーを起動します。
+  このファイルに書かれた [t]js のリンクと (s)cssのリンクと、その依存関係にあるファイルはビルドされます
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+	<meta charset="UTF-8" />
+	<title>misskey</title>
+	<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
+	<meta
+		http-equiv="Content-Security-Policy"
+		content="default-src 'self';
+		  script-src 'self';
+		  style-src 'self' 'unsafe-inline';
+		  img-src 'self' data: www.google.com xn--931a.moe localhost:3000 localhost:5137 127.0.0.1:5173 127.0.0.1:3000"
+	/>
+</head>
+
+<body>
+<div id="app"></div>
+<script type="module" src="./_dev_boot_.ts"></script>
+</body>
+</html>
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index f108a0c64e..98a07d907d 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -293,6 +293,7 @@ const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroup
 watch(lang, () => {
 	miLocalStorage.setItem('lang', lang.value as string);
 	miLocalStorage.removeItem('locale');
+  miLocalStorage.removeItem('localeVersion');
 });
 
 watch(fontSize, () => {
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index a8ce4537e5..9c27eeec54 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<XTimeline class="tl"/>
 	<div class="shape1"></div>
 	<div class="shape2"></div>
-	<img src="/client-assets/misskey.svg" class="misskey"/>
+	<img :src="misskeysvg" class="misskey"/>
 	<div class="emojis">
 		<MkEmoji :normal="true" :noStyle="true" emoji="👍"/>
 		<MkEmoji :normal="true" :noStyle="true" emoji="❤"/>
@@ -38,6 +38,7 @@ import * as Misskey from 'misskey-js';
 import XTimeline from './welcome.timeline.vue';
 import MarqueeText from '@/components/MkMarquee.vue';
 import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
+import misskeysvg from '/client-assets/misskey.svg';
 import MkInfo from '@/components/MkInfo.vue';
 import { instanceName } from '@/config.js';
 import * as os from '@/os.js';
@@ -55,7 +56,6 @@ function getInstanceIcon(instance: Misskey.entities.FederationInstance): string
 	if (!instance.iconUrl) {
 		return '';
 	}
-
 	return getProxiedImageUrl(instance.iconUrl, 'preview');
 }
 
diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts
new file mode 100644
index 0000000000..6b4bb73ffa
--- /dev/null
+++ b/packages/frontend/vite.config.local-dev.ts
@@ -0,0 +1,53 @@
+import dns from 'dns';
+import { defineConfig } from 'vite';
+import { getConfig } from './vite.config.js';
+
+dns.setDefaultResultOrder('ipv4first');
+
+const defaultConfig = getConfig();
+
+const devConfig = {
+	// 基本の設定は vite.config.js から引き継ぐ
+	...defaultConfig,
+	root: 'src',
+	publicDir: '../assets',
+	base: './',
+	server: {
+		host: 'localhost',
+		port: 5173,
+		proxy: {
+			'/api': {
+				changeOrigin: true,
+				target: 'http://localhost:3000/',
+			},
+			'/assets': 'http://localhost:3000/',
+			'/files': 'http://localhost:3000/',
+			'/twemoji': 'http://localhost:3000/',
+			'/fluent-emoji': 'http://localhost:3000/',
+			'/sw.js': 'http://localhost:3000/',
+			'/streaming': {
+				target: 'ws://localhost:3000/',
+				ws: true,
+			},
+			'/favicon.ico': 'http://localhost:3000/',
+			'/identicon': {
+				target: 'http://localhost:3000/',
+				rewrite(path) {
+					return path.replace('@localhost:5173', '');
+				},
+			},
+			'/url': 'http://localhost:3000',
+			'/proxy': 'http://localhost:3000',
+		},
+	},
+	build: {
+		...defaultConfig.build,
+		rollupOptions: {
+			...defaultConfig.build?.rollupOptions,
+			input: 'index.html',
+		},
+	},
+};
+
+export default defineConfig(({ command, mode }) => devConfig);
+

From 9059b837fa4c56c9aa84471601d038aabdc68a2b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Fri, 8 Dec 2023 09:00:23 +0900
Subject: [PATCH 158/435] fix CONTRIBUTING.md (#12600)

---
 CONTRIBUTING.md | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f8b4fe5930..7f6c1f4f82 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -117,6 +117,10 @@ command.
 - Server-side source files and automatically builds them if they are modified. Automatically start the server process(es).
 - Vite HMR (just the `vite` command) is available. The behavior may be different from production.
 - Service Worker is watched by esbuild.
+- The front end can be viewed by accessing `http://localhost:5173`.
+- The backend listens on the port configured with `port` in .config/default.yml.
+If you have not changed it from the default, it will be "http://localhost:3000".
+If "port" in .config/default.yml is set to something other than 3000, you need to change the proxy settings in packages/frontend/vite.config.local-dev.ts.
 
 ### Dev Container
 Instead of running `pnpm` locally, you can use Dev Container to set up your development environment.
@@ -138,11 +142,6 @@ After finishing the migration, run the `pnpm dev` command to start the developme
 pnpm dev
 ```
 
-To access Misskey once it's launched, type `http://localhost:[port]` in your browser's address bar.  
-The [port] part will contain the value set in the port item of .config/default.yml (3000 will be entered by default)
-
-caution: If you use a port other than 3000, you need to change the proxy settings in packages/frontend/vite.config.local-dev.ts.
-
 ## Testing
 - Test codes are located in [`/packages/backend/test`](/packages/backend/test).
 

From f80ae7f686dc5309f46c164b85227000a201126f Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 8 Dec 2023 09:00:37 +0900
Subject: [PATCH 159/435] chore(deps): bump actions/labeler from 4 to 5
 (#12584)

Bumps [actions/labeler](https://github.com/actions/labeler) from 4 to 5.
- [Release notes](https://github.com/actions/labeler/releases)
- [Commits](https://github.com/actions/labeler/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/labeler
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/labeler.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index fa4a58c3a9..88e2aceaed 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -11,6 +11,6 @@ jobs:
       pull-requests: write
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/labeler@v4
+    - uses: actions/labeler@v5
       with:
         repo-token: "${{ secrets.GITHUB_TOKEN }}"

From 40a9263083ca7d11096ec4851ea61e97c7da79ae Mon Sep 17 00:00:00 2001
From: mia <74628488+squili@users.noreply.github.com>
Date: Thu, 7 Dec 2023 16:34:37 -0800
Subject: [PATCH 160/435] switch to more optimized containerfile

---
 Containerfile         |  57 ++++++++++++++++++++++++
 Dockerfile            | 101 ------------------------------------------
 scripts/trim-deps.mjs |  35 +++++++++++++++
 3 files changed, 92 insertions(+), 101 deletions(-)
 create mode 100644 Containerfile
 delete mode 100644 Dockerfile
 create mode 100644 scripts/trim-deps.mjs

diff --git a/Containerfile b/Containerfile
new file mode 100644
index 0000000000..6696b5092a
--- /dev/null
+++ b/Containerfile
@@ -0,0 +1,57 @@
+# syntax = docker/dockerfile:1.4
+
+ARG NODE_VERSION=21.4.0-alpine3.18
+
+FROM node:${NODE_VERSION} as build
+
+RUN corepack enable
+
+WORKDIR /outpatient
+
+RUN apk add git
+
+COPY . ./
+
+RUN git submodule update
+RUN pnpm config set fetch-retries 5
+RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
+	pnpm i
+RUN pnpm build
+RUN node scripts/trim-deps.mjs
+RUN mv packages/frontend/assets sharkey-assets
+RUN rm -r node_modules packages/frontend packages/sw
+RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
+	pnpm i --prod
+RUN rm -rf .git
+
+FROM node:${NODE_VERSION}
+
+WORKDIR /outpatient
+
+RUN apk add ffmpeg tini
+
+COPY --from=build /outpatient/built ./built
+COPY --from=build /outpatient/node_modules ./node_modules
+COPY --from=build /outpatient/packages/backend/built ./packages/backend/built
+COPY --from=build /outpatient/packages/backend/node_modules ./packages/backend/node_modules
+COPY --from=build /outpatient/packages/megalodon/lib ./packages/megalodon/lib
+COPY --from=build /outpatient/packages/megalodon/node_modules ./packages/megalodon/node_modules
+COPY --from=build /outpatient/packages/misskey-js/built ./packages/misskey-js/built
+COPY --from=build /outpatient/packages/misskey-js/node_modules ./packages/misskey-js/node_modules
+COPY --from=build /outpatient/fluent-emojis ./fluent-emojis
+COPY --from=build /outpatient/sharkey-assets ./packages/frontend/assets
+
+COPY package.json ./package.json
+COPY pnpm-workspace.yaml ./pnpm-workspace.yaml
+COPY packages/backend/package.json ./packages/backend/package.json
+COPY packages/backend/check_connect.js ./packages/backend/check_connect.js
+COPY packages/backend/ormconfig.js ./packages/backend/ormconfig.js
+COPY packages/backend/migration ./packages/backend/migration
+COPY packages/backend/assets ./packages/backend/assets
+COPY packages/megalodon/package.json ./packages/megalodon/package.json
+COPY packages/misskey-js/package.json ./packages/misskey-js/package.json
+
+ENV NODE_ENV=production
+RUN corepack enable
+ENTRYPOINT ["/usr/bin/tini", "--"]
+CMD ["pnpm", "run", "migrateandstart"]
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 66018a01c8..0000000000
--- a/Dockerfile
+++ /dev/null
@@ -1,101 +0,0 @@
-# syntax = docker/dockerfile:1.4
-
-ARG NODE_VERSION=20.5.1-bullseye
-
-# build assets & compile TypeScript
-
-FROM --platform=$BUILDPLATFORM node:${NODE_VERSION} AS native-builder
-
-RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
-	--mount=type=cache,target=/var/lib/apt,sharing=locked \
-	rm -f /etc/apt/apt.conf.d/docker-clean \
-	; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache \
-	&& apt-get update \
-	&& apt-get install -yqq --no-install-recommends \
-	build-essential curl ca-certificates
-
-ARG TARGETARCH
-
-RUN curl -L https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-$TARGETARCH-static.tar.xz -o /ffmpeg.tar.xz \
-	&& tar xvf /ffmpeg.tar.xz -C / --strip-components 1 --wildcards 'ffmpeg-*-static/ffmpeg' 'ffmpeg-*-static/ffprobe'
-
-RUN corepack enable
-
-WORKDIR /sharkey
-
-COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
-COPY --link ["scripts", "./scripts"]
-COPY --link ["packages/megalodon/package.json", "./packages/megalodon/"]
-COPY --link ["packages/backend/package.json", "./packages/backend/"]
-COPY --link ["packages/frontend/package.json", "./packages/frontend/"]
-COPY --link ["packages/sw/package.json", "./packages/sw/"]
-COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
-
-RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
-	pnpm i --frozen-lockfile --aggregate-output
-
-COPY --link . ./
-
-ARG NODE_ENV=production
-
-RUN git submodule update --init
-RUN pnpm build
-RUN rm -rf .git/
-
-# build native dependencies for target platform
-
-FROM --platform=$TARGETPLATFORM node:${NODE_VERSION} AS target-builder
-
-RUN apt-get update \
-	&& apt-get install -yqq --no-install-recommends \
-	build-essential
-
-RUN corepack enable
-
-WORKDIR /sharkey
-
-COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
-COPY --link ["scripts", "./scripts"]
-COPY --link ["packages/megalodon/package.json", "./packages/megalodon/"]
-COPY --link ["packages/backend/package.json", "./packages/backend/"]
-
-RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
-	pnpm i --frozen-lockfile --aggregate-output
-
-FROM --platform=$TARGETPLATFORM node:${NODE_VERSION}-slim AS runner
-
-ARG UID="991"
-ARG GID="991"
-
-RUN apt-get update \
-	&& apt-get install -y --no-install-recommends \
-	tini curl libjemalloc-dev libjemalloc2 \
-	&& ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so \
-	&& corepack enable \
-	&& groupadd -g "${GID}" sharkey \
-	&& useradd -l -u "${UID}" -g "${GID}" -m -d /sharkey sharkey \
-	&& find / -type d -path /proc -prune -o -type f -perm /u+s -ignore_readdir_race -exec chmod u-s {} \; \
-	&& find / -type d -path /proc -prune -o -type f -perm /g+s -ignore_readdir_race -exec chmod g-s {} \; \
-	&& apt-get clean \
-	&& rm -rf /var/lib/apt/lists
-
-USER sharkey
-WORKDIR /sharkey
-
-COPY --chown=sharkey:sharkey --from=target-builder /sharkey/node_modules ./node_modules
-COPY --chown=sharkey:sharkey --from=target-builder /sharkey/packages/megalodon/node_modules ./packages/megalodon/node_modules
-COPY --chown=sharkey:sharkey --from=target-builder /sharkey/packages/backend/node_modules ./packages/backend/node_modules
-COPY --chown=sharkey:sharkey --from=native-builder /ffmpeg /usr/local/bin/
-COPY --chown=sharkey:sharkey --from=native-builder /ffprobe /usr/local/bin/
-COPY --chown=sharkey:sharkey --from=native-builder /sharkey/built ./built
-COPY --chown=sharkey:sharkey --from=native-builder /sharkey/packages/megalodon/lib ./packages/megalodon/lib
-COPY --chown=sharkey:sharkey --from=native-builder /sharkey/packages/backend/built ./packages/backend/built
-COPY --chown=sharkey:sharkey --from=native-builder /sharkey/fluent-emojis /sharkey/fluent-emojis
-COPY --chown=sharkey:sharkey . ./
-
-ENV LD_PRELOAD=/usr/local/lib/libjemalloc.so
-ENV NODE_ENV=production
-VOLUME "/sharkey/files"
-HEALTHCHECK --interval=5s --retries=20 CMD ["/bin/bash", "/sharkey/healthcheck.sh"]
-ENTRYPOINT ["/usr/bin/tini", "--"]
-CMD ["pnpm", "run", "migrateandstart"]
diff --git a/scripts/trim-deps.mjs b/scripts/trim-deps.mjs
new file mode 100644
index 0000000000..2983f28cb4
--- /dev/null
+++ b/scripts/trim-deps.mjs
@@ -0,0 +1,35 @@
+// trims dependencies for production
+// only run after a full build
+
+import fs from 'node:fs'
+
+const checks = ['dependencies', 'optionalDependencies', 'devDependencies']
+
+function removeDeps(path, patterns) {
+	let pkg = JSON.parse(fs.readFileSync(path));
+	for (const pattern of patterns) {
+		if (typeof pattern === 'string') {
+			for (const check of checks) {
+				if (pkg[check] !== undefined && pkg[check][pattern] !== undefined) {
+					delete pkg[check][pattern];
+				}
+			}
+		} else if (pattern instanceof RegExp) {
+			for (const check of checks) {
+				if (pkg[check] !== undefined) {
+					for (const dep of Object.keys(pkg[check])) {
+						if (pattern.exec(dep) !== null) {
+							delete pkg[check][dep];
+						}
+					}
+				}
+			}
+		}
+	}
+	fs.writeFileSync(path, JSON.stringify(pkg, undefined, 2));
+}
+
+removeDeps('package.json', ['execa', 'cssnano', 'postcss', 'terser', 'typescript'])
+removeDeps('packages/backend/package.json', ['bufferutil', 'utf-8-validate', /^@swc\//, 'typescript'])
+removeDeps('packages/megalodon/package.json', [/^@types\//, 'typescript'])
+removeDeps('packages/misskey-js/package.json', [/^@swc\//, 'typescript'])

From 5aaef3f5b6ec9b101d7df237fecc0a82605ebf2a Mon Sep 17 00:00:00 2001
From: mia <74628488+squili@users.noreply.github.com>
Date: Thu, 7 Dec 2023 16:42:53 -0800
Subject: [PATCH 161/435] containerfile -> dockerfile

---
 Containerfile => Dockerfile | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename Containerfile => Dockerfile (100%)

diff --git a/Containerfile b/Dockerfile
similarity index 100%
rename from Containerfile
rename to Dockerfile

From 7ad27640127a2b723f18914207429da115f9daaf Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Fri, 8 Dec 2023 01:49:58 +0100
Subject: [PATCH 162/435] fix: paths

---
 Dockerfile | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index 6696b5092a..ce926dd9d6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -6,7 +6,7 @@ FROM node:${NODE_VERSION} as build
 
 RUN corepack enable
 
-WORKDIR /outpatient
+WORKDIR /sharkey
 
 RUN apk add git
 
@@ -26,20 +26,20 @@ RUN rm -rf .git
 
 FROM node:${NODE_VERSION}
 
-WORKDIR /outpatient
+WORKDIR /sharkey
 
 RUN apk add ffmpeg tini
 
-COPY --from=build /outpatient/built ./built
-COPY --from=build /outpatient/node_modules ./node_modules
-COPY --from=build /outpatient/packages/backend/built ./packages/backend/built
-COPY --from=build /outpatient/packages/backend/node_modules ./packages/backend/node_modules
-COPY --from=build /outpatient/packages/megalodon/lib ./packages/megalodon/lib
-COPY --from=build /outpatient/packages/megalodon/node_modules ./packages/megalodon/node_modules
-COPY --from=build /outpatient/packages/misskey-js/built ./packages/misskey-js/built
-COPY --from=build /outpatient/packages/misskey-js/node_modules ./packages/misskey-js/node_modules
-COPY --from=build /outpatient/fluent-emojis ./fluent-emojis
-COPY --from=build /outpatient/sharkey-assets ./packages/frontend/assets
+COPY --from=build /sharkey/built ./built
+COPY --from=build /sharkey/node_modules ./node_modules
+COPY --from=build /sharkey/packages/backend/built ./packages/backend/built
+COPY --from=build /sharkey/packages/backend/node_modules ./packages/backend/node_modules
+COPY --from=build /sharkey/packages/megalodon/lib ./packages/megalodon/lib
+COPY --from=build /sharkey/packages/megalodon/node_modules ./packages/megalodon/node_modules
+COPY --from=build /sharkey/packages/misskey-js/built ./packages/misskey-js/built
+COPY --from=build /sharkey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules
+COPY --from=build /sharkey/fluent-emojis ./fluent-emojis
+COPY --from=build /sharkey/sharkey-assets ./packages/frontend/assets
 
 COPY package.json ./package.json
 COPY pnpm-workspace.yaml ./pnpm-workspace.yaml

From 7ab3725585b88658294017e5789f77bf686e81d0 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Fri, 8 Dec 2023 01:54:09 +0100
Subject: [PATCH 163/435] fix: init submodules

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index ce926dd9d6..7ec6f4ba51 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -12,7 +12,7 @@ RUN apk add git
 
 COPY . ./
 
-RUN git submodule update
+RUN git submodule update --init --recursive
 RUN pnpm config set fetch-retries 5
 RUN --mount=type=cache,target=/root/.local/share/pnpm/store,sharing=locked \
 	pnpm i

From 507376b9adb87cc1274427b79af5d2bf873ddb6c Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Fri, 8 Dec 2023 02:01:40 +0100
Subject: [PATCH 164/435] upd: add python

---
 Dockerfile | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Dockerfile b/Dockerfile
index 7ec6f4ba51..fc891ff9d8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,6 +10,11 @@ WORKDIR /sharkey
 
 RUN apk add git
 
+ENV PYTHONUNBUFFERED=1
+RUN apk add --update python3 && ln -sf python3 /usr/bin/python
+RUN python3 -m ensurepip
+RUN pip3 install --no-cache --upgrade pip setuptools
+
 COPY . ./
 
 RUN git submodule update --init --recursive

From 8fc31107b8de4aeb53304b6368a1ab9c7ba2a3dc Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Fri, 8 Dec 2023 02:08:26 +0100
Subject: [PATCH 165/435] fix: include build tools

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index fc891ff9d8..a635ce5fdb 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,7 +8,7 @@ RUN corepack enable
 
 WORKDIR /sharkey
 
-RUN apk add git
+RUN apk add git linux-headers build-base
 
 ENV PYTHONUNBUFFERED=1
 RUN apk add --update python3 && ln -sf python3 /usr/bin/python

From 107cbac914ee8d53c993a331f8e4cddaeb7534f9 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Fri, 8 Dec 2023 02:44:53 +0100
Subject: [PATCH 166/435] fix: tini location

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index a635ce5fdb..440c04e2df 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -58,5 +58,5 @@ COPY packages/misskey-js/package.json ./packages/misskey-js/package.json
 
 ENV NODE_ENV=production
 RUN corepack enable
-ENTRYPOINT ["/usr/bin/tini", "--"]
+ENTRYPOINT ["/sbin/tini", "--"]
 CMD ["pnpm", "run", "migrateandstart"]

From ac4089f37d7a5cf2d6da80318345a03da24b6811 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, 8 Dec 2023 13:06:42 +0900
Subject: [PATCH 167/435] =?UTF-8?q?enhance(frontend):=20=E3=82=A6=E3=82=A3?=
 =?UTF-8?q?=E3=82=B8=E3=82=A7=E3=83=83=E3=83=88=E3=82=92=E9=9D=9E=E8=A1=A8?=
 =?UTF-8?q?=E7=A4=BA=E3=81=AB=E3=81=A7=E3=81=8D=E3=82=8BPageMeta=E3=82=92?=
 =?UTF-8?q?=E8=BF=BD=E5=8A=A0=20(#12456)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (enhance) ウィジェットを非表示にできるPageMetaを追加

* fix lint

* rename

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 packages/frontend/src/pages/admin/index.vue    |  3 ++-
 packages/frontend/src/pages/settings/index.vue |  1 +
 packages/frontend/src/scripts/page-metadata.ts |  1 +
 packages/frontend/src/ui/classic.vue           | 12 ++++++------
 packages/frontend/src/ui/universal.vue         | 14 +++++++-------
 5 files changed, 17 insertions(+), 14 deletions(-)

diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index 414889125c..9fcbb3ae6f 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -261,6 +261,7 @@ provideMetadataReceiver((info) => {
 		childInfo.value = null;
 	} else {
 		childInfo.value = info;
+		INFO.value.needWideArea = info.value.needWideArea ?? undefined;
 	}
 });
 
@@ -268,7 +269,7 @@ function invite() {
 	os.api('admin/invite/create').then(x => {
 		os.alert({
 			type: 'info',
-			text: x?.[0].code,
+			text: x[0].code,
 		});
 	}).catch(err => {
 		os.alert({
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index 49290e7c22..633ee894a9 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -236,6 +236,7 @@ provideMetadataReceiver((info) => {
 		childInfo.value = null;
 	} else {
 		childInfo.value = info;
+		INFO.value.needWideArea = info.value?.needWideArea ?? undefined;
 	}
 });
 
diff --git a/packages/frontend/src/scripts/page-metadata.ts b/packages/frontend/src/scripts/page-metadata.ts
index 330ba8da83..369e46aae1 100644
--- a/packages/frontend/src/scripts/page-metadata.ts
+++ b/packages/frontend/src/scripts/page-metadata.ts
@@ -15,6 +15,7 @@ export type PageMetadata = {
 	icon?: string | null;
 	avatar?: Misskey.entities.User | null;
 	userName?: Misskey.entities.User | null;
+	needWideArea?: boolean;
 };
 
 export function definePageMetadata(metadata: PageMetadata | null | Ref<PageMetadata | null> | ComputedRef<PageMetadata | null>): void {
diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue
index 1a9f939c83..b5381396cd 100644
--- a/packages/frontend/src/ui/classic.vue
+++ b/packages/frontend/src/ui/classic.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div v-if="!showMenuOnTop" class="sidebar">
 			<XSidebar/>
 		</div>
-		<div v-else ref="widgetsLeft" class="widgets left">
+		<div v-else-if="!pageMetadata?.needWideArea" ref="widgetsLeft" class="widgets left">
 			<XWidgets place="left" :marginTop="'var(--margin)'" @mounted="attachSticky(widgetsLeft)"/>
 		</div>
 
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</div>
 		</main>
 
-		<div v-if="isDesktop" ref="widgetsRight" class="widgets right">
+		<div v-if="isDesktop && !pageMetadata?.needWideArea" ref="widgetsRight" class="widgets right">
 			<XWidgets :place="showMenuOnTop ? 'right' : null" :marginTop="showMenuOnTop ? '0' : 'var(--margin)'" @mounted="attachSticky(widgetsRight)"/>
 		</div>
 	</div>
@@ -64,7 +64,7 @@ const DESKTOP_THRESHOLD = 1100;
 
 const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
 
-const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
+const pageMetadata = ref<null | PageMetadata>();
 const widgetsShowing = ref(false);
 const fullView = ref(false);
 const globalHeaderHeight = ref(0);
@@ -76,9 +76,9 @@ const widgetsRight = ref();
 
 provide('router', mainRouter);
 provideMetadataReceiver((info) => {
-	pageMetadata.value = info;
-	if (pageMetadata.value.value) {
-		document.title = `${pageMetadata.value.value.title} | ${instanceName}`;
+	pageMetadata.value = info.value;
+	if (pageMetadata.value) {
+		document.title = `${pageMetadata.value.title} | ${instanceName}`;
 	}
 });
 provide('shouldHeaderThin', showMenuOnTop.value);
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index 4721507f7e..cba7b82610 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -18,11 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div :class="$style.spacer"></div>
 	</MkStickyContainer>
 
-	<div v-if="isDesktop" :class="$style.widgets">
+	<div v-if="isDesktop && !pageMetadata?.needWideArea" :class="$style.widgets">
 		<XWidgets/>
 	</div>
 
-	<button v-if="!isDesktop && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"><i class="ti ti-apps"></i></button>
+	<button v-if="(!isDesktop || pageMetadata?.needWideArea) && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"><i class="ti ti-apps"></i></button>
 
 	<div v-if="isMobile" ref="navFooter" :class="$style.nav">
 		<button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator"><i class="_indicatorCircle"></i></span></button>
@@ -95,7 +95,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, provide, onMounted, computed, ref, ComputedRef, watch, shallowRef, Ref } from 'vue';
+import { defineAsyncComponent, provide, onMounted, computed, ref, watch, shallowRef, Ref } from 'vue';
 import XCommon from './_common_/common.vue';
 import type MkStickyContainer from '@/components/global/MkStickyContainer.vue';
 import { instanceName } from '@/config.js';
@@ -127,16 +127,16 @@ window.addEventListener('resize', () => {
 	isMobile.value = deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD;
 });
 
-const pageMetadata = ref<null | ComputedRef<PageMetadata>>();
+const pageMetadata = ref<null | PageMetadata>();
 const widgetsShowing = ref(false);
 const navFooter = shallowRef<HTMLElement>();
 const contents = shallowRef<InstanceType<typeof MkStickyContainer>>();
 
 provide('router', mainRouter);
 provideMetadataReceiver((info) => {
-	pageMetadata.value = info;
-	if (pageMetadata.value.value) {
-		document.title = `${pageMetadata.value.value.title} | ${instanceName}`;
+	pageMetadata.value = info.value;
+	if (pageMetadata.value) {
+		document.title = `${pageMetadata.value.title} | ${instanceName}`;
 	}
 });
 

From e38af60fd029ed0ce982e3006e8680f3560bd885 Mon Sep 17 00:00:00 2001
From: ikasoba <57828948+ikasoba@users.noreply.github.com>
Date: Fri, 8 Dec 2023 15:15:17 +0900
Subject: [PATCH 168/435] =?UTF-8?q?fix:=20`secure:=20true`=20=E3=81=AA?=
 =?UTF-8?q?=E3=82=A8=E3=83=B3=E3=83=89=E3=83=9D=E3=82=A4=E3=83=B3=E3=83=88?=
 =?UTF-8?q?=E3=81=AE=E5=9E=8B=E3=81=8C=20misskey-js=20=E3=81=AB=E5=90=AB?=
 =?UTF-8?q?=E3=81=BE=E3=82=8C=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84=20(#1260?=
 =?UTF-8?q?3)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 作った

* 修正

* 修正
---
 .../src/server/api/openapi/gen-spec.ts        |    7 +-
 packages/misskey-js/etc/misskey-js.api.md     |  118 +-
 .../misskey-js/generator/src/generator.ts     |   65 +
 packages/misskey-js/src/api.ts                |    2 +
 .../misskey-js/src/autogen/apiClientJSDoc.ts  | 3973 +++++++++++++++++
 packages/misskey-js/src/autogen/endpoint.ts   |   68 +-
 packages/misskey-js/src/autogen/entities.ts   |   33 +-
 packages/misskey-js/src/autogen/models.ts     |    4 +-
 packages/misskey-js/src/autogen/types.ts      | 2261 +++++++++-
 9 files changed, 6520 insertions(+), 11 deletions(-)
 create mode 100644 packages/misskey-js/src/autogen/apiClientJSDoc.ts

diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts
index 30bf6b8b3e..0e71510b48 100644
--- a/packages/backend/src/server/api/openapi/gen-spec.ts
+++ b/packages/backend/src/server/api/openapi/gen-spec.ts
@@ -43,7 +43,7 @@ export function genOpenapiSpec(config: Config) {
 
 	// 書き換えたりするのでディープコピーしておく。そのまま編集するとメモリ上の値が汚れて次回以降の出力に影響する
 	const copiedEndpoints = JSON.parse(JSON.stringify(endpoints)) as IEndpoint[];
-	for (const endpoint of copiedEndpoints.filter(ep => !ep.meta.secure)) {
+	for (const endpoint of copiedEndpoints) {
 		const errors = {} as any;
 
 		if (endpoint.meta.errors) {
@@ -59,6 +59,11 @@ export function genOpenapiSpec(config: Config) {
 		const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res) : {};
 
 		let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n';
+
+		if (endpoint.meta.secure) {
+			desc += '**Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.\n';
+		}
+
 		desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`;
 		if (endpoint.meta.kind) {
 			const kind = endpoint.meta.kind;
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 6225f9e236..abb3cae4b1 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -125,6 +125,9 @@ type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['reques
 // @public (undocumented)
 type AdminEmojiDeleteRequest = operations['admin/emoji/delete']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminEmojiImportZipRequest = operations['admin/emoji/import-zip']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 type AdminEmojiListRemoteRequest = operations['admin/emoji/list-remote']['requestBody']['content']['application/json'];
 
@@ -375,8 +378,6 @@ class APIClient {
     fetch: FetchLike;
     // (undocumented)
     origin: string;
-    // (undocumented)
-    request<E extends keyof Endpoints, P extends Endpoints[E]['req']>(endpoint: E, params?: P, credential?: string | null): Promise<SwitchCaseResponseType<E, P>>;
 }
 
 // @public (undocumented)
@@ -409,6 +410,9 @@ type ApShowRequest = operations['ap/show']['requestBody']['content']['applicatio
 // @public (undocumented)
 type ApShowResponse = operations['ap/show']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type AuthAcceptRequest = operations['auth/accept']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 type AuthSessionGenerateRequest = operations['auth/session/generate']['requestBody']['content']['application/json'];
 
@@ -1062,6 +1066,7 @@ declare namespace entities {
         AdminEmojiCopyResponse,
         AdminEmojiDeleteBulkRequest,
         AdminEmojiDeleteRequest,
+        AdminEmojiImportZipRequest,
         AdminEmojiListRemoteRequest,
         AdminEmojiListRemoteResponse,
         AdminEmojiListRequest,
@@ -1138,6 +1143,7 @@ declare namespace entities {
         AppCreateResponse,
         AppShowRequest,
         AppShowResponse,
+        AuthAcceptRequest,
         AuthSessionGenerateRequest,
         AuthSessionGenerateResponse,
         AuthSessionShowRequest,
@@ -1297,13 +1303,31 @@ declare namespace entities {
         HashtagsUsersRequest,
         HashtagsUsersResponse,
         IResponse,
+        I2faDoneRequest,
+        I2faKeyDoneRequest,
+        I2faPasswordLessRequest,
+        I2faRegisterKeyRequest,
+        I2faRegisterRequest,
+        I2faUpdateKeyRequest,
+        I2faRemoveKeyRequest,
+        I2faUnregisterRequest,
+        IAppsRequest,
+        IAuthorizedAppsRequest,
         IClaimAchievementRequest,
+        IChangePasswordRequest,
+        IDeleteAccountRequest,
+        IExportFollowingRequest,
         IFavoritesRequest,
         IFavoritesResponse,
         IGalleryLikesRequest,
         IGalleryLikesResponse,
         IGalleryPostsRequest,
         IGalleryPostsResponse,
+        IImportBlockingRequest,
+        IImportFollowingRequest,
+        IImportMutingRequest,
+        IImportUserListsRequest,
+        IImportAntennasRequest,
         INotificationsRequest,
         INotificationsResponse,
         INotificationsGroupedRequest,
@@ -1315,6 +1339,7 @@ declare namespace entities {
         IPinRequest,
         IPinResponse,
         IReadAnnouncementRequest,
+        IRegenerateTokenRequest,
         IRegistryGetAllRequest,
         IRegistryGetDetailRequest,
         IRegistryGetRequest,
@@ -1322,10 +1347,15 @@ declare namespace entities {
         IRegistryKeysRequest,
         IRegistryRemoveRequest,
         IRegistrySetRequest,
+        IRevokeTokenRequest,
+        ISigninHistoryRequest,
+        ISigninHistoryResponse,
         IUnpinRequest,
         IUnpinResponse,
+        IUpdateEmailRequest,
         IUpdateRequest,
         IUpdateResponse,
+        IMoveRequest,
         IWebhooksCreateRequest,
         IWebhooksShowRequest,
         IWebhooksUpdateRequest,
@@ -1340,6 +1370,8 @@ declare namespace entities {
         EmojisResponse,
         EmojiRequest,
         EmojiResponse,
+        MiauthGenTokenRequest,
+        MiauthGenTokenResponse,
         MuteCreateRequest,
         MuteDeleteRequest,
         MuteListRequest,
@@ -1402,6 +1434,7 @@ declare namespace entities {
         NotesUserListTimelineRequest,
         NotesUserListTimelineResponse,
         NotificationsCreateRequest,
+        PagePushRequest,
         PagesCreateRequest,
         PagesCreateResponse,
         PagesDeleteRequest,
@@ -1770,12 +1803,51 @@ type HashtagsUsersRequest = operations['hashtags/users']['requestBody']['content
 // @public (undocumented)
 type HashtagsUsersResponse = operations['hashtags/users']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type I2faDoneRequest = operations['i/2fa/done']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type I2faKeyDoneRequest = operations['i/2fa/key-done']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type I2faPasswordLessRequest = operations['i/2fa/password-less']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type I2faRegisterKeyRequest = operations['i/2fa/register-key']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type I2faRegisterRequest = operations['i/2fa/register']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type I2faRemoveKeyRequest = operations['i/2fa/remove-key']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type I2faUnregisterRequest = operations['i/2fa/unregister']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type I2faUpdateKeyRequest = operations['i/2fa/update-key']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IAppsRequest = operations['i/apps']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IAuthorizedAppsRequest = operations['i/authorized-apps']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IChangePasswordRequest = operations['i/change-password']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 type IClaimAchievementRequest = operations['i/claim-achievement']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type ID = string;
 
+// @public (undocumented)
+type IDeleteAccountRequest = operations['i/delete-account']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IExportFollowingRequest = operations['i/export-following']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 type IFavoritesRequest = operations['i/favorites']['requestBody']['content']['application/json'];
 
@@ -1794,6 +1866,24 @@ type IGalleryPostsRequest = operations['i/gallery/posts']['requestBody']['conten
 // @public (undocumented)
 type IGalleryPostsResponse = operations['i/gallery/posts']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type IImportAntennasRequest = operations['i/import-antennas']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IImportBlockingRequest = operations['i/import-blocking']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IImportFollowingRequest = operations['i/import-following']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IImportMutingRequest = operations['i/import-muting']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IImportUserListsRequest = operations['i/import-user-lists']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type IMoveRequest = operations['i/move']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 type INotificationsGroupedRequest = operations['i/notifications-grouped']['requestBody']['content']['application/json'];
 
@@ -1845,6 +1935,9 @@ type IPinResponse = operations['i/pin']['responses']['200']['content']['applicat
 // @public (undocumented)
 type IReadAnnouncementRequest = operations['i/read-announcement']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IRegenerateTokenRequest = operations['i/regenerate-token']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json'];
 
@@ -1869,15 +1962,27 @@ type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content'
 // @public (undocumented)
 type IResponse = operations['i']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type IRevokeTokenRequest = operations['i/revoke-token']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 function isAPIError(reason: any): reason is APIError;
 
+// @public (undocumented)
+type ISigninHistoryRequest = operations['i/signin-history']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type ISigninHistoryResponse = operations['i/signin-history']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IUnpinRequest = operations['i/unpin']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type IUpdateEmailRequest = operations['i/update-email']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json'];
 
@@ -1908,6 +2013,12 @@ type MetaRequest = operations['meta']['requestBody']['content']['application/jso
 // @public (undocumented)
 type MetaResponse = operations['meta']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type MiauthGenTokenRequest = operations['miauth/gen-token']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type MiauthGenTokenResponse = operations['miauth/gen-token']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type ModerationLog = {
     id: ID;
@@ -2240,6 +2351,9 @@ type PageEvent = {
     user: User;
 };
 
+// @public (undocumented)
+type PagePushRequest = operations['page-push']['requestBody']['content']['application/json'];
+
 // @public (undocumented)
 type PagesCreateRequest = operations['pages/create']['requestBody']['content']['application/json'];
 
diff --git a/packages/misskey-js/generator/src/generator.ts b/packages/misskey-js/generator/src/generator.ts
index 7ed3ae120c..f12ed94513 100644
--- a/packages/misskey-js/generator/src/generator.ts
+++ b/packages/misskey-js/generator/src/generator.ts
@@ -159,6 +159,68 @@ async function generateEndpoints(
 	await writeFile(endpointOutputPath, endpointOutputLine.join('\n'));
 }
 
+async function generateApiClientJSDoc(
+	openApiDocs: OpenAPIV3.Document,
+	apiClientFileName: string,
+	endpointsFileName: string,
+	warningsOutputPath: string,
+) {
+	const endpoints: { operationId: string; description: string; }[] = [];
+
+	// misskey-jsはPOST固定で送っているので、こちらも決め打ちする。別メソッドに対応することがあればこちらも直す必要あり
+	const paths = openApiDocs.paths;
+	const postPathItems = Object.keys(paths)
+		.map(it => paths[it]?.post)
+		.filter(filterUndefined);
+
+	for (const operation of postPathItems) {
+		// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+		const operationId = operation.operationId!;
+
+		if (operation.description) {
+			endpoints.push({
+				operationId: operationId,
+				description: operation.description,
+			});
+		}
+	}
+
+	const endpointOutputLine: string[] = [];
+
+	endpointOutputLine.push(generateVersionHeaderComment(openApiDocs));
+	endpointOutputLine.push('');
+
+	endpointOutputLine.push(`import type { SwitchCaseResponseType } from '${toImportPath(apiClientFileName)}';`);
+	endpointOutputLine.push(`import type { Endpoints } from '${toImportPath(endpointsFileName)}';`);
+	endpointOutputLine.push('');
+
+	endpointOutputLine.push(`declare module '${toImportPath(apiClientFileName)}' {`);
+	endpointOutputLine.push('  export interface APIClient {');
+	for (let i = 0; i < endpoints.length; i++) {
+		const endpoint = endpoints[i];
+
+		endpointOutputLine.push(
+			'    /**',
+			`     * ${endpoint.description.split('\n').join('\n     * ')}`,
+			'     */',
+			`    request<E extends '${endpoint.operationId}', P extends Endpoints[E][\'req\']>(`,
+			'      endpoint: E,',
+			'      params: P,',
+			'      credential?: string | null,',
+			'    ): Promise<SwitchCaseResponseType<E, P>>;',
+		);
+
+		if (i < endpoints.length - 1) {
+			endpointOutputLine.push('\n');
+		}
+	}
+	endpointOutputLine.push('  }');
+	endpointOutputLine.push('}');
+	endpointOutputLine.push('');
+
+	await writeFile(warningsOutputPath, endpointOutputLine.join('\n'));
+}
+
 function isRequestBodyObject(value: unknown): value is OpenAPIV3.RequestBodyObject {
 	if (!value) {
 		return false;
@@ -279,6 +341,9 @@ async function main() {
 	const entitiesFileName = `${generatePath}/entities.ts`;
 	const endpointFileName = `${generatePath}/endpoint.ts`;
 	await generateEndpoints(openApiDocs, typeFileName, entitiesFileName, endpointFileName);
+
+	const apiClientWarningFileName = `${generatePath}/apiClientJSDoc.ts`;
+	await generateApiClientJSDoc(openApiDocs, '../api.ts', endpointFileName, apiClientWarningFileName);
 }
 
 main();
diff --git a/packages/misskey-js/src/api.ts b/packages/misskey-js/src/api.ts
index c2fa4f1790..0d10faaada 100644
--- a/packages/misskey-js/src/api.ts
+++ b/packages/misskey-js/src/api.ts
@@ -1,3 +1,5 @@
+import './autogen/apiClientJSDoc';
+
 import { SwitchCaseResponseType } from './api.types';
 import type { Endpoints } from './api.types';
 
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
new file mode 100644
index 0000000000..7d58dcb5c8
--- /dev/null
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -0,0 +1,3973 @@
+/*
+ * version: 2023.11.0-beta.3
+ * generatedAt: 2023-12-08T04:57:48.424Z
+ */
+
+import type { SwitchCaseResponseType } from '../api.js';
+import type { Endpoints } from './endpoint.js';
+
+declare module '../api.js' {
+  export interface APIClient {
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/meta', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/abuse-user-reports', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'admin/accounts/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/accounts/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/accounts/find-by-email', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/ad/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/ad/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/ad/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/ad/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/announcements/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/announcements/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/announcements/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/announcements/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/avatar-decorations/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/avatar-decorations/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/avatar-decorations/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/avatar-decorations/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/delete-all-files-of-a-user', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/unset-user-avatar', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/unset-user-banner', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/drive/clean-remote-files', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/drive/cleanup', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/drive/files', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/drive/show-file', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/add-aliases-bulk', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/add', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/copy', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/delete-bulk', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/import-zip', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/list-remote', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/remove-aliases-bulk', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/set-aliases-bulk', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/set-category-bulk', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/set-license-bulk', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/emoji/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/federation/delete-all-files', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/federation/refresh-remote-instance-metadata', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/federation/remove-all-following', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/federation/update-instance', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/get-index-stats', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/get-table-stats', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/get-user-ips', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/invite/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/invite/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/promo/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/queue/clear', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/queue/deliver-delayed', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/queue/inbox-delayed', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/queue/promote', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/queue/stats', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/relays/add', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/relays/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/relays/remove', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/reset-password', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/resolve-abuse-user-report', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/send-email', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/server-info', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/show-moderation-logs', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/show-user', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/show-users', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/suspend-user', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/unsuspend-user', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/update-meta', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/delete-account', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/update-user-note', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/roles/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/roles/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/roles/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/roles/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/roles/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/roles/assign', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/roles/unassign', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'admin/roles/update-default-policies', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'admin/roles/users', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'announcements', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'antennas/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'antennas/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    request<E extends 'antennas/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    request<E extends 'antennas/notes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    request<E extends 'antennas/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'antennas/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'ap/get', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'ap/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'app/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'app/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'auth/accept', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'auth/session/generate', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'auth/session/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'auth/session/userkey', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:blocks*
+     */
+    request<E extends 'blocking/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:blocks*
+     */
+    request<E extends 'blocking/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:blocks*
+     */
+    request<E extends 'blocking/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    request<E extends 'channels/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'channels/featured', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    request<E extends 'channels/follow', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:channels*
+     */
+    request<E extends 'channels/followed', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:channels*
+     */
+    request<E extends 'channels/owned', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'channels/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'channels/timeline', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    request<E extends 'channels/unfollow', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    request<E extends 'channels/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    request<E extends 'channels/favorite', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:channels*
+     */
+    request<E extends 'channels/unfavorite', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:channels*
+     */
+    request<E extends 'channels/my-favorites', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'channels/search', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/active-users', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/ap-request', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/drive', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/federation', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/instance', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/notes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/user/drive', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/user/following', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/user/notes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/user/pv', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/user/reactions', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'charts/users', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'clips/add-note', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'clips/remove-note', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'clips/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'clips/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    request<E extends 'clips/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No* / **Permission**: *read:account*
+     */
+    request<E extends 'clips/notes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No* / **Permission**: *read:account*
+     */
+    request<E extends 'clips/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'clips/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
+     */
+    request<E extends 'clips/favorite', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
+     */
+    request<E extends 'clips/unfavorite', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:clip-favorite*
+     */
+    request<E extends 'clips/my-favorites', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    request<E extends 'drive', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    request<E extends 'drive/files', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Find the notes to which the given file is attached.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    request<E extends 'drive/files/attached-notes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Check if a given file exists.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    request<E extends 'drive/files/check-existence', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Upload a new drive file.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    request<E extends 'drive/files/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Delete an existing drive file.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    request<E extends 'drive/files/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Search for a drive file by a hash of the contents.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    request<E extends 'drive/files/find-by-hash', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Search for a drive file by the given parameters.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    request<E extends 'drive/files/find', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show the properties of a drive file.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    request<E extends 'drive/files/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Update the properties of a drive file.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    request<E extends 'drive/files/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Request the server to download a new drive file from the specified URL.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    request<E extends 'drive/files/upload-from-url', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    request<E extends 'drive/folders', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    request<E extends 'drive/folders/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    request<E extends 'drive/folders/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    request<E extends 'drive/folders/find', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    request<E extends 'drive/folders/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:drive*
+     */
+    request<E extends 'drive/folders/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:drive*
+     */
+    request<E extends 'drive/stream', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'email-address/available', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'endpoint', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'endpoints', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'export-custom-emojis', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'federation/followers', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'federation/following', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'federation/instances', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'federation/show-instance', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'federation/update-remote-user', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'federation/users', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'federation/stats', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    request<E extends 'following/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    request<E extends 'following/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    request<E extends 'following/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    request<E extends 'following/update-all', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    request<E extends 'following/invalidate', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    request<E extends 'following/requests/accept', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    request<E extends 'following/requests/cancel', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:following*
+     */
+    request<E extends 'following/requests/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:following*
+     */
+    request<E extends 'following/requests/reject', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'gallery/featured', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'gallery/popular', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'gallery/posts', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:gallery*
+     */
+    request<E extends 'gallery/posts/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:gallery*
+     */
+    request<E extends 'gallery/posts/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
+     */
+    request<E extends 'gallery/posts/like', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'gallery/posts/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
+     */
+    request<E extends 'gallery/posts/unlike', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:gallery*
+     */
+    request<E extends 'gallery/posts/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'get-online-users-count', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'get-avatar-decorations', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'hashtags/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'hashtags/search', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'hashtags/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'hashtags/trend', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'hashtags/users', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/2fa/done', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/2fa/key-done', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/2fa/password-less', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/2fa/register-key', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/2fa/register', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/2fa/update-key', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/2fa/remove-key', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/2fa/unregister', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/apps', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/authorized-apps', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/claim-achievement', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/change-password', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/delete-account', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/export-blocking', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/export-following', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/export-mute', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/export-notes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/export-favorites', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/export-user-lists', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/export-antennas', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:favorites*
+     */
+    request<E extends 'i/favorites', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:gallery-likes*
+     */
+    request<E extends 'i/gallery/likes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:gallery*
+     */
+    request<E extends 'i/gallery/posts', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/import-blocking', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/import-following', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/import-muting', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/import-user-lists', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/import-antennas', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:notifications*
+     */
+    request<E extends 'i/notifications', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:notifications*
+     */
+    request<E extends 'i/notifications-grouped', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:page-likes*
+     */
+    request<E extends 'i/page-likes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:pages*
+     */
+    request<E extends 'i/pages', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'i/pin', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'i/read-all-unread-notes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'i/read-announcement', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/regenerate-token', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/registry/get-all', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/registry/get-detail', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/registry/get', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/registry/keys-with-type', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/registry/keys', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/registry/remove', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/registry/scopes-with-domain', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/registry/set', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/revoke-token', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/signin-history', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'i/unpin', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/update-email', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'i/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'i/move', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'i/webhooks/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    request<E extends 'i/webhooks/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    request<E extends 'i/webhooks/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'i/webhooks/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'i/webhooks/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'invite/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'invite/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'invite/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'invite/limit', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'meta', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'emojis', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'emoji', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'miauth/gen-token', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:mutes*
+     */
+    request<E extends 'mute/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:mutes*
+     */
+    request<E extends 'mute/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:mutes*
+     */
+    request<E extends 'mute/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:mutes*
+     */
+    request<E extends 'renote-mute/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:mutes*
+     */
+    request<E extends 'renote-mute/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:mutes*
+     */
+    request<E extends 'renote-mute/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'my/apps', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/children', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/clips', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/conversation', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:notes*
+     */
+    request<E extends 'notes/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:notes*
+     */
+    request<E extends 'notes/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:favorites*
+     */
+    request<E extends 'notes/favorites/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:favorites*
+     */
+    request<E extends 'notes/favorites/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/featured', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/global-timeline', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'notes/hybrid-timeline', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/local-timeline', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'notes/mentions', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'notes/polls/recommendation', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:votes*
+     */
+    request<E extends 'notes/polls/vote', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/reactions', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:reactions*
+     */
+    request<E extends 'notes/reactions/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:reactions*
+     */
+    request<E extends 'notes/reactions/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/renotes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/replies', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/search-by-tag', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/search', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'notes/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'notes/state', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'notes/thread-muting/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'notes/thread-muting/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'notes/timeline', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'notes/translate', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:notes*
+     */
+    request<E extends 'notes/unrenote', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'notes/user-list-timeline', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:notifications*
+     */
+    request<E extends 'notifications/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:notifications*
+     */
+    request<E extends 'notifications/mark-all-as-read', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:notifications*
+     */
+    request<E extends 'notifications/test-notification', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'page-push', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:pages*
+     */
+    request<E extends 'pages/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:pages*
+     */
+    request<E extends 'pages/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'pages/featured', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:page-likes*
+     */
+    request<E extends 'pages/like', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'pages/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:page-likes*
+     */
+    request<E extends 'pages/unlike', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:pages*
+     */
+    request<E extends 'pages/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:flash*
+     */
+    request<E extends 'flash/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:flash*
+     */
+    request<E extends 'flash/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'flash/featured', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
+     */
+    request<E extends 'flash/like', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'flash/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
+     */
+    request<E extends 'flash/unlike', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:flash*
+     */
+    request<E extends 'flash/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:flash*
+     */
+    request<E extends 'flash/my', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:flash-likes*
+     */
+    request<E extends 'flash/my-likes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'ping', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'pinned-users', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'promo/read', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'roles/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'roles/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'roles/users', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'roles/notes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Request a users password to be reset.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'request-reset-password', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Only available when running with <code>NODE_ENV=testing</code>. Reset the database and flush Redis.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'reset-db', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Complete the password reset that was previously requested.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'reset-password', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'server-info', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'stats', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Check push notification registration exists.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'sw/show-registration', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Update push notification registration.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'sw/update-registration', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Register to receive push notifications.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'sw/register', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Unregister from receiving push notifications.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'sw/unregister', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Endpoint for testing input validation.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'test', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'username/available', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show all clips this user owns.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/clips', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show everyone that follows this user.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/followers', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show everyone that this user is following.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/following', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show all gallery posts by the given user.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/gallery/posts', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Get a list of other users that the specified user frequently replies to.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/get-frequently-replied-users', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/featured-notes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Create a new list of users.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'users/lists/create', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Delete an existing list of users.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'users/lists/delete', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show all lists that the authenticated user has created.
+     * 
+     * **Credential required**: *No* / **Permission**: *read:account*
+     */
+    request<E extends 'users/lists/list', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Remove a user from a list.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'users/lists/pull', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Add a user to an existing list.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'users/lists/push', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show the properties of a list.
+     * 
+     * **Credential required**: *No* / **Permission**: *read:account*
+     */
+    request<E extends 'users/lists/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'users/lists/favorite', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'users/lists/unfavorite', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Update the properties of a list.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'users/lists/update', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'users/lists/create-from-public', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'users/lists/update-membership', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No* / **Permission**: *read:account*
+     */
+    request<E extends 'users/lists/get-memberships', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/notes', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show all pages this user created.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/pages', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show all flashs this user created.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/flashs', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show all reactions this user made.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/reactions', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show users that the authenticated user might be interested to follow.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *read:account*
+     */
+    request<E extends 'users/recommendation', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show the different kinds of relations between the authenticated user and the specified user(s).
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'users/relation', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * File a report.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'users/report-abuse', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Search for a user by username and/or host.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/search-by-username-and-host', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Search for users.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/search', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * Show the properties of a user.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'users/show', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'users/achievements', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes* / **Permission**: *write:account*
+     */
+    request<E extends 'users/update-memo', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'fetch-rss', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *Yes*
+     */
+    request<E extends 'fetch-external-resources', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+
+    /**
+     * No description provided.
+     * 
+     * **Credential required**: *No*
+     */
+    request<E extends 'retention', P extends Endpoints[E]['req']>(
+      endpoint: E,
+      params: P,
+      credential?: string | null,
+    ): Promise<SwitchCaseResponseType<E, P>>;
+  }
+}
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 89faea6b40..5efe582434 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -1,6 +1,6 @@
 /*
- * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-04T11:17:52.156Z
+ * version: 2023.11.0-beta.3
+ * generatedAt: 2023-12-08T04:57:48.415Z
  */
 
 import type {
@@ -41,6 +41,7 @@ import type {
 	AdminEmojiCopyResponse,
 	AdminEmojiDeleteBulkRequest,
 	AdminEmojiDeleteRequest,
+	AdminEmojiImportZipRequest,
 	AdminEmojiListRemoteRequest,
 	AdminEmojiListRemoteResponse,
 	AdminEmojiListRequest,
@@ -117,6 +118,7 @@ import type {
 	AppCreateResponse,
 	AppShowRequest,
 	AppShowResponse,
+	AuthAcceptRequest,
 	AuthSessionGenerateRequest,
 	AuthSessionGenerateResponse,
 	AuthSessionShowRequest,
@@ -276,13 +278,31 @@ import type {
 	HashtagsUsersRequest,
 	HashtagsUsersResponse,
 	IResponse,
+	I2faDoneRequest,
+	I2faKeyDoneRequest,
+	I2faPasswordLessRequest,
+	I2faRegisterKeyRequest,
+	I2faRegisterRequest,
+	I2faUpdateKeyRequest,
+	I2faRemoveKeyRequest,
+	I2faUnregisterRequest,
+	IAppsRequest,
+	IAuthorizedAppsRequest,
 	IClaimAchievementRequest,
+	IChangePasswordRequest,
+	IDeleteAccountRequest,
+	IExportFollowingRequest,
 	IFavoritesRequest,
 	IFavoritesResponse,
 	IGalleryLikesRequest,
 	IGalleryLikesResponse,
 	IGalleryPostsRequest,
 	IGalleryPostsResponse,
+	IImportBlockingRequest,
+	IImportFollowingRequest,
+	IImportMutingRequest,
+	IImportUserListsRequest,
+	IImportAntennasRequest,
 	INotificationsRequest,
 	INotificationsResponse,
 	INotificationsGroupedRequest,
@@ -294,6 +314,7 @@ import type {
 	IPinRequest,
 	IPinResponse,
 	IReadAnnouncementRequest,
+	IRegenerateTokenRequest,
 	IRegistryGetAllRequest,
 	IRegistryGetDetailRequest,
 	IRegistryGetRequest,
@@ -301,10 +322,15 @@ import type {
 	IRegistryKeysRequest,
 	IRegistryRemoveRequest,
 	IRegistrySetRequest,
+	IRevokeTokenRequest,
+	ISigninHistoryRequest,
+	ISigninHistoryResponse,
 	IUnpinRequest,
 	IUnpinResponse,
+	IUpdateEmailRequest,
 	IUpdateRequest,
 	IUpdateResponse,
+	IMoveRequest,
 	IWebhooksCreateRequest,
 	IWebhooksShowRequest,
 	IWebhooksUpdateRequest,
@@ -319,6 +345,8 @@ import type {
 	EmojisResponse,
 	EmojiRequest,
 	EmojiResponse,
+	MiauthGenTokenRequest,
+	MiauthGenTokenResponse,
 	MuteCreateRequest,
 	MuteDeleteRequest,
 	MuteListRequest,
@@ -381,6 +409,7 @@ import type {
 	NotesUserListTimelineRequest,
 	NotesUserListTimelineResponse,
 	NotificationsCreateRequest,
+	PagePushRequest,
 	PagesCreateRequest,
 	PagesCreateResponse,
 	PagesDeleteRequest,
@@ -511,6 +540,7 @@ export type Endpoints = {
 	'admin/emoji/copy': { req: AdminEmojiCopyRequest; res: AdminEmojiCopyResponse };
 	'admin/emoji/delete-bulk': { req: AdminEmojiDeleteBulkRequest; res: EmptyResponse };
 	'admin/emoji/delete': { req: AdminEmojiDeleteRequest; res: EmptyResponse };
+	'admin/emoji/import-zip': { req: AdminEmojiImportZipRequest; res: EmptyResponse };
 	'admin/emoji/list-remote': { req: AdminEmojiListRemoteRequest; res: AdminEmojiListRemoteResponse };
 	'admin/emoji/list': { req: AdminEmojiListRequest; res: AdminEmojiListResponse };
 	'admin/emoji/remove-aliases-bulk': { req: AdminEmojiRemoveAliasesBulkRequest; res: EmptyResponse };
@@ -568,6 +598,7 @@ export type Endpoints = {
 	'ap/show': { req: ApShowRequest; res: ApShowResponse };
 	'app/create': { req: AppCreateRequest; res: AppCreateResponse };
 	'app/show': { req: AppShowRequest; res: AppShowResponse };
+	'auth/accept': { req: AuthAcceptRequest; res: EmptyResponse };
 	'auth/session/generate': { req: AuthSessionGenerateRequest; res: AuthSessionGenerateResponse };
 	'auth/session/show': { req: AuthSessionShowRequest; res: AuthSessionShowResponse };
 	'auth/session/userkey': { req: AuthSessionUserkeyRequest; res: AuthSessionUserkeyResponse };
@@ -631,6 +662,7 @@ export type Endpoints = {
 	'email-address/available': { req: EmailAddressAvailableRequest; res: EmailAddressAvailableResponse };
 	'endpoint': { req: EndpointRequest; res: EmptyResponse };
 	'endpoints': { req: EmptyRequest; res: EndpointsResponse };
+	'export-custom-emojis': { req: EmptyRequest; res: EmptyResponse };
 	'federation/followers': { req: FederationFollowersRequest; res: FederationFollowersResponse };
 	'federation/following': { req: FederationFollowingRequest; res: FederationFollowingResponse };
 	'federation/instances': { req: FederationInstancesRequest; res: FederationInstancesResponse };
@@ -664,10 +696,34 @@ export type Endpoints = {
 	'hashtags/trend': { req: EmptyRequest; res: HashtagsTrendResponse };
 	'hashtags/users': { req: HashtagsUsersRequest; res: HashtagsUsersResponse };
 	'i': { req: EmptyRequest; res: IResponse };
+	'i/2fa/done': { req: I2faDoneRequest; res: EmptyResponse };
+	'i/2fa/key-done': { req: I2faKeyDoneRequest; res: EmptyResponse };
+	'i/2fa/password-less': { req: I2faPasswordLessRequest; res: EmptyResponse };
+	'i/2fa/register-key': { req: I2faRegisterKeyRequest; res: EmptyResponse };
+	'i/2fa/register': { req: I2faRegisterRequest; res: EmptyResponse };
+	'i/2fa/update-key': { req: I2faUpdateKeyRequest; res: EmptyResponse };
+	'i/2fa/remove-key': { req: I2faRemoveKeyRequest; res: EmptyResponse };
+	'i/2fa/unregister': { req: I2faUnregisterRequest; res: EmptyResponse };
+	'i/apps': { req: IAppsRequest; res: EmptyResponse };
+	'i/authorized-apps': { req: IAuthorizedAppsRequest; res: EmptyResponse };
 	'i/claim-achievement': { req: IClaimAchievementRequest; res: EmptyResponse };
+	'i/change-password': { req: IChangePasswordRequest; res: EmptyResponse };
+	'i/delete-account': { req: IDeleteAccountRequest; res: EmptyResponse };
+	'i/export-blocking': { req: EmptyRequest; res: EmptyResponse };
+	'i/export-following': { req: IExportFollowingRequest; res: EmptyResponse };
+	'i/export-mute': { req: EmptyRequest; res: EmptyResponse };
+	'i/export-notes': { req: EmptyRequest; res: EmptyResponse };
+	'i/export-favorites': { req: EmptyRequest; res: EmptyResponse };
+	'i/export-user-lists': { req: EmptyRequest; res: EmptyResponse };
+	'i/export-antennas': { req: EmptyRequest; res: EmptyResponse };
 	'i/favorites': { req: IFavoritesRequest; res: IFavoritesResponse };
 	'i/gallery/likes': { req: IGalleryLikesRequest; res: IGalleryLikesResponse };
 	'i/gallery/posts': { req: IGalleryPostsRequest; res: IGalleryPostsResponse };
+	'i/import-blocking': { req: IImportBlockingRequest; res: EmptyResponse };
+	'i/import-following': { req: IImportFollowingRequest; res: EmptyResponse };
+	'i/import-muting': { req: IImportMutingRequest; res: EmptyResponse };
+	'i/import-user-lists': { req: IImportUserListsRequest; res: EmptyResponse };
+	'i/import-antennas': { req: IImportAntennasRequest; res: EmptyResponse };
 	'i/notifications': { req: INotificationsRequest; res: INotificationsResponse };
 	'i/notifications-grouped': { req: INotificationsGroupedRequest; res: INotificationsGroupedResponse };
 	'i/page-likes': { req: IPageLikesRequest; res: IPageLikesResponse };
@@ -675,15 +731,21 @@ export type Endpoints = {
 	'i/pin': { req: IPinRequest; res: IPinResponse };
 	'i/read-all-unread-notes': { req: EmptyRequest; res: EmptyResponse };
 	'i/read-announcement': { req: IReadAnnouncementRequest; res: EmptyResponse };
+	'i/regenerate-token': { req: IRegenerateTokenRequest; res: EmptyResponse };
 	'i/registry/get-all': { req: IRegistryGetAllRequest; res: EmptyResponse };
 	'i/registry/get-detail': { req: IRegistryGetDetailRequest; res: EmptyResponse };
 	'i/registry/get': { req: IRegistryGetRequest; res: EmptyResponse };
 	'i/registry/keys-with-type': { req: IRegistryKeysWithTypeRequest; res: EmptyResponse };
 	'i/registry/keys': { req: IRegistryKeysRequest; res: EmptyResponse };
 	'i/registry/remove': { req: IRegistryRemoveRequest; res: EmptyResponse };
+	'i/registry/scopes-with-domain': { req: EmptyRequest; res: EmptyResponse };
 	'i/registry/set': { req: IRegistrySetRequest; res: EmptyResponse };
+	'i/revoke-token': { req: IRevokeTokenRequest; res: EmptyResponse };
+	'i/signin-history': { req: ISigninHistoryRequest; res: ISigninHistoryResponse };
 	'i/unpin': { req: IUnpinRequest; res: IUnpinResponse };
+	'i/update-email': { req: IUpdateEmailRequest; res: EmptyResponse };
 	'i/update': { req: IUpdateRequest; res: IUpdateResponse };
+	'i/move': { req: IMoveRequest; res: EmptyResponse };
 	'i/webhooks/create': { req: IWebhooksCreateRequest; res: EmptyResponse };
 	'i/webhooks/list': { req: EmptyRequest; res: EmptyResponse };
 	'i/webhooks/show': { req: IWebhooksShowRequest; res: EmptyResponse };
@@ -696,6 +758,7 @@ export type Endpoints = {
 	'meta': { req: MetaRequest; res: MetaResponse };
 	'emojis': { req: EmptyRequest; res: EmojisResponse };
 	'emoji': { req: EmojiRequest; res: EmojiResponse };
+	'miauth/gen-token': { req: MiauthGenTokenRequest; res: MiauthGenTokenResponse };
 	'mute/create': { req: MuteCreateRequest; res: EmptyResponse };
 	'mute/delete': { req: MuteDeleteRequest; res: EmptyResponse };
 	'mute/list': { req: MuteListRequest; res: MuteListResponse };
@@ -736,6 +799,7 @@ export type Endpoints = {
 	'notifications/create': { req: NotificationsCreateRequest; res: EmptyResponse };
 	'notifications/mark-all-as-read': { req: EmptyRequest; res: EmptyResponse };
 	'notifications/test-notification': { req: EmptyRequest; res: EmptyResponse };
+	'page-push': { req: PagePushRequest; res: EmptyResponse };
 	'pages/create': { req: PagesCreateRequest; res: PagesCreateResponse };
 	'pages/delete': { req: PagesDeleteRequest; res: EmptyResponse };
 	'pages/featured': { req: EmptyRequest; res: PagesFeaturedResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 611f1b9504..4de3c80a7f 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -1,6 +1,6 @@
 /*
- * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-04T11:17:52.154Z
+ * version: 2023.11.0-beta.3
+ * generatedAt: 2023-12-08T04:57:48.409Z
  */
 
 import { operations } from './types.js';
@@ -43,6 +43,7 @@ export type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody'
 export type AdminEmojiCopyResponse = operations['admin/emoji/copy']['responses']['200']['content']['application/json'];
 export type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['requestBody']['content']['application/json'];
 export type AdminEmojiDeleteRequest = operations['admin/emoji/delete']['requestBody']['content']['application/json'];
+export type AdminEmojiImportZipRequest = operations['admin/emoji/import-zip']['requestBody']['content']['application/json'];
 export type AdminEmojiListRemoteRequest = operations['admin/emoji/list-remote']['requestBody']['content']['application/json'];
 export type AdminEmojiListRemoteResponse = operations['admin/emoji/list-remote']['responses']['200']['content']['application/json'];
 export type AdminEmojiListRequest = operations['admin/emoji/list']['requestBody']['content']['application/json'];
@@ -119,6 +120,7 @@ export type AppCreateRequest = operations['app/create']['requestBody']['content'
 export type AppCreateResponse = operations['app/create']['responses']['200']['content']['application/json'];
 export type AppShowRequest = operations['app/show']['requestBody']['content']['application/json'];
 export type AppShowResponse = operations['app/show']['responses']['200']['content']['application/json'];
+export type AuthAcceptRequest = operations['auth/accept']['requestBody']['content']['application/json'];
 export type AuthSessionGenerateRequest = operations['auth/session/generate']['requestBody']['content']['application/json'];
 export type AuthSessionGenerateResponse = operations['auth/session/generate']['responses']['200']['content']['application/json'];
 export type AuthSessionShowRequest = operations['auth/session/show']['requestBody']['content']['application/json'];
@@ -278,13 +280,31 @@ export type HashtagsTrendResponse = operations['hashtags/trend']['responses']['2
 export type HashtagsUsersRequest = operations['hashtags/users']['requestBody']['content']['application/json'];
 export type HashtagsUsersResponse = operations['hashtags/users']['responses']['200']['content']['application/json'];
 export type IResponse = operations['i']['responses']['200']['content']['application/json'];
+export type I2faDoneRequest = operations['i/2fa/done']['requestBody']['content']['application/json'];
+export type I2faKeyDoneRequest = operations['i/2fa/key-done']['requestBody']['content']['application/json'];
+export type I2faPasswordLessRequest = operations['i/2fa/password-less']['requestBody']['content']['application/json'];
+export type I2faRegisterKeyRequest = operations['i/2fa/register-key']['requestBody']['content']['application/json'];
+export type I2faRegisterRequest = operations['i/2fa/register']['requestBody']['content']['application/json'];
+export type I2faUpdateKeyRequest = operations['i/2fa/update-key']['requestBody']['content']['application/json'];
+export type I2faRemoveKeyRequest = operations['i/2fa/remove-key']['requestBody']['content']['application/json'];
+export type I2faUnregisterRequest = operations['i/2fa/unregister']['requestBody']['content']['application/json'];
+export type IAppsRequest = operations['i/apps']['requestBody']['content']['application/json'];
+export type IAuthorizedAppsRequest = operations['i/authorized-apps']['requestBody']['content']['application/json'];
 export type IClaimAchievementRequest = operations['i/claim-achievement']['requestBody']['content']['application/json'];
+export type IChangePasswordRequest = operations['i/change-password']['requestBody']['content']['application/json'];
+export type IDeleteAccountRequest = operations['i/delete-account']['requestBody']['content']['application/json'];
+export type IExportFollowingRequest = operations['i/export-following']['requestBody']['content']['application/json'];
 export type IFavoritesRequest = operations['i/favorites']['requestBody']['content']['application/json'];
 export type IFavoritesResponse = operations['i/favorites']['responses']['200']['content']['application/json'];
 export type IGalleryLikesRequest = operations['i/gallery/likes']['requestBody']['content']['application/json'];
 export type IGalleryLikesResponse = operations['i/gallery/likes']['responses']['200']['content']['application/json'];
 export type IGalleryPostsRequest = operations['i/gallery/posts']['requestBody']['content']['application/json'];
 export type IGalleryPostsResponse = operations['i/gallery/posts']['responses']['200']['content']['application/json'];
+export type IImportBlockingRequest = operations['i/import-blocking']['requestBody']['content']['application/json'];
+export type IImportFollowingRequest = operations['i/import-following']['requestBody']['content']['application/json'];
+export type IImportMutingRequest = operations['i/import-muting']['requestBody']['content']['application/json'];
+export type IImportUserListsRequest = operations['i/import-user-lists']['requestBody']['content']['application/json'];
+export type IImportAntennasRequest = operations['i/import-antennas']['requestBody']['content']['application/json'];
 export type INotificationsRequest = operations['i/notifications']['requestBody']['content']['application/json'];
 export type INotificationsResponse = operations['i/notifications']['responses']['200']['content']['application/json'];
 export type INotificationsGroupedRequest = operations['i/notifications-grouped']['requestBody']['content']['application/json'];
@@ -296,6 +316,7 @@ export type IPagesResponse = operations['i/pages']['responses']['200']['content'
 export type IPinRequest = operations['i/pin']['requestBody']['content']['application/json'];
 export type IPinResponse = operations['i/pin']['responses']['200']['content']['application/json'];
 export type IReadAnnouncementRequest = operations['i/read-announcement']['requestBody']['content']['application/json'];
+export type IRegenerateTokenRequest = operations['i/regenerate-token']['requestBody']['content']['application/json'];
 export type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json'];
 export type IRegistryGetDetailRequest = operations['i/registry/get-detail']['requestBody']['content']['application/json'];
 export type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content']['application/json'];
@@ -303,10 +324,15 @@ export type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type
 export type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json'];
 export type IRegistryRemoveRequest = operations['i/registry/remove']['requestBody']['content']['application/json'];
 export type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content']['application/json'];
+export type IRevokeTokenRequest = operations['i/revoke-token']['requestBody']['content']['application/json'];
+export type ISigninHistoryRequest = operations['i/signin-history']['requestBody']['content']['application/json'];
+export type ISigninHistoryResponse = operations['i/signin-history']['responses']['200']['content']['application/json'];
 export type IUnpinRequest = operations['i/unpin']['requestBody']['content']['application/json'];
 export type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['application/json'];
+export type IUpdateEmailRequest = operations['i/update-email']['requestBody']['content']['application/json'];
 export type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json'];
 export type IUpdateResponse = operations['i/update']['responses']['200']['content']['application/json'];
+export type IMoveRequest = operations['i/move']['requestBody']['content']['application/json'];
 export type IWebhooksCreateRequest = operations['i/webhooks/create']['requestBody']['content']['application/json'];
 export type IWebhooksShowRequest = operations['i/webhooks/show']['requestBody']['content']['application/json'];
 export type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
@@ -321,6 +347,8 @@ export type MetaResponse = operations['meta']['responses']['200']['content']['ap
 export type EmojisResponse = operations['emojis']['responses']['200']['content']['application/json'];
 export type EmojiRequest = operations['emoji']['requestBody']['content']['application/json'];
 export type EmojiResponse = operations['emoji']['responses']['200']['content']['application/json'];
+export type MiauthGenTokenRequest = operations['miauth/gen-token']['requestBody']['content']['application/json'];
+export type MiauthGenTokenResponse = operations['miauth/gen-token']['responses']['200']['content']['application/json'];
 export type MuteCreateRequest = operations['mute/create']['requestBody']['content']['application/json'];
 export type MuteDeleteRequest = operations['mute/delete']['requestBody']['content']['application/json'];
 export type MuteListRequest = operations['mute/list']['requestBody']['content']['application/json'];
@@ -383,6 +411,7 @@ export type NotesUnrenoteRequest = operations['notes/unrenote']['requestBody']['
 export type NotesUserListTimelineRequest = operations['notes/user-list-timeline']['requestBody']['content']['application/json'];
 export type NotesUserListTimelineResponse = operations['notes/user-list-timeline']['responses']['200']['content']['application/json'];
 export type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json'];
+export type PagePushRequest = operations['page-push']['requestBody']['content']['application/json'];
 export type PagesCreateRequest = operations['pages/create']['requestBody']['content']['application/json'];
 export type PagesCreateResponse = operations['pages/create']['responses']['200']['content']['application/json'];
 export type PagesDeleteRequest = operations['pages/delete']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index 0607cd1370..2c25e82d12 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -1,6 +1,6 @@
 /*
- * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-04T11:17:52.151Z
+ * version: 2023.11.0-beta.3
+ * generatedAt: 2023-12-08T04:57:48.405Z
  */
 
 import { components } from './types.js';
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index e09f24d46f..cecc2c872f 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -2,8 +2,8 @@
 /* eslint @typescript-eslint/no-explicit-any: 0 */
 
 /*
- * version: 2023.12.0-beta.1
- * generatedAt: 2023-12-04T11:17:51.997Z
+ * version: 2023.11.0-beta.3
+ * generatedAt: 2023-12-08T04:57:48.142Z
  */
 
 /**
@@ -278,6 +278,16 @@ export type paths = {
      */
     post: operations['admin/emoji/delete'];
   };
+  '/admin/emoji/import-zip': {
+    /**
+     * admin/emoji/import-zip
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['admin/emoji/import-zip'];
+  };
   '/admin/emoji/list-remote': {
     /**
      * admin/emoji/list-remote
@@ -791,6 +801,16 @@ export type paths = {
      */
     post: operations['app/show'];
   };
+  '/auth/accept': {
+    /**
+     * auth/accept
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['auth/accept'];
+  };
   '/auth/session/generate': {
     /**
      * auth/session/generate
@@ -1442,6 +1462,16 @@ export type paths = {
      */
     post: operations['endpoints'];
   };
+  '/export-custom-emojis': {
+    /**
+     * export-custom-emojis
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['export-custom-emojis'];
+  };
   '/federation/followers': {
     /**
      * federation/followers
@@ -1767,6 +1797,106 @@ export type paths = {
      */
     post: operations['i'];
   };
+  '/i/2fa/done': {
+    /**
+     * i/2fa/done
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/2fa/done'];
+  };
+  '/i/2fa/key-done': {
+    /**
+     * i/2fa/key-done
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/2fa/key-done'];
+  };
+  '/i/2fa/password-less': {
+    /**
+     * i/2fa/password-less
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/2fa/password-less'];
+  };
+  '/i/2fa/register-key': {
+    /**
+     * i/2fa/register-key
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/2fa/register-key'];
+  };
+  '/i/2fa/register': {
+    /**
+     * i/2fa/register
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/2fa/register'];
+  };
+  '/i/2fa/update-key': {
+    /**
+     * i/2fa/update-key
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/2fa/update-key'];
+  };
+  '/i/2fa/remove-key': {
+    /**
+     * i/2fa/remove-key
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/2fa/remove-key'];
+  };
+  '/i/2fa/unregister': {
+    /**
+     * i/2fa/unregister
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/2fa/unregister'];
+  };
+  '/i/apps': {
+    /**
+     * i/apps
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/apps'];
+  };
+  '/i/authorized-apps': {
+    /**
+     * i/authorized-apps
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/authorized-apps'];
+  };
   '/i/claim-achievement': {
     /**
      * i/claim-achievement
@@ -1776,6 +1906,96 @@ export type paths = {
      */
     post: operations['i/claim-achievement'];
   };
+  '/i/change-password': {
+    /**
+     * i/change-password
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/change-password'];
+  };
+  '/i/delete-account': {
+    /**
+     * i/delete-account
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/delete-account'];
+  };
+  '/i/export-blocking': {
+    /**
+     * i/export-blocking
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/export-blocking'];
+  };
+  '/i/export-following': {
+    /**
+     * i/export-following
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/export-following'];
+  };
+  '/i/export-mute': {
+    /**
+     * i/export-mute
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/export-mute'];
+  };
+  '/i/export-notes': {
+    /**
+     * i/export-notes
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/export-notes'];
+  };
+  '/i/export-favorites': {
+    /**
+     * i/export-favorites
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/export-favorites'];
+  };
+  '/i/export-user-lists': {
+    /**
+     * i/export-user-lists
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/export-user-lists'];
+  };
+  '/i/export-antennas': {
+    /**
+     * i/export-antennas
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/export-antennas'];
+  };
   '/i/favorites': {
     /**
      * i/favorites
@@ -1803,6 +2023,56 @@ export type paths = {
      */
     post: operations['i/gallery/posts'];
   };
+  '/i/import-blocking': {
+    /**
+     * i/import-blocking
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/import-blocking'];
+  };
+  '/i/import-following': {
+    /**
+     * i/import-following
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/import-following'];
+  };
+  '/i/import-muting': {
+    /**
+     * i/import-muting
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/import-muting'];
+  };
+  '/i/import-user-lists': {
+    /**
+     * i/import-user-lists
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/import-user-lists'];
+  };
+  '/i/import-antennas': {
+    /**
+     * i/import-antennas
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/import-antennas'];
+  };
   '/i/notifications': {
     /**
      * i/notifications
@@ -1866,6 +2136,16 @@ export type paths = {
      */
     post: operations['i/read-announcement'];
   };
+  '/i/regenerate-token': {
+    /**
+     * i/regenerate-token
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/regenerate-token'];
+  };
   '/i/registry/get-all': {
     /**
      * i/registry/get-all
@@ -1920,6 +2200,16 @@ export type paths = {
      */
     post: operations['i/registry/remove'];
   };
+  '/i/registry/scopes-with-domain': {
+    /**
+     * i/registry/scopes-with-domain
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/registry/scopes-with-domain'];
+  };
   '/i/registry/set': {
     /**
      * i/registry/set
@@ -1929,6 +2219,26 @@ export type paths = {
      */
     post: operations['i/registry/set'];
   };
+  '/i/revoke-token': {
+    /**
+     * i/revoke-token
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/revoke-token'];
+  };
+  '/i/signin-history': {
+    /**
+     * i/signin-history
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/signin-history'];
+  };
   '/i/unpin': {
     /**
      * i/unpin
@@ -1938,6 +2248,16 @@ export type paths = {
      */
     post: operations['i/unpin'];
   };
+  '/i/update-email': {
+    /**
+     * i/update-email
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/update-email'];
+  };
   '/i/update': {
     /**
      * i/update
@@ -1947,6 +2267,16 @@ export type paths = {
      */
     post: operations['i/update'];
   };
+  '/i/move': {
+    /**
+     * i/move
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['i/move'];
+  };
   '/i/webhooks/create': {
     /**
      * i/webhooks/create
@@ -2069,6 +2399,16 @@ export type paths = {
      */
     post: operations['emoji'];
   };
+  '/miauth/gen-token': {
+    /**
+     * miauth/gen-token
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['miauth/gen-token'];
+  };
   '/mute/create': {
     /**
      * mute/create
@@ -2443,6 +2783,16 @@ export type paths = {
      */
     post: operations['notifications/test-notification'];
   };
+  '/page-push': {
+    /**
+     * page-push
+     * @description No description provided.
+     *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
+     */
+    post: operations['page-push'];
+  };
   '/pages/create': {
     /**
      * pages/create
@@ -5831,6 +6181,59 @@ export type operations = {
       };
     };
   };
+  /**
+   * admin/emoji/import-zip
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'admin/emoji/import-zip': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          fileId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * admin/emoji/list-remote
    * @description No description provided.
@@ -9108,6 +9511,58 @@ export type operations = {
       };
     };
   };
+  /**
+   * auth/accept
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'auth/accept': {
+    requestBody: {
+      content: {
+        'application/json': {
+          token: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * auth/session/generate
    * @description No description provided.
@@ -12804,6 +13259,57 @@ export type operations = {
       };
     };
   };
+  /**
+   * export-custom-emojis
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'export-custom-emojis': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * federation/followers
    * @description No description provided.
@@ -14666,6 +15172,544 @@ export type operations = {
       };
     };
   };
+  /**
+   * i/2fa/done
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/2fa/done': {
+    requestBody: {
+      content: {
+        'application/json': {
+          token: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/2fa/key-done
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/2fa/key-done': {
+    requestBody: {
+      content: {
+        'application/json': {
+          password: string;
+          token?: string | null;
+          name: string;
+          credential: Record<string, never>;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/2fa/password-less
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/2fa/password-less': {
+    requestBody: {
+      content: {
+        'application/json': {
+          value: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/2fa/register-key
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/2fa/register-key': {
+    requestBody: {
+      content: {
+        'application/json': {
+          password: string;
+          token?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/2fa/register
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/2fa/register': {
+    requestBody: {
+      content: {
+        'application/json': {
+          password: string;
+          token?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/2fa/update-key
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/2fa/update-key': {
+    requestBody: {
+      content: {
+        'application/json': {
+          name: string;
+          credentialId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/2fa/remove-key
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/2fa/remove-key': {
+    requestBody: {
+      content: {
+        'application/json': {
+          password: string;
+          token?: string | null;
+          credentialId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/2fa/unregister
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/2fa/unregister': {
+    requestBody: {
+      content: {
+        'application/json': {
+          password: string;
+          token?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/apps
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/apps': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @enum {string} */
+          sort?: '+createdAt' | '-createdAt' | '+lastUsedAt' | '-lastUsedAt';
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/authorized-apps
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/authorized-apps': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** @default 0 */
+          offset?: number;
+          /**
+           * @default desc
+           * @enum {string}
+           */
+          sort?: 'desc' | 'asc';
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * i/claim-achievement
    * @description No description provided.
@@ -14718,6 +15762,480 @@ export type operations = {
       };
     };
   };
+  /**
+   * i/change-password
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/change-password': {
+    requestBody: {
+      content: {
+        'application/json': {
+          currentPassword: string;
+          newPassword: string;
+          token?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/delete-account
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/delete-account': {
+    requestBody: {
+      content: {
+        'application/json': {
+          password: string;
+          token?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/export-blocking
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/export-blocking': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/export-following
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/export-following': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default false */
+          excludeMuting?: boolean;
+          /** @default false */
+          excludeInactive?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/export-mute
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/export-mute': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/export-notes
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/export-notes': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/export-favorites
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/export-favorites': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/export-user-lists
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/export-user-lists': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/export-antennas
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/export-antennas': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * i/favorites
    * @description No description provided.
@@ -14896,6 +16414,302 @@ export type operations = {
       };
     };
   };
+  /**
+   * i/import-blocking
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/import-blocking': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          fileId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/import-following
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/import-following': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          fileId: string;
+          withReplies?: boolean;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/import-muting
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/import-muting': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          fileId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/import-user-lists
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/import-user-lists': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          fileId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/import-antennas
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/import-antennas': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          fileId: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * i/notifications
    * @description No description provided.
@@ -15302,6 +17116,58 @@ export type operations = {
       };
     };
   };
+  /**
+   * i/regenerate-token
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/regenerate-token': {
+    requestBody: {
+      content: {
+        'application/json': {
+          password: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * i/registry/get-all
    * @description No description provided.
@@ -15623,6 +17489,51 @@ export type operations = {
       };
     };
   };
+  /**
+   * i/registry/scopes-with-domain
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/registry/scopes-with-domain': {
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * i/registry/set
    * @description No description provided.
@@ -15678,6 +17589,119 @@ export type operations = {
       };
     };
   };
+  /**
+   * i/revoke-token
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/revoke-token': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          tokenId?: string;
+          token?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
+  /**
+   * i/signin-history
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/signin-history': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** @default 10 */
+          limit?: number;
+          /** Format: misskey:id */
+          sinceId?: string;
+          /** Format: misskey:id */
+          untilId?: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Signin'][];
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * i/unpin
    * @description No description provided.
@@ -15732,6 +17756,66 @@ export type operations = {
       };
     };
   };
+  /**
+   * i/update-email
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/update-email': {
+    requestBody: {
+      content: {
+        'application/json': {
+          password: string;
+          email?: string | null;
+          token?: string | null;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * i/update
    * @description No description provided.
@@ -15834,6 +17918,64 @@ export type operations = {
       };
     };
   };
+  /**
+   * i/move
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'i/move': {
+    requestBody: {
+      content: {
+        'application/json': {
+          moveToAccount: string;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description To many requests */
+      429: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * i/webhooks/create
    * @description No description provided.
@@ -16526,6 +18668,66 @@ export type operations = {
       };
     };
   };
+  /**
+   * miauth/gen-token
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'miauth/gen-token': {
+    requestBody: {
+      content: {
+        'application/json': {
+          session: string | null;
+          name?: string | null;
+          description?: string | null;
+          iconUrl?: string | null;
+          permission: string[];
+        };
+      };
+    };
+    responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            token: string;
+          };
+        };
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * mute/create
    * @description No description provided.
@@ -18926,6 +21128,61 @@ export type operations = {
       };
     };
   };
+  /**
+   * page-push
+   * @description No description provided.
+   *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
+   */
+  'page-push': {
+    requestBody: {
+      content: {
+        'application/json': {
+          /** Format: misskey:id */
+          pageId: string;
+          event: string;
+          var?: unknown;
+        };
+      };
+    };
+    responses: {
+      /** @description OK (without any results) */
+      204: {
+        content: never;
+      };
+      /** @description Client error */
+      400: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Authentication error */
+      401: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Forbidden error */
+      403: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description I'm Ai */
+      418: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+      /** @description Internal server error */
+      500: {
+        content: {
+          'application/json': components['schemas']['Error'];
+        };
+      };
+    };
+  };
   /**
    * pages/create
    * @description No description provided.

From b760db13bcda4accbe6c18463f27548ec77159dc Mon Sep 17 00:00:00 2001
From: kakkokari-gtyih <daisho7308+f@gmail.com>
Date: Fri, 8 Dec 2023 16:32:24 +0900
Subject: [PATCH 169/435] =?UTF-8?q?fix(dev)=20=E4=B8=80=E9=83=A8=E3=81=AE?=
 =?UTF-8?q?=E3=82=A2=E3=82=BB=E3=83=83=E3=83=88=E3=81=8C=E8=AA=AD=E3=81=BF?=
 =?UTF-8?q?=E8=BE=BC=E3=81=BE=E3=82=8C=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/vite.config.local-dev.ts | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts
index 6b4bb73ffa..6b6394a722 100644
--- a/packages/frontend/vite.config.local-dev.ts
+++ b/packages/frontend/vite.config.local-dev.ts
@@ -21,6 +21,8 @@ const devConfig = {
 				target: 'http://localhost:3000/',
 			},
 			'/assets': 'http://localhost:3000/',
+			'/static-assets': 'http://localhost:3000/',
+			'/client-assets': 'http://localhost:3000/',
 			'/files': 'http://localhost:3000/',
 			'/twemoji': 'http://localhost:3000/',
 			'/fluent-emoji': 'http://localhost:3000/',

From c54d1cdde295ce692df144467e74418924adfd14 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, 8 Dec 2023 16:54:33 +0900
Subject: [PATCH 170/435] =?UTF-8?q?fix(dev-frontend)=20=E3=82=B5=E3=83=BC?=
 =?UTF-8?q?=E3=83=90=E3=83=BC=E3=82=B5=E3=82=A4=E3=83=89=E3=81=AEHTML?=
 =?UTF-8?q?=E3=81=A8=E5=99=9B=E3=81=BF=E5=90=88=E3=82=8F=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E9=83=A8=E5=88=86=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12605)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/index.html | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/index.html b/packages/frontend/src/index.html
index 4db52747a8..4489f351f8 100644
--- a/packages/frontend/src/index.html
+++ b/packages/frontend/src/index.html
@@ -21,10 +21,11 @@
 		  style-src 'self' 'unsafe-inline';
 		  img-src 'self' data: www.google.com xn--931a.moe localhost:3000 localhost:5137 127.0.0.1:5173 127.0.0.1:3000"
 	/>
+	<meta property="og:site_name" content="[DEV BUILD] Misskey" />
 </head>
 
 <body>
-<div id="app"></div>
+<div id="misskey_app"></div>
 <script type="module" src="./_dev_boot_.ts"></script>
 </body>
 </html>

From ab5d2eca1fbdbd17a4913229cd5812337d9ed7be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?=
 =?UTF-8?q?=E3=81=AB=E3=82=85?=
 <17376330+u1-liquid@users.noreply.github.com>
Date: Fri, 8 Dec 2023 17:48:18 +0900
Subject: [PATCH 171/435] =?UTF-8?q?enhance(frontend):=20window.open?=
 =?UTF-8?q?=E3=82=84a=E3=82=BF=E3=82=B0=E3=81=ABnoopener=E3=82=AA=E3=83=97?=
 =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E3=81=A4=E3=81=91=E3=82=8B?=
 =?UTF-8?q?=20(MisskeyIO#283)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkGoogle.vue          |  2 +-
 packages/frontend/src/components/MkLink.vue            |  2 +-
 packages/frontend/src/components/MkPageWindow.vue      |  2 +-
 .../frontend/src/components/MkVisitorDashboard.vue     | 10 +++++-----
 packages/frontend/src/components/global/MkA.vue        |  2 +-
 packages/frontend/src/components/global/MkUrl.vue      |  2 +-
 packages/frontend/src/pages/admin-file.vue             |  2 +-
 packages/frontend/src/pages/admin/queue.vue            |  2 +-
 packages/frontend/src/pages/instance-info.vue          |  2 +-
 packages/frontend/src/plugin.ts                        |  2 +-
 packages/frontend/src/scripts/get-note-menu.ts         |  4 ++--
 packages/frontend/src/ui/_common_/common.ts            |  8 ++++----
 12 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/packages/frontend/src/components/MkGoogle.vue b/packages/frontend/src/components/MkGoogle.vue
index efbd775f5c..fb142b31b5 100644
--- a/packages/frontend/src/components/MkGoogle.vue
+++ b/packages/frontend/src/components/MkGoogle.vue
@@ -23,7 +23,7 @@ const query = ref(props.q);
 const search = () => {
 	const sp = new URLSearchParams();
 	sp.append('q', query.value);
-	window.open(`https://www.google.com/search?${sp.toString()}`, '_blank');
+	window.open(`https://www.google.com/search?${sp.toString()}`, '_blank', 'noopener');
 };
 </script>
 
diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue
index 808a071d10..8517eff40b 100644
--- a/packages/frontend/src/components/MkLink.vue
+++ b/packages/frontend/src/components/MkLink.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <component
-	:is="self ? 'MkA' : 'a'" ref="el" style="word-break: break-all;" class="_link" :[attr]="self ? url.substring(local.length) : url" :rel="rel" :target="target"
+	:is="self ? 'MkA' : 'a'" ref="el" style="word-break: break-all;" class="_link" :[attr]="self ? url.substring(local.length) : url" :rel="rel ?? 'nofollow noopener'" :target="target"
 	:title="url"
 >
 	<slot></slot>
diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue
index 441296e05d..23eb70ecd2 100644
--- a/packages/frontend/src/components/MkPageWindow.vue
+++ b/packages/frontend/src/components/MkPageWindow.vue
@@ -112,7 +112,7 @@ const contextmenu = computed(() => ([{
 	icon: 'ti ti-external-link',
 	text: i18n.ts.openInNewTab,
 	action: () => {
-		window.open(url + router.getCurrentPath(), '_blank');
+		window.open(url + router.getCurrentPath(), '_blank', 'noopener');
 		windowEl.value.close();
 	},
 }, {
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 7a41720e3f..102cb8d139 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -107,31 +107,31 @@ function showMenu(ev) {
 		text: i18n.ts.impressum,
 		icon: 'ti ti-file-invoice',
 		action: () => {
-			window.open(instance.impressumUrl, '_blank');
+			window.open(instance.impressumUrl, '_blank', 'noopener');
 		},
 	} : undefined, (instance.tosUrl) ? {
 		text: i18n.ts.termsOfService,
 		icon: 'ti ti-notebook',
 		action: () => {
-			window.open(instance.tosUrl, '_blank');
+			window.open(instance.tosUrl, '_blank', 'noopener');
 		},
 	} : undefined, (instance.privacyPolicyUrl) ? {
 		text: i18n.ts.privacyPolicy,
 		icon: 'ti ti-shield-lock',
 		action: () => {
-			window.open(instance.privacyPolicyUrl, '_blank');
+			window.open(instance.privacyPolicyUrl, '_blank', 'noopener');
 		},
 	} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : null, {
 		text: i18n.ts.help,
 		icon: 'ti ti-help-circle',
 		action: () => {
-			window.open('https://misskey-hub.net/help.md', '_blank');
+			window.open('https://misskey-hub.net/help.md', '_blank', 'noopener');
 		},
 	}], ev.currentTarget ?? ev.target);
 }
 
 function exploreOtherServers() {
-	window.open('https://join.misskey.page/instances', '_blank');
+	window.open('https://join.misskey.page/instances', '_blank', 'noopener');
 }
 </script>
 
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index 008d10f8eb..809dae421a 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -61,7 +61,7 @@ function onContextmenu(ev) {
 		icon: 'ti ti-external-link',
 		text: i18n.ts.openInNewTab,
 		action: () => {
-			window.open(props.to, '_blank');
+			window.open(props.to, '_blank', 'noopener');
 		},
 	}, {
 		icon: 'ti ti-link',
diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue
index db8a8399b5..9a59b5a68e 100644
--- a/packages/frontend/src/components/global/MkUrl.vue
+++ b/packages/frontend/src/components/global/MkUrl.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <component
-	:is="self ? 'MkA' : 'a'" ref="el" :class="$style.root" class="_link" :[attr]="self ? props.url.substring(local.length) : props.url" :rel="rel" :target="target"
+	:is="self ? 'MkA' : 'a'" ref="el" :class="$style.root" class="_link" :[attr]="self ? props.url.substring(local.length) : props.url" :rel="rel ?? 'nofollow noopener'" :target="target"
 	@contextmenu.stop="() => {}"
 >
 	<template v-if="!self">
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue
index aefff69c60..dd61aea4c7 100644
--- a/packages/frontend/src/pages/admin-file.vue
+++ b/packages/frontend/src/pages/admin-file.vue
@@ -120,7 +120,7 @@ const headerActions = computed(() => [{
 	text: i18n.ts.openInNewTab,
 	icon: 'ti ti-external-link',
 	handler: () => {
-		window.open(file.value.url, '_blank');
+		window.open(file.value.url, '_blank', 'noopener');
 	},
 }]);
 
diff --git a/packages/frontend/src/pages/admin/queue.vue b/packages/frontend/src/pages/admin/queue.vue
index f07fba8d15..5a8f960cf6 100644
--- a/packages/frontend/src/pages/admin/queue.vue
+++ b/packages/frontend/src/pages/admin/queue.vue
@@ -56,7 +56,7 @@ const headerActions = computed(() => [{
 	icon: 'ti ti-external-link',
 	text: i18n.ts.dashboard,
 	handler: () => {
-		window.open(config.url + '/queue', '_blank');
+		window.open(config.url + '/queue', '_blank', 'noopener');
 	},
 }]);
 
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 93d74fb42e..97dc0a8633 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -218,7 +218,7 @@ const headerActions = computed(() => [{
 	text: `https://${props.host}`,
 	icon: 'ti ti-external-link',
 	handler: () => {
-		window.open(`https://${props.host}`, '_blank');
+		window.open(`https://${props.host}`, '_blank', 'noopener');
 	},
 }]);
 
diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts
index e24f646a35..5e49af4858 100644
--- a/packages/frontend/src/plugin.ts
+++ b/packages/frontend/src/plugin.ts
@@ -96,7 +96,7 @@ function createPluginEnv(opts: { plugin: Plugin; storageKey: string }): Record<s
 		}),
 		'Plugin:open_url': values.FN_NATIVE(([url]) => {
 			utils.assertString(url);
-			window.open(url.value, '_blank');
+			window.open(url.value, '_blank', 'noopener');
 		}),
 		'Plugin:config': values.OBJ(config),
 	};
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 763f6ff513..14ada9b7f0 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -278,7 +278,7 @@ export function getNoteMenu(props: {
 				icon: 'ti ti-external-link',
 				text: i18n.ts.showOnRemote,
 				action: () => {
-					window.open(appearNote.url ?? appearNote.uri, '_blank');
+					window.open(appearNote.url ?? appearNote.uri, '_blank', 'noopener');
 				},
 			} : undefined,
 			...(isSupportShare() ? [{
@@ -382,7 +382,7 @@ export function getNoteMenu(props: {
 			icon: 'ti ti-external-link',
 			text: i18n.ts.showOnRemote,
 			action: () => {
-				window.open(appearNote.url ?? appearNote.uri, '_blank');
+				window.open(appearNote.url ?? appearNote.uri, '_blank', 'noopener');
 			},
 		} : undefined]
 			.filter(x => x !== undefined);
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index 64008c5748..48a1144df7 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -83,25 +83,25 @@ export function openInstanceMenu(ev: MouseEvent) {
 		text: i18n.ts.impressum,
 		icon: 'ti ti-file-invoice',
 		action: () => {
-			window.open(instance.impressumUrl, '_blank');
+			window.open(instance.impressumUrl, '_blank', 'noopener');
 		},
 	} : undefined, (instance.tosUrl) ? {
 		text: i18n.ts.termsOfService,
 		icon: 'ti ti-notebook',
 		action: () => {
-			window.open(instance.tosUrl, '_blank');
+			window.open(instance.tosUrl, '_blank', 'noopener');
 		},
 	} : undefined, (instance.privacyPolicyUrl) ? {
 		text: i18n.ts.privacyPolicy,
 		icon: 'ti ti-shield-lock',
 		action: () => {
-			window.open(instance.privacyPolicyUrl, '_blank');
+			window.open(instance.privacyPolicyUrl, '_blank', 'noopener');
 		},
 	} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : null, {
 		text: i18n.ts.help,
 		icon: 'ti ti-help-circle',
 		action: () => {
-			window.open('https://misskey-hub.net/help.html', '_blank');
+			window.open('https://misskey-hub.net/help.html', '_blank', 'noopener');
 		},
 	}, ($i) ? {
 		text: i18n.ts._initialTutorial.launchTutorial,

From d10048edac6a9d7f33bff40562a3fb8c230dbd05 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Fri, 8 Dec 2023 20:16:15 +0900
Subject: [PATCH 172/435] chore: fix labeler's config (#12609)

---
 .github/labeler.yml | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/.github/labeler.yml b/.github/labeler.yml
index 137be487c0..ae0ca1704e 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -1,21 +1,27 @@
 'packages/backend':
-- packages/backend/**/*
+- any:
+  - changed-files: ['packages/backend/**/*']
 
 'packages/backend:test':
-- packages/backend/test/**/*
+- any:
+  - changed-files: ['packages/backend/test/**/*']
 
 'packages/frontend':
-- packages/frontend/**/*
+- any:
+  - changed-files: ['packages/frontend/**/*']
 
 'packages/frontend:test':
-- cypress/**/*
+- any:
+  - changed-files: ['cypress/**/*']
 
 'packages/sw':
-- packages/sw/**/*
+- any:
+  - changed-files: ['packages/sw/**/*']
 
 'packages/misskey-js':
-- packages/misskey-js/**/*
+- any:
+  - changed-files: ['packages/misskey-js/**/*']
 
 'packages/misskey-js:test':
-- packages/misskey-js/test/**/*
-- packages/misskey-js/test-d/**/*
+- any:
+  - changed-files: ['packages/misskey-js/test/**/*', 'packages/misskey-js/test-d/**/*']

From 2c6fc0ba63d0c1d8dd7946a8a2667b5ffbfc037b 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, 8 Dec 2023 20:16:49 +0900
Subject: [PATCH 173/435] =?UTF-8?q?fix(dev-frontend)=20=E8=B6=B3=E3=82=8A?=
 =?UTF-8?q?=E3=81=A6=E3=81=AA=E3=81=84CSP=E3=82=92=E8=BF=BD=E5=8A=A0=20(#1?=
 =?UTF-8?q?2606)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(dev-frontend) サーバーサイドのHTMLと噛み合わない部分を修正

* cspをなおした

* typo
---
 packages/frontend/src/index.html | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/index.html b/packages/frontend/src/index.html
index 4489f351f8..b93e32265e 100644
--- a/packages/frontend/src/index.html
+++ b/packages/frontend/src/index.html
@@ -19,7 +19,8 @@
 		content="default-src 'self';
 		  script-src 'self';
 		  style-src 'self' 'unsafe-inline';
-		  img-src 'self' data: www.google.com xn--931a.moe localhost:3000 localhost:5137 127.0.0.1:5173 127.0.0.1:3000"
+		  img-src 'self' data: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
+			media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;"
 	/>
 	<meta property="og:site_name" content="[DEV BUILD] Misskey" />
 </head>

From 05166f79e119023420d5f77720defaf07a9c2000 Mon Sep 17 00:00:00 2001
From: ShittyKopper <shittykopper@w.on-t.work>
Date: Thu, 7 Dec 2023 16:54:31 +0300
Subject: [PATCH 174/435] upd: add option for reply depth

---
 locales/en-US.yml                                           | 2 ++
 locales/index.d.ts                                          | 2 ++
 locales/ja-JP.yml                                           | 2 ++
 packages/frontend/src/components/MkNoteSub.vue              | 5 +++--
 packages/frontend/src/components/SkNoteSub.vue              | 5 +++--
 packages/frontend/src/pages/settings/general.vue            | 6 ++++++
 .../frontend/src/pages/settings/preferences-backups.vue     | 1 +
 packages/frontend/src/store.ts                              | 4 ++++
 8 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 6a8227511e..b9017317ed 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -944,6 +944,8 @@ approvalStatus: "Approval Status"
 document: "Documentation"
 numberOfPageCache: "Number of cached pages"
 numberOfPageCacheDescription: "Increasing this number will improve convenience for but cause more load as more memory usage on the user's device."
+numberOfReplies: "Number of replies in a thread"
+numberOfRepliesDescription: "Increasing this number will display more replies. Setting this too high can cause replies to be cramped and unreadable."
 logoutConfirm: "Really log out?"
 lastActiveDate: "Last used at"
 statusbar: "Status bar"
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 98ab44dd41..6181e08b56 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -947,6 +947,8 @@ export interface Locale {
     "document": string;
     "numberOfPageCache": string;
     "numberOfPageCacheDescription": string;
+    "numberOfReplies": string;
+    "numberOfRepliesDescription": string;
     "logoutConfirm": string;
     "lastActiveDate": string;
     "statusbar": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0af37e041f..5fd94e5340 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -944,6 +944,8 @@ approvalStatus: "承認状況"
 document: "ドキュメント"
 numberOfPageCache: "ページキャッシュ数"
 numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。"
+numberOfReplies: ""
+numberOfRepliesDescription: ""
 logoutConfirm: "ログアウトしますか?"
 lastActiveDate: "最終利用日時"
 statusbar: "ステータスバー"
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 384a85e546..5b1e1af308 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</footer>
 		</div>
 	</div>
-	<template v-if="depth < 5">
+	<template v-if="depth < numberOfReplies">
 		<MkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="$style.reply" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws"/>
 	</template>
 	<div v-else :class="$style.more">
@@ -124,6 +124,7 @@ const translation = ref<any>(null);
 const translating = ref(false);
 const isDeleted = ref(false);
 const renoted = ref(false);
+const numberOfReplies = ref(defaultStore.state.numberOfReplies);
 const reactButton = shallowRef<HTMLElement>();
 const renoteButton = shallowRef<HTMLElement>();
 const quoteButton = shallowRef<HTMLElement>();
@@ -390,7 +391,7 @@ function menu(viaKeyboard = false): void {
 if (props.detail) {
 	os.api('notes/children', {
 		noteId: props.note.id,
-		limit: 5,
+		limit: numberOfReplies.value,
 		showQuotes: false,
 	}).then(res => {
 		replies = res;
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index 64c71efd4e..dd4abe8f58 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -72,7 +72,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</footer>
 		</div>
 	</div>
-	<template v-if="depth < 5">
+	<template v-if="depth < numberOfReplies">
 		<SkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="[$style.reply, { [$style.single]: replies.length === 1 }]" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws"/>
 	</template>
 	<div v-else :class="$style.more">
@@ -133,6 +133,7 @@ const translation = ref<any>(null);
 const translating = ref(false);
 const isDeleted = ref(false);
 const renoted = ref(false);
+const numberOfReplies = ref(defaultStore.state.numberOfReplies);
 const reactButton = shallowRef<HTMLElement>();
 const renoteButton = shallowRef<HTMLElement>();
 const quoteButton = shallowRef<HTMLElement>();
@@ -399,7 +400,7 @@ function menu(viaKeyboard = false): void {
 if (props.detail) {
 	os.api('notes/children', {
 		noteId: props.note.id,
-		limit: 5,
+		limit: numberOfReplies.value,
 		showQuotes: false,
 	}).then(res => {
 		replies = res;
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 38a1b5c2a7..419ea13713 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -89,6 +89,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<option value="1_1">{{ i18n.t('limitTo', { x: '1:1' }) }}</option>
 				<option value="2_3">{{ i18n.t('limitTo', { x: '2:3' }) }}</option>
 			</MkRadios>
+
+			<MkRange v-model="numberOfReplies" :min="2" :max="20" :step="1" easing>
+				<template #label>{{ i18n.ts.numberOfReplies }}</template>
+				<template #caption>{{ i18n.ts.numberOfRepliesDescription }}</template>
+			</MkRange>
 		</div>
 	</FormSection>
 
@@ -268,6 +273,7 @@ const nsfw = computed(defaultStore.makeGetterSetter('nsfw'));
 const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm'));
 const showFixedPostFormInChannel = computed(defaultStore.makeGetterSetter('showFixedPostFormInChannel'));
 const numberOfPageCache = computed(defaultStore.makeGetterSetter('numberOfPageCache'));
+const numberOfReplies = computed(defaultStore.makeGetterSetter('numberOfReplies'));
 const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker'));
 const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll'));
 const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu'));
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index 3f038f8293..f7768ebe5c 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -90,6 +90,7 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
 	'reportError',
 	'squareAvatars',
 	'numberOfPageCache',
+	'numberOfReplies',
 	'aiChanMode',
 	'mediaListWithOneImageAppearance',
 ];
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index eb16222968..a53336b5f7 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -346,6 +346,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: 3,
 	},
+	numberOfReplies: {
+		where: 'device',
+		default: 5,
+	},
 	showNoteActionsOnlyHover: {
 		where: 'device',
 		default: false,

From 26ec7f4e7c07c9f6ead30bd72ff78271eaf6d507 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Fri, 8 Dec 2023 12:39:29 +0000
Subject: [PATCH 175/435] japanese translations

---
 locales/ja-JP.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 5fd94e5340..6006487a9c 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -944,8 +944,8 @@ approvalStatus: "承認状況"
 document: "ドキュメント"
 numberOfPageCache: "ページキャッシュ数"
 numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。"
-numberOfReplies: ""
-numberOfRepliesDescription: ""
+numberOfReplies: "スレッド内の返信数"
+numberOfRepliesDescription: "この数値を大きくすると、より多くの返信が表示されます。この値を大きくしすぎると、返信が窮屈になり、読めなくなることがあります。"
 logoutConfirm: "ログアウトしますか?"
 lastActiveDate: "最終利用日時"
 statusbar: "ステータスバー"

From 6c1f839cbe1506f2e72bd606c1b96153555d21f4 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sat, 9 Dec 2023 09:54:43 +0900
Subject: [PATCH 176/435] =?UTF-8?q?chore:=20labeler=E3=81=8C=E6=B2=BB?=
 =?UTF-8?q?=E3=81=A3=E3=81=A6=E3=81=84=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F?=
 =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12610)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix ci

* fix

* fix labeler.yml

* Revert "fix labeler.yml"

This reverts commit 9b6a7d02cdd65ab9ad14c666c6ebe4eadeabeadc.

---------

Co-authored-by: samunohito <46447427+samunohito@users.noreply.github.com>
---
 .github/labeler.yml | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/.github/labeler.yml b/.github/labeler.yml
index ae0ca1704e..a77f73706b 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -1,27 +1,34 @@
 'packages/backend':
 - any:
-  - changed-files: ['packages/backend/**/*']
+  - changed-files:
+    - any-glob-to-any-file: ['packages/backend/**/*']
 
 'packages/backend:test':
 - any:
-  - changed-files: ['packages/backend/test/**/*']
+  - changed-files:
+    - any-glob-to-any-file: ['packages/backend/test/**/*']
 
 'packages/frontend':
 - any:
-  - changed-files: ['packages/frontend/**/*']
+  - changed-files:
+    - any-glob-to-any-file: ['packages/frontend/**/*']
 
 'packages/frontend:test':
 - any:
-  - changed-files: ['cypress/**/*']
+  - changed-files:
+    - any-glob-to-any-file: ['cypress/**/*']
 
 'packages/sw':
 - any:
-  - changed-files: ['packages/sw/**/*']
+  - changed-files:
+    - any-glob-to-any-file: ['packages/sw/**/*']
 
 'packages/misskey-js':
 - any:
-  - changed-files: ['packages/misskey-js/**/*']
+  - changed-files:
+    - any-glob-to-any-file: ['packages/misskey-js/**/*']
 
 'packages/misskey-js:test':
 - any:
-  - changed-files: ['packages/misskey-js/test/**/*', 'packages/misskey-js/test-d/**/*']
+  - changed-files:
+    - any-glob-to-any-file: ['packages/misskey-js/test/**/*', 'packages/misskey-js/test-d/**/*']

From 42e33c18e6f5b19b1e3a6505931ad79e40f1f632 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:30:27 +0000
Subject: [PATCH 177/435] migrate PR template

---
 .forgejo/pull_request_template.md | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 .forgejo/pull_request_template.md

diff --git a/.forgejo/pull_request_template.md b/.forgejo/pull_request_template.md
new file mode 100644
index 0000000000..63eb2ab623
--- /dev/null
+++ b/.forgejo/pull_request_template.md
@@ -0,0 +1,24 @@
+<!-- ℹ お読みください / README
+PRありがとうございます! PRを作成する前に、コントリビューションガイドをご確認ください:
+Thank you for your PR! Before creating a PR, please check the contribution guide:
+https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md
+-->
+	
+## What
+<!-- このPRで何をしたのか? どう変わるのか? -->
+<!-- What did you do with this PR? How will it change things? -->
+	
+## Why
+<!-- なぜそうするのか? どういう意図なのか? 何が困っているのか? -->
+<!-- Why do you do it? What are your intentions? What is the problem? -->
+	
+## Additional info (optional)
+<!-- テスト観点など -->
+<!-- Test perspective, etc -->
+	
+## Checklist
+- [ ] Read the [contribution guide](https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md)
+- [ ] Test working in a local environment
+- [ ] (If needed) Add story of storybook
+- [ ] (If needed) Update CHANGELOG.md
+- [ ] (If possible) Add tests
\ No newline at end of file

From 1c054377845869beb3fd1279069142db2feb630f Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:33:53 +0000
Subject: [PATCH 178/435] migrate feat issue template

---
 .forgejo/ISSUE_TEMPLATE/02_feature-request.md | 10 ++++++++++
 1 file changed, 10 insertions(+)
 create mode 100644 .forgejo/ISSUE_TEMPLATE/02_feature-request.md

diff --git a/.forgejo/ISSUE_TEMPLATE/02_feature-request.md b/.forgejo/ISSUE_TEMPLATE/02_feature-request.md
new file mode 100644
index 0000000000..db73f0dc7f
--- /dev/null
+++ b/.forgejo/ISSUE_TEMPLATE/02_feature-request.md
@@ -0,0 +1,10 @@
+---
+name: "Feature Request"
+about: "Suggest an idea for this project"
+title: "feat: "
+---
+## Summary
+<!-- Tell us what the suggestion is -->
+
+## Purpose
+<!-- Describe the specific problem or need you think this feature will solve, and who it will help. -->
\ No newline at end of file

From 04cbd850ca7eaf64ca437964653a41f6d071e5be Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:36:42 +0000
Subject: [PATCH 179/435] Update .gitea/ISSUE_TEMPLATE/02_feature-request.md

---
 {.forgejo => .gitea}/ISSUE_TEMPLATE/02_feature-request.md | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename {.forgejo => .gitea}/ISSUE_TEMPLATE/02_feature-request.md (100%)

diff --git a/.forgejo/ISSUE_TEMPLATE/02_feature-request.md b/.gitea/ISSUE_TEMPLATE/02_feature-request.md
similarity index 100%
rename from .forgejo/ISSUE_TEMPLATE/02_feature-request.md
rename to .gitea/ISSUE_TEMPLATE/02_feature-request.md

From fe56c4ee373ee26b0d806776ea83531a541c55d1 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:37:00 +0000
Subject: [PATCH 180/435] Update .gitea/pull_request_template.md

---
 {.forgejo => .gitea}/pull_request_template.md | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename {.forgejo => .gitea}/pull_request_template.md (100%)

diff --git a/.forgejo/pull_request_template.md b/.gitea/pull_request_template.md
similarity index 100%
rename from .forgejo/pull_request_template.md
rename to .gitea/pull_request_template.md

From 22f1fb069e27bc18bada4ac91417c3fe14261762 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:38:11 +0000
Subject: [PATCH 181/435] Add .gitea/ISSUE_TEMPLATE/01_bug_report.yml

---
 .gitea/ISSUE_TEMPLATE/01_bug_report.yml | 90 +++++++++++++++++++++++++
 1 file changed, 90 insertions(+)
 create mode 100644 .gitea/ISSUE_TEMPLATE/01_bug_report.yml

diff --git a/.gitea/ISSUE_TEMPLATE/01_bug_report.yml b/.gitea/ISSUE_TEMPLATE/01_bug_report.yml
new file mode 100644
index 0000000000..be6d9f401b
--- /dev/null
+++ b/.gitea/ISSUE_TEMPLATE/01_bug_report.yml
@@ -0,0 +1,90 @@
+name: 🐛 Bug Report
+description: Create a report to help us improve
+
+body:
+  - type: markdown
+    attributes:
+      value: |
+        Thanks for reporting!
+        First, in order to avoid duplicate Issues, please search to see if the problem you found has already been reported.
+        Also, If you are NOT owner/admin of server, PLEASE DONT REPORT SERVER SPECIFIC ISSUES TO HERE! (e.g. feature XXX is not working in misskey.example) Please try with another misskey servers, and if your issue is only reproducible with specific server, contact your server's owner/admin first.
+
+  - type: textarea
+    attributes:
+      label: 💡 Summary
+      description: Tell us what the bug is
+    validations:
+      required: true
+
+  - type: textarea
+    attributes:
+      label: 🥰 Expected Behavior
+      description: Tell us what should happen
+    validations:
+      required: true
+
+  - type: textarea
+    attributes:
+      label: 🤬 Actual Behavior
+      description: |
+        Tell us what happens instead of the expected behavior.
+        Please include errors from the developer console and/or server log files if you have access to them.
+    validations:
+      required: true
+
+  - type: textarea
+    attributes:
+      label: 📝 Steps to Reproduce
+      placeholder: |
+        1.
+        2.
+        3.
+    validations:
+      required: false
+
+  - type: textarea
+    attributes:
+      label: 💻 Frontend Environment
+      description: |
+        Tell us where on the platform it happens
+        DO NOT WRITE "latest". Please provide the specific version.
+
+        Examples:
+          * Model and OS of the device(s): MacBook Pro (14inch, 2021), macOS Ventura 13.4
+          * Browser: Chrome 113.0.5672.126
+          * Server URL: misskey.io
+          * Misskey: 13.x.x
+      value: |
+        * Model and OS of the device(s):
+        * Browser:
+        * Server URL:
+        * Misskey:
+      render: markdown
+    validations:
+      required: false
+
+  - type: textarea
+    attributes:
+      label: 🛰 Backend Environment (for server admin)
+      description: |
+        Tell us where on the platform it happens
+        DO NOT WRITE "latest". Please provide the specific version.
+        If you are using a managed service, put that after the version.
+
+        Examples:
+          * Installation Method or Hosting Service: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment
+          * Misskey: 13.x.x
+          * Node: 20.x.x
+          * PostgreSQL: 15.x.x
+          * Redis: 7.x.x
+          * OS and Architecture: Ubuntu 22.04.2 LTS aarch64
+      value: |
+        * Installation Method or Hosting Service:
+        * Misskey:
+        * Node:
+        * PostgreSQL:
+        * Redis:
+        * OS and Architecture:
+      render: markdown
+    validations:
+      required: false
\ No newline at end of file

From 7d16eca0be1d56068355cde455c5ec1f1ce3e6e0 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:38:25 +0000
Subject: [PATCH 182/435] Update .gitea/ISSUE_TEMPLATE/01_bug-report.yml

---
 .gitea/ISSUE_TEMPLATE/{01_bug_report.yml => 01_bug-report.yml} | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename .gitea/ISSUE_TEMPLATE/{01_bug_report.yml => 01_bug-report.yml} (100%)

diff --git a/.gitea/ISSUE_TEMPLATE/01_bug_report.yml b/.gitea/ISSUE_TEMPLATE/01_bug-report.yml
similarity index 100%
rename from .gitea/ISSUE_TEMPLATE/01_bug_report.yml
rename to .gitea/ISSUE_TEMPLATE/01_bug-report.yml

From d3065d29c822c7dd421210f2f443aa45b1b12b0c Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:38:51 +0000
Subject: [PATCH 183/435] Add .gitea/ISSUE_TEMPLATE/config.yml

---
 .gitea/ISSUE_TEMPLATE/config.yml | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 .gitea/ISSUE_TEMPLATE/config.yml

diff --git a/.gitea/ISSUE_TEMPLATE/config.yml b/.gitea/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000000..ace35b3fe0
--- /dev/null
+++ b/.gitea/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,4 @@
+contact_links:
+  - name: 💬 Transfem.org Discord
+    url: https://discord.gg/xTtXc7We
+    about: Chat freely about Sharkey
\ No newline at end of file

From 9d88343ce64a59303d42fdcafbb9856b4f2b4ab4 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:45:37 +0000
Subject: [PATCH 184/435] Add .forgejo/workflows/lint.yml

---
 .forgejo/workflows/lint.yml | 83 +++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)
 create mode 100644 .forgejo/workflows/lint.yml

diff --git a/.forgejo/workflows/lint.yml b/.forgejo/workflows/lint.yml
new file mode 100644
index 0000000000..a376cb4f73
--- /dev/null
+++ b/.forgejo/workflows/lint.yml
@@ -0,0 +1,83 @@
+name: Lint
+
+on:
+  push:
+    branches:
+      - master
+      - develop
+    paths:
+      - packages/**
+  pull_request:
+
+jobs:
+  pnpm_install:
+    runs-on: docker
+    steps:
+    - uses: actions/checkout@v4.1.1
+      with:
+        fetch-depth: 0
+        submodules: true
+    - uses: https://github.com/pnpm/action-setup@v2
+      with:
+        version: 8
+        run_install: false
+    - uses: https://code.forgejo.org/actions/setup-node@v4
+      with:
+        node-version-file: '.node-version'
+        cache: 'pnpm'
+    - run: corepack enable
+    - run: pnpm i --frozen-lockfile
+
+  lint:
+    needs: [pnpm_install]
+    runs-on: docker
+    continue-on-error: true
+    strategy:
+      matrix:
+        workspace:
+        - backend
+        - frontend
+        - sw
+        - misskey-js
+    steps:
+    - uses: actions/checkout@v4.1.1
+      with:
+        fetch-depth: 0
+        submodules: true
+    - uses: https://github.com/pnpm/action-setup@v2
+      with:
+        version: 7
+        run_install: false
+    - uses: https://code.forgejo.org/actions/setup-node@v4
+      with:
+        node-version-file: '.node-version'
+		cache: 'pnpm'
+    - run: corepack enable
+    - run: pnpm i --frozen-lockfile
+    - run: pnpm --filter ${{ matrix.workspace }} run eslint
+
+  typecheck:
+    needs: [pnpm_install]
+    runs-on: docker
+    continue-on-error: true
+    strategy:
+      matrix:
+        workspace:
+        - backend
+        - misskey-js
+    steps:
+    - uses: actions/checkout@v4.1.1
+      with:
+        fetch-depth: 0
+        submodules: true
+    - uses: https://github.com/pnpm/action-setup@v2
+      with:
+        version: 7
+        run_install: false
+    - uses: https://code.forgejo.org/actions/setup-node@v4
+      with:
+        node-version-file: '.node-version'
+		cache: 'pnpm'
+    - run: corepack enable
+    - run: pnpm i --frozen-lockfile
+    - run: pnpm --filter ${{ matrix.workspace }} run typecheck

From 36714930d19dc0da89f00887395436a5b16fb9d7 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:46:01 +0000
Subject: [PATCH 185/435] Update .forgejo/workflows/lint.yml

---
 .forgejo/workflows/lint.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.forgejo/workflows/lint.yml b/.forgejo/workflows/lint.yml
index a376cb4f73..e4945c7af3 100644
--- a/.forgejo/workflows/lint.yml
+++ b/.forgejo/workflows/lint.yml
@@ -51,7 +51,7 @@ jobs:
     - uses: https://code.forgejo.org/actions/setup-node@v4
       with:
         node-version-file: '.node-version'
-		cache: 'pnpm'
+        cache: 'pnpm'
     - run: corepack enable
     - run: pnpm i --frozen-lockfile
     - run: pnpm --filter ${{ matrix.workspace }} run eslint

From 02b10d64dd03727e7ff6da54391950d8346d8b18 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:46:12 +0000
Subject: [PATCH 186/435] Update .forgejo/workflows/lint.yml

---
 .forgejo/workflows/lint.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.forgejo/workflows/lint.yml b/.forgejo/workflows/lint.yml
index e4945c7af3..d2d11d3d4e 100644
--- a/.forgejo/workflows/lint.yml
+++ b/.forgejo/workflows/lint.yml
@@ -77,7 +77,7 @@ jobs:
     - uses: https://code.forgejo.org/actions/setup-node@v4
       with:
         node-version-file: '.node-version'
-		cache: 'pnpm'
+        cache: 'pnpm'
     - run: corepack enable
     - run: pnpm i --frozen-lockfile
     - run: pnpm --filter ${{ matrix.workspace }} run typecheck

From de70b58b394c125e5eee47113e7381a30df37abe Mon Sep 17 00:00:00 2001
From: Amelia Yukii <admin@transfem.org>
Date: Sat, 9 Dec 2023 01:49:19 +0000
Subject: [PATCH 187/435] Update .forgejo/workflows/lint.yml

Signed-off-by: Amelia Yukii <admin@transfem.org>
---
 .forgejo/workflows/lint.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.forgejo/workflows/lint.yml b/.forgejo/workflows/lint.yml
index d2d11d3d4e..132c6c455d 100644
--- a/.forgejo/workflows/lint.yml
+++ b/.forgejo/workflows/lint.yml
@@ -3,7 +3,7 @@ name: Lint
 on:
   push:
     branches:
-      - master
+      - stable
       - develop
     paths:
       - packages/**

From 614238ebb1fe83384be6e7513c054bb16b95e827 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:52:08 +0000
Subject: [PATCH 188/435] Add .forgejo/workflows/docker-develop.yml

---
 .forgejo/workflows/docker-develop.yml | 58 +++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)
 create mode 100644 .forgejo/workflows/docker-develop.yml

diff --git a/.forgejo/workflows/docker-develop.yml b/.forgejo/workflows/docker-develop.yml
new file mode 100644
index 0000000000..0c8338c4df
--- /dev/null
+++ b/.forgejo/workflows/docker-develop.yml
@@ -0,0 +1,58 @@
+name: Publish Docker image (develop)
+
+on:
+  push:
+    branches:
+      - develop
+    paths:
+      - packages/**
+      - locales/**
+  workflow_dispatch:
+
+env:
+  REGISTRY: git.joinsharkey.org
+
+jobs:
+  push_to_registry:
+    name: Push Docker image to GHCR
+    runs-on: docker
+    steps:
+      - name: install packages
+        run: apt-get update && apt-get install -y wget git curl
+      - uses: https://code.forgejo.org/actions/setup-node@v3
+        with:
+          node-version: 20
+      - name: Install docker
+        run: |
+          echo deb http://deb.debian.org/debian bullseye-backports main | tee /etc/apt/sources.list.d/backports.list && apt-get -qq update
+          DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -qq -y -t bullseye-backports docker.io
+      - name: Check out the repo
+        uses: actions/checkout@v4.1.1
+      - name: Set up Docker Buildx
+        id: buildx
+        uses: https://github.com/docker/setup-buildx-action@v3.0.0
+        with:
+          platforms: linux/amd64,linux/arm64
+      - name: Docker meta
+        id: meta
+        uses: https://github.com/docker/metadata-action@v5
+        with:
+          images: ${{ env.REGISTRY }}/sharkey/sharkey
+      - name: Log in to GHCR
+        uses: https://github.com/docker/login-action@v3
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: Marie
+          password: ${{ secrets.TOKEN }}
+      - name: Build and Push to GHCR
+        id: build
+        uses: https://github.com/docker/build-push-action@v5
+        with:
+          builder: ${{ steps.buildx.outputs.name }}
+          context: .
+          push: true
+          platforms: ${{ steps.buildx.outputs.platforms }}
+          provenance: false
+          tags:  ${{ env.REGISTRY }}/sharkey/sharkey:develop
+          labels: develop
+          build-args: NODE_ENV=development
\ No newline at end of file

From 97a66d1da391f4da2812672b0a9a187e59281d45 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 9 Dec 2023 01:56:16 +0000
Subject: [PATCH 189/435] Add .forgejo/workflows/docker.yml

---
 .forgejo/workflows/docker.yml | 65 +++++++++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 .forgejo/workflows/docker.yml

diff --git a/.forgejo/workflows/docker.yml b/.forgejo/workflows/docker.yml
new file mode 100644
index 0000000000..b9dce0a155
--- /dev/null
+++ b/.forgejo/workflows/docker.yml
@@ -0,0 +1,65 @@
+name: Publish Docker image
+
+on:
+  push:
+    branches:
+      - stable
+    paths:
+      - packages/**
+      - locales/**
+  workflow_dispatch:
+
+env:
+  REGISTRY: git.joinsharkey.org
+
+jobs:
+  push_to_registry:
+    name: Push Docker image to GHCR
+    runs-on: docker
+
+    steps:
+      - name: install packages
+        run: apt-get update && apt-get install -y wget git curl
+      - uses: https://code.forgejo.org/actions/setup-node@v3
+        with:
+          node-version: 20
+      - name: Install docker
+        run: |
+          echo deb http://deb.debian.org/debian bullseye-backports main | tee /etc/apt/sources.list.d/backports.list && apt-get -qq update
+          DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -qq -y -t bullseye-backports docker.io
+      - name: Check out the repo
+        uses: actions/checkout@v4.1.1
+      - name: Set up Docker Buildx
+        id: buildx
+        uses: https://github.com/docker/setup-buildx-action@v3.0.0
+        with:
+          platforms: linux/amd64,linux/arm64
+      - name: Docker meta
+        id: meta
+        uses: https://github.com/docker/metadata-action@v5
+        with:
+          images: ${{ env.REGISTRY }}/sharkey/sharkey
+          tags: |
+            type=edge
+            type=ref,event=pr
+            type=ref,event=branch
+            type=semver,pattern={{version}}
+            type=semver,pattern={{major}}.{{minor}}
+            type=semver,pattern={{major}}
+      - name: Log in to GHCR
+        uses: https://github.com/docker/login-action@v3
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: Marie
+          password: ${{ secrets.TOKEN }}
+      - name: Build and Push to GHCR
+        id: build
+        uses: https://github.com/docker/build-push-action@v5
+        with:
+          builder: ${{ steps.buildx.outputs.name }}
+          context: .
+          push: true
+          platforms: ${{ steps.buildx.outputs.platforms }}
+          provenance: false
+          tags: ${{ env.REGISTRY }}/sharkey/sharkey:stable
+          labels: stable
\ No newline at end of file

From 702fc6b6f739e7a86db3b2bddc772a88c7fa8776 Mon Sep 17 00:00:00 2001
From: Insert5StarName <anime@shourai.de>
Date: Sat, 9 Dec 2023 03:01:42 +0100
Subject: [PATCH 190/435] chore: update repo links

---
 package.json                                              | 2 +-
 packages/backend/migration/1557761316509-AddSomeUrls.js   | 4 ++--
 packages/backend/src/server/web/views/base.pug            | 4 ++--
 packages/backend/src/server/web/views/error.pug           | 6 +++---
 packages/frontend/src/components/MkSignupDialog.rules.vue | 2 +-
 packages/frontend/src/components/MkUpdated.vue            | 2 +-
 packages/frontend/src/pages/about-sharkey.vue             | 2 +-
 7 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/package.json b/package.json
index df95e29ad5..fbc9728b30 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
 	"codename": "shonk",
 	"repository": {
 		"type": "git",
-		"url": "https://github.com/transfem-org/sharkey.git"
+		"url": "https://git.joinsharkey.org/Sharkey/Sharkey.git"
 	},
 	"packageManager": "pnpm@8.10.5",
 	"workspaces": [
diff --git a/packages/backend/migration/1557761316509-AddSomeUrls.js b/packages/backend/migration/1557761316509-AddSomeUrls.js
index 0eef193281..b83ce2ed5e 100644
--- a/packages/backend/migration/1557761316509-AddSomeUrls.js
+++ b/packages/backend/migration/1557761316509-AddSomeUrls.js
@@ -6,8 +6,8 @@
 export class AddSomeUrls1557761316509 {
     async up(queryRunner) {
         await queryRunner.query(`ALTER TABLE "meta" ADD "ToSUrl" character varying(512)`);
-        await queryRunner.query(`ALTER TABLE "meta" ADD "repositoryUrl" character varying(512) NOT NULL DEFAULT 'https://github.com/transfem-org/sharkey'`);
-        await queryRunner.query(`ALTER TABLE "meta" ADD "feedbackUrl" character varying(512) DEFAULT 'https://github.com/transfem-org/sharkey/issues/new'`);
+        await queryRunner.query(`ALTER TABLE "meta" ADD "repositoryUrl" character varying(512) NOT NULL DEFAULT 'https://git.joinsharkey.org/Sharkey/Sharkey'`);
+        await queryRunner.query(`ALTER TABLE "meta" ADD "feedbackUrl" character varying(512) DEFAULT 'https://git.joinsharkey.org/Sharkey/Sharkey/issues/new/choose'`);
     }
     async down(queryRunner) {
         await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "feedbackUrl"`);
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 018b0bed16..5e6f6cedd6 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -9,7 +9,7 @@ doctype html
 	-
 	  _____ _                _
 	 /  ___| |              | |
-	 \ `--.| |__   __ _ _ __| | _____ _   _ 
+	 \ `--.| |__   __ _ _ __| | _____ _   _
 	  `--. \ '_ \ / _` | '__| |/ / _ \ | | |
 	 /\__/ / | | | (_| | |  |   <  __/ |_| |
 	 \____/|_| |_|\__,_|_|  |_|\_\___|\__, |
@@ -18,7 +18,7 @@ doctype html
 
 	 Thank you for using Sharkey!
 	 If you are reading this message... how about joining the development?
-	 https://github.com/transfem-org/sharkey
+	 https://git.joinsharkey.org/Sharkey/Sharkey
 
 
 html
diff --git a/packages/backend/src/server/web/views/error.pug b/packages/backend/src/server/web/views/error.pug
index 679970a0a5..00a2a72d7a 100644
--- a/packages/backend/src/server/web/views/error.pug
+++ b/packages/backend/src/server/web/views/error.pug
@@ -4,7 +4,7 @@ doctype html
 	-
 	  _____ _                _
 	 /  ___| |              | |
-	 \ `--.| |__   __ _ _ __| | _____ _   _ 
+	 \ `--.| |__   __ _ _ __| | _____ _   _
 	  `--. \ '_ \ / _` | '__| |/ / _ \ | | |
 	 /\__/ / | | | (_| | |  |   <  __/ |_| |
 	 \____/|_| |_|\__,_|_|  |_|\_\___|\__, |
@@ -13,8 +13,8 @@ doctype html
 
 	 Thank you for using Sharkey!
 	 If you are reading this message... how about joining the development?
-	 https://github.com/transfem-org/sharkey
-	 
+	 https://git.joinsharkey.org/Sharkey/Sharkey
+
 
 html
 
diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue
index 7d044a1289..09eac0732a 100644
--- a/packages/frontend/src/components/MkSignupDialog.rules.vue
+++ b/packages/frontend/src/components/MkSignupDialog.rules.vue
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<template #label>{{ i18n.ts.basicNotesBeforeCreateAccount }}</template>
 				<template #suffix><i v-if="agreeNote" class="ph-check ph-bold ph-lg" style="color: var(--success)"></i></template>
 
-				<a href="https://github.com/transfem-org/JoinSharkey/blob/main/IMPORTANT_NOTES.md" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }} <i class="ph-arrow-square-out ph-bold ph-lg"></i></a>
+				<a href="https://git.joinsharkey.org/Sharkey/JoinSharkey/src/branch/main/IMPORTANT_NOTES.md" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }} <i class="ph-arrow-square-out ph-bold ph-lg"></i></a>
 
 				<MkSwitch :modelValue="agreeNote" style="margin-top: 16px;" data-cy-signup-rules-notes-agree @update:modelValue="updateAgreeNote">{{ i18n.ts.agree }}</MkSwitch>
 			</MkFolder>
diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue
index b442672af1..07efaf8982 100644
--- a/packages/frontend/src/components/MkUpdated.vue
+++ b/packages/frontend/src/components/MkUpdated.vue
@@ -27,7 +27,7 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
 
 const whatIsNew = () => {
 	modal.value.close();
-	window.open(`https://github.com/transfem-org/Sharkey/releases/tag/${version}`, '_blank');
+	window.open(`https://git.joinsharkey.org/Sharkey/Sharkey/releases/tag/${version}`, '_blank');
 };
 
 onMounted(() => {
diff --git a/packages/frontend/src/pages/about-sharkey.vue b/packages/frontend/src/pages/about-sharkey.vue
index 0425201bc5..be9b04ee69 100644
--- a/packages/frontend/src/pages/about-sharkey.vue
+++ b/packages/frontend/src/pages/about-sharkey.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 				<FormSection>
 					<div class="_gaps_s">
-						<FormLink to="https://github.com/transfem-org/Sharkey" external>
+						<FormLink to="https://git.joinsharkey.org/Sharkey/Sharkey" external>
 							<template #icon><i class="ph-code ph-bold ph-lg"></i></template>
 							{{ i18n.ts._aboutMisskey.source }}
 							<template #suffix>GitHub</template>

From 5bde0a4a451fe3f13190871456689795e736ad9a Mon Sep 17 00:00:00 2001
From: Insert5StarName <anime@shourai.de>
Date: Sat, 9 Dec 2023 03:42:22 +0100
Subject: [PATCH 191/435] chore: remove .github

---
 .github/FUNDING.yml                           |   4 -
 .github/ISSUE_TEMPLATE/01_bug-report.yml      |  91 ---------
 .github/ISSUE_TEMPLATE/02_feature-request.yml |  17 --
 .github/ISSUE_TEMPLATE/config.yml             |   4 -
 .github/PULL_REQUEST_TEMPLATE/01_bug.md       |  23 ---
 .github/PULL_REQUEST_TEMPLATE/02_enhance.md   |  23 ---
 .github/PULL_REQUEST_TEMPLATE/03_release.md   |  20 --
 .github/dependabot.yml                        |  32 ---
 .github/labeler.yml                           |  21 --
 .github/misskey/test.yml                      |  15 --
 .github/pull_request_template.md              |  24 ---
 .github/workflows/api-misskey-js.yml          |  36 ----
 .github/workflows/check_copyright_year.yml    |  18 --
 .github/workflows/clear-untagged-packages.yml |  18 --
 .github/workflows/docker-develop.yml          |  66 -------
 .github/workflows/docker.yml                  |  64 ------
 .github/workflows/dockle.yml                  |  30 ---
 .github/workflows/get-api-diff.yml            | 186 ------------------
 .github/workflows/labeler.yml                 |  16 --
 .github/workflows/lint.yml                    |  85 --------
 .github/workflows/ok-to-test.yml              |  36 ----
 .github/workflows/package.yml                 |  74 -------
 .github/workflows/pr-preview-deploy.yml       |  92 ---------
 .github/workflows/pr-preview-destroy.yml      |  54 -----
 .github/workflows/report-api-diff.yml         |  85 --------
 .github/workflows/test-backend.yml            |  59 ------
 .github/workflows/test-frontend.yml           | 120 -----------
 .github/workflows/test-misskey-js.yml         |  52 -----
 .github/workflows/test-production.yml         |  42 ----
 .github/workflows/welcome.yml                 |  25 ---
 30 files changed, 1432 deletions(-)
 delete mode 100644 .github/FUNDING.yml
 delete mode 100644 .github/ISSUE_TEMPLATE/01_bug-report.yml
 delete mode 100644 .github/ISSUE_TEMPLATE/02_feature-request.yml
 delete mode 100644 .github/ISSUE_TEMPLATE/config.yml
 delete mode 100644 .github/PULL_REQUEST_TEMPLATE/01_bug.md
 delete mode 100644 .github/PULL_REQUEST_TEMPLATE/02_enhance.md
 delete mode 100644 .github/PULL_REQUEST_TEMPLATE/03_release.md
 delete mode 100644 .github/dependabot.yml
 delete mode 100644 .github/labeler.yml
 delete mode 100644 .github/misskey/test.yml
 delete mode 100644 .github/pull_request_template.md
 delete mode 100644 .github/workflows/api-misskey-js.yml
 delete mode 100644 .github/workflows/check_copyright_year.yml
 delete mode 100644 .github/workflows/clear-untagged-packages.yml
 delete mode 100644 .github/workflows/docker-develop.yml
 delete mode 100644 .github/workflows/docker.yml
 delete mode 100644 .github/workflows/dockle.yml
 delete mode 100644 .github/workflows/get-api-diff.yml
 delete mode 100644 .github/workflows/labeler.yml
 delete mode 100644 .github/workflows/lint.yml
 delete mode 100644 .github/workflows/ok-to-test.yml
 delete mode 100644 .github/workflows/package.yml
 delete mode 100644 .github/workflows/pr-preview-deploy.yml
 delete mode 100644 .github/workflows/pr-preview-destroy.yml
 delete mode 100644 .github/workflows/report-api-diff.yml
 delete mode 100644 .github/workflows/test-backend.yml
 delete mode 100644 .github/workflows/test-frontend.yml
 delete mode 100644 .github/workflows/test-misskey-js.yml
 delete mode 100644 .github/workflows/test-production.yml
 delete mode 100644 .github/workflows/welcome.yml

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index 2383856700..0000000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-# These are supported funding model platforms
-
-github: transfem-org
-ko-fi: transfem
diff --git a/.github/ISSUE_TEMPLATE/01_bug-report.yml b/.github/ISSUE_TEMPLATE/01_bug-report.yml
deleted file mode 100644
index f74719989f..0000000000
--- a/.github/ISSUE_TEMPLATE/01_bug-report.yml
+++ /dev/null
@@ -1,91 +0,0 @@
-name: 🐛 Bug Report
-description: Create a report to help us improve
-labels: ["⚠️bug?"]
-
-body:
-  - type: markdown
-    attributes:
-      value: |
-        Thanks for reporting!
-        First, in order to avoid duplicate Issues, please search to see if the problem you found has already been reported.
-        Also, If you are NOT owner/admin of server, PLEASE DONT REPORT SERVER SPECIFIC ISSUES TO HERE! (e.g. feature XXX is not working in misskey.example) Please try with another misskey servers, and if your issue is only reproducible with specific server, contact your server's owner/admin first.
-
-  - type: textarea
-    attributes:
-      label: 💡 Summary
-      description: Tell us what the bug is
-    validations:
-      required: true
-
-  - type: textarea
-    attributes:
-      label: 🥰 Expected Behavior
-      description: Tell us what should happen
-    validations:
-      required: true
-
-  - type: textarea
-    attributes:
-      label: 🤬 Actual Behavior
-      description: |
-        Tell us what happens instead of the expected behavior.
-        Please include errors from the developer console and/or server log files if you have access to them.
-    validations:
-      required: true
-
-  - type: textarea
-    attributes:
-      label: 📝 Steps to Reproduce
-      placeholder: |
-        1.
-        2.
-        3.
-    validations:
-      required: false
-
-  - type: textarea
-    attributes:
-      label: 💻 Frontend Environment
-      description: |
-        Tell us where on the platform it happens
-        DO NOT WRITE "latest". Please provide the specific version.
-
-        Examples:
-          * Model and OS of the device(s): MacBook Pro (14inch, 2021), macOS Ventura 13.4
-          * Browser: Chrome 113.0.5672.126
-          * Server URL: misskey.io
-          * Misskey: 13.x.x
-      value: |
-        * Model and OS of the device(s):
-        * Browser:
-        * Server URL:
-        * Misskey:
-      render: markdown
-    validations:
-      required: false
-
-  - type: textarea
-    attributes:
-      label: 🛰 Backend Environment (for server admin)
-      description: |
-        Tell us where on the platform it happens
-        DO NOT WRITE "latest". Please provide the specific version.
-        If you are using a managed service, put that after the version.
-
-        Examples:
-          * Installation Method or Hosting Service: docker compose, k8s/docker, systemd, "Misskey install shell script", development environment
-          * Misskey: 13.x.x
-          * Node: 20.x.x
-          * PostgreSQL: 15.x.x
-          * Redis: 7.x.x
-          * OS and Architecture: Ubuntu 22.04.2 LTS aarch64
-      value: |
-        * Installation Method or Hosting Service:
-        * Misskey:
-        * Node:
-        * PostgreSQL:
-        * Redis:
-        * OS and Architecture:
-      render: markdown
-    validations:
-      required: false
diff --git a/.github/ISSUE_TEMPLATE/02_feature-request.yml b/.github/ISSUE_TEMPLATE/02_feature-request.yml
deleted file mode 100644
index 8420475b3e..0000000000
--- a/.github/ISSUE_TEMPLATE/02_feature-request.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-name: ✨ Feature Request
-description: Suggest an idea for this project
-labels: ["✨Feature"]
-
-body:
-  - type: textarea
-    attributes:
-      label: Summary
-      description: Tell us what the suggestion is
-    validations:
-      required: true
-  - type: textarea
-    attributes:
-      label: Purpose
-      description: Describe the specific problem or need you think this feature will solve, and who it will help.
-    validations:
-      required: true
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
deleted file mode 100644
index 6c8c6f7a49..0000000000
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-contact_links:
-  - name: 💬 Transfem.org Discord
-    url: https://discord.gg/xTtXc7We
-    about: Chat freely about Sharkey
diff --git a/.github/PULL_REQUEST_TEMPLATE/01_bug.md b/.github/PULL_REQUEST_TEMPLATE/01_bug.md
deleted file mode 100644
index 0739fee709..0000000000
--- a/.github/PULL_REQUEST_TEMPLATE/01_bug.md
+++ /dev/null
@@ -1,23 +0,0 @@
-<!-- ℹ お読みください / README
-PRありがとうございます! PRを作成する前に、コントリビューションガイドをご確認ください:
-Thank you for your PR! Before creating a PR, please check the contribution guide:
-https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md
--->
-
-## What
-<!-- このPRで何をしたのか? どう変わるのか? -->
-<!-- What did you do with this PR? How will it change things? -->
-
-## Why
-<!-- なぜそうするのか? どういう意図なのか? 何が困っているのか? -->
-<!-- Why do you do it? What are your intentions? What is the problem? -->
-
-## Additional info (optional)
-<!-- テスト観点など -->
-<!-- Test perspective, etc -->
-
-## Checklist
-- [ ] Read the [contribution guide](https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md)
-- [ ] Test working in a local environment
-- [ ] (If needed) Update CHANGELOG.md
-- [ ] (If possible) Add tests
diff --git a/.github/PULL_REQUEST_TEMPLATE/02_enhance.md b/.github/PULL_REQUEST_TEMPLATE/02_enhance.md
deleted file mode 100644
index 0739fee709..0000000000
--- a/.github/PULL_REQUEST_TEMPLATE/02_enhance.md
+++ /dev/null
@@ -1,23 +0,0 @@
-<!-- ℹ お読みください / README
-PRありがとうございます! PRを作成する前に、コントリビューションガイドをご確認ください:
-Thank you for your PR! Before creating a PR, please check the contribution guide:
-https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md
--->
-
-## What
-<!-- このPRで何をしたのか? どう変わるのか? -->
-<!-- What did you do with this PR? How will it change things? -->
-
-## Why
-<!-- なぜそうするのか? どういう意図なのか? 何が困っているのか? -->
-<!-- Why do you do it? What are your intentions? What is the problem? -->
-
-## Additional info (optional)
-<!-- テスト観点など -->
-<!-- Test perspective, etc -->
-
-## Checklist
-- [ ] Read the [contribution guide](https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md)
-- [ ] Test working in a local environment
-- [ ] (If needed) Update CHANGELOG.md
-- [ ] (If possible) Add tests
diff --git a/.github/PULL_REQUEST_TEMPLATE/03_release.md b/.github/PULL_REQUEST_TEMPLATE/03_release.md
deleted file mode 100644
index b5b832e1dc..0000000000
--- a/.github/PULL_REQUEST_TEMPLATE/03_release.md
+++ /dev/null
@@ -1,20 +0,0 @@
-## Summary
-This is a release PR.
-
-For more information on the release instructions, please see:
-https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md#release
-
-## For reviewers
-- CHANGELOGに抜け漏れは無いか
-- バージョンの上げ方は適切か
-- 他にこのリリースに含めなければならない変更は無いか
-- 全体的な変更内容を俯瞰し問題は無いか
-- レビューされていないコミットがある場合は、それが問題ないか
-- 最終的な動作確認を行い問題は無いか
-
-などを確認し、リリースする準備が整っていると思われる場合は approve してください。
-
-## Checklist
-- [ ] package.jsonのバージョンが正しく更新されている
-- [ ] CHANGELOGが過不足無く更新されている
-- [ ] CIが全て通っている
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 5955f6b5d9..0000000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-# To get started with Dependabot version updates, you'll need to specify which
-# package ecosystems to update and where the package manifests are located.
-# Please see the documentation for all configuration options:
-# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
-
-version: 2
-updates:
-- package-ecosystem: github-actions
-  directory: "/"
-  schedule:
-    interval: daily
-  open-pull-requests-limit: 100
-
-# Add only the root, not each workspace item
-# https://github.com/dependabot/dependabot-core/issues/4993#issuecomment-1289133027
-- package-ecosystem: npm
-  directory: "/"
-  schedule:
-    interval: daily
-  # PNPM has an issue with dependabot. See:
-  # https://github.com/dependabot/dependabot-core/issues/7258
-  # https://github.com/pnpm/pnpm/issues/6530
-  # TODO: Restore this when the issue is solved
-  open-pull-requests-limit: 0
-  groups:
-    swc:
-      patterns:
-        - "@swc/*"
-    storybook:
-      patterns:
-        - "storybook*"
-        - "@storybook/*"
diff --git a/.github/labeler.yml b/.github/labeler.yml
deleted file mode 100644
index 137be487c0..0000000000
--- a/.github/labeler.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-'packages/backend':
-- packages/backend/**/*
-
-'packages/backend:test':
-- packages/backend/test/**/*
-
-'packages/frontend':
-- packages/frontend/**/*
-
-'packages/frontend:test':
-- cypress/**/*
-
-'packages/sw':
-- packages/sw/**/*
-
-'packages/misskey-js':
-- packages/misskey-js/**/*
-
-'packages/misskey-js:test':
-- packages/misskey-js/test/**/*
-- packages/misskey-js/test-d/**/*
diff --git a/.github/misskey/test.yml b/.github/misskey/test.yml
deleted file mode 100644
index 7a4aa4ae6c..0000000000
--- a/.github/misskey/test.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-url: 'http://misskey.local'
-
-# ローカルでテストするときにポートを被らないようにするためデフォルトのものとは変える(以下同じ)
-port: 61812
-
-db:
-  host: 127.0.0.1
-  port: 54312
-  db: test-misskey
-  user: postgres
-  pass: ''
-redis:
-  host: 127.0.0.1
-  port: 56312
-id: aidx
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
deleted file mode 100644
index e78b82c47c..0000000000
--- a/.github/pull_request_template.md
+++ /dev/null
@@ -1,24 +0,0 @@
-<!-- ℹ お読みください / README
-PRありがとうございます! PRを作成する前に、コントリビューションガイドをご確認ください:
-Thank you for your PR! Before creating a PR, please check the contribution guide:
-https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md
--->
-
-## What
-<!-- このPRで何をしたのか? どう変わるのか? -->
-<!-- What did you do with this PR? How will it change things? -->
-
-## Why
-<!-- なぜそうするのか? どういう意図なのか? 何が困っているのか? -->
-<!-- Why do you do it? What are your intentions? What is the problem? -->
-
-## Additional info (optional)
-<!-- テスト観点など -->
-<!-- Test perspective, etc -->
-
-## Checklist
-- [ ] Read the [contribution guide](https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md)
-- [ ] Test working in a local environment
-- [ ] (If needed) Add story of storybook
-- [ ] (If needed) Update CHANGELOG.md
-- [ ] (If possible) Add tests
diff --git a/.github/workflows/api-misskey-js.yml b/.github/workflows/api-misskey-js.yml
deleted file mode 100644
index 7818370c0a..0000000000
--- a/.github/workflows/api-misskey-js.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-name: API report (misskey.js)
-
-on: [push, pull_request]
-
-jobs:
-  report:
-
-    runs-on: ubuntu-latest
-
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v4.1.1
-
-      - run: corepack enable
-
-      - name: Setup Node.js
-        uses: actions/setup-node@v4
-        with:
-          node-version-file: '.node-version'
-          cache: 'pnpm'
-
-      - name: Install dependencies
-        run: pnpm i --frozen-lockfile
-
-      - name: Build
-        run: pnpm --filter misskey-js build
-
-      - name: Check files
-        run: ls packages/misskey-js/built
-
-      - name: API report
-        run: pnpm --filter misskey-js api-prod
-
-      - name: Show report
-        if: always()
-        run: cat packages/misskey-js/temp/misskey-js.api.md
diff --git a/.github/workflows/check_copyright_year.yml b/.github/workflows/check_copyright_year.yml
deleted file mode 100644
index 03dfcd0a0b..0000000000
--- a/.github/workflows/check_copyright_year.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-name: Check copyright year
-
-on:
-  push:
-    branches:
-      - master
-      - develop
-
-jobs:
-  check_copyright_year:
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v4.1.1
-    - run: |
-        if [ "$(grep Copyright COPYING | sed -e 's/.*2014-\([0-9]*\) .*/\1/g')" -ne "$(date +%Y)" ]; then
-          echo "Please change copyright year!"
-          exit 1
-        fi
diff --git a/.github/workflows/clear-untagged-packages.yml b/.github/workflows/clear-untagged-packages.yml
deleted file mode 100644
index 5f94d54bcb..0000000000
--- a/.github/workflows/clear-untagged-packages.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-name: Remove old package versions
-
-on:
-  workflow_dispatch:
-
-jobs:
-  remove-package-versions:
-    runs-on: ubuntu-latest
-    permissions:
-      contents: read
-      packages: write
-    steps:
-      - uses: actions/delete-package-versions@v4
-        with: 
-          package-name: sharkey
-          package-type: container
-          min-versions-to-keep: 10
-          delete-only-untagged-versions: 'true'
diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml
deleted file mode 100644
index e5e8392d78..0000000000
--- a/.github/workflows/docker-develop.yml
+++ /dev/null
@@ -1,66 +0,0 @@
-name: Publish Docker image (develop)
-
-on:
-  push:
-    branches:
-      - develop
-    paths:
-      - packages/**
-      - locales/**
-  workflow_dispatch:
-
-env:
-  REGISTRY: ghcr.io
-
-jobs:
-  push_to_registry:
-    name: Push Docker image to GHCR
-    runs-on: ubuntu-latest
-    if: github.repository == 'transfem-org/Sharkey'
-    permissions:
-      contents: read
-      packages: write
-    steps:
-      - name: Remove unnecessary files
-        run: |
-          sudo rm -rf /usr/share/dotnet
-          sudo rm -rf "$AGENT_TOOLSDIRECTORY"
-      - name: Check out the repo
-        uses: actions/checkout@v4.1.1
-      - name: Set up Docker Buildx
-        id: buildx
-        uses: docker/setup-buildx-action@v3.0.0
-        with:
-          platforms: linux/amd64,linux/arm64
-      - name: Docker meta
-        id: meta
-        uses: docker/metadata-action@v5
-        with:
-          images: ${{ env.REGISTRY }}/transfem-org/sharkey
-      - name: Log in to GHCR
-        uses: docker/login-action@v3
-        with:
-          registry: ${{ env.REGISTRY }}
-          username: ${{ github.actor }}
-          password: ${{ secrets.GITHUB_TOKEN }}
-      - name: Build and Push to GHCR
-        id: build
-        uses: docker/build-push-action@v5
-        with:
-          builder: ${{ steps.buildx.outputs.name }}
-          context: .
-          push: true
-          platforms: ${{ steps.buildx.outputs.platforms }}
-          provenance: false
-          tags:  ${{ env.REGISTRY }}/transfem-org/sharkey:develop
-          labels: develop
-          cache-from: type=gha
-          cache-to: type=gha,mode=max
-          build-args: NODE_ENV=development
-      - name: Push update to server
-        if: steps.build.outcome == 'success'
-        uses: indiesdev/curl@v1.1
-        with:
-          url: ${{ secrets.AUTO_UPDATE_DEV_URL }}
-          method: POST
-          timeout: 600000
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
deleted file mode 100644
index 8eabaa2882..0000000000
--- a/.github/workflows/docker.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-name: Publish Docker image
-
-on:
-  push:
-    branches:
-      - stable
-    paths:
-      - packages/**
-      - locales/**
-  release:
-    types: [published]
-  workflow_dispatch:
-
-env:
-  REGISTRY: ghcr.io
-
-jobs:
-  push_to_registry:
-    name: Push Docker image to GHCR
-    runs-on: ubuntu-latest
-    if: github.repository == 'transfem-org/Sharkey'
-    permissions:
-      contents: read
-      packages: write
-
-    steps:
-      - name: Check out the repo
-        uses: actions/checkout@v4.1.1
-      - name: Set up Docker Buildx
-        id: buildx
-        uses: docker/setup-buildx-action@v3.0.0
-        with:
-          platforms: linux/amd64,linux/arm64
-      - name: Docker meta
-        id: meta
-        uses: docker/metadata-action@v5
-        with:
-          images: ${{ env.REGISTRY }}/transfem-org/sharkey
-          tags: |
-            type=edge
-            type=ref,event=pr
-            type=ref,event=branch
-            type=semver,pattern={{version}}
-            type=semver,pattern={{major}}.{{minor}}
-            type=semver,pattern={{major}}
-      - name: Log in to GHCR
-        uses: docker/login-action@v3
-        with:
-          registry: ${{ env.REGISTRY }}
-          username: ${{ github.actor }}
-          password: ${{ secrets.GITHUB_TOKEN }}
-      - name: Build and Push to GHCR
-        id: build
-        uses: docker/build-push-action@v5
-        with:
-          builder: ${{ steps.buildx.outputs.name }}
-          context: .
-          push: true
-          platforms: ${{ steps.buildx.outputs.platforms }}
-          provenance: false
-          tags: ${{ env.REGISTRY }}/transfem-org/sharkey:stable
-          labels: stable
-          cache-from: type=gha
-          cache-to: type=gha,mode=max
diff --git a/.github/workflows/dockle.yml b/.github/workflows/dockle.yml
deleted file mode 100644
index edb18b04da..0000000000
--- a/.github/workflows/dockle.yml
+++ /dev/null
@@ -1,30 +0,0 @@
----
-name: Dockle
-
-on:
-  push:
-    branches:
-      - master
-      - develop
-  pull_request:
-
-jobs:
-  dockle:
-    runs-on: ubuntu-latest
-    env:
-      DOCKER_CONTENT_TRUST: 1
-    steps:
-      - uses: actions/checkout@v4.1.1
-      - 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
-      - run: |
-          cp .config/docker_example.env .config/docker.env
-          cp ./docker-compose.yml.example ./docker-compose.yml
-      - run: |
-          docker compose up -d web
-          docker tag "$(docker compose images web | awk 'OFS=":" {print $4}' | tail -n +2)" misskey-web:latest
-      - run: |
-          cmd="dockle --exit-code 1 misskey-web:latest ${image_name}"
-          echo "> ${cmd}"
-          eval "${cmd}"
diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
deleted file mode 100644
index 8e3437ad86..0000000000
--- a/.github/workflows/get-api-diff.yml
+++ /dev/null
@@ -1,186 +0,0 @@
-# this name is used in report-api-diff.yml so be careful when change name
-name: Get api.json from Misskey
-
-on:
-  pull_request:
-    branches:
-      - master
-      - develop
-
-jobs:
-  get-base:
-    runs-on: ubuntu-latest
-    permissions:
-      contents: read
-
-    strategy:
-      matrix:
-        node-version: [20.5.1]
-
-    services:
-      db:
-        image: postgres:13
-        ports:
-          - 5432:5432
-        env:
-          POSTGRES_DB: misskey
-          POSTGRES_HOST_AUTH_METHOD: trust
-          POSTGRES_USER: example-misskey-user
-          POSTGRESS_PASS: example-misskey-pass
-      redis:
-        image: redis:7
-        ports:
-          - 6379:6379
-
-    steps:
-    - uses: actions/checkout@v4.1.1
-      with:
-        repository: ${{ github.event.pull_request.base.repo.full_name }}
-        ref: ${{ github.base_ref }}
-        submodules: true
-    - name: Install pnpm
-      uses: pnpm/action-setup@v2
-      with:
-        version: 8
-        run_install: false
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.0
-      with:
-        node-version: ${{ matrix.node-version }}
-        cache: 'pnpm'
-    - run: corepack enable
-    - run: pnpm i --frozen-lockfile
-    - name: Check pnpm-lock.yaml
-      run: git diff --exit-code pnpm-lock.yaml
-    - name: Copy Configure
-      run: cp .config/example.yml .config/default.yml
-    - name: Build
-      run: pnpm build
-    - name : Migrate
-      run: pnpm migrate
-    - name: Launch misskey
-      run: |
-        screen -S misskey -dm pnpm run dev
-        sleep 30s
-    - name: Wait for Misskey to be ready
-      run: |
-        MAX_RETRIES=12
-        RETRY_DELAY=5
-        count=0
-        until $(curl --output /dev/null --silent --head --fail http://localhost:3000) || [[ $count -eq $MAX_RETRIES ]]; do
-          printf '.'
-          sleep $RETRY_DELAY
-          count=$((count + 1))
-        done
-
-        if [[ $count -eq $MAX_RETRIES ]]; then
-          echo "Failed to connect to Misskey after $MAX_RETRIES attempts."
-          exit 1
-        fi
-    - id: fetch
-      name: Get api.json from Misskey
-      run: |
-        RESULT=$(curl --retry 5 --retry-delay 5 --retry-max-time 60 http://localhost:3000/api.json)
-        echo $RESULT > api-base.json
-    - name: Upload Artifact
-      uses: actions/upload-artifact@v3
-      with:
-        name: api-artifact
-        path: api-base.json
-    - name: Kill Misskey Job
-      run: screen -S misskey -X quit
-
-  get-head:
-    runs-on: ubuntu-latest
-    permissions:
-      contents: read
-
-    strategy:
-      matrix:
-        node-version: [20.5.1]
-
-    services:
-      db:
-        image: postgres:13
-        ports:
-          - 5432:5432
-        env:
-          POSTGRES_DB: misskey
-          POSTGRES_HOST_AUTH_METHOD: trust
-          POSTGRES_USER: example-misskey-user
-          POSTGRESS_PASS: example-misskey-pass
-      redis:
-        image: redis:7
-        ports:
-          - 6379:6379
-
-    steps:
-    - uses: actions/checkout@v4.1.1
-      with:
-        repository: ${{ github.event.pull_request.head.repo.full_name }}
-        ref: ${{ github.head_ref }}
-        submodules: true
-    - name: Install pnpm
-      uses: pnpm/action-setup@v2
-      with:
-        version: 8
-        run_install: false
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.0
-      with:
-        node-version: ${{ matrix.node-version }}
-        cache: 'pnpm'
-    - run: corepack enable
-    - run: pnpm i --frozen-lockfile
-    - name: Check pnpm-lock.yaml
-      run: git diff --exit-code pnpm-lock.yaml
-    - name: Copy Configure
-      run: cp .config/example.yml .config/default.yml
-    - name: Build
-      run: pnpm build
-    - name : Migrate
-      run: pnpm migrate
-    - name: Launch misskey
-      run: |
-        screen -S misskey -dm pnpm run dev
-        sleep 30s
-    - name: Wait for Misskey to be ready
-      run: |
-        MAX_RETRIES=12
-        RETRY_DELAY=5
-        count=0
-        until $(curl --output /dev/null --silent --head --fail http://localhost:3000) || [[ $count -eq $MAX_RETRIES ]]; do
-          printf '.'
-          sleep $RETRY_DELAY
-          count=$((count + 1))
-        done
-
-        if [[ $count -eq $MAX_RETRIES ]]; then
-          echo "Failed to connect to Misskey after $MAX_RETRIES attempts."
-          exit 1
-        fi
-    - id: fetch
-      name: Get api.json from Misskey
-      run: |
-        RESULT=$(curl --retry 5 --retry-delay 5 --retry-max-time 60 http://localhost:3000/api.json)
-        echo $RESULT > api-head.json
-    - name: Upload Artifact
-      uses: actions/upload-artifact@v3
-      with:
-        name: api-artifact
-        path: api-head.json
-    - name: Kill Misskey Job
-      run: screen -S misskey -X quit
-
-  save-pr-number:
-    runs-on: ubuntu-latest
-    steps:
-      - name: Save PR number
-        env:
-          PR_NUMBER: ${{ github.event.number }}
-        run: |
-          echo "$PR_NUMBER" > ./pr_number
-      - uses: actions/upload-artifact@v3
-        with:
-          name: api-artifact
-          path: pr_number
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
deleted file mode 100644
index 88e2aceaed..0000000000
--- a/.github/workflows/labeler.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-name: "Pull Request Labeler"
-on:
-  pull_request_target:
-    branches-ignore:
-      - 'l10n_develop'
-
-jobs:
-  triage:
-    permissions:
-      contents: read
-      pull-requests: write
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/labeler@v5
-      with:
-        repo-token: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
deleted file mode 100644
index c63ccc87fc..0000000000
--- a/.github/workflows/lint.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-name: Lint
-
-on:
-  push:
-    branches:
-      - master
-      - develop
-    paths:
-      - packages/**
-  pull_request:
-    branches-ignore:
-      - weblate
-
-jobs:
-  pnpm_install:
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v4.1.1
-      with:
-        fetch-depth: 0
-        submodules: true
-    - uses: pnpm/action-setup@v2
-      with:
-        version: 8
-        run_install: false
-    - uses: actions/setup-node@v4
-      with:
-        node-version-file: '.node-version'
-        cache: 'pnpm'
-    - run: corepack enable
-    - run: pnpm i --frozen-lockfile
-
-  lint:
-    needs: [pnpm_install]
-    runs-on: ubuntu-latest
-    continue-on-error: true
-    strategy:
-      matrix:
-        workspace:
-        - backend
-        - frontend
-        - sw
-        - misskey-js
-    steps:
-    - uses: actions/checkout@v4.1.1
-      with:
-        fetch-depth: 0
-        submodules: true
-    - uses: pnpm/action-setup@v2
-      with:
-        version: 7
-        run_install: false
-    - uses: actions/setup-node@v4
-      with:
-        node-version-file: '.node-version'
-        cache: 'pnpm'
-    - run: corepack enable
-    - run: pnpm i --frozen-lockfile
-    - run: pnpm --filter ${{ matrix.workspace }} run eslint
-
-  typecheck:
-    needs: [pnpm_install]
-    runs-on: ubuntu-latest
-    continue-on-error: true
-    strategy:
-      matrix:
-        workspace:
-        - backend
-        - misskey-js
-    steps:
-    - uses: actions/checkout@v4.1.1
-      with:
-        fetch-depth: 0
-        submodules: true
-    - uses: pnpm/action-setup@v2
-      with:
-        version: 7
-        run_install: false
-    - uses: actions/setup-node@v4
-      with:
-        node-version-file: '.node-version'
-        cache: 'pnpm'
-    - run: corepack enable
-    - run: pnpm i --frozen-lockfile
-    - run: pnpm --filter ${{ matrix.workspace }} run typecheck
diff --git a/.github/workflows/ok-to-test.yml b/.github/workflows/ok-to-test.yml
deleted file mode 100644
index c02b980e4d..0000000000
--- a/.github/workflows/ok-to-test.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-# If someone with write access comments "/ok-to-test" on a pull request, emit a repository_dispatch event
-name: Ok To Test
-
-on:
-  issue_comment:
-    types: [created]
-
-jobs:
-  ok-to-test:
-    runs-on: ubuntu-latest
-    # Only run for PRs, not issue comments
-    if: ${{ github.event.issue.pull_request }}
-    steps:
-    # Generate a GitHub App installation access token from an App ID and private key
-    # To create a new GitHub App:
-    #   https://developer.github.com/apps/building-github-apps/creating-a-github-app/
-    # See app.yml for an example app manifest
-    - name: Generate token
-      id: generate_token
-      uses: tibdex/github-app-token@v2
-      with:
-        app_id: ${{ secrets.DEPLOYBOT_APP_ID }}
-        private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }}
-
-    - name: Slash Command Dispatch
-      uses: peter-evans/slash-command-dispatch@v3
-      env:
-        TOKEN: ${{ steps.generate_token.outputs.token }}
-      with:
-        token: ${{ env.TOKEN }} # GitHub App installation access token
-        # token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} # PAT or OAuth token will also work
-        reaction-token: ${{ secrets.GITHUB_TOKEN }}
-        issue-type: pull-request
-        commands: deploy
-        named-args: true
-        permission: write
diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
deleted file mode 100644
index 1f9238780f..0000000000
--- a/.github/workflows/package.yml
+++ /dev/null
@@ -1,74 +0,0 @@
-name: Publish prebuild
-
-on:
-  push:
-    branches:
-      - stable
-  release:
-    types: [published]
-  workflow_dispatch:
-
-jobs:
-  build_binaries:
-    name: Build & ship binaries
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        node-version: [20.x]
-        python-version: [3.11.x]
-    if: github.repository == 'transfem-org/Sharkey'
-    permissions:
-      contents: read
-      packages: write
-
-    steps:
-      - name: Check out the repo
-        uses: actions/checkout@v4.1.1
-        with:
-          lfs: true
-          submodules: 'recursive'
-        
-      - name: Use Node.js ${{ matrix.node-version }}
-        uses: actions/setup-node@v4
-        with:
-          node-version: ${{ matrix.node-version }}
-
-      - name: Setup Python
-        uses: actions/setup-python@v5.0.0
-        with:
-          python-version: ${{ matrix.python-version }}
-
-      - name: Cache APT Packages
-        uses: awalsh128/cache-apt-pkgs-action@v1.3.1
-        with:
-          packages: "build-essential binfmt-support qemu-user-static ffmpeg tini curl libjemalloc-dev libjemalloc2 uuid-dev libx11-dev libxkbfile-dev execstack libgconf-2-4 libsecret-1-dev"
-
-      - name: Set pnpm store path
-        run: echo "PNPM_STORE_PATH=$(pnpm store path)" >> $GITHUB_ENV
-
-      - name: Cache node modules
-        uses: actions/cache@v3
-        with:
-          path: ${{ env.PNPM_STORE_PATH }}
-          key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
-          restore-keys: |
-            pnpm-${{ runner.os }}-
-
-      - name: Build
-        run: |
-          corepack enable
-          corepack prepare pnpm@latest --activate
-          CI=true pnpm install
-          CI=true pnpm run build
-          rm -rdf packages/backend/node_modules
-          rm -rdf packages/frontend/node_modules
-          rm -rdf packages/megalodon/node_modules
-          rm -rdf packages/misskey-js/node_modules
-          rm -rdf node_modules
-          CI=true pnpm --prod --no-optional install
-          tar -czf /tmp/workspace.tar.gz .
-      - name: Upload linux x64
-        uses: actions/upload-artifact@v3.1.3
-        with: 
-          name: sharkey-linux-x64
-          path: /tmp/workspace.tar.gz
diff --git a/.github/workflows/pr-preview-deploy.yml b/.github/workflows/pr-preview-deploy.yml
deleted file mode 100644
index cb9a4ebfc8..0000000000
--- a/.github/workflows/pr-preview-deploy.yml
+++ /dev/null
@@ -1,92 +0,0 @@
-# Run secret-dependent integration tests only after /deploy approval
-on:
-  repository_dispatch:
-    types: [deploy-command]
-
-name: Deploy preview environment
-
-jobs:
-  # Repo owner has commented /deploy on a (fork-based) pull request
-  deploy-preview-environment:
-    runs-on: ubuntu-latest
-    if:
-      github.event.client_payload.slash_command.sha != '' &&
-      contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.sha)
-    steps:
-    - uses: actions/github-script@v7
-      id: check-id
-      env:
-        number: ${{ github.event.client_payload.pull_request.number }}
-        job: ${{ github.job }}
-      with:
-        github-token: ${{ secrets.GITHUB_TOKEN }}
-        result-encoding: string
-        script: |
-          const { data: pull } = await github.rest.pulls.get({
-            ...context.repo,
-            pull_number: process.env.number
-          });
-          const ref = pull.head.sha;
-
-          const { data: checks } = await github.rest.checks.listForRef({
-            ...context.repo,
-            ref
-          });
-
-          const check = checks.check_runs.filter(c => c.name === process.env.job);
-
-          return check[0].id;
-
-    - uses: actions/github-script@v7
-      env:
-        check_id: ${{ steps.check-id.outputs.result }}
-        details_url: ${{ github.server_url }}/${{ github.repository }}/runs/${{ github.run_id }}
-      with:
-        github-token: ${{ secrets.GITHUB_TOKEN }}
-        script: |
-          await github.rest.checks.update({
-            ...context.repo,
-            check_run_id: process.env.check_id,
-            status: 'in_progress',
-            details_url: process.env.details_url
-          });
-
-    # Check out merge commit
-    - name: Fork based /deploy checkout
-      uses: actions/checkout@v4.1.1
-      with:
-        ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'
-
-    # <insert integration tests needing secrets>
-    - name: Context
-      uses: okteto/context@latest
-      with:
-        token: ${{ secrets.OKTETO_TOKEN }}
-
-    - name: Deploy preview environment
-      uses: ikuradon/deploy-preview@latest
-      env:
-        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      with:
-        name: pr-${{ github.event.client_payload.pull_request.number }}-syuilo
-        timeout: 15m
-
-    # Update check run called "integration-fork"
-    - uses: actions/github-script@v7
-      id: update-check-run
-      if: ${{ always() }}
-      env:
-        # Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run
-        conclusion: ${{ job.status }}
-        check_id: ${{ steps.check-id.outputs.result }}
-      with:
-        github-token: ${{ secrets.GITHUB_TOKEN }}
-        script: |
-          const { data: result } = await github.rest.checks.update({
-            ...context.repo,
-            check_run_id: process.env.check_id,
-            status: 'completed',
-            conclusion: process.env.conclusion
-          });
-
-          return result;
diff --git a/.github/workflows/pr-preview-destroy.yml b/.github/workflows/pr-preview-destroy.yml
deleted file mode 100644
index 47d9eb313a..0000000000
--- a/.github/workflows/pr-preview-destroy.yml
+++ /dev/null
@@ -1,54 +0,0 @@
-# file: .github/workflows/preview-closed.yaml
-on:
-  pull_request:
-    types:
-      - closed
-
-name: Destroy preview environment
-
-jobs:
-  destroy-preview-environment:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/github-script@v7
-        id: check-conclusion
-        env:
-          number: ${{ github.event.number }}
-        with:
-          github-token: ${{ secrets.GITHUB_TOKEN }}
-          result-encoding: string
-          script: |
-            const { data: pull } = await github.rest.pulls.get({
-              ...context.repo,
-              pull_number: process.env.number
-            });
-            const ref = pull.head.sha;
-
-            const { data: checks } = await github.rest.checks.listForRef({
-              ...context.repo,
-              ref
-            });
-
-            const check = checks.check_runs.filter(c => c.name === 'deploy-preview-environment');
-
-            if (check.length === 0) {
-              return;
-            }
-
-            const { data: result } = await github.rest.checks.get({
-              ...context.repo,
-              check_run_id: check[0].id,
-            });
-
-            return result.conclusion;
-      - name: Context
-        if: steps.check-conclusion.outputs.result == 'success'
-        uses: okteto/context@latest
-        with:
-          token: ${{ secrets.OKTETO_TOKEN }}
-
-      - name: Destroy preview environment
-        if: steps.check-conclusion.outputs.result == 'success'
-        uses: okteto/destroy-preview@latest
-        with:
-          name: pr-${{ github.event.number }}-syuilo
diff --git a/.github/workflows/report-api-diff.yml b/.github/workflows/report-api-diff.yml
deleted file mode 100644
index 2868d6cc09..0000000000
--- a/.github/workflows/report-api-diff.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-name: Report API Diff
-
-on:
-  workflow_run:
-    types: [completed]
-    workflows:
-      - Get api.json from Misskey # get-api-diff.yml
-
-jobs:
-  compare-diff:
-    runs-on: ubuntu-latest
-    if: ${{ github.event.workflow_run.conclusion == 'success' }}
-    permissions:
-      pull-requests: write
-
-# api-artifact
-    steps:
-      - name: Download artifact
-        uses: actions/github-script@v7
-        with:
-          script: |
-            let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
-               owner: context.repo.owner,
-               repo: context.repo.repo,
-               run_id: context.payload.workflow_run.id,
-            });
-            let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
-              return artifact.name == "api-artifact"
-            })[0];
-            let download = await github.rest.actions.downloadArtifact({
-               owner: context.repo.owner,
-               repo: context.repo.repo,
-               artifact_id: matchArtifact.id,
-               archive_format: 'zip',
-            });
-            let fs = require('fs');
-            fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/api-artifact.zip`, Buffer.from(download.data));
-      - name: Extract artifact
-        run: unzip api-artifact.zip -d artifacts
-      - name: Load PR Number
-        id: load-pr-num
-        run: echo "pr-number=$(cat artifacts/pr_number)" >> "$GITHUB_OUTPUT"
-
-      - name: Output base
-        run: cat ./artifacts/api-base.json
-      - name: Output head
-        run: cat ./artifacts/api-head.json
-      - name: Arrange json files
-        run: |
-          jq '.' ./artifacts/api-base.json > ./api-base.json
-          jq '.' ./artifacts/api-head.json > ./api-head.json
-      - name: Get diff of 2 files
-        run: diff -u --label=base --label=head ./api-base.json ./api-head.json | cat > api.json.diff
-      - name: Get full diff
-        run: diff --label=base --label=head --new-line-format='+%L' --old-line-format='-%L' --unchanged-line-format=' %L' ./api-base.json ./api-head.json | cat > api-full.json.diff
-      - name: Echo full diff
-        run: cat ./api-full.json.diff
-      - name: Upload full diff to Artifact
-        uses: actions/upload-artifact@v3
-        with:
-          name: api-artifact
-          path: |
-            api-full.json.diff
-            api-base.json
-            api-head.json
-      - id: out-diff
-        name: Build diff Comment
-        run: |
-          cat <<- EOF > ./output.md
-          このPRによるapi.jsonの差分
-          <details>
-          <summary>差分はこちら</summary>
-
-          \`\`\`diff
-          $(cat ./api.json.diff)
-          \`\`\`
-          </details>
-
-          [Get diff files from Workflow Page](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})
-          EOF
-      - uses: thollander/actions-comment-pull-request@v2
-        with:
-          pr_number: ${{ steps.load-pr-num.outputs.pr-number }}
-          comment_tag: show_diff
-          filePath: ./output.md
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
deleted file mode 100644
index 711e8bb7f5..0000000000
--- a/.github/workflows/test-backend.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-name: Test (backend)
-
-on:
-  push:
-    branches:
-      - master
-      - develop
-  pull_request:
-
-jobs:
-  jest:
-    runs-on: ubuntu-latest
-
-    strategy:
-      matrix:
-        node-version: [20.5.1]
-
-    services:
-      postgres:
-        image: postgres:13
-        ports:
-          - 54312:5432
-        env:
-          POSTGRES_DB: test-misskey
-          POSTGRES_HOST_AUTH_METHOD: trust
-      redis:
-        image: redis:7
-        ports:
-          - 56312:6379
-
-    steps:
-    - uses: actions/checkout@v4.1.1
-      with:
-        submodules: true
-    - name: Install pnpm
-      uses: pnpm/action-setup@v2
-      with:
-        version: 8
-        run_install: false
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4
-      with:
-        node-version: ${{ matrix.node-version }}
-        cache: 'pnpm'
-    - run: corepack enable
-    - run: pnpm i --frozen-lockfile
-    - name: Check pnpm-lock.yaml
-      run: git diff --exit-code pnpm-lock.yaml
-    - name: Copy Configure
-      run: cp .github/misskey/test.yml .config
-    - name: Build
-      run: pnpm build
-    - name: Test
-      run: pnpm jest-and-coverage
-    - name: Upload Coverage
-      uses: codecov/codecov-action@v3
-      with:
-        token: ${{ secrets.CODECOV_TOKEN }}
-        files: ./packages/backend/coverage/coverage-final.json
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
deleted file mode 100644
index 62b2fe3e38..0000000000
--- a/.github/workflows/test-frontend.yml
+++ /dev/null
@@ -1,120 +0,0 @@
-name: Test (frontend)
-
-on:
-  push:
-    branches:
-      - master
-      - develop
-  pull_request:
-
-jobs:
-  vitest:
-    runs-on: ubuntu-latest
-
-    strategy:
-      matrix:
-        node-version: [20.5.1]
-
-    steps:
-    - uses: actions/checkout@v4.1.1
-      with:
-        submodules: true
-    - name: Install pnpm
-      uses: pnpm/action-setup@v2
-      with:
-        version: 8
-        run_install: false
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4
-      with:
-        node-version: ${{ matrix.node-version }}
-        cache: 'pnpm'
-    - run: corepack enable
-    - run: pnpm i --frozen-lockfile
-    - name: Check pnpm-lock.yaml
-      run: git diff --exit-code pnpm-lock.yaml
-    - name: Copy Configure
-      run: cp .github/misskey/test.yml .config
-    - name: Build
-      run: pnpm build
-    - name: Test
-      run: pnpm --filter frontend test-and-coverage
-    - name: Upload Coverage
-      uses: codecov/codecov-action@v3
-      with:
-        token: ${{ secrets.CODECOV_TOKEN }}
-        files: ./packages/frontend/coverage/coverage-final.json
-
-  e2e:
-    runs-on: ubuntu-latest
-
-    strategy:
-      fail-fast: false
-      matrix:
-        node-version: [20.5.1]
-        browser: [chrome]
-
-    services:
-      postgres:
-        image: postgres:13
-        ports:
-          - 54312:5432
-        env:
-          POSTGRES_DB: test-misskey
-          POSTGRES_HOST_AUTH_METHOD: trust
-      redis:
-        image: redis:7
-        ports:
-          - 56312:6379
-
-    steps:
-    - uses: actions/checkout@v4.1.1
-      with:
-        submodules: true
-    # https://github.com/cypress-io/cypress-docker-images/issues/150
-    #- name: Install mplayer for FireFox
-    #  run: sudo apt install mplayer -y
-    #  if: ${{ matrix.browser == 'firefox' }}
-    #- uses: browser-actions/setup-firefox@latest
-    #  if: ${{ matrix.browser == 'firefox' }}
-    - name: Install pnpm
-      uses: pnpm/action-setup@v2
-      with:
-        version: 7
-        run_install: false
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4
-      with:
-        node-version: ${{ matrix.node-version }}
-        cache: 'pnpm'
-    - run: corepack enable
-    - run: pnpm i --frozen-lockfile
-    - name: Copy Configure
-      run: cp .github/misskey/test.yml .config
-    - name: Build
-      run: pnpm build
-    # https://github.com/cypress-io/cypress/issues/4351#issuecomment-559489091
-    - name: ALSA Env
-      run: echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc
-    # XXX: This tries reinstalling Cypress if the binary is not cached
-    # Remove this when the cache issue is fixed
-    - name: Cypress install
-      run: pnpm exec cypress install
-    - name: Cypress run
-      uses: cypress-io/github-action@v6
-      with:
-        install: false
-        start: pnpm start:test
-        wait-on: 'http://localhost:61812'
-        headed: true
-        browser: ${{ matrix.browser }}
-    - uses: actions/upload-artifact@v3
-      if: failure()
-      with:
-        name: ${{ matrix.browser }}-cypress-screenshots
-        path: cypress/screenshots
-    - uses: actions/upload-artifact@v3
-      if: always()
-      with:
-        name: ${{ matrix.browser }}-cypress-videos
-        path: cypress/videos
diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml
deleted file mode 100644
index 36816c78b8..0000000000
--- a/.github/workflows/test-misskey-js.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
-# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
-
-name: Test (misskey.js)
-
-on:
-  push:
-    branches: [ develop ]
-  pull_request:
-    branches: [ develop ]
-
-jobs:
-  test:
-
-    runs-on: ubuntu-latest
-
-    strategy:
-      matrix:
-        node-version: [20.5.1]
-        # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
-
-    steps:
-      - name: Checkout
-        uses: actions/checkout@v4.1.1
-
-      - run: corepack enable
-
-      - name: Setup Node.js ${{ matrix.node-version }}
-        uses: actions/setup-node@v4
-        with:
-          node-version: ${{ matrix.node-version }}
-          cache: 'pnpm'
-
-      - name: Install dependencies
-        run: pnpm i --frozen-lockfile
-
-      - name: Check pnpm-lock.yaml
-        run: git diff --exit-code pnpm-lock.yaml
-
-      - name: Build
-        run: pnpm --filter misskey-js build
-
-      - name: Test
-        run: pnpm --filter misskey-js test
-        env:
-          CI: true
-
-      - name: Upload Coverage
-        uses: codecov/codecov-action@v3
-        with:
-          token: ${{ secrets.CODECOV_TOKEN }}
-          files: ./packages/misskey-js/coverage/coverage-final.json
diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml
deleted file mode 100644
index 6b98fc51e6..0000000000
--- a/.github/workflows/test-production.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: Test (production install and build)
-
-on:
-  push:
-    branches:
-      - master
-      - develop
-  pull_request:
-
-env:
-  NODE_ENV: production
-
-jobs:
-  production:
-    runs-on: ubuntu-latest
-
-    strategy:
-      matrix:
-        node-version: [20.5.1]
-
-    steps:
-    - uses: actions/checkout@v4.1.1
-      with:
-        submodules: true
-    - name: Install pnpm
-      uses: pnpm/action-setup@v2
-      with:
-        version: 8
-        run_install: false
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4
-      with:
-        node-version: ${{ matrix.node-version }}
-        cache: 'pnpm'
-    - run: corepack enable
-    - run: pnpm i --frozen-lockfile
-    - name: Check pnpm-lock.yaml
-      run: git diff --exit-code pnpm-lock.yaml
-    - name: Copy Configure
-      run: cp .github/misskey/test.yml .config/default.yml
-    - name: Build
-      run: pnpm build
diff --git a/.github/workflows/welcome.yml b/.github/workflows/welcome.yml
deleted file mode 100644
index fb6c9ebdff..0000000000
--- a/.github/workflows/welcome.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-name: Welcome
-on:
-  pull_request:
-    types: [opened]
-  issues:
-    types: [opened]
-jobs:
-  run:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/first-interaction@v1.3.0
-        with:
-          repo-token: ${{ secrets.GITHUB_TOKEN }}
-          issue-message: |
-            👋 @{{ author }}
-             Thanks for opening your first issue here! If you are reporting a bug, please make sure to include steps on how to reproduce it! :D
-
-          pr-message: |
-            👋 @{{ author }}
-            Thanks for opening this pull request! We will review it as soon as we can :3
-            Please check out our contributing guidelines in the meantime.
-
-         # FIRST_PR_MERGED: |
-         #   🎉 @{{ author }}
-         #   Congrats on getting your first pull request merged! We are proud of you :3 ❤️

From 84e82f03e94329d2bf681281250e356e68a81655 Mon Sep 17 00:00:00 2001
From: Insert5StarName <anime@shourai.de>
Date: Sat, 9 Dec 2023 03:44:57 +0100
Subject: [PATCH 192/435] chore: replace discord link

---
 .gitea/ISSUE_TEMPLATE/config.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.gitea/ISSUE_TEMPLATE/config.yml b/.gitea/ISSUE_TEMPLATE/config.yml
index ace35b3fe0..b845c1c9ac 100644
--- a/.gitea/ISSUE_TEMPLATE/config.yml
+++ b/.gitea/ISSUE_TEMPLATE/config.yml
@@ -1,4 +1,4 @@
 contact_links:
   - name: 💬 Transfem.org Discord
-    url: https://discord.gg/xTtXc7We
-    about: Chat freely about Sharkey
\ No newline at end of file
+    url: https://discord.gg/HJcAanTR6H
+    about: Chat freely about Sharkey

From fcf0f5f6b52efc9d7c42d4968de5590554b042f9 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 9 Dec 2023 12:58:00 +0900
Subject: [PATCH 193/435] fix(frontend): disable Mk:apiExternal

---
 CHANGELOG.md                                  |  1 +
 packages/frontend/src/os.ts                   |  4 +-
 packages/frontend/src/scripts/aiscript/api.ts |  2 +
 packages/frontend/src/scripts/api.ts          | 48 -------------------
 4 files changed, 5 insertions(+), 50 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7352db8fab..c7ee4b0388 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -46,6 +46,7 @@
 - Fix: 共有機能をサポートしていないブラウザの場合は共有ボタンを非表示にする #11305
 - Fix: 通知のグルーピング設定を変更してもリロードされるまで表示が変わらない問題を修正 #12470
 - Fix: 長い名前のチャンネルにおける投稿フォームの表示が崩れる問題を修正
+- Fix: セキュリティ向上のためAiScriptの`Mk:apiExternal`を無効化
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 8093335a28..8aed5797e1 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -5,8 +5,8 @@
 
 // TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する
 
-import { pendingApiRequestsCount, api, apiExternal, apiGet } from '@/scripts/api.js';
-export { pendingApiRequestsCount, api, apiExternal, apiGet };
+import { pendingApiRequestsCount, api, apiGet } from '@/scripts/api.js';
+export { pendingApiRequestsCount, api, apiGet };
 import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue';
 import { EventEmitter } from 'eventemitter3';
 import insertTextAtCursor from 'insert-text-at-cursor';
diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts
index fb7ab924b7..038ae23109 100644
--- a/packages/frontend/src/scripts/aiscript/api.ts
+++ b/packages/frontend/src/scripts/aiscript/api.ts
@@ -50,6 +50,7 @@ export function createAiScriptEnv(opts) {
 				return values.ERROR('request_failed', utils.jsToVal(err));
 			});
 		}),
+		/* セキュリティ上の問題があるため無効化
 		'Mk:apiExternal': values.FN_NATIVE(async ([host, ep, param, token]) => {
 			utils.assertString(host);
 			utils.assertString(ep);
@@ -60,6 +61,7 @@ export function createAiScriptEnv(opts) {
 				return values.ERROR('request_failed', utils.jsToVal(err));
 			});
 		}),
+		*/
 		'Mk:save': values.FN_NATIVE(([key, value]) => {
 			utils.assertString(key);
 			miLocalStorage.setItem(`aiscript:${opts.storageKey}:${key.value}`, JSON.stringify(utils.valToJs(value)));
diff --git a/packages/frontend/src/scripts/api.ts b/packages/frontend/src/scripts/api.ts
index 0f54f779a6..8f3a163938 100644
--- a/packages/frontend/src/scripts/api.ts
+++ b/packages/frontend/src/scripts/api.ts
@@ -56,54 +56,6 @@ export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoin
 	return promise;
 }
 
-export function apiExternal<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
-	hostUrl: string,
-	endpoint: E, data: P = {} as any,
-	token?: string | null | undefined,
-	signal?: AbortSignal,
-): Promise<Misskey.api.SwitchCaseResponseType<E, P>> {
-	if (!/^https?:\/\//.test(hostUrl)) throw new Error('invalid host name');
-	if (endpoint.includes('://')) throw new Error('invalid endpoint');
-	pendingApiRequestsCount.value++;
-
-	const onFinally = () => {
-		pendingApiRequestsCount.value--;
-	};
-
-	const promise = new Promise<Misskey.Endpoints[E]['res'] | void>((resolve, reject) => {
-		// Append a credential
-		(data as any).i = token;
-
-		const fullUrl = (hostUrl.slice(-1) === '/' ? hostUrl.slice(0, -1) : hostUrl)
-				+ '/api/' + (endpoint.slice(0, 1) === '/' ? endpoint.slice(1) : endpoint);
-		// Send request
-		window.fetch(fullUrl, {
-			method: 'POST',
-			body: JSON.stringify(data),
-			credentials: 'omit',
-			cache: 'no-cache',
-			headers: {
-				'Content-Type': 'application/json',
-			},
-			signal,
-		}).then(async (res) => {
-			const body = res.status === 204 ? null : await res.json();
-
-			if (res.status === 200) {
-				resolve(body);
-			} else if (res.status === 204) {
-				resolve();
-			} else {
-				reject(body.error);
-			}
-		}).catch(reject);
-	});
-
-	promise.then(onFinally, onFinally);
-
-	return promise;
-}
-
 // Implements Misskey.api.ApiClient.request
 export function apiGet<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
 	endpoint: E,

From 319267e0969dfa52c5dbf681a25ad19ba391ebf1 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 9 Dec 2023 13:02:14 +0900
Subject: [PATCH 194/435] update deps

---
 package.json                               |    8 +-
 packages/backend/package.json              |   28 +-
 packages/frontend/package.json             |   62 +-
 packages/misskey-js/generator/package.json |    2 +-
 packages/misskey-js/package.json           |   12 +-
 packages/sw/package.json                   |    4 +-
 pnpm-lock.yaml                             | 1694 ++++++++++----------
 7 files changed, 881 insertions(+), 929 deletions(-)

diff --git a/package.json b/package.json
index 7e00d43dbe..d2685c3203 100644
--- a/package.json
+++ b/package.json
@@ -50,13 +50,13 @@
 		"js-yaml": "4.1.0",
 		"postcss": "8.4.32",
 		"terser": "5.24.0",
-		"typescript": "5.3.2"
+		"typescript": "5.3.3"
 	},
 	"devDependencies": {
-		"@typescript-eslint/eslint-plugin": "6.13.1",
-		"@typescript-eslint/parser": "6.13.1",
+		"@typescript-eslint/eslint-plugin": "6.13.2",
+		"@typescript-eslint/parser": "6.13.2",
 		"cross-env": "7.0.3",
-		"cypress": "13.6.0",
+		"cypress": "13.6.1",
 		"eslint": "8.55.0",
 		"start-server-and-test": "2.0.3",
 		"ncp": "2.0.0"
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 25984214eb..85bdfbebf4 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -61,13 +61,13 @@
 	"dependencies": {
 		"@aws-sdk/client-s3": "3.412.0",
 		"@aws-sdk/lib-storage": "3.412.0",
-		"@bull-board/api": "5.10.1",
-		"@bull-board/fastify": "5.10.1",
-		"@bull-board/ui": "5.10.1",
+		"@bull-board/api": "5.10.2",
+		"@bull-board/fastify": "5.10.2",
+		"@bull-board/ui": "5.10.2",
 		"@discordapp/twemoji": "14.1.2",
-		"@fastify/accepts": "4.2.0",
+		"@fastify/accepts": "4.3.0",
 		"@fastify/cookie": "9.2.0",
-		"@fastify/cors": "8.4.1",
+		"@fastify/cors": "8.4.2",
 		"@fastify/express": "2.3.0",
 		"@fastify/http-proxy": "9.3.0",
 		"@fastify/multipart": "8.0.0",
@@ -89,7 +89,7 @@
 		"bcryptjs": "2.4.3",
 		"blurhash": "2.0.5",
 		"body-parser": "1.20.2",
-		"bullmq": "4.14.4",
+		"bullmq": "4.15.2",
 		"cacheable-lookup": "7.0.0",
 		"cbor": "9.0.1",
 		"chalk": "5.3.0",
@@ -117,7 +117,7 @@
 		"js-yaml": "4.1.0",
 		"jsdom": "23.0.1",
 		"json5": "2.2.3",
-		"jsonld": "8.3.1",
+		"jsonld": "8.3.2",
 		"jsrsasign": "10.9.0",
 		"meilisearch": "0.36.0",
 		"mfm-js": "0.23.3",
@@ -134,7 +134,7 @@
 		"oauth2orize": "1.12.0",
 		"oauth2orize-pkce": "0.1.2",
 		"os-utils": "0.0.14",
-		"otpauth": "9.2.0",
+		"otpauth": "9.2.1",
 		"parse5": "7.1.2",
 		"pg": "8.11.3",
 		"pkce-challenge": "4.0.1",
@@ -148,7 +148,7 @@
 		"ratelimiter": "3.4.1",
 		"re2": "1.20.9",
 		"redis-lock": "0.1.4",
-		"reflect-metadata": "0.1.13",
+		"reflect-metadata": "0.1.14",
 		"rename": "1.0.4",
 		"rss-parser": "3.13.0",
 		"rxjs": "7.8.1",
@@ -167,7 +167,7 @@
 		"tsconfig-paths": "4.2.0",
 		"twemoji-parser": "14.0.0",
 		"typeorm": "0.3.17",
-		"typescript": "5.3.2",
+		"typescript": "5.3.3",
 		"ulid": "2.3.0",
 		"vary": "1.1.2",
 		"web-push": "3.6.6",
@@ -187,14 +187,14 @@
 		"@types/content-disposition": "0.5.8",
 		"@types/fluent-ffmpeg": "2.1.24",
 		"@types/http-link-header": "1.0.5",
-		"@types/jest": "29.5.10",
+		"@types/jest": "29.5.11",
 		"@types/js-yaml": "4.0.9",
 		"@types/jsdom": "21.1.6",
 		"@types/jsonld": "1.5.13",
 		"@types/jsrsasign": "10.5.12",
 		"@types/mime-types": "2.1.4",
 		"@types/ms": "0.7.34",
-		"@types/node": "20.10.3",
+		"@types/node": "20.10.4",
 		"@types/node-fetch": "3.0.3",
 		"@types/nodemailer": "6.4.14",
 		"@types/oauth": "0.9.4",
@@ -217,8 +217,8 @@
 		"@types/vary": "1.1.3",
 		"@types/web-push": "3.6.3",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "6.13.1",
-		"@typescript-eslint/parser": "6.13.1",
+		"@typescript-eslint/eslint-plugin": "6.13.2",
+		"@typescript-eslint/parser": "6.13.2",
 		"aws-sdk-client-mock": "3.0.0",
 		"cross-env": "7.0.3",
 		"eslint": "8.55.0",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 5946490103..b78cfb2395 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -22,7 +22,7 @@
 		"@rollup/plugin-alias": "5.1.0",
 		"@rollup/plugin-json": "6.0.1",
 		"@rollup/plugin-replace": "5.0.5",
-		"@rollup/pluginutils": "5.0.5",
+		"@rollup/pluginutils": "5.1.0",
 		"@syuilo/aiscript": "0.16.0",
 		"@tabler/icons-webfont": "2.37.0",
 		"@vitejs/plugin-vue": "4.5.1",
@@ -34,12 +34,12 @@
 		"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
 		"buraha": "0.0.1",
 		"canvas-confetti": "1.6.1",
-		"chart.js": "4.4.0",
+		"chart.js": "4.4.1",
 		"chartjs-adapter-date-fns": "3.0.0",
 		"chartjs-chart-matrix": "2.0.1",
 		"chartjs-plugin-gradient": "0.6.1",
 		"chartjs-plugin-zoom": "2.0.1",
-		"chromatic": "9.1.0",
+		"chromatic": "10.1.0",
 		"compare-versions": "6.1.0",
 		"cropperjs": "2.0.0-beta.4",
 		"date-fns": "2.30.0",
@@ -57,9 +57,9 @@
 		"photoswipe": "5.4.3",
 		"punycode": "2.3.1",
 		"querystring": "0.2.1",
-		"rollup": "4.6.1",
+		"rollup": "4.7.0",
 		"sanitize-html": "2.11.0",
-		"shiki": "^0.14.5",
+		"shiki": "0.14.6",
 		"sass": "1.69.5",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
@@ -69,39 +69,39 @@
 		"tsc-alias": "1.8.8",
 		"tsconfig-paths": "4.2.0",
 		"twemoji-parser": "14.0.0",
-		"typescript": "5.3.2",
+		"typescript": "5.3.3",
 		"uuid": "9.0.1",
 		"v-code-diff": "1.7.2",
 		"vanilla-tilt": "1.8.1",
-		"vite": "5.0.5",
-		"vue": "3.3.9",
+		"vite": "5.0.7",
+		"vue": "3.3.11",
 		"vuedraggable": "next"
 	},
 	"devDependencies": {
-		"@storybook/addon-actions": "7.6.3",
-		"@storybook/addon-essentials": "7.6.3",
-		"@storybook/addon-interactions": "7.6.3",
-		"@storybook/addon-links": "7.6.3",
-		"@storybook/addon-storysource": "7.6.3",
-		"@storybook/addons": "7.6.3",
-		"@storybook/blocks": "7.6.3",
-		"@storybook/core-events": "7.6.3",
+		"@storybook/addon-actions": "7.6.4",
+		"@storybook/addon-essentials": "7.6.4",
+		"@storybook/addon-interactions": "7.6.4",
+		"@storybook/addon-links": "7.6.4",
+		"@storybook/addon-storysource": "7.6.4",
+		"@storybook/addons": "7.6.4",
+		"@storybook/blocks": "7.6.4",
+		"@storybook/core-events": "7.6.4",
 		"@storybook/jest": "0.2.3",
-		"@storybook/manager-api": "7.6.3",
-		"@storybook/preview-api": "7.6.3",
-		"@storybook/react": "7.6.3",
-		"@storybook/react-vite": "7.6.3",
+		"@storybook/manager-api": "7.6.4",
+		"@storybook/preview-api": "7.6.4",
+		"@storybook/react": "7.6.4",
+		"@storybook/react-vite": "7.6.4",
 		"@storybook/testing-library": "0.2.2",
-		"@storybook/theming": "7.6.3",
-		"@storybook/types": "7.6.3",
-		"@storybook/vue3": "7.6.3",
-		"@storybook/vue3-vite": "7.6.3",
+		"@storybook/theming": "7.6.4",
+		"@storybook/types": "7.6.4",
+		"@storybook/vue3": "7.6.4",
+		"@storybook/vue3-vite": "7.6.4",
 		"@testing-library/vue": "8.0.1",
 		"@types/escape-regexp": "0.0.3",
 		"@types/estree": "1.0.5",
 		"@types/matter-js": "0.19.5",
 		"@types/micromatch": "4.0.6",
-		"@types/node": "20.10.3",
+		"@types/node": "20.10.4",
 		"@types/punycode": "2.1.3",
 		"@types/sanitize-html": "2.9.5",
 		"@types/throttle-debounce": "5.0.2",
@@ -109,13 +109,13 @@
 		"@types/uuid": "9.0.7",
 		"@types/websocket": "1.0.10",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "6.13.1",
-		"@typescript-eslint/parser": "6.13.1",
+		"@typescript-eslint/eslint-plugin": "6.13.2",
+		"@typescript-eslint/parser": "6.13.2",
 		"@vitest/coverage-v8": "0.34.6",
-		"@vue/runtime-core": "3.3.9",
+		"@vue/runtime-core": "3.3.11",
 		"acorn": "8.11.2",
 		"cross-env": "7.0.3",
-		"cypress": "13.6.0",
+		"cypress": "13.6.1",
 		"eslint": "8.55.0",
 		"eslint-plugin-import": "2.29.0",
 		"eslint-plugin-vue": "9.19.2",
@@ -129,13 +129,13 @@
 		"react": "18.2.0",
 		"react-dom": "18.2.0",
 		"start-server-and-test": "2.0.3",
-		"storybook": "7.6.3",
+		"storybook": "7.6.4",
 		"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.6",
 		"vitest-fetch-mock": "0.2.2",
 		"vue-eslint-parser": "9.3.2",
-		"vue-tsc": "1.8.24"
+		"vue-tsc": "1.8.25"
 	}
 }
diff --git a/packages/misskey-js/generator/package.json b/packages/misskey-js/generator/package.json
index e12520f043..50b23f5792 100644
--- a/packages/misskey-js/generator/package.json
+++ b/packages/misskey-js/generator/package.json
@@ -12,7 +12,7 @@
 		"@typescript-eslint/eslint-plugin": "6.11.0",
 		"@typescript-eslint/parser": "6.11.0",
 		"eslint": "8.53.0",
-		"typescript": "5.3.2",
+		"typescript": "5.3.3",
 		"tsx": "4.4.0",
 		"ts-case-convert": "2.0.2",
 		"openapi-types": "12.1.3",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index e447383a4f..9c917e1b95 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -21,19 +21,19 @@
 		"url": "git+https://github.com/misskey-dev/misskey.js.git"
 	},
 	"devDependencies": {
-		"@microsoft/api-extractor": "7.38.3",
+		"@microsoft/api-extractor": "7.38.5",
 		"@swc/jest": "0.2.29",
-		"@types/jest": "29.5.10",
-		"@types/node": "20.10.3",
-		"@typescript-eslint/eslint-plugin": "6.13.1",
-		"@typescript-eslint/parser": "6.13.1",
+		"@types/jest": "29.5.11",
+		"@types/node": "20.10.4",
+		"@typescript-eslint/eslint-plugin": "6.13.2",
+		"@typescript-eslint/parser": "6.13.2",
 		"eslint": "8.55.0",
 		"jest": "29.7.0",
 		"jest-fetch-mock": "3.0.3",
 		"jest-websocket-mock": "2.5.0",
 		"mock-socket": "9.3.1",
 		"tsd": "0.29.0",
-		"typescript": "5.3.2",
+		"typescript": "5.3.3",
 		"ncp": "2.0.0"
 	},
 	"files": [
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 64d1def267..ffd7bfd8ba 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -14,11 +14,11 @@
 		"misskey-js": "workspace:*"
 	},
 	"devDependencies": {
-		"@typescript-eslint/parser": "6.13.1",
+		"@typescript-eslint/parser": "6.13.2",
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
 		"eslint": "8.55.0",
 		"eslint-plugin-import": "2.29.0",
-		"typescript": "5.3.2"
+		"typescript": "5.3.3"
 	},
 	"type": "module"
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 707deb9f10..c2e3aff9b3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -28,25 +28,25 @@ importers:
         specifier: 5.24.0
         version: 5.24.0
       typescript:
-        specifier: 5.3.2
-        version: 5.3.2
+        specifier: 5.3.3
+        version: 5.3.3
     optionalDependencies:
       '@tensorflow/tfjs-core':
         specifier: 4.4.0
         version: 4.4.0
     devDependencies:
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.13.1
-        version: 6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2)
+        specifier: 6.13.2
+        version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.13.1
-        version: 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+        specifier: 6.13.2
+        version: 6.13.2(eslint@8.55.0)(typescript@5.3.3)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.6.0
-        version: 13.6.0
+        specifier: 13.6.1
+        version: 13.6.1
       eslint:
         specifier: 8.55.0
         version: 8.55.0
@@ -66,26 +66,26 @@ importers:
         specifier: 3.412.0
         version: 3.412.0(@aws-sdk/client-s3@3.412.0)
       '@bull-board/api':
-        specifier: 5.10.1
-        version: 5.10.1(@bull-board/ui@5.10.1)
+        specifier: 5.10.2
+        version: 5.10.2(@bull-board/ui@5.10.2)
       '@bull-board/fastify':
-        specifier: 5.10.1
-        version: 5.10.1
+        specifier: 5.10.2
+        version: 5.10.2
       '@bull-board/ui':
-        specifier: 5.10.1
-        version: 5.10.1
+        specifier: 5.10.2
+        version: 5.10.2
       '@discordapp/twemoji':
         specifier: 14.1.2
         version: 14.1.2
       '@fastify/accepts':
-        specifier: 4.2.0
-        version: 4.2.0
+        specifier: 4.3.0
+        version: 4.3.0
       '@fastify/cookie':
         specifier: 9.2.0
         version: 9.2.0
       '@fastify/cors':
-        specifier: 8.4.1
-        version: 8.4.1
+        specifier: 8.4.2
+        version: 8.4.2
       '@fastify/express':
         specifier: 2.3.0
         version: 2.3.0
@@ -103,10 +103,10 @@ importers:
         version: 8.2.0
       '@nestjs/common':
         specifier: 10.2.10
-        version: 10.2.10(reflect-metadata@0.1.13)(rxjs@7.8.1)
+        version: 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
       '@nestjs/core':
         specifier: 10.2.10
-        version: 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.13)(rxjs@7.8.1)
+        version: 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1)
       '@nestjs/testing':
         specifier: 10.2.10
         version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
@@ -150,8 +150,8 @@ importers:
         specifier: 1.20.2
         version: 1.20.2
       bullmq:
-        specifier: 4.14.4
-        version: 4.14.4
+        specifier: 4.15.2
+        version: 4.15.2
       cacheable-lookup:
         specifier: 7.0.0
         version: 7.0.0
@@ -234,8 +234,8 @@ importers:
         specifier: 2.2.3
         version: 2.2.3
       jsonld:
-        specifier: 8.3.1
-        version: 8.3.1
+        specifier: 8.3.2
+        version: 8.3.2
       jsrsasign:
         specifier: 10.9.0
         version: 10.9.0
@@ -285,8 +285,8 @@ importers:
         specifier: 0.0.14
         version: 0.0.14
       otpauth:
-        specifier: 9.2.0
-        version: 9.2.0
+        specifier: 9.2.1
+        version: 9.2.1
       parse5:
         specifier: 7.1.2
         version: 7.1.2
@@ -327,8 +327,8 @@ importers:
         specifier: 0.1.4
         version: 0.1.4
       reflect-metadata:
-        specifier: 0.1.13
-        version: 0.1.13
+        specifier: 0.1.14
+        version: 0.1.14
       rename:
         specifier: 1.0.4
         version: 1.0.4
@@ -384,8 +384,8 @@ importers:
         specifier: 0.3.17
         version: 0.3.17(ioredis@5.3.2)(pg@8.11.3)
       typescript:
-        specifier: 5.3.2
-        version: 5.3.2
+        specifier: 5.3.3
+        version: 5.3.3
       ulid:
         specifier: 2.3.0
         version: 2.3.0
@@ -527,8 +527,8 @@ importers:
         specifier: 1.0.5
         version: 1.0.5
       '@types/jest':
-        specifier: 29.5.10
-        version: 29.5.10
+        specifier: 29.5.11
+        version: 29.5.11
       '@types/js-yaml':
         specifier: 4.0.9
         version: 4.0.9
@@ -548,8 +548,8 @@ importers:
         specifier: 0.7.34
         version: 0.7.34
       '@types/node':
-        specifier: 20.10.3
-        version: 20.10.3
+        specifier: 20.10.4
+        version: 20.10.4
       '@types/node-fetch':
         specifier: 3.0.3
         version: 3.0.3
@@ -617,11 +617,11 @@ importers:
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.13.1
-        version: 6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2)
+        specifier: 6.13.2
+        version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.13.1
-        version: 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+        specifier: 6.13.2
+        version: 6.13.2(eslint@8.55.0)(typescript@5.3.3)
       aws-sdk-client-mock:
         specifier: 3.0.0
         version: 3.0.0
@@ -633,13 +633,13 @@ importers:
         version: 8.55.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)
+        version: 2.29.0(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)
       execa:
         specifier: 8.0.1
         version: 8.0.1
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.10.3)
+        version: 29.7.0(@types/node@20.10.4)
       jest-mock:
         specifier: 29.7.0
         version: 29.7.0
@@ -657,16 +657,16 @@ importers:
         version: 2.1.1
       '@rollup/plugin-alias':
         specifier: 5.1.0
-        version: 5.1.0(rollup@4.6.1)
+        version: 5.1.0(rollup@4.7.0)
       '@rollup/plugin-json':
         specifier: 6.0.1
-        version: 6.0.1(rollup@4.6.1)
+        version: 6.0.1(rollup@4.7.0)
       '@rollup/plugin-replace':
         specifier: 5.0.5
-        version: 5.0.5(rollup@4.6.1)
+        version: 5.0.5(rollup@4.7.0)
       '@rollup/pluginutils':
-        specifier: 5.0.5
-        version: 5.0.5(rollup@4.6.1)
+        specifier: 5.1.0
+        version: 5.1.0(rollup@4.7.0)
       '@syuilo/aiscript':
         specifier: 0.16.0
         version: 0.16.0
@@ -675,7 +675,7 @@ importers:
         version: 2.37.0
       '@vitejs/plugin-vue':
         specifier: 4.5.1
-        version: 4.5.1(vite@5.0.5)(vue@3.3.9)
+        version: 4.5.1(vite@5.0.7)(vue@3.3.11)
       '@vue/compiler-sfc':
         specifier: 3.3.9
         version: 3.3.9
@@ -701,23 +701,23 @@ importers:
         specifier: 1.6.1
         version: 1.6.1
       chart.js:
-        specifier: 4.4.0
-        version: 4.4.0
+        specifier: 4.4.1
+        version: 4.4.1
       chartjs-adapter-date-fns:
         specifier: 3.0.0
-        version: 3.0.0(chart.js@4.4.0)(date-fns@2.30.0)
+        version: 3.0.0(chart.js@4.4.1)(date-fns@2.30.0)
       chartjs-chart-matrix:
         specifier: 2.0.1
-        version: 2.0.1(chart.js@4.4.0)
+        version: 2.0.1(chart.js@4.4.1)
       chartjs-plugin-gradient:
         specifier: 0.6.1
-        version: 0.6.1(chart.js@4.4.0)
+        version: 0.6.1(chart.js@4.4.1)
       chartjs-plugin-zoom:
         specifier: 2.0.1
-        version: 2.0.1(chart.js@4.4.0)
+        version: 2.0.1(chart.js@4.4.1)
       chromatic:
-        specifier: 9.1.0
-        version: 9.1.0
+        specifier: 10.1.0
+        version: 10.1.0
       compare-versions:
         specifier: 6.1.0
         version: 6.1.0
@@ -770,8 +770,8 @@ importers:
         specifier: 0.2.1
         version: 0.2.1
       rollup:
-        specifier: 4.6.1
-        version: 4.6.1
+        specifier: 4.7.0
+        version: 4.7.0
       sanitize-html:
         specifier: 2.11.0
         version: 2.11.0
@@ -779,8 +779,8 @@ importers:
         specifier: 1.69.5
         version: 1.69.5
       shiki:
-        specifier: ^0.14.5
-        version: 0.14.5
+        specifier: 0.14.6
+        version: 0.14.6
       strict-event-emitter-types:
         specifier: 2.0.0
         version: 2.0.0
@@ -806,84 +806,84 @@ importers:
         specifier: 14.0.0
         version: 14.0.0
       typescript:
-        specifier: 5.3.2
-        version: 5.3.2
+        specifier: 5.3.3
+        version: 5.3.3
       uuid:
         specifier: 9.0.1
         version: 9.0.1
       v-code-diff:
         specifier: 1.7.2
-        version: 1.7.2(vue@3.3.9)
+        version: 1.7.2(vue@3.3.11)
       vanilla-tilt:
         specifier: 1.8.1
         version: 1.8.1
       vite:
-        specifier: 5.0.5
-        version: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
+        specifier: 5.0.7
+        version: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
       vue:
-        specifier: 3.3.9
-        version: 3.3.9(typescript@5.3.2)
+        specifier: 3.3.11
+        version: 3.3.11(typescript@5.3.3)
       vuedraggable:
         specifier: next
-        version: 4.1.0(vue@3.3.9)
+        version: 4.1.0(vue@3.3.11)
     devDependencies:
       '@storybook/addon-actions':
-        specifier: 7.6.3
-        version: 7.6.3
+        specifier: 7.6.4
+        version: 7.6.4
       '@storybook/addon-essentials':
-        specifier: 7.6.3
-        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.4
+        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/addon-interactions':
-        specifier: 7.6.3
-        version: 7.6.3
+        specifier: 7.6.4
+        version: 7.6.4
       '@storybook/addon-links':
-        specifier: 7.6.3
-        version: 7.6.3(react@18.2.0)
+        specifier: 7.6.4
+        version: 7.6.4(react@18.2.0)
       '@storybook/addon-storysource':
-        specifier: 7.6.3
-        version: 7.6.3
+        specifier: 7.6.4
+        version: 7.6.4
       '@storybook/addons':
-        specifier: 7.6.3
-        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.4
+        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/blocks':
-        specifier: 7.6.3
-        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.4
+        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/core-events':
-        specifier: 7.6.3
-        version: 7.6.3
+        specifier: 7.6.4
+        version: 7.6.4
       '@storybook/jest':
         specifier: 0.2.3
         version: 0.2.3(vitest@0.34.6)
       '@storybook/manager-api':
-        specifier: 7.6.3
-        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.4
+        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/preview-api':
-        specifier: 7.6.3
-        version: 7.6.3
+        specifier: 7.6.4
+        version: 7.6.4
       '@storybook/react':
-        specifier: 7.6.3
-        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)
+        specifier: 7.6.4
+        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
       '@storybook/react-vite':
-        specifier: 7.6.3
-        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.6.1)(typescript@5.3.2)(vite@5.0.5)
+        specifier: 7.6.4
+        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)(rollup@4.7.0)(typescript@5.3.3)(vite@5.0.7)
       '@storybook/testing-library':
         specifier: 0.2.2
         version: 0.2.2
       '@storybook/theming':
-        specifier: 7.6.3
-        version: 7.6.3(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.4
+        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/types':
-        specifier: 7.6.3
-        version: 7.6.3
+        specifier: 7.6.4
+        version: 7.6.4
       '@storybook/vue3':
-        specifier: 7.6.3
-        version: 7.6.3(@vue/compiler-core@3.3.9)(vue@3.3.9)
+        specifier: 7.6.4
+        version: 7.6.4(@vue/compiler-core@3.3.9)(vue@3.3.11)
       '@storybook/vue3-vite':
-        specifier: 7.6.3
-        version: 7.6.3(@vue/compiler-core@3.3.9)(typescript@5.3.2)(vite@5.0.5)(vue@3.3.9)
+        specifier: 7.6.4
+        version: 7.6.4(@vue/compiler-core@3.3.9)(typescript@5.3.3)(vite@5.0.7)(vue@3.3.11)
       '@testing-library/vue':
         specifier: 8.0.1
-        version: 8.0.1(@vue/compiler-sfc@3.3.9)(vue@3.3.9)
+        version: 8.0.1(@vue/compiler-sfc@3.3.9)(vue@3.3.11)
       '@types/escape-regexp':
         specifier: 0.0.3
         version: 0.0.3
@@ -897,8 +897,8 @@ importers:
         specifier: 4.0.6
         version: 4.0.6
       '@types/node':
-        specifier: 20.10.3
-        version: 20.10.3
+        specifier: 20.10.4
+        version: 20.10.4
       '@types/punycode':
         specifier: 2.1.3
         version: 2.1.3
@@ -921,17 +921,17 @@ importers:
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.13.1
-        version: 6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2)
+        specifier: 6.13.2
+        version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.13.1
-        version: 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+        specifier: 6.13.2
+        version: 6.13.2(eslint@8.55.0)(typescript@5.3.3)
       '@vitest/coverage-v8':
         specifier: 0.34.6
         version: 0.34.6(vitest@0.34.6)
       '@vue/runtime-core':
-        specifier: 3.3.9
-        version: 3.3.9
+        specifier: 3.3.11
+        version: 3.3.11
       acorn:
         specifier: 8.11.2
         version: 8.11.2
@@ -939,14 +939,14 @@ importers:
         specifier: 7.0.3
         version: 7.0.3
       cypress:
-        specifier: 13.6.0
-        version: 13.6.0
+        specifier: 13.6.1
+        version: 13.6.1
       eslint:
         specifier: 8.55.0
         version: 8.55.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)
+        version: 2.29.0(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)
       eslint-plugin-vue:
         specifier: 9.19.2
         version: 9.19.2(eslint@8.55.0)
@@ -961,7 +961,7 @@ importers:
         version: 4.0.5
       msw:
         specifier: 1.3.2
-        version: 1.3.2(typescript@5.3.2)
+        version: 1.3.2(typescript@5.3.3)
       msw-storybook-addon:
         specifier: 1.10.0
         version: 1.10.0(msw@1.3.2)
@@ -981,11 +981,11 @@ importers:
         specifier: 2.0.3
         version: 2.0.3
       storybook:
-        specifier: 7.6.3
-        version: 7.6.3
+        specifier: 7.6.4
+        version: 7.6.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.6.3)(@storybook/components@7.5.3)(@storybook/core-events@7.6.3)(@storybook/manager-api@7.6.3)(@storybook/preview-api@7.6.3)(@storybook/theming@7.6.3)(@storybook/types@7.6.3)(react-dom@18.2.0)(react@18.2.0)
+        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.4)(@storybook/components@7.6.3)(@storybook/core-events@7.6.4)(@storybook/manager-api@7.6.4)(@storybook/preview-api@7.6.4)(@storybook/theming@7.6.4)(@storybook/types@7.6.4)(react-dom@18.2.0)(react@18.2.0)
       summaly:
         specifier: github:misskey-dev/summaly
         version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8
@@ -1002,8 +1002,8 @@ importers:
         specifier: 9.3.2
         version: 9.3.2(eslint@8.55.0)
       vue-tsc:
-        specifier: 1.8.24
-        version: 1.8.24(typescript@5.3.2)
+        specifier: 1.8.25
+        version: 1.8.25(typescript@5.3.3)
 
   packages/misskey-js:
     dependencies:
@@ -1021,29 +1021,29 @@ importers:
         version: 4.4.0
     devDependencies:
       '@microsoft/api-extractor':
-        specifier: 7.38.3
-        version: 7.38.3(@types/node@20.10.3)
+        specifier: 7.38.5
+        version: 7.38.5(@types/node@20.10.4)
       '@swc/jest':
         specifier: 0.2.29
         version: 0.2.29(@swc/core@1.3.100)
       '@types/jest':
-        specifier: 29.5.10
-        version: 29.5.10
+        specifier: 29.5.11
+        version: 29.5.11
       '@types/node':
-        specifier: 20.10.3
-        version: 20.10.3
+        specifier: 20.10.4
+        version: 20.10.4
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.13.1
-        version: 6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2)
+        specifier: 6.13.2
+        version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.13.1
-        version: 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+        specifier: 6.13.2
+        version: 6.13.2(eslint@8.55.0)(typescript@5.3.3)
       eslint:
         specifier: 8.55.0
         version: 8.55.0
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.10.3)
+        version: 29.7.0(@types/node@20.10.4)
       jest-fetch-mock:
         specifier: 3.0.3
         version: 3.0.3
@@ -1060,8 +1060,8 @@ importers:
         specifier: 0.29.0
         version: 0.29.0
       typescript:
-        specifier: 5.3.2
-        version: 5.3.2
+        specifier: 5.3.3
+        version: 5.3.3
 
   packages/misskey-js/generator:
     devDependencies:
@@ -1073,10 +1073,10 @@ importers:
         version: 20.9.1
       '@typescript-eslint/eslint-plugin':
         specifier: 6.11.0
-        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2)
+        version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 6.11.0
-        version: 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+        version: 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       eslint:
         specifier: 8.53.0
         version: 8.53.0
@@ -1093,8 +1093,8 @@ importers:
         specifier: 4.4.0
         version: 4.4.0
       typescript:
-        specifier: 5.3.2
-        version: 5.3.2
+        specifier: 5.3.3
+        version: 5.3.3
 
   packages/sw:
     dependencies:
@@ -1109,8 +1109,8 @@ importers:
         version: link:../misskey-js
     devDependencies:
       '@typescript-eslint/parser':
-        specifier: 6.13.1
-        version: 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+        specifier: 6.13.2
+        version: 6.13.2(eslint@8.55.0)(typescript@5.3.3)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: /@types/serviceworker@0.0.67
@@ -1119,10 +1119,10 @@ importers:
         version: 8.55.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)
+        version: 2.29.0(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)
       typescript:
-        specifier: 5.3.2
-        version: 5.3.2
+        specifier: 5.3.3
+        version: 5.3.3
 
 packages:
 
@@ -1891,31 +1891,13 @@ packages:
     resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/compat-data': 7.22.9
+      '@babel/compat-data': 7.23.5
       '@babel/helper-validator-option': 7.23.5
-      browserslist: 4.21.9
+      browserslist: 4.22.2
       lru-cache: 5.1.1
       semver: 6.3.1
     dev: true
 
-  /@babel/helper-create-class-features-plugin@7.22.9(@babel/core@7.23.5):
-    resolution: {integrity: sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-    dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-annotate-as-pure': 7.22.5
-      '@babel/helper-environment-visitor': 7.22.5
-      '@babel/helper-function-name': 7.22.5
-      '@babel/helper-member-expression-to-functions': 7.22.5
-      '@babel/helper-optimise-call-expression': 7.22.5
-      '@babel/helper-replace-supers': 7.22.9(@babel/core@7.23.5)
-      '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
-      '@babel/helper-split-export-declaration': 7.22.6
-      semver: 6.3.1
-    dev: true
-
   /@babel/helper-create-class-features-plugin@7.23.5(@babel/core@7.23.5):
     resolution: {integrity: sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==}
     engines: {node: '>=6.9.0'}
@@ -1946,18 +1928,6 @@ packages:
       semver: 6.3.1
     dev: true
 
-  /@babel/helper-create-regexp-features-plugin@7.22.9(@babel/core@7.23.5):
-    resolution: {integrity: sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-    dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-annotate-as-pure': 7.22.5
-      regexpu-core: 5.3.2
-      semver: 6.3.1
-    dev: true
-
   /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==}
     peerDependencies:
@@ -2006,13 +1976,6 @@ packages:
       '@babel/types': 7.22.17
     dev: true
 
-  /@babel/helper-member-expression-to-functions@7.22.5:
-    resolution: {integrity: sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.23.5
-    dev: true
-
   /@babel/helper-member-expression-to-functions@7.23.0:
     resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==}
     engines: {node: '>=6.9.0'}
@@ -2098,18 +2061,6 @@ packages:
       '@babel/helper-optimise-call-expression': 7.22.5
     dev: true
 
-  /@babel/helper-replace-supers@7.22.9(@babel/core@7.23.5):
-    resolution: {integrity: sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-    dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-environment-visitor': 7.22.5
-      '@babel/helper-member-expression-to-functions': 7.22.5
-      '@babel/helper-optimise-call-expression': 7.22.5
-    dev: true
-
   /@babel/helper-simple-access@7.22.5:
     resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
     engines: {node: '>=6.9.0'}
@@ -2138,7 +2089,6 @@ packages:
   /@babel/helper-string-parser@7.23.4:
     resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
     engines: {node: '>=6.9.0'}
-    dev: true
 
   /@babel/helper-validator-identifier@7.22.15:
     resolution: {integrity: sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==}
@@ -2147,7 +2097,6 @@ packages:
   /@babel/helper-validator-identifier@7.22.20:
     resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
     engines: {node: '>=6.9.0'}
-    dev: true
 
   /@babel/helper-validator-identifier@7.22.5:
     resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
@@ -2168,7 +2117,7 @@ packages:
     resolution: {integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==}
     engines: {node: '>=6.9.0'}
     dependencies:
-      '@babel/helper-function-name': 7.22.5
+      '@babel/helper-function-name': 7.23.0
       '@babel/template': 7.22.15
       '@babel/types': 7.23.5
     dev: true
@@ -2242,7 +2191,6 @@ packages:
     hasBin: true
     dependencies:
       '@babel/types': 7.23.5
-    dev: true
 
   /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==}
@@ -2610,7 +2558,7 @@ packages:
       '@babel/core': ^7.0.0
     dependencies:
       '@babel/core': 7.23.5
-      '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.23.5)
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
@@ -2669,17 +2617,6 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-class-properties@7.22.5(@babel/core@7.23.5):
-    resolution: {integrity: sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.23.5)
-      '@babel/helper-plugin-utils': 7.22.5
-    dev: true
-
   /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==}
     engines: {node: '>=6.9.0'}
@@ -2925,7 +2862,7 @@ packages:
       '@babel/core': ^7.0.0
     dependencies:
       '@babel/core': 7.23.5
-      '@babel/helper-create-regexp-features-plugin': 7.22.9(@babel/core@7.23.5)
+      '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.5)
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
@@ -3019,17 +2956,6 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-private-methods@7.22.5(@babel/core@7.23.5):
-    resolution: {integrity: sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.23.5
-      '@babel/helper-create-class-features-plugin': 7.22.9(@babel/core@7.23.5)
-      '@babel/helper-plugin-utils': 7.22.5
-    dev: true
-
   /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.23.5):
     resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==}
     engines: {node: '>=6.9.0'}
@@ -3064,23 +2990,23 @@ packages:
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-react-jsx-self@7.21.0(@babel/core@7.22.11):
+  /@babel/plugin-transform-react-jsx-self@7.21.0(@babel/core@7.23.5):
     resolution: {integrity: sha512-f/Eq+79JEu+KUANFks9UZCcvydOOGMgF7jBrcwjHa5jTZD8JivnhCJYvmlhR/WTXBWonDExPoW0eO/CR4QJirA==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
-  /@babel/plugin-transform-react-jsx-source@7.19.6(@babel/core@7.22.11):
+  /@babel/plugin-transform-react-jsx-source@7.19.6(@babel/core@7.23.5):
     resolution: {integrity: sha512-RpAi004QyMNisst/pvSanoRdJ4q+jMCWyk9zdw/CyLB9j8RXEahodR6l2GyttDRyEVWZtbN+TpLiHJ3t34LbsQ==}
     engines: {node: '>=6.9.0'}
     peerDependencies:
       '@babel/core': ^7.0.0-0
     dependencies:
-      '@babel/core': 7.22.11
+      '@babel/core': 7.23.5
       '@babel/helper-plugin-utils': 7.22.5
     dev: true
 
@@ -3297,7 +3223,7 @@ packages:
       babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.5)
       babel-plugin-polyfill-corejs3: 0.8.6(@babel/core@7.23.5)
       babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.5)
-      core-js-compat: 3.31.1
+      core-js-compat: 3.33.3
       semver: 6.3.1
     transitivePeerDependencies:
       - supports-color
@@ -3448,7 +3374,6 @@ packages:
       '@babel/helper-string-parser': 7.23.4
       '@babel/helper-validator-identifier': 7.22.20
       to-fast-properties: 2.0.0
-    dev: true
 
   /@base2/pretty-print-object@1.0.1:
     resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==}
@@ -3458,29 +3383,29 @@ packages:
     resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
     dev: true
 
-  /@bull-board/api@5.10.1(@bull-board/ui@5.10.1):
-    resolution: {integrity: sha512-IBsMtB1xBz+bhIYt3aAXHu9LghTjJ8zKOxJdn7K+z0ZpD0hBF4xWSan5dEhQumk69qO9xq+2EX4wgwrc1mPUxw==}
+  /@bull-board/api@5.10.2(@bull-board/ui@5.10.2):
+    resolution: {integrity: sha512-Gx98cqN0cryJB35mVKjYsEnD3NxArWY3Xi2E5Wrr17QTVOzWEP4jyDQ/riiapVdnYqc9RSsxCCmdIaNdNPcXlQ==}
     peerDependencies:
-      '@bull-board/ui': 5.10.1
+      '@bull-board/ui': 5.10.2
     dependencies:
-      '@bull-board/ui': 5.10.1
+      '@bull-board/ui': 5.10.2
       redis-info: 3.1.0
     dev: false
 
-  /@bull-board/fastify@5.10.1:
-    resolution: {integrity: sha512-Wj6y4T1BXlY3EVjYnRoRDN1K/NxZlQmyzCjOkeQujkivaXgKQZUhEYVgamg5nMwt6gD4/ve1eQok1eXX3luY5Q==}
+  /@bull-board/fastify@5.10.2:
+    resolution: {integrity: sha512-NrV1PBu1jwXMBnLslxWLjmt4Qb0oPDSngcUXRll5B8Lvm6E8jtecmnVuNb2X1EtpIGVqhgwlGZ+Q7AC+3ZBMFg==}
     dependencies:
-      '@bull-board/api': 5.10.1(@bull-board/ui@5.10.1)
-      '@bull-board/ui': 5.10.1
+      '@bull-board/api': 5.10.2(@bull-board/ui@5.10.2)
+      '@bull-board/ui': 5.10.2
       '@fastify/static': 6.12.0
       '@fastify/view': 8.2.0
       ejs: 3.1.9
     dev: false
 
-  /@bull-board/ui@5.10.1:
-    resolution: {integrity: sha512-8jpNh5XM9mzBd/2PTXqqIzF5S6fau+GyAjiN8LCgpW+ralQZ9QtJ8B00srp/2x1cQro9BVV3g9dXt1nWmkz6yw==}
+  /@bull-board/ui@5.10.2:
+    resolution: {integrity: sha512-wU9XmrX/COISZ3+sn3VEDB1UtPt7szu4QSKTw1O0q+U1JLM4Kxfs3tH9ZAIulzMrY+CQtkJXd+dKZPuRqy4rfQ==}
     dependencies:
-      '@bull-board/api': 5.10.1(@bull-board/ui@5.10.1)
+      '@bull-board/api': 5.10.2(@bull-board/ui@5.10.2)
     dev: false
 
   /@canvas/image-data@1.0.0:
@@ -3669,7 +3594,7 @@ packages:
     dependencies:
       ky: 0.33.3
       ky-universal: 0.11.0(ky@0.33.3)
-      undici: 5.22.1
+      undici: 5.28.1
     transitivePeerDependencies:
       - web-streams-polyfill
     dev: false
@@ -4148,8 +4073,8 @@ packages:
     engines: {node: '>=14'}
     dev: false
 
-  /@fastify/accepts@4.2.0:
-    resolution: {integrity: sha512-nM2OsxJvdBZOcpMqtbolW7RMB2bn/L1PDZ03+zVlC+/JVdBEW+ufWEKmtsMVzelU5TwyTc/+X/CRr/98JfE9mQ==}
+  /@fastify/accepts@4.3.0:
+    resolution: {integrity: sha512-QK4FoqXdwwPmaPOLL6NrxsyaXVvdviYVoS6ltHyOLdFlUyREIaMykHQIp+x0aJz9hB3B3n/Ht6QRdvBeGkptGQ==}
     dependencies:
       accepts: 1.3.8
       fastify-plugin: 4.5.0
@@ -4173,7 +4098,6 @@ packages:
   /@fastify/busboy@2.1.0:
     resolution: {integrity: sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==}
     engines: {node: '>=14'}
-    dev: true
 
   /@fastify/cookie@9.2.0:
     resolution: {integrity: sha512-fkg1yjjQRHPFAxSHeLC8CqYuNzvR6Lwlj/KjrzQcGjNBK+K82nW+UfCjfN71g1GkoVoc1GTOgIWkFJpcMfMkHQ==}
@@ -4182,8 +4106,8 @@ packages:
       fastify-plugin: 4.5.0
     dev: false
 
-  /@fastify/cors@8.4.1:
-    resolution: {integrity: sha512-iYQJtrY3pFiDS5mo5zRaudzg2OcUdJ96PD6xfkKOOEilly5nnrFZx/W6Sce2T79xxlEn2qpU3t5+qS2phS369w==}
+  /@fastify/cors@8.4.2:
+    resolution: {integrity: sha512-IVynbcPG9eWiJ0P/A1B+KynmiU/yTYbu3ooBUSIeHfca/N1XLb9nIJVCws+YTr2q63MA8Y6QLeXQczEv4npM9g==}
     dependencies:
       fastify-plugin: 4.5.0
       mnemonist: 0.39.5
@@ -4434,7 +4358,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
@@ -4455,14 +4379,14 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.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.10.3)
+      jest-config: 29.7.0(@types/node@20.10.4)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -4497,7 +4421,7 @@ packages:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       jest-mock: 29.7.0
     dev: true
 
@@ -4524,7 +4448,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -4557,7 +4481,7 @@ packages:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.18
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       chalk: 4.1.2
       collect-v8-coverage: 1.0.1
       exit: 0.1.2
@@ -4651,7 +4575,7 @@ packages:
     dependencies:
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       '@types/yargs': 16.0.5
       chalk: 4.1.2
     dev: true
@@ -4663,12 +4587,12 @@ packages:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       '@types/yargs': 17.0.19
       chalk: 4.1.2
     dev: true
 
-  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.2)(vite@5.0.5):
+  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.0.7):
     resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==}
     peerDependencies:
       typescript: '>= 4.3.x'
@@ -4680,9 +4604,9 @@ packages:
       glob: 7.2.3
       glob-promise: 4.2.2(glob@7.2.3)
       magic-string: 0.27.0
-      react-docgen-typescript: 2.2.2(typescript@5.3.2)
-      typescript: 5.3.2
-      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
+      react-docgen-typescript: 2.2.2(typescript@5.3.3)
+      typescript: 5.3.3
+      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
     dev: true
 
   /@jridgewell/gen-mapping@0.3.2:
@@ -4771,24 +4695,24 @@ packages:
       react: 18.2.0
     dev: true
 
-  /@microsoft/api-extractor-model@7.28.2(@types/node@20.10.3):
-    resolution: {integrity: sha512-vkojrM2fo3q4n4oPh4uUZdjJ2DxQ2+RnDQL/xhTWSRUNPF6P4QyrvY357HBxbnltKcYu+nNNolVqc6TIGQ73Ig==}
+  /@microsoft/api-extractor-model@7.28.3(@types/node@20.10.4):
+    resolution: {integrity: sha512-wT/kB2oDbdZXITyDh2SQLzaWwTOFbV326fP0pUwNW00WeliARs0qjmXBWmGWardEzp2U3/axkO3Lboqun6vrig==}
     dependencies:
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.61.0(@types/node@20.10.3)
+      '@rushstack/node-core-library': 3.62.0(@types/node@20.10.4)
     transitivePeerDependencies:
       - '@types/node'
     dev: true
 
-  /@microsoft/api-extractor@7.38.3(@types/node@20.10.3):
-    resolution: {integrity: sha512-xt9iYyC5f39281j77JTA9C3ISJpW1XWkCcnw+2vM78CPnro6KhPfwQdPDfwS5JCPNuq0grm8cMdPUOPvrchDWw==}
+  /@microsoft/api-extractor@7.38.5(@types/node@20.10.4):
+    resolution: {integrity: sha512-c/w2zfqBcBJxaCzpJNvFoouWewcYrUOfeu5ZkWCCIXTF9a/gXM85RGevEzlMAIEGM/kssAAZSXRJIZ3Q5vLFow==}
     hasBin: true
     dependencies:
-      '@microsoft/api-extractor-model': 7.28.2(@types/node@20.10.3)
+      '@microsoft/api-extractor-model': 7.28.3(@types/node@20.10.4)
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.61.0(@types/node@20.10.3)
+      '@rushstack/node-core-library': 3.62.0(@types/node@20.10.4)
       '@rushstack/rig-package': 0.5.1
       '@rushstack/ts-command-line': 4.17.1
       colors: 1.2.5
@@ -4908,7 +4832,7 @@ packages:
       tar-fs: 2.1.1
     dev: true
 
-  /@nestjs/common@10.2.10(reflect-metadata@0.1.13)(rxjs@7.8.1):
+  /@nestjs/common@10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1):
     resolution: {integrity: sha512-fwAk931rjW8CNH2Mgwawq/7HWHH1dxkOLdcgs7U52ddLk8CtHXjejm1cbNahewlSbNhvlOl7y1STLHutE6sUqw==}
     peerDependencies:
       class-transformer: '*'
@@ -4922,13 +4846,13 @@ packages:
         optional: true
     dependencies:
       iterare: 1.2.1
-      reflect-metadata: 0.1.13
+      reflect-metadata: 0.1.14
       rxjs: 7.8.1
       tslib: 2.6.2
       uid: 2.0.2
     dev: false
 
-  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.13)(rxjs@7.8.1):
+  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1):
     resolution: {integrity: sha512-+ckOI6BPi2ZMHikT9MCG4ctHDc4OnjhoIytrn7f2AYMMXI4bnutJhqyQKc30VDka5x3Wq6QAD57pgSP7y+JjJg==}
     requiresBuild: true
     peerDependencies:
@@ -4946,12 +4870,12 @@ packages:
       '@nestjs/websockets':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.10(reflect-metadata@0.1.13)(rxjs@7.8.1)
+      '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
       '@nuxtjs/opencollective': 0.3.2
       fast-safe-stringify: 2.1.1
       iterare: 1.2.1
       path-to-regexp: 3.2.0
-      reflect-metadata: 0.1.13
+      reflect-metadata: 0.1.14
       rxjs: 7.8.1
       tslib: 2.6.2
       uid: 2.0.2
@@ -4972,8 +4896,8 @@ packages:
       '@nestjs/platform-express':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.10(reflect-metadata@0.1.13)(rxjs@7.8.1)
-      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.13)(rxjs@7.8.1)
+      '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1)
       tslib: 2.6.2
     dev: false
 
@@ -5631,7 +5555,7 @@ packages:
       '@babel/runtime': 7.23.2
     dev: true
 
-  /@rollup/plugin-alias@5.1.0(rollup@4.6.1):
+  /@rollup/plugin-alias@5.1.0(rollup@4.7.0):
     resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5640,11 +5564,11 @@ packages:
       rollup:
         optional: true
     dependencies:
-      rollup: 4.6.1
+      rollup: 4.7.0
       slash: 4.0.0
     dev: false
 
-  /@rollup/plugin-json@6.0.1(rollup@4.6.1):
+  /@rollup/plugin-json@6.0.1(rollup@4.7.0):
     resolution: {integrity: sha512-RgVfl5hWMkxN1h/uZj8FVESvPuBJ/uf6ly6GTj0GONnkfoBN5KC0MSz+PN2OLDgYXMhtG0mWpTrkiOjoxAIevw==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5653,11 +5577,11 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.0.5(rollup@4.6.1)
-      rollup: 4.6.1
+      '@rollup/pluginutils': 5.1.0(rollup@4.7.0)
+      rollup: 4.7.0
     dev: false
 
-  /@rollup/plugin-replace@5.0.5(rollup@4.6.1):
+  /@rollup/plugin-replace@5.0.5(rollup@4.7.0):
     resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5666,13 +5590,13 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.0.5(rollup@4.6.1)
+      '@rollup/pluginutils': 5.1.0(rollup@4.7.0)
       magic-string: 0.30.5
-      rollup: 4.6.1
+      rollup: 4.7.0
     dev: false
 
-  /@rollup/pluginutils@5.0.5(rollup@4.6.1):
-    resolution: {integrity: sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==}
+  /@rollup/pluginutils@5.1.0(rollup@4.7.0):
+    resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
       rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
@@ -5683,101 +5607,108 @@ packages:
       '@types/estree': 1.0.5
       estree-walker: 2.0.2
       picomatch: 2.3.1
-      rollup: 4.6.1
+      rollup: 4.7.0
 
-  /@rollup/rollup-android-arm-eabi@4.6.1:
-    resolution: {integrity: sha512-0WQ0ouLejaUCRsL93GD4uft3rOmB8qoQMU05Kb8CmMtMBe7XUDLAltxVZI1q6byNqEtU7N1ZX1Vw5lIpgulLQA==}
+  /@rollup/rollup-android-arm-eabi@4.7.0:
+    resolution: {integrity: sha512-rGku10pL1StFlFvXX5pEv88KdGW6DHUghsxyP/aRYb9eH+74jTGJ3U0S/rtlsQ4yYq1Hcc7AMkoJOb1xu29Fxw==}
     cpu: [arm]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-android-arm64@4.6.1:
-    resolution: {integrity: sha512-1TKm25Rn20vr5aTGGZqo6E4mzPicCUD79k17EgTLAsXc1zysyi4xXKACfUbwyANEPAEIxkzwue6JZ+stYzWUTA==}
+  /@rollup/rollup-android-arm64@4.7.0:
+    resolution: {integrity: sha512-/EBw0cuJ/KVHiU2qyVYUhogXz7W2vXxBzeE9xtVIMC+RyitlY2vvaoysMUqASpkUtoNIHlnKTu/l7mXOPgnKOA==}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-arm64@4.6.1:
-    resolution: {integrity: sha512-cEXJQY/ZqMACb+nxzDeX9IPLAg7S94xouJJCNVE5BJM8JUEP4HeTF+ti3cmxWeSJo+5D+o8Tc0UAWUkfENdeyw==}
+  /@rollup/rollup-darwin-arm64@4.7.0:
+    resolution: {integrity: sha512-4VXG1bgvClJdbEYYjQ85RkOtwN8sqI3uCxH0HC5w9fKdqzRzgG39K7GAehATGS8jghA7zNoS5CjSKkDEqWmNZg==}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-x64@4.6.1:
-    resolution: {integrity: sha512-LoSU9Xu56isrkV2jLldcKspJ7sSXmZWkAxg7sW/RfF7GS4F5/v4EiqKSMCFbZtDu2Nc1gxxFdQdKwkKS4rwxNg==}
+  /@rollup/rollup-darwin-x64@4.7.0:
+    resolution: {integrity: sha512-/ImhO+T/RWJ96hUbxiCn2yWI0/MeQZV/aeukQQfhxiSXuZJfyqtdHPUPrc84jxCfXTxbJLmg4q+GBETeb61aNw==}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm-gnueabihf@4.6.1:
-    resolution: {integrity: sha512-EfI3hzYAy5vFNDqpXsNxXcgRDcFHUWSx5nnRSCKwXuQlI5J9dD84g2Usw81n3FLBNsGCegKGwwTVsSKK9cooSQ==}
+  /@rollup/rollup-linux-arm-gnueabihf@4.7.0:
+    resolution: {integrity: sha512-zhye8POvTyUXlKbfPBVqoHy3t43gIgffY+7qBFqFxNqVtltQLtWeHNAbrMnXiLIfYmxcoL/feuLDote2tx+Qbg==}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-gnu@4.6.1:
-    resolution: {integrity: sha512-9lhc4UZstsegbNLhH0Zu6TqvDfmhGzuCWtcTFXY10VjLLUe4Mr0Ye2L3rrtHaDd/J5+tFMEuo5LTCSCMXWfUKw==}
+  /@rollup/rollup-linux-arm64-gnu@4.7.0:
+    resolution: {integrity: sha512-RAdr3OJnUum6Vs83cQmKjxdTg31zJnLLTkjhcFt0auxM6jw00GD6IPFF42uasYPr/wGC6TRm7FsQiJyk0qIEfg==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-musl@4.6.1:
-    resolution: {integrity: sha512-FfoOK1yP5ksX3wwZ4Zk1NgyGHZyuRhf99j64I5oEmirV8EFT7+OhUZEnP+x17lcP/QHJNWGsoJwrz4PJ9fBEXw==}
+  /@rollup/rollup-linux-arm64-musl@4.7.0:
+    resolution: {integrity: sha512-nhWwYsiJwZGq7SyR3afS3EekEOsEAlrNMpPC4ZDKn5ooYSEjDLe9W/xGvoIV8/F/+HNIY6jY8lIdXjjxfxopXw==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-gnu@4.6.1:
-    resolution: {integrity: sha512-DNGZvZDO5YF7jN5fX8ZqmGLjZEXIJRdJEdTFMhiyXqyXubBa0WVLDWSNlQ5JR2PNgDbEV1VQowhVRUh+74D+RA==}
+  /@rollup/rollup-linux-riscv64-gnu@4.7.0:
+    resolution: {integrity: sha512-rlfy5RnQG1aop1BL/gjdH42M2geMUyVQqd52GJVirqYc787A/XVvl3kQ5NG/43KXgOgE9HXgCaEH05kzQ+hLoA==}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@rollup/rollup-linux-x64-gnu@4.7.0:
+    resolution: {integrity: sha512-cCkoGlGWfBobdDtiiypxf79q6k3/iRVGu1HVLbD92gWV5WZbmuWJCgRM4x2N6i7ljGn1cGytPn9ZAfS8UwF6vg==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-musl@4.6.1:
-    resolution: {integrity: sha512-RkJVNVRM+piYy87HrKmhbexCHg3A6Z6MU0W9GHnJwBQNBeyhCJG9KDce4SAMdicQnpURggSvtbGo9xAWOfSvIQ==}
+  /@rollup/rollup-linux-x64-musl@4.7.0:
+    resolution: {integrity: sha512-R2oBf2p/Arc1m+tWmiWbpHBjEcJnHVnv6bsypu4tcKdrYTpDfl1UT9qTyfkIL1iiii5D4WHxUHCg5X0pzqmxFg==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-arm64-msvc@4.6.1:
-    resolution: {integrity: sha512-v2FVT6xfnnmTe3W9bJXl6r5KwJglMK/iRlkKiIFfO6ysKs0rDgz7Cwwf3tjldxQUrHL9INT/1r4VA0n9L/F1vQ==}
+  /@rollup/rollup-win32-arm64-msvc@4.7.0:
+    resolution: {integrity: sha512-CPtgaQL1aaPc80m8SCVEoxFGHxKYIt3zQYC3AccL/SqqiWXblo3pgToHuBwR8eCP2Toa+X1WmTR/QKFMykws7g==}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-ia32-msvc@4.6.1:
-    resolution: {integrity: sha512-YEeOjxRyEjqcWphH9dyLbzgkF8wZSKAKUkldRY6dgNR5oKs2LZazqGB41cWJ4Iqqcy9/zqYgmzBkRoVz3Q9MLw==}
+  /@rollup/rollup-win32-ia32-msvc@4.7.0:
+    resolution: {integrity: sha512-pmioUlttNh9GXF5x2CzNa7Z8kmRTyhEzzAC+2WOOapjewMbl+3tGuAnxbwc5JyG8Jsz2+hf/QD/n5VjimOZ63g==}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-x64-msvc@4.6.1:
-    resolution: {integrity: sha512-0zfTlFAIhgz8V2G8STq8toAjsYYA6eci1hnXuyOTUFnymrtJwnS6uGKiv3v5UrPZkBlamLvrLV2iiaeqCKzb0A==}
+  /@rollup/rollup-win32-x64-msvc@4.7.0:
+    resolution: {integrity: sha512-SeZzC2QhhdBQUm3U0c8+c/P6UlRyBcLL2Xp5KX7z46WXZxzR8RJSIWL9wSUeBTgxog5LTPJuPj0WOT9lvrtP7Q==}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rushstack/node-core-library@3.61.0(@types/node@20.10.3):
-    resolution: {integrity: sha512-tdOjdErme+/YOu4gPed3sFS72GhtWCgNV9oDsHDnoLY5oDfwjKUc9Z+JOZZ37uAxcm/OCahDHfuu2ugqrfWAVQ==}
+  /@rushstack/node-core-library@3.62.0(@types/node@20.10.4):
+    resolution: {integrity: sha512-88aJn2h8UpSvdwuDXBv1/v1heM6GnBf3RjEy6ZPP7UnzHNCqOHA2Ut+ScYUbXcqIdfew9JlTAe3g+cnX9xQ/Aw==}
     peerDependencies:
       '@types/node': '*'
     peerDependenciesMeta:
       '@types/node':
         optional: true
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       colors: 1.2.5
       fs-extra: 7.0.1
       import-lazy: 4.0.0
@@ -6364,10 +6295,10 @@ packages:
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
     dev: false
 
-  /@storybook/addon-actions@7.6.3:
-    resolution: {integrity: sha512-f4HXteYE8IJXztAK+ab5heSjXWNWvyIAU63T3Fqe3zmqONwCerUKY54Op+RkAZc/R6aALTxvGRKAH2ff8g2vjQ==}
+  /@storybook/addon-actions@7.6.4:
+    resolution: {integrity: sha512-91UD5KPDik74VKVioPMcbwwvDXN/non8p1wArYAHCHCmd/Pts5MJRiFueSdfomSpNjUtjtn6eSXtwpIL3XVOfQ==}
     dependencies:
-      '@storybook/core-events': 7.6.3
+      '@storybook/core-events': 7.6.4
       '@storybook/global': 5.0.0
       '@types/uuid': 9.0.7
       dequal: 2.0.3
@@ -6375,18 +6306,18 @@ packages:
       uuid: 9.0.1
     dev: true
 
-  /@storybook/addon-backgrounds@7.6.3:
-    resolution: {integrity: sha512-ZZFNf8FBYBsuXvXdVk3sBgxJTn6s0HznuEE9OmAA7tMsLEDlUiWS9LEvjX2jX5K0kWivHTkJDTXV0NcLL1vWAg==}
+  /@storybook/addon-backgrounds@7.6.4:
+    resolution: {integrity: sha512-gNy3kIkHSr+Lg/jVDHwbZjIe1po5SDGZNVe39vrJwnqGz8T1clWes9WHCL6zk/uaCDA3yUna2Nt/KlOFAWDSoQ==}
     dependencies:
       '@storybook/global': 5.0.0
       memoizerific: 1.11.3
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-controls@7.6.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-xsM3z+CY1YOPqrcCldQLoon947fbd/o3gSO7hM3NwKiw/2WikExPO3VM4R2oi4W4PvnhkSOIO+ZDRuSs1yFmOg==}
+  /@storybook/addon-controls@7.6.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-k4AtZfazmD/nL3JAtLGAB7raPhkhUo0jWnaZWrahd9h1Fm13mBU/RW+JzTRhCw3Mp2HPERD7NI5Qcd2fUP6WDA==}
     dependencies:
-      '@storybook/blocks': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/blocks': 7.6.4(react-dom@18.2.0)(react@18.2.0)
       lodash: 4.17.21
       ts-dedent: 2.2.0
     transitivePeerDependencies:
@@ -6398,27 +6329,27 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-docs@7.6.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-2Ts+3EFg9ehkQdbjBWnCH1SE0BdyCLN6hO2N030tGxi0Vko9t9O7NLj5qdBwxLcEzb/XzL4zWukzfU17pktQwA==}
+  /@storybook/addon-docs@7.6.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-PbFMbvC9sK3sGdMhwmagXs9TqopTp9FySji+L8O7W9SHRC6wSmdwoWWPWybkOYxr/z/wXi7EM0azSAX7yQxLbw==}
     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.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/client-logger': 7.6.3
-      '@storybook/components': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/csf-plugin': 7.6.3
-      '@storybook/csf-tools': 7.6.3
+      '@storybook/blocks': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/client-logger': 7.6.4
+      '@storybook/components': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/csf-plugin': 7.6.4
+      '@storybook/csf-tools': 7.6.4
       '@storybook/global': 5.0.0
       '@storybook/mdx2-csf': 1.0.0
-      '@storybook/node-logger': 7.6.3
-      '@storybook/postinstall': 7.6.3
-      '@storybook/preview-api': 7.6.3
-      '@storybook/react-dom-shim': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.3
+      '@storybook/node-logger': 7.6.4
+      '@storybook/postinstall': 7.6.4
+      '@storybook/preview-api': 7.6.4
+      '@storybook/react-dom-shim': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/theming': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.4
       fs-extra: 11.1.1
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -6432,25 +6363,25 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-essentials@7.6.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-bpbt5O0wcB83VLZg8QMXut+8g+7EF4iuevpwiynN9mbpQFvG49c6SE6T2eFJKTvVb4zszyfcNA0Opne2G83wZw==}
+  /@storybook/addon-essentials@7.6.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-J+zPmP4pbuuFxQ3pjLRYQRnxEtp7jF3xRXGFO8brVnEqtqoxwJ6j3euUrRLe0rpGAU3AD7dYfaaFjd3xkENgTw==}
     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.6.3
-      '@storybook/addon-backgrounds': 7.6.3
-      '@storybook/addon-controls': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-docs': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-highlight': 7.6.3
-      '@storybook/addon-measure': 7.6.3
-      '@storybook/addon-outline': 7.6.3
-      '@storybook/addon-toolbars': 7.6.3
-      '@storybook/addon-viewport': 7.6.3
-      '@storybook/core-common': 7.6.3
-      '@storybook/manager-api': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/node-logger': 7.6.3
-      '@storybook/preview-api': 7.6.3
+      '@storybook/addon-actions': 7.6.4
+      '@storybook/addon-backgrounds': 7.6.4
+      '@storybook/addon-controls': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-docs': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-highlight': 7.6.4
+      '@storybook/addon-measure': 7.6.4
+      '@storybook/addon-outline': 7.6.4
+      '@storybook/addon-toolbars': 7.6.4
+      '@storybook/addon-viewport': 7.6.4
+      '@storybook/core-common': 7.6.4
+      '@storybook/manager-api': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/node-logger': 7.6.4
+      '@storybook/preview-api': 7.6.4
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       ts-dedent: 2.2.0
@@ -6461,24 +6392,24 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-highlight@7.6.3:
-    resolution: {integrity: sha512-Z9AJ05XCTzFZPAxQSkQf9/Hazf5/QQI0jYSsvKqt7Vk+03q5727oD9KcIY5IHPYqQqN9fHExQh1eyqY8AnS8mg==}
+  /@storybook/addon-highlight@7.6.4:
+    resolution: {integrity: sha512-0kvjDzquoPwWWU61QYmEtcSGWXufnV7Z/bfBTYh132uxvV/X9YzDFcXXrxGL7sBJkK32gNUUBDuiTOxs5NxyOQ==}
     dependencies:
       '@storybook/global': 5.0.0
     dev: true
 
-  /@storybook/addon-interactions@7.6.3:
-    resolution: {integrity: sha512-Gm2UJvQC8xs9KIbVZQegTLT3VBsEZIRsXy3htNqWjSdoJZK5M4/YJ3jB247CA/Jc+Mkj7d5SlJe+bCGEzjKTbw==}
+  /@storybook/addon-interactions@7.6.4:
+    resolution: {integrity: sha512-LjK9uhkgnbGyDwwa7pQhLptDEHeTIFmy+KurfJs9T08DpvRFfuuzyW4mj/hA63R1W5yjFSAhRiZj26+D7kBIyw==}
     dependencies:
       '@storybook/global': 5.0.0
-      '@storybook/types': 7.6.3
+      '@storybook/types': 7.6.4
       jest-mock: 27.5.1
       polished: 4.2.2
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-links@7.6.3(react@18.2.0):
-    resolution: {integrity: sha512-dUIf6Y0nckxZfVQvQSqcthaycRxy69dCJLo3aORrOPL8NvGz3v1bK0AUded5wv8vnOVxfSx/Zqu7MyFr9xyjOA==}
+  /@storybook/addon-links@7.6.4(react@18.2.0):
+    resolution: {integrity: sha512-TEhxYdMhJO28gD84ej1FCwLv9oLuCPt77bRXip9ndaNPRTdHYdWv6IP94dhbuDi8eHux7Z4A/mllciFuDFrnCw==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
     peerDependenciesMeta:
@@ -6491,65 +6422,66 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-measure@7.6.3:
-    resolution: {integrity: sha512-DqxADof04ktA5GSA8XnckYGdVYyC4oN8vfKSGcPzpcKrJ2uVr0BXbcyJAEcJAshEJimmpA6nH5TxabXDFBZgPQ==}
+  /@storybook/addon-measure@7.6.4:
+    resolution: {integrity: sha512-73wsJ8PALsgWniR3MA/cmxcFuU6cRruWdIyYzOMgM8ife2Jm3xSkV7cTTXAqXt2H9Uuki4PGnuMHWWFLpPeyVA==}
     dependencies:
       '@storybook/global': 5.0.0
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/addon-outline@7.6.3:
-    resolution: {integrity: sha512-M7d2tcqBBl+mPBUS6Nrwis50QYSCcmT/uKamud7CnlIWsMH/5GZFfAzGSLY5ETfiGsSFYssOwrXLOV4y0enu2g==}
+  /@storybook/addon-outline@7.6.4:
+    resolution: {integrity: sha512-CFxGASRse/qeFocetDKFNeWZ3Aa2wapVtRciDNa4Zx7k1wCnTjEsPIm54waOuCaNVcrvO+nJUAZG5WyiorQvcg==}
     dependencies:
       '@storybook/global': 5.0.0
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-storysource@7.6.3:
-    resolution: {integrity: sha512-iiuOMdCSxl4NHRkZmpvEqKvs3NLM+tv0vUDeWS9BACmDEsSgqlo8UQChueplm9GWyYLxTarMkGk9yxanGcLS3w==}
+  /@storybook/addon-storysource@7.6.4:
+    resolution: {integrity: sha512-D63IB8bkqn5ZDq4yjvkcLVfGz3OcAQUohlxSFR1e7COo8jMSTiQWjN7xaVPNOnVJRCj6GrlRlto/hqGl+F+WiQ==}
     dependencies:
+      '@storybook/source-loader': 7.6.4
       estraverse: 5.3.0
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/addon-toolbars@7.6.3:
-    resolution: {integrity: sha512-8GpwOt0J5yLrJhTr9/h0a/LTDjt49FhdvdxiVWLlLMrjIXSIc7j193ZgoHfnlwVhJS5zojcjB+HmRw/E+AneoA==}
+  /@storybook/addon-toolbars@7.6.4:
+    resolution: {integrity: sha512-ENMQJgU4sRCLLDVXYfa+P3cQVV9PC0ZxwVAKeM3NPYPNH/ODoryGNtq+Q68LwHlM4ObCE2oc9MzaQqPxloFcCw==}
     dev: true
 
-  /@storybook/addon-viewport@7.6.3:
-    resolution: {integrity: sha512-I9FQxHi4W7RUyZut4NziYa+nkBCpD1k2YpEDE5IwSC3lqQpDzFZN89eNWQtZ38tIU4c90jL3L1k69IHvANGHsA==}
+  /@storybook/addon-viewport@7.6.4:
+    resolution: {integrity: sha512-SoTcHIoqybhYD28v7QExF1EZnl7FfxuP74VDhtze5LyMd2CbqmVnUfwewLCz/3IvCNce0GqdNyg1m6QJ7Eq1uw==}
     dependencies:
       memoizerific: 1.11.3
     dev: true
 
-  /@storybook/addons@7.6.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-UuqMOcr+x+4ogtn889wGgVAFxswHjN8ybD6ZTuRatLXA3YC2aywKGL1Xz0bmrTfv5WTlNxOPuwoTIhIH/P073w==}
+  /@storybook/addons@7.6.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-YnmLyR/ciALtzoi9HEu+Y+NJWeOVEBo9PRgQaG7zGiNDvOrLY69uU3Ej0+TZlrTqBqce42bRCrDINJfnk0Mfsg==}
     dependencies:
-      '@storybook/manager-api': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.6.3
-      '@storybook/types': 7.6.3
+      '@storybook/manager-api': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.6.4
+      '@storybook/types': 7.6.4
     transitivePeerDependencies:
       - react
       - react-dom
     dev: true
 
-  /@storybook/blocks@7.6.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-EyjyNNCZMcV9UnBSujwduiq+F1VLVX/f16fTTPqqZOHigyfrG5LoEYC6dwOC4yO/xfWY+h3qJ51yiugMxVl0Vg==}
+  /@storybook/blocks@7.6.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-iXinXXhTUBtReREP1Jifpu35DnGg7FidehjvCM8sM4E4aymfb8czdg9DdvG46T2UFUPUct36nnjIdMLWOya8Bw==}
     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.6.3
-      '@storybook/client-logger': 7.6.3
-      '@storybook/components': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.6.3
+      '@storybook/channels': 7.6.4
+      '@storybook/client-logger': 7.6.4
+      '@storybook/components': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 7.6.4
       '@storybook/csf': 0.1.2
-      '@storybook/docs-tools': 7.6.3
+      '@storybook/docs-tools': 7.6.4
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.6.3
-      '@storybook/theming': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.3
+      '@storybook/manager-api': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.6.4
+      '@storybook/theming': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.4
       '@types/lodash': 4.14.191
       color-convert: 2.0.1
       dequal: 2.0.3
@@ -6571,13 +6503,13 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-manager@7.6.3:
-    resolution: {integrity: sha512-eLMjRudhiRsg7kgbmPcCkuVf2ut753fbiVR7REtqIYwq5vu8UeNOzt1vA6HgfsUj77/7+1zG8/zeyBv/5nY5mw==}
+  /@storybook/builder-manager@7.6.4:
+    resolution: {integrity: sha512-k5+D3fXw7LdMOWd5tF7cIq8L3irrdW6/vmcEHLaJj1EXZ+DvsNCH9xSsLS+6zfrUcxug4oSfRqvF87w6Oz3DtA==}
     dependencies:
       '@fal-works/esbuild-plugin-global-externals': 2.1.2
-      '@storybook/core-common': 7.6.3
-      '@storybook/manager': 7.6.3
-      '@storybook/node-logger': 7.6.3
+      '@storybook/core-common': 7.6.4
+      '@storybook/manager': 7.6.4
+      '@storybook/node-logger': 7.6.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.20)
@@ -6595,8 +6527,8 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-vite@7.6.3(typescript@5.3.2)(vite@5.0.5):
-    resolution: {integrity: sha512-r/G/6wdwgbhMiMZ8Z+Js8VLjIo7a0DG5SxJorTHSWNi0+jyM+3Qlg3Xj96I8yL4gfTIKWVScHqHprhjRb2E64g==}
+  /@storybook/builder-vite@7.6.4(typescript@5.3.3)(vite@5.0.7):
+    resolution: {integrity: sha512-eqb3mLUfuXd4a7+46cWevQ9qH81FvHy1lrAbZGwp4bQ/Tj0YF8Ej7lKBbg7zoIwiu2zDci+BbMiaDOY1kPtILw==}
     peerDependencies:
       '@preact/preset-vite': '*'
       typescript: '>= 4.3.x'
@@ -6610,14 +6542,14 @@ packages:
       vite-plugin-glimmerx:
         optional: true
     dependencies:
-      '@storybook/channels': 7.6.3
-      '@storybook/client-logger': 7.6.3
-      '@storybook/core-common': 7.6.3
-      '@storybook/csf-plugin': 7.6.3
-      '@storybook/node-logger': 7.6.3
-      '@storybook/preview': 7.6.3
-      '@storybook/preview-api': 7.6.3
-      '@storybook/types': 7.6.3
+      '@storybook/channels': 7.6.4
+      '@storybook/client-logger': 7.6.4
+      '@storybook/core-common': 7.6.4
+      '@storybook/csf-plugin': 7.6.4
+      '@storybook/node-logger': 7.6.4
+      '@storybook/preview': 7.6.4
+      '@storybook/preview-api': 7.6.4
+      '@storybook/types': 7.6.4
       '@types/find-cache-dir': 3.2.1
       browser-assert: 1.2.1
       es-module-lexer: 0.9.3
@@ -6626,24 +6558,13 @@ packages:
       fs-extra: 11.1.1
       magic-string: 0.30.5
       rollup: 3.29.4
-      typescript: 5.3.2
-      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
+      typescript: 5.3.3
+      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - encoding
       - supports-color
     dev: true
 
-  /@storybook/channels@7.5.3:
-    resolution: {integrity: sha512-dhWuV2o2lmxH0RKuzND8jxYzvSQTSmpE13P0IT/k8+I1up/rSNYOBQJT6SalakcNWXFAMXguo/8E7ApmnKKcEw==}
-    dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/core-events': 7.5.3
-      '@storybook/global': 5.0.0
-      qs: 6.11.1
-      telejson: 7.2.0
-      tiny-invariant: 1.3.1
-    dev: true
-
   /@storybook/channels@7.6.3:
     resolution: {integrity: sha512-o9J0TBbFon16tUlU5V6kJgzAlsloJcS1cTHWqh3VWczohbRm+X1PLNUihJ7Q8kBWXAuuJkgBu7RQH7Ib46WyYg==}
     dependencies:
@@ -6655,22 +6576,33 @@ packages:
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/cli@7.6.3:
-    resolution: {integrity: sha512-OuYnzZlAtpGm4rDgI4ZWkNbAkddutlJh6KmoU9oQAlZP0zmETyJN8REUWjj5T9Z1AS2iXjCMGlFVd4TC8nKocw==}
+  /@storybook/channels@7.6.4:
+    resolution: {integrity: sha512-Z4PY09/Czl70ap4ObmZ4bgin+EQhPaA3HdrEDNwpnH7A9ttfEO5u5KThytIjMq6kApCCihmEPDaYltoVrfYJJA==}
+    dependencies:
+      '@storybook/client-logger': 7.6.4
+      '@storybook/core-events': 7.6.4
+      '@storybook/global': 5.0.0
+      qs: 6.11.1
+      telejson: 7.2.0
+      tiny-invariant: 1.3.1
+    dev: true
+
+  /@storybook/cli@7.6.4:
+    resolution: {integrity: sha512-GqvaFdkkBMJOdnrVe82XY0V3b+qFMhRNyVoTv2nqB87iMUXZHqh4Pu4LqwaJBsBpuNregvCvVOPe9LGgoOzy4A==}
     hasBin: true
     dependencies:
       '@babel/core': 7.23.5
       '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
       '@babel/types': 7.23.5
       '@ndelangen/get-tarball': 3.0.7
-      '@storybook/codemod': 7.6.3
-      '@storybook/core-common': 7.6.3
-      '@storybook/core-events': 7.6.3
-      '@storybook/core-server': 7.6.3
-      '@storybook/csf-tools': 7.6.3
-      '@storybook/node-logger': 7.6.3
-      '@storybook/telemetry': 7.6.3
-      '@storybook/types': 7.6.3
+      '@storybook/codemod': 7.6.4
+      '@storybook/core-common': 7.6.4
+      '@storybook/core-events': 7.6.4
+      '@storybook/core-server': 7.6.4
+      '@storybook/csf-tools': 7.6.4
+      '@storybook/node-logger': 7.6.4
+      '@storybook/telemetry': 7.6.4
+      '@storybook/types': 7.6.4
       '@types/semver': 7.5.6
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
@@ -6707,62 +6639,39 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/client-logger@7.5.3:
-    resolution: {integrity: sha512-vUFYALypjix5FoJ5M/XUP6KmyTnQJNW1poHdW7WXUVSg+lBM6E5eAtjTm0hdxNNDH8KSrdy24nCLra5h0X0BWg==}
-    dependencies:
-      '@storybook/global': 5.0.0
-    dev: true
-
   /@storybook/client-logger@7.6.3:
     resolution: {integrity: sha512-BpsCnefrBFdxD6ukMjAblm1D6zB4U5HR1I85VWw6LOqZrfzA6l/1uBxItz0XG96HTjngbvAabWf5k7ZFCx5UCg==}
     dependencies:
       '@storybook/global': 5.0.0
     dev: true
 
-  /@storybook/codemod@7.6.3:
-    resolution: {integrity: sha512-A1i8+WQfNg3frVcwSyu8E/cDkCu88Sw7JiGNnq9iW2e2oWMr2awpCDgXp8WfTK+HiDb2X1Pq5y/GmUlh3qr77Q==}
+  /@storybook/client-logger@7.6.4:
+    resolution: {integrity: sha512-vJwMShC98tcoFruRVQ4FphmFqvAZX1FqZqjFyk6IxtFumPKTVSnXJjlU1SnUIkSK2x97rgdUMqkdI+wAv/tugQ==}
+    dependencies:
+      '@storybook/global': 5.0.0
+    dev: true
+
+  /@storybook/codemod@7.6.4:
+    resolution: {integrity: sha512-q4rZVOfozxzbDRH/LzuFDoIGBdXs+orAm18fi6iAx8PeMHe8J/MOXKccNV1zdkm/h7mTQowuRo45KwJHw8vX+g==}
     dependencies:
       '@babel/core': 7.23.5
       '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
       '@babel/types': 7.23.5
       '@storybook/csf': 0.1.2
-      '@storybook/csf-tools': 7.6.3
-      '@storybook/node-logger': 7.6.3
-      '@storybook/types': 7.6.3
+      '@storybook/csf-tools': 7.6.4
+      '@storybook/node-logger': 7.6.4
+      '@storybook/types': 7.6.4
       '@types/cross-spawn': 6.0.2
       cross-spawn: 7.0.3
       globby: 11.1.0
       jscodeshift: 0.15.1(@babel/preset-env@7.23.5)
       lodash: 4.17.21
       prettier: 2.8.8
-      recast: 0.23.1
+      recast: 0.23.4
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/components@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-M3+cjvEsDGLUx8RvK5wyF6/13LNlUnKbMgiDE8Sxk/v/WPpyhOAIh/B8VmrU1psahS61Jd4MTkFmLf1cWau1vw==}
-    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.5.3
-      '@storybook/csf': 0.1.0
-      '@storybook/global': 5.0.0
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.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.6.3(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-UNV0WoUo+W0huOLvoEMuqRN/VB4p0CNswrXN1mi/oGWvAFJ8idu63lSuV4uQ/LKxAZ6v3Kpdd+oK/o+OeOoL6w==}
     peerDependencies:
@@ -6786,19 +6695,42 @@ packages:
       - '@types/react-dom'
     dev: true
 
-  /@storybook/core-client@7.6.3:
-    resolution: {integrity: sha512-RM0Svlajddl8PP4Vq7LK8T22sFefNcTDgo82iRPZzGz0oH8LT0oXGFanj2Nkn0jruOBFClkiJ7EcwrbGJZHELg==}
+  /@storybook/components@7.6.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-K5RvEObJAnX+SbGJbkM1qrZEk+VR2cUhRCSrFnlfMwsn8/60T3qoH7U8bCXf8krDgbquhMwqev5WzDB+T1VV8g==}
+    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.6.3
-      '@storybook/preview-api': 7.6.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.6.4
+      '@storybook/csf': 0.1.2
+      '@storybook/global': 5.0.0
+      '@storybook/theming': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.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.6.3:
-    resolution: {integrity: sha512-/ZE4BEyGwBHCQCOo681GyBKF4IqCiwVV/ZJCHTMTHFCPLJT2r+Qwv4tnI7xt1kwflOlbBlG6B6CvAqTjjVw/Ew==}
+  /@storybook/core-client@7.6.4:
+    resolution: {integrity: sha512-0msqdGd+VYD1dRgAJ2StTu4d543Wveb7LVVujX3PwD/QCxmCaVUHuAoZrekM/H7jZLw546ZIbLZo0xWrADAUMw==}
     dependencies:
-      '@storybook/core-events': 7.6.3
-      '@storybook/node-logger': 7.6.3
-      '@storybook/types': 7.6.3
+      '@storybook/client-logger': 7.6.4
+      '@storybook/preview-api': 7.6.4
+    dev: true
+
+  /@storybook/core-common@7.6.4:
+    resolution: {integrity: sha512-qes4+mXqINu0kCgSMFjk++GZokmYjb71esId0zyJsk0pcIPkAiEjnhbSEQkMhbUfcvO1lztoaQTBW2P7Rd1tag==}
+    dependencies:
+      '@storybook/core-events': 7.6.4
+      '@storybook/node-logger': 7.6.4
+      '@storybook/types': 7.6.4
       '@types/find-cache-dir': 3.2.1
       '@types/node': 18.17.15
       '@types/node-fetch': 2.6.4
@@ -6824,36 +6756,36 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/core-events@7.5.3:
-    resolution: {integrity: sha512-DFOpyQ22JD5C1oeOFzL8wlqSWZzrqgDfDbUGP8xdO4wJu+FVTxnnWN6ZYLdTPB1u27DOhd7TzjQMfLDHLu7kbQ==}
-    dependencies:
-      ts-dedent: 2.2.0
-    dev: true
-
   /@storybook/core-events@7.6.3:
     resolution: {integrity: sha512-Vu3JX1mjtR8AX84lyqWsi2s2lhD997jKRWVznI3wx+UpTk8t7TTMLFk2rGYJRjaornhrqwvLYpnmtxRSxW9BOQ==}
     dependencies:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/core-server@7.6.3:
-    resolution: {integrity: sha512-IsM24MmiFmtZeyqoijiExpIPkJNBaWQg9ttkkHS6iYwf3yFNBpYVbvuX2OpT7FDdiF3uTl0R8IvfnJR58tHD7w==}
+  /@storybook/core-events@7.6.4:
+    resolution: {integrity: sha512-i3xzcJ19ILSy4oJL5Dz9y0IlyApynn5RsGhAMIsW+mcfri+hGfeakq1stNCo0o7jW4Y3A7oluFTtIoK8DOxQdQ==}
+    dependencies:
+      ts-dedent: 2.2.0
+    dev: true
+
+  /@storybook/core-server@7.6.4:
+    resolution: {integrity: sha512-mXxZMpCwOhjEPPRjqrTHdiCpFdkc47f46vlgTj02SX+9xKHxslmZ2D3JG/8O4Ab9tG+bBl6lBm3RIrIzaiCu9Q==}
     dependencies:
       '@aw-web-design/x-default-browser': 1.4.126
       '@discoveryjs/json-ext': 0.5.7
-      '@storybook/builder-manager': 7.6.3
-      '@storybook/channels': 7.6.3
-      '@storybook/core-common': 7.6.3
-      '@storybook/core-events': 7.6.3
+      '@storybook/builder-manager': 7.6.4
+      '@storybook/channels': 7.6.4
+      '@storybook/core-common': 7.6.4
+      '@storybook/core-events': 7.6.4
       '@storybook/csf': 0.1.2
-      '@storybook/csf-tools': 7.6.3
+      '@storybook/csf-tools': 7.6.4
       '@storybook/docs-mdx': 0.1.0
       '@storybook/global': 5.0.0
-      '@storybook/manager': 7.6.3
-      '@storybook/node-logger': 7.6.3
-      '@storybook/preview-api': 7.6.3
-      '@storybook/telemetry': 7.6.3
-      '@storybook/types': 7.6.3
+      '@storybook/manager': 7.6.4
+      '@storybook/node-logger': 7.6.4
+      '@storybook/preview-api': 7.6.4
+      '@storybook/telemetry': 7.6.4
+      '@storybook/types': 7.6.4
       '@types/detect-port': 1.3.2
       '@types/node': 18.17.15
       '@types/pretty-hrtime': 1.0.1
@@ -6887,37 +6819,31 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/csf-plugin@7.6.3:
-    resolution: {integrity: sha512-8bMYPsWw2tv+fqZ5H436l4x1KLSB6gIcm6snsjyF916yCHG6WcWm+EI6+wNUoySEtrQY2AiwFJqE37wI5OUJFg==}
+  /@storybook/csf-plugin@7.6.4:
+    resolution: {integrity: sha512-7g9p8s2ITX+Z9iThK5CehPhJOcusVN7JcUEEW+gVF5PlYT+uk/x+66gmQno+scQuNkV9+8UJD6RLFjP+zg2uCA==}
     dependencies:
-      '@storybook/csf-tools': 7.6.3
+      '@storybook/csf-tools': 7.6.4
       unplugin: 1.4.0
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/csf-tools@7.6.3:
-    resolution: {integrity: sha512-Zi3pg2pg88/mvBKewkfWhFUR1J4uYpHI5fSjOE+J/FeZObX/DIE7r+wJxZ0UBGyrk0Wy7Jajlb2uSP56Y0i19w==}
+  /@storybook/csf-tools@7.6.4:
+    resolution: {integrity: sha512-6sLayuhgReIK3/QauNj5BW4o4ZfEMJmKf+EWANPEM/xEOXXqrog6Un8sjtBuJS9N1DwyhHY6xfkEiPAwdttwqw==}
     dependencies:
       '@babel/generator': 7.23.5
-      '@babel/parser': 7.23.3
+      '@babel/parser': 7.23.5
       '@babel/traverse': 7.23.5
       '@babel/types': 7.23.5
       '@storybook/csf': 0.1.2
-      '@storybook/types': 7.6.3
+      '@storybook/types': 7.6.4
       fs-extra: 11.1.1
-      recast: 0.23.1
+      recast: 0.23.4
       ts-dedent: 2.2.0
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/csf@0.1.0:
-    resolution: {integrity: sha512-uk+jMXCZ8t38jSTHk2o5btI+aV2Ksbvl6DoOv3r6VaCM1KZqeuMwtwywIQdflkA8/6q/dKT8z8L+g8hC4GC3VQ==}
-    dependencies:
-      type-fest: 2.19.0
-    dev: true
-
   /@storybook/csf@0.1.2:
     resolution: {integrity: sha512-ePrvE/pS1vsKR9Xr+o+YwdqNgHUyXvg+1Xjx0h9LrVx7Zq4zNe06pd63F5EvzTbCbJsHj7GHr9tkiaqm7U8WRA==}
     dependencies:
@@ -6928,12 +6854,12 @@ packages:
     resolution: {integrity: sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==}
     dev: true
 
-  /@storybook/docs-tools@7.6.3:
-    resolution: {integrity: sha512-6MtirRCQIkBeQ3bksPignZgUuFmjWqcFleTEN6vrNEfbCzMlMvuBGfm9tl4sS3n8ATWmKGj87DcJepPOT3FB4A==}
+  /@storybook/docs-tools@7.6.4:
+    resolution: {integrity: sha512-2eGam43aD7O3cocA72Z63kRi7t/ziMSpst0qB218QwBWAeZjT4EYDh8V6j/Xhv6zVQL3msW7AglrQP5kCKPvPA==}
     dependencies:
-      '@storybook/core-common': 7.6.3
-      '@storybook/preview-api': 7.6.3
-      '@storybook/types': 7.6.3
+      '@storybook/core-common': 7.6.4
+      '@storybook/preview-api': 7.6.4
+      '@storybook/types': 7.6.4
       '@types/doctrine': 0.0.3
       assert: 2.1.0
       doctrine: 3.0.0
@@ -6966,17 +6892,17 @@ packages:
       - vitest
     dev: true
 
-  /@storybook/manager-api@7.6.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-soDH7GZuukkhYRGzlw4jhCm5EzjfkuIAtb37/DFplqxuVbvlyJEVzkMUM2KQO7kq0/8GlWPiZ5mn56wagYyhKQ==}
+  /@storybook/manager-api@7.6.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-RFb/iaBJfXygSgXkINPRq8dXu7AxBicTGX7MxqKXbz5FU7ANwV7abH6ONBYURkSDOH9//TQhRlVkF5u8zWg3bw==}
     dependencies:
-      '@storybook/channels': 7.6.3
-      '@storybook/client-logger': 7.6.3
-      '@storybook/core-events': 7.6.3
+      '@storybook/channels': 7.6.4
+      '@storybook/client-logger': 7.6.4
+      '@storybook/core-events': 7.6.4
       '@storybook/csf': 0.1.2
       '@storybook/global': 5.0.0
-      '@storybook/router': 7.6.3
-      '@storybook/theming': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.3
+      '@storybook/router': 7.6.4
+      '@storybook/theming': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.4
       dequal: 2.0.3
       lodash: 4.17.21
       memoizerific: 1.11.3
@@ -6989,31 +6915,31 @@ packages:
       - react-dom
     dev: true
 
-  /@storybook/manager@7.6.3:
-    resolution: {integrity: sha512-6eMaogHANCSVV2zLPt4Q7fp8RT+AdlOe6IR0583AuqpepcFzj33iGNYABk2rmXAlkD0WzoLcC4H5mouU0fduLA==}
+  /@storybook/manager@7.6.4:
+    resolution: {integrity: sha512-Ug2ejfKgKre8h/RJbkumukwAA44TbvTPEjDcJmyFdAI+kHYhOYdKPEC2UNmVYz8/4HjwMTJQ3M7t/esK8HHY4A==}
     dev: true
 
   /@storybook/mdx2-csf@1.0.0:
     resolution: {integrity: sha512-dBAnEL4HfxxJmv7LdEYUoZlQbWj9APZNIbOaq0tgF8XkxiIbzqvgB0jhL/9UOrysSDbQWBiCRTu2wOVxedGfmw==}
     dev: true
 
-  /@storybook/node-logger@7.6.3:
-    resolution: {integrity: sha512-7yL0CMHuh1DhpUAoKCU0a53DvxBpkUom9SX5RaC1G2A9BK/B3XcHtDPAC0uyUwNCKLJMZo9QtmJspvxWjR0LtA==}
+  /@storybook/node-logger@7.6.4:
+    resolution: {integrity: sha512-GDkEnnDj4Op+PExs8ZY/P6ox3wg453CdEIaR8PR9TxF/H/T2fBL6puzma3hN2CMam6yzfAL8U+VeIIDLQ5BZdQ==}
     dev: true
 
-  /@storybook/postinstall@7.6.3:
-    resolution: {integrity: sha512-WpgdpJpY6rionluxjFZLbKiSDjvQJ5cPgufjvBRuXTsnVOsH3JNRWnPdkQkJLT9uTUMoNcyBMxbjYkK3vU6wSg==}
+  /@storybook/postinstall@7.6.4:
+    resolution: {integrity: sha512-7uoB82hSzlFSdDMS3hKQD+AaeSvPit/fAMvXCBxn0/D0UGJUZcq4M9JcKBwEHkZJcbuDROgOTJ6TUeXi/FWO0w==}
     dev: true
 
-  /@storybook/preview-api@7.6.3:
-    resolution: {integrity: sha512-uPaK7yLE1P++F+IOb/1j9pgdCwfMYZrUPHogF/Mf9r4cfEjDCcIeKgGMcsbU1KnkzNQQGPh8JRzRr/iYnLjswg==}
+  /@storybook/preview-api@7.6.4:
+    resolution: {integrity: sha512-KhisNdQX5NdfAln+spLU4B82d804GJQp/CnI5M1mm/taTnjvMgs/wTH9AmR89OPoq+tFZVW0vhy2zgPS3ar71A==}
     dependencies:
-      '@storybook/channels': 7.6.3
-      '@storybook/client-logger': 7.6.3
-      '@storybook/core-events': 7.6.3
+      '@storybook/channels': 7.6.4
+      '@storybook/client-logger': 7.6.4
+      '@storybook/core-events': 7.6.4
       '@storybook/csf': 0.1.2
       '@storybook/global': 5.0.0
-      '@storybook/types': 7.6.3
+      '@storybook/types': 7.6.4
       '@types/qs': 6.9.7
       dequal: 2.0.3
       lodash: 4.17.21
@@ -7024,12 +6950,12 @@ packages:
       util-deprecate: 1.0.2
     dev: true
 
-  /@storybook/preview@7.6.3:
-    resolution: {integrity: sha512-obSmKN8arWSHuLbCDM1H0lTVRMvAP/l7vOi6TQtFi6TxBz9MRCJA3Ugc0PZrbDADVZP+cp0ZJA0JQtAm+SqNAA==}
+  /@storybook/preview@7.6.4:
+    resolution: {integrity: sha512-p9xIvNkgXgTpSRphOMV9KpIiNdkymH61jBg3B0XyoF6IfM1S2/mQGvC89lCVz1dMGk2SrH4g87/WcOapkU5ArA==}
     dev: true
 
-  /@storybook/react-dom-shim@7.6.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-UtaEaTQB27aBsAmn5IfAYkX2xl4wWWXkoAO/jUtx86FQ/r85FG0zxh/rac6IgzjYUqzjJtjIeLdeciG/48hMMA==}
+  /@storybook/react-dom-shim@7.6.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-wGJfomlDEBnowNmhmumWDu/AcUInxSoPqUUJPgk2f5oL0EW17fR9fDP/juG3XOEdieMDM0jDX48GML7lyvL2fg==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -7038,24 +6964,24 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/react-vite@7.6.3(react-dom@18.2.0)(react@18.2.0)(rollup@4.6.1)(typescript@5.3.2)(vite@5.0.5):
-    resolution: {integrity: sha512-sPrNJbnThmxsSeNj6vyG9pCCnnYzyiS+f7DVy2qeQrXvEuCYiQc503bavE3BKLxqjZQ3SkbhPsiEHcaw3I9x7A==}
+  /@storybook/react-vite@7.6.4(react-dom@18.2.0)(react@18.2.0)(rollup@4.7.0)(typescript@5.3.3)(vite@5.0.7):
+    resolution: {integrity: sha512-1NYzCJRO6k/ZyoMzpu1FQiaUaiLNjAvTAB1x3HE7oY/tEIT8kGpzXGYH++LJVWvyP/5dSWlUnRSy2rJvySraiw==}
     engines: {node: '>=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 || ^5.0.0
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.2)(vite@5.0.5)
-      '@rollup/pluginutils': 5.0.5(rollup@4.6.1)
-      '@storybook/builder-vite': 7.6.3(typescript@5.3.2)(vite@5.0.5)
-      '@storybook/react': 7.6.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2)
-      '@vitejs/plugin-react': 3.1.0(vite@5.0.5)
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.0.7)
+      '@rollup/pluginutils': 5.1.0(rollup@4.7.0)
+      '@storybook/builder-vite': 7.6.4(typescript@5.3.3)(vite@5.0.7)
+      '@storybook/react': 7.6.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
+      '@vitejs/plugin-react': 3.1.0(vite@5.0.7)
       magic-string: 0.30.5
       react: 18.2.0
       react-docgen: 7.0.1
       react-dom: 18.2.0(react@18.2.0)
-      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - encoding
@@ -7065,8 +6991,8 @@ packages:
       - vite-plugin-glimmerx
     dev: true
 
-  /@storybook/react@7.6.3(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-W+530cC0BAU+yBc7NzSXYWR3e8Lo5qMsmFJjWYK7zGW/YZGhSG3mjhF9pDzNM+cMtHvUS6qf5PJPQM8jePpPhg==}
+  /@storybook/react@7.6.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-XYRP+eylH3JqkCuziwtQGY5vOCeDreOibRYJmj5na6k4QbURjGVB44WCIW04gWVlmBXM9SqLAmserUi3HP890Q==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -7076,13 +7002,13 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.6.3
-      '@storybook/core-client': 7.6.3
-      '@storybook/docs-tools': 7.6.3
+      '@storybook/client-logger': 7.6.4
+      '@storybook/core-client': 7.6.4
+      '@storybook/docs-tools': 7.6.4
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.6.3
-      '@storybook/react-dom-shim': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.3
+      '@storybook/preview-api': 7.6.4
+      '@storybook/react-dom-shim': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.4
       '@types/escodegen': 0.0.6
       '@types/estree': 0.0.51
       '@types/node': 18.17.15
@@ -7098,27 +7024,37 @@ packages:
       react-element-to-jsx-string: 15.0.0(react-dom@18.2.0)(react@18.2.0)
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      typescript: 5.3.2
+      typescript: 5.3.3
       util-deprecate: 1.0.2
     transitivePeerDependencies:
       - encoding
       - supports-color
     dev: true
 
-  /@storybook/router@7.6.3:
-    resolution: {integrity: sha512-NZfhJqsXYca9mZCL/LGx6FmZDbrxX2S4ImW7Tqdtcc/sSlZ0BpCDkNUTesCA287cmoKMhXZRh/+bU+C2h2a+bw==}
+  /@storybook/router@7.6.4:
+    resolution: {integrity: sha512-5MQ7Z4D7XNPN2yhFgjey7hXOYd6s8CggUqeAwhzGTex90SMCkKHSz1hfkcXn1ZqBPaall2b53uK553OvPLp9KQ==}
     dependencies:
-      '@storybook/client-logger': 7.6.3
+      '@storybook/client-logger': 7.6.4
       memoizerific: 1.11.3
       qs: 6.11.1
     dev: true
 
-  /@storybook/telemetry@7.6.3:
-    resolution: {integrity: sha512-NDCZWhVIUI3M6Lq4M/HPOvZqDXqANDNbI3kyHr4pFGoVaCUXuDPokL9wR+CZcMvATkJ1gHrfLPBdcRq6Biw3Iw==}
+  /@storybook/source-loader@7.6.4:
+    resolution: {integrity: sha512-1wb/3bVpJZ/3r3qUrLK8jb0kLuvwjNi5T1kci5huREdc1TrIxZXoPw9EiyjcMCZzCURkoj7euNLrLHGyzdBTLg==}
     dependencies:
-      '@storybook/client-logger': 7.6.3
-      '@storybook/core-common': 7.6.3
-      '@storybook/csf-tools': 7.6.3
+      '@storybook/csf': 0.1.2
+      '@storybook/types': 7.6.4
+      estraverse: 5.3.0
+      lodash: 4.17.21
+      prettier: 2.8.8
+    dev: true
+
+  /@storybook/telemetry@7.6.4:
+    resolution: {integrity: sha512-Q4QpvcgloHUEqC9PGo7tgqkUH91/PjX+74/0Hi9orLo8QmLMgdYS5fweFwgSKoTwDGNg2PaHp/jqvhhw7UmnJA==}
+    dependencies:
+      '@storybook/client-logger': 7.6.4
+      '@storybook/core-common': 7.6.4
+      '@storybook/csf-tools': 7.6.4
       chalk: 4.1.2
       detect-package-manager: 2.0.1
       fetch-retry: 5.0.4
@@ -7137,20 +7073,6 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/theming@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-Cjmthe1MAk0z4RKCZ7m72gAD8YD0zTAH97z5ryM1Qv84QXjiCQ143fGOmYz1xEQdNFpOThPcwW6FEccLHTkVcg==}
-    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.5.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.6.3(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-9ToNU2LM6a2kVBjOXitXEeEOuMurVLhn+uaZO1dJjv8NGnJVYiLwNPwrLsImiUD8/XXNuil972aanBR6+Aj9jw==}
     peerDependencies:
@@ -7165,13 +7087,18 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/types@7.5.3:
-    resolution: {integrity: sha512-iu5W0Kdd6nysN5CPkY4GRl+0BpxRTdSfBIJak7mb6xCIHSB5t1tw4BOuqMQ5EgpikRY3MWJ4gY647QkWBX3MNQ==}
+  /@storybook/theming@7.6.4(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-Z/dcC5EpkIXelYCkt9ojnX6D7qGOng8YHxV/OWlVE9TrEGYVGPOEfwQryR0RhmGpDha1TYESLYrsDb4A8nJ1EA==}
+    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.5.3
-      '@types/babel__core': 7.20.0
-      '@types/express': 4.17.17
-      file-system-cache: 2.3.0
+      '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0)
+      '@storybook/client-logger': 7.6.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.6.3:
@@ -7183,19 +7110,28 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.6.3(@vue/compiler-core@3.3.9)(typescript@5.3.2)(vite@5.0.5)(vue@3.3.9):
-    resolution: {integrity: sha512-7NupDZn7FNm8SJSfTWOj1mrfyjGNII7bpnlvt1iH88L8UECmIh/gbDoBNi1XcGmPK09nrx3RnFdsxN5PVpeLPQ==}
+  /@storybook/types@7.6.4:
+    resolution: {integrity: sha512-qyiiXPCvol5uVgfubcIMzJBA0awAyFPU+TyUP1mkPYyiTHnsHYel/mKlSdPjc8a97N3SlJXHOCx41Hde4IyJgg==}
+    dependencies:
+      '@storybook/channels': 7.6.4
+      '@types/babel__core': 7.20.0
+      '@types/express': 4.17.17
+      file-system-cache: 2.3.0
+    dev: true
+
+  /@storybook/vue3-vite@7.6.4(@vue/compiler-core@3.3.9)(typescript@5.3.3)(vite@5.0.7)(vue@3.3.11):
+    resolution: {integrity: sha512-M1cT6lZsRqwws+H+pv2K/jJGiXUfGHAz7nc0DwK/YYT/w0OOVp5hinh/IvNwIYW5zUmwIyQVEoGrrxQEi0S79w==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
       vite: ^3.0.0 || ^4.0.0 || ^5.0.0
     dependencies:
-      '@storybook/builder-vite': 7.6.3(typescript@5.3.2)(vite@5.0.5)
-      '@storybook/core-server': 7.6.3
-      '@storybook/vue3': 7.6.3(@vue/compiler-core@3.3.9)(vue@3.3.9)
-      '@vitejs/plugin-vue': 4.5.1(vite@5.0.5)(vue@3.3.9)
+      '@storybook/builder-vite': 7.6.4(typescript@5.3.3)(vite@5.0.7)
+      '@storybook/core-server': 7.6.4
+      '@storybook/vue3': 7.6.4(@vue/compiler-core@3.3.9)(vue@3.3.11)
+      '@vitejs/plugin-vue': 4.5.1(vite@5.0.7)(vue@3.3.11)
       magic-string: 0.30.5
-      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
-      vue-docgen-api: 4.64.1(vue@3.3.9)
+      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vue-docgen-api: 4.64.1(vue@3.3.11)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - '@vue/compiler-core'
@@ -7208,24 +7144,24 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.6.3(@vue/compiler-core@3.3.9)(vue@3.3.9):
-    resolution: {integrity: sha512-XoJOMcRngxOblXGFiaz5oqsCbTy+TVoWrOodjizYfJcMXIceKZrPz+bNI4x6HL5koXrx1HWy5VN187QGqy+RJg==}
+  /@storybook/vue3@7.6.4(@vue/compiler-core@3.3.9)(vue@3.3.11):
+    resolution: {integrity: sha512-9BYxqj9C30/7UBspP4sE0UE/4fgE17jymJHWkxFbEE95MfbLkDFRnJ3Y09VPK0OnmbsBW5xBciMyjGV9Lq2pmg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
       '@vue/compiler-core': ^3.0.0
       vue: ^3.0.0
     dependencies:
-      '@storybook/core-client': 7.6.3
-      '@storybook/docs-tools': 7.6.3
+      '@storybook/core-client': 7.6.4
+      '@storybook/docs-tools': 7.6.4
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.6.3
-      '@storybook/types': 7.6.3
+      '@storybook/preview-api': 7.6.4
+      '@storybook/types': 7.6.4
       '@vue/compiler-core': 3.3.9
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      vue: 3.3.9(typescript@5.3.2)
-      vue-component-type-helpers: 1.8.24
+      vue: 3.3.11(typescript@5.3.3)
+      vue-component-type-helpers: 1.8.25
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -7699,7 +7635,7 @@ packages:
       '@testing-library/dom': 9.2.0
     dev: true
 
-  /@testing-library/vue@8.0.1(@vue/compiler-sfc@3.3.9)(vue@3.3.9):
+  /@testing-library/vue@8.0.1(@vue/compiler-sfc@3.3.9)(vue@3.3.11):
     resolution: {integrity: sha512-l51ZEpjTQ6glq3wM+asQ1GbKJMGcxwgHEygETx0aCRN4TjFEGvMZy4YdWKs/y7bu4bmLrxcxhbEPP7iPSW/2OQ==}
     engines: {node: '>=14'}
     peerDependencies:
@@ -7709,8 +7645,8 @@ packages:
       '@babel/runtime': 7.23.2
       '@testing-library/dom': 9.3.3
       '@vue/compiler-sfc': 3.3.9
-      '@vue/test-utils': 2.4.1(vue@3.3.9)
-      vue: 3.3.9(typescript@5.3.2)
+      '@vue/test-utils': 2.4.1(vue@3.3.11)
+      vue: 3.3.11(typescript@5.3.3)
     transitivePeerDependencies:
       - '@vue/server-renderer'
     dev: true
@@ -7732,7 +7668,7 @@ packages:
   /@types/accepts@1.3.7:
     resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/archiver@6.0.2:
@@ -7752,8 +7688,8 @@ packages:
   /@types/babel__core@7.20.0:
     resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==}
     dependencies:
-      '@babel/parser': 7.23.3
-      '@babel/types': 7.22.17
+      '@babel/parser': 7.23.5
+      '@babel/types': 7.23.5
       '@types/babel__generator': 7.6.4
       '@types/babel__template': 7.4.1
       '@types/babel__traverse': 7.20.0
@@ -7762,20 +7698,20 @@ packages:
   /@types/babel__generator@7.6.4:
     resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==}
     dependencies:
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.5
     dev: true
 
   /@types/babel__template@7.4.1:
     resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==}
     dependencies:
-      '@babel/parser': 7.23.3
-      '@babel/types': 7.22.17
+      '@babel/parser': 7.23.5
+      '@babel/types': 7.23.5
     dev: true
 
   /@types/babel__traverse@7.20.0:
     resolution: {integrity: sha512-TBOjqAGf0hmaqRwpii5LLkJLg7c6OMm4nHLmpsUxwk9bBHtoTC6dAHdVWdGv4TBxj2CZOZY8Xfq8WmfoVi7n4Q==}
     dependencies:
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.5
     dev: true
 
   /@types/bcryptjs@2.4.6:
@@ -7786,7 +7722,7 @@ packages:
     resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
     dependencies:
       '@types/connect': 3.4.35
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/braces@3.0.1:
@@ -7798,7 +7734,7 @@ packages:
     dependencies:
       '@types/http-cache-semantics': 4.0.1
       '@types/keyv': 3.1.4
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       '@types/responselike': 1.0.0
     dev: false
 
@@ -7831,7 +7767,7 @@ packages:
   /@types/connect@3.4.35:
     resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/content-disposition@0.5.8:
@@ -7845,7 +7781,7 @@ packages:
   /@types/cross-spawn@6.0.2:
     resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/debug@4.1.7:
@@ -7903,7 +7839,7 @@ packages:
   /@types/express-serve-static-core@4.17.33:
     resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       '@types/qs': 6.9.7
       '@types/range-parser': 1.2.4
     dev: true
@@ -7924,20 +7860,20 @@ packages:
   /@types/fluent-ffmpeg@2.1.24:
     resolution: {integrity: sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/glob@7.2.0:
     resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/graceful-fs@4.1.6:
     resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/http-cache-semantics@4.0.1:
@@ -7946,7 +7882,7 @@ packages:
   /@types/http-link-header@1.0.5:
     resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/istanbul-lib-coverage@2.0.4:
@@ -7972,8 +7908,8 @@ packages:
       pretty-format: 28.1.3
     dev: true
 
-  /@types/jest@29.5.10:
-    resolution: {integrity: sha512-tE4yxKEphEyxj9s4inideLHktW/x6DwesIwWZ9NN1FKf9zbJYsnhBoA9vrHA/IuIOKwPa5PcFBNV4lpMIOEzyQ==}
+  /@types/jest@29.5.11:
+    resolution: {integrity: sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==}
     dependencies:
       expect: 29.7.0
       pretty-format: 29.7.0
@@ -7990,7 +7926,7 @@ packages:
   /@types/jsdom@21.1.6:
     resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       '@types/tough-cookie': 4.0.2
       parse5: 7.1.2
     dev: true
@@ -8014,7 +7950,7 @@ packages:
   /@types/keyv@3.1.4:
     resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: false
 
   /@types/lodash@4.14.191:
@@ -8063,7 +7999,7 @@ packages:
   /@types/node-fetch@2.6.4:
     resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       form-data: 3.0.1
 
   /@types/node-fetch@3.0.3:
@@ -8076,8 +8012,8 @@ packages:
     resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==}
     dev: true
 
-  /@types/node@20.10.3:
-    resolution: {integrity: sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==}
+  /@types/node@20.10.4:
+    resolution: {integrity: sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==}
     dependencies:
       undici-types: 5.26.5
 
@@ -8090,7 +8026,7 @@ packages:
   /@types/nodemailer@6.4.14:
     resolution: {integrity: sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/normalize-package-data@2.4.1:
@@ -8107,13 +8043,13 @@ packages:
     resolution: {integrity: sha512-Ali0fUUn+zgr4Yy/pCTFbuiaiJpq7l7OQwFnxYVchNbNGIx0c4Wkcdje6WO89I91RAaYF+gVc1pOaizA4YKZmA==}
     dependencies:
       '@types/express': 4.17.17
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/oauth@0.9.4:
     resolution: {integrity: sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/offscreencanvas@2019.3.0:
@@ -8129,7 +8065,7 @@ packages:
   /@types/pg@8.10.9:
     resolution: {integrity: sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       pg-protocol: 1.6.0
       pg-types: 4.0.1
     dev: true
@@ -8153,7 +8089,7 @@ packages:
   /@types/qrcode@1.5.5:
     resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/qs@6.9.7:
@@ -8183,7 +8119,7 @@ packages:
   /@types/readdir-glob@1.1.1:
     resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/rename@1.0.7:
@@ -8197,7 +8133,7 @@ packages:
   /@types/responselike@1.0.0:
     resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: false
 
   /@types/sanitize-html@2.9.5:
@@ -8223,7 +8159,7 @@ packages:
     resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
     dependencies:
       '@types/mime': 3.0.1
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/serviceworker@0.0.67:
@@ -8233,7 +8169,7 @@ packages:
   /@types/set-cookie-parser@2.4.3:
     resolution: {integrity: sha512-7QhnH7bi+6KAhBB+Auejz1uV9DHiopZqu7LfR/5gZZTkejJV5nYeZZpgfFoE0N8aDsXuiYpfKyfyMatCwQhyTQ==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/sharp@0.32.0:
@@ -8296,13 +8232,13 @@ packages:
   /@types/vary@1.1.3:
     resolution: {integrity: sha512-XJT8/ZQCL7NUut9QDLf6l24JfAEl7bnNdgxfj50cHIpEPRJLHHDDFOAq6i+GsEmeFfH7NamhBE4c4Thtb2egWg==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/web-push@3.6.3:
     resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/webgl-ext@0.0.30:
@@ -8313,13 +8249,13 @@ packages:
   /@types/websocket@1.0.10:
     resolution: {integrity: sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/ws@8.5.10:
     resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /@types/yargs-parser@21.0.0:
@@ -8342,11 +8278,11 @@ packages:
     resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
     requiresBuild: true
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
     dev: true
     optional: true
 
-  /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.2):
+  /@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.53.0)(typescript@5.3.3):
     resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8358,10 +8294,10 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/scope-manager': 6.11.0
-      '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
-      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
       debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
@@ -8369,14 +8305,14 @@ packages:
       ignore: 5.2.4
       natural-compare: 1.4.0
       semver: 7.5.4
-      ts-api-utils: 1.0.1(typescript@5.3.2)
-      typescript: 5.3.2
+      ts-api-utils: 1.0.1(typescript@5.3.3)
+      typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/eslint-plugin@6.13.1(@typescript-eslint/parser@6.13.1)(eslint@8.55.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==}
+  /@typescript-eslint/eslint-plugin@6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
@@ -8387,24 +8323,24 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
-      '@typescript-eslint/scope-manager': 6.13.1
-      '@typescript-eslint/type-utils': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
-      '@typescript-eslint/utils': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
-      '@typescript-eslint/visitor-keys': 6.13.1
+      '@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/scope-manager': 6.13.2
+      '@typescript-eslint/type-utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/visitor-keys': 6.13.2
       debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.55.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
       semver: 7.5.4
-      ts-api-utils: 1.0.1(typescript@5.3.2)
-      typescript: 5.3.2
+      ts-api-utils: 1.0.1(typescript@5.3.3)
+      typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.2):
+  /@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3):
     resolution: {integrity: sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8416,17 +8352,17 @@ packages:
     dependencies:
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
-      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
+      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
       debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
-      typescript: 5.3.2
+      typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.13.1(eslint@8.55.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==}
+  /@typescript-eslint/parser@6.13.2(eslint@8.55.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
@@ -8435,13 +8371,13 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/scope-manager': 6.13.1
-      '@typescript-eslint/types': 6.13.1
-      '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.2)
-      '@typescript-eslint/visitor-keys': 6.13.1
+      '@typescript-eslint/scope-manager': 6.13.2
+      '@typescript-eslint/types': 6.13.2
+      '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3)
+      '@typescript-eslint/visitor-keys': 6.13.2
       debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.55.0
-      typescript: 5.3.2
+      typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -8454,15 +8390,15 @@ packages:
       '@typescript-eslint/visitor-keys': 6.11.0
     dev: true
 
-  /@typescript-eslint/scope-manager@6.13.1:
-    resolution: {integrity: sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==}
+  /@typescript-eslint/scope-manager@6.13.2:
+    resolution: {integrity: sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dependencies:
-      '@typescript-eslint/types': 6.13.1
-      '@typescript-eslint/visitor-keys': 6.13.1
+      '@typescript-eslint/types': 6.13.2
+      '@typescript-eslint/visitor-keys': 6.13.2
     dev: true
 
-  /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.2):
+  /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.3):
     resolution: {integrity: sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8472,18 +8408,18 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
-      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.2)
+      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
-      ts-api-utils: 1.0.1(typescript@5.3.2)
-      typescript: 5.3.2
+      ts-api-utils: 1.0.1(typescript@5.3.3)
+      typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/type-utils@6.13.1(eslint@8.55.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==}
+  /@typescript-eslint/type-utils@6.13.2(eslint@8.55.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
@@ -8492,12 +8428,12 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.2)
-      '@typescript-eslint/utils': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+      '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
       debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.55.0
-      ts-api-utils: 1.0.1(typescript@5.3.2)
-      typescript: 5.3.2
+      ts-api-utils: 1.0.1(typescript@5.3.3)
+      typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -8507,12 +8443,12 @@ packages:
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
-  /@typescript-eslint/types@6.13.1:
-    resolution: {integrity: sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==}
+  /@typescript-eslint/types@6.13.2:
+    resolution: {integrity: sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
-  /@typescript-eslint/typescript-estree@6.11.0(typescript@5.3.2):
+  /@typescript-eslint/typescript-estree@6.11.0(typescript@5.3.3):
     resolution: {integrity: sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8527,14 +8463,14 @@ packages:
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
-      ts-api-utils: 1.0.1(typescript@5.3.2)
-      typescript: 5.3.2
+      ts-api-utils: 1.0.1(typescript@5.3.3)
+      typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/typescript-estree@6.13.1(typescript@5.3.2):
-    resolution: {integrity: sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==}
+  /@typescript-eslint/typescript-estree@6.13.2(typescript@5.3.3):
+    resolution: {integrity: sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       typescript: '*'
@@ -8542,19 +8478,19 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/types': 6.13.1
-      '@typescript-eslint/visitor-keys': 6.13.1
+      '@typescript-eslint/types': 6.13.2
+      '@typescript-eslint/visitor-keys': 6.13.2
       debug: 4.3.4(supports-color@5.5.0)
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
-      ts-api-utils: 1.0.1(typescript@5.3.2)
-      typescript: 5.3.2
+      ts-api-utils: 1.0.1(typescript@5.3.3)
+      typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.3.2):
+  /@typescript-eslint/utils@6.11.0(eslint@8.53.0)(typescript@5.3.3):
     resolution: {integrity: sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8565,7 +8501,7 @@ packages:
       '@types/semver': 7.5.6
       '@typescript-eslint/scope-manager': 6.11.0
       '@typescript-eslint/types': 6.11.0
-      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.2)
+      '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       eslint: 8.53.0
       semver: 7.5.4
     transitivePeerDependencies:
@@ -8573,8 +8509,8 @@ packages:
       - typescript
     dev: true
 
-  /@typescript-eslint/utils@6.13.1(eslint@8.55.0)(typescript@5.3.2):
-    resolution: {integrity: sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==}
+  /@typescript-eslint/utils@6.13.2(eslint@8.55.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
@@ -8582,9 +8518,9 @@ packages:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.6
-      '@typescript-eslint/scope-manager': 6.13.1
-      '@typescript-eslint/types': 6.13.1
-      '@typescript-eslint/typescript-estree': 6.13.1(typescript@5.3.2)
+      '@typescript-eslint/scope-manager': 6.13.2
+      '@typescript-eslint/types': 6.13.2
+      '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3)
       eslint: 8.55.0
       semver: 7.5.4
     transitivePeerDependencies:
@@ -8600,11 +8536,11 @@ packages:
       eslint-visitor-keys: 3.4.3
     dev: true
 
-  /@typescript-eslint/visitor-keys@6.13.1:
-    resolution: {integrity: sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==}
+  /@typescript-eslint/visitor-keys@6.13.2:
+    resolution: {integrity: sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dependencies:
-      '@typescript-eslint/types': 6.13.1
+      '@typescript-eslint/types': 6.13.2
       eslint-visitor-keys: 3.4.3
     dev: true
 
@@ -8612,31 +8548,31 @@ packages:
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
     dev: true
 
-  /@vitejs/plugin-react@3.1.0(vite@5.0.5):
+  /@vitejs/plugin-react@3.1.0(vite@5.0.7):
     resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
       vite: ^4.1.0-beta.0
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.22.11)
-      '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.22.11)
+      '@babel/core': 7.23.5
+      '@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.23.5)
+      '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.23.5)
       magic-string: 0.27.0
       react-refresh: 0.14.0
-      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@vitejs/plugin-vue@4.5.1(vite@5.0.5)(vue@3.3.9):
+  /@vitejs/plugin-vue@4.5.1(vite@5.0.7)(vue@3.3.11):
     resolution: {integrity: sha512-DaUzYFr+2UGDG7VSSdShKa9sIWYBa1LL8KC0MNOf2H5LjcTPjob0x8LbkqXWmAtbANJCkpiQTj66UVcQkN2s3g==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
       vite: ^4.0.0 || ^5.0.0
       vue: ^3.2.25
     dependencies:
-      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
-      vue: 3.3.9(typescript@5.3.2)
+      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vue: 3.3.11(typescript@5.3.3)
 
   /@vitest/coverage-v8@0.34.6(vitest@0.34.6):
     resolution: {integrity: sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==}
@@ -8716,6 +8652,14 @@ packages:
       path-browserify: 1.0.1
     dev: true
 
+  /@vue/compiler-core@3.3.11:
+    resolution: {integrity: sha512-h97/TGWBilnLuRaj58sxNrsUU66fwdRKLOLQ9N/5iNDfp+DZhYH9Obhe0bXxhedl8fjAgpRANpiZfbgWyruQ0w==}
+    dependencies:
+      '@babel/parser': 7.23.5
+      '@vue/shared': 3.3.11
+      estree-walker: 2.0.2
+      source-map-js: 1.0.2
+
   /@vue/compiler-core@3.3.9:
     resolution: {integrity: sha512-+/Lf68Vr/nFBA6ol4xOtJrW+BQWv3QWKfRwGSm70jtXwfhZNF4R/eRgyVJYoxFRhdCTk/F6g99BP0ffPgZihfQ==}
     dependencies:
@@ -8724,12 +8668,32 @@ packages:
       estree-walker: 2.0.2
       source-map-js: 1.0.2
 
+  /@vue/compiler-dom@3.3.11:
+    resolution: {integrity: sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw==}
+    dependencies:
+      '@vue/compiler-core': 3.3.11
+      '@vue/shared': 3.3.11
+
   /@vue/compiler-dom@3.3.9:
     resolution: {integrity: sha512-nfWubTtLXuT4iBeDSZ5J3m218MjOy42Vp2pmKVuBKo2/BLcrFUX8nCSr/bKRFiJ32R8qbdnnnBgRn9AdU5v0Sg==}
     dependencies:
       '@vue/compiler-core': 3.3.9
       '@vue/shared': 3.3.9
 
+  /@vue/compiler-sfc@3.3.11:
+    resolution: {integrity: sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==}
+    dependencies:
+      '@babel/parser': 7.23.5
+      '@vue/compiler-core': 3.3.11
+      '@vue/compiler-dom': 3.3.11
+      '@vue/compiler-ssr': 3.3.11
+      '@vue/reactivity-transform': 3.3.11
+      '@vue/shared': 3.3.11
+      estree-walker: 2.0.2
+      magic-string: 0.30.5
+      postcss: 8.4.32
+      source-map-js: 1.0.2
+
   /@vue/compiler-sfc@3.3.9:
     resolution: {integrity: sha512-wy0CNc8z4ihoDzjASCOCsQuzW0A/HP27+0MDSSICMjVIFzk/rFViezkR3dzH+miS2NDEz8ywMdbjO5ylhOLI2A==}
     dependencies:
@@ -8744,14 +8708,20 @@ packages:
       postcss: 8.4.32
       source-map-js: 1.0.2
 
+  /@vue/compiler-ssr@3.3.11:
+    resolution: {integrity: sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==}
+    dependencies:
+      '@vue/compiler-dom': 3.3.11
+      '@vue/shared': 3.3.11
+
   /@vue/compiler-ssr@3.3.9:
     resolution: {integrity: sha512-NO5oobAw78R0G4SODY5A502MGnDNiDjf6qvhn7zD7TJGc8XDeIEw4fg6JU705jZ/YhuokBKz0A5a/FL/XZU73g==}
     dependencies:
       '@vue/compiler-dom': 3.3.9
       '@vue/shared': 3.3.9
 
-  /@vue/language-core@1.8.24(typescript@5.3.2):
-    resolution: {integrity: sha512-2ClHvij0WlsDWryPzXJCSpPc6rusZFNoVtRZGgGGkKCmKuIREDDKmH8j+1tYyxPYyH0qL6pZ6+IHD8KIm5nWAw==}
+  /@vue/language-core@1.8.25(typescript@5.3.3):
+    resolution: {integrity: sha512-NJk/5DnAZlpvXX8BdWmHI45bWGLViUaS3R/RMrmFSvFMSbJKuEODpM4kR0F0Ofv5SFzCWuNiMhxameWpVdQsnA==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
@@ -8766,10 +8736,19 @@ packages:
       minimatch: 9.0.3
       muggle-string: 0.3.1
       path-browserify: 1.0.1
-      typescript: 5.3.2
+      typescript: 5.3.3
       vue-template-compiler: 2.7.14
     dev: true
 
+  /@vue/reactivity-transform@3.3.11:
+    resolution: {integrity: sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg==}
+    dependencies:
+      '@babel/parser': 7.23.5
+      '@vue/compiler-core': 3.3.11
+      '@vue/shared': 3.3.11
+      estree-walker: 2.0.2
+      magic-string: 0.30.5
+
   /@vue/reactivity-transform@3.3.9:
     resolution: {integrity: sha512-HnUFm7Ry6dFa4Lp63DAxTixUp8opMtQr6RxQCpDI1vlh12rkGIeYqMvJtK+IKyEfEOa2I9oCkD1mmsPdaGpdVg==}
     dependencies:
@@ -8779,37 +8758,40 @@ packages:
       estree-walker: 2.0.2
       magic-string: 0.30.5
 
-  /@vue/reactivity@3.3.9:
-    resolution: {integrity: sha512-VmpIqlNp+aYDg2X0xQhJqHx9YguOmz2UxuUJDckBdQCNkipJvfk9yA75woLWElCa0Jtyec3lAAt49GO0izsphw==}
+  /@vue/reactivity@3.3.11:
+    resolution: {integrity: sha512-D5tcw091f0nuu+hXq5XANofD0OXnBmaRqMYl5B3fCR+mX+cXJIGNw/VNawBqkjLNWETrFW0i+xH9NvDbTPVh7g==}
     dependencies:
-      '@vue/shared': 3.3.9
+      '@vue/shared': 3.3.11
 
-  /@vue/runtime-core@3.3.9:
-    resolution: {integrity: sha512-xxaG9KvPm3GTRuM4ZyU8Tc+pMVzcu6eeoSRQJ9IE7NmCcClW6z4B3Ij6L4EDl80sxe/arTtQ6YmgiO4UZqRc+w==}
+  /@vue/runtime-core@3.3.11:
+    resolution: {integrity: sha512-g9ztHGwEbS5RyWaOpXuyIVFTschclnwhqEbdy5AwGhYOgc7m/q3NFwr50MirZwTTzX55JY8pSkeib9BX04NIpw==}
     dependencies:
-      '@vue/reactivity': 3.3.9
-      '@vue/shared': 3.3.9
+      '@vue/reactivity': 3.3.11
+      '@vue/shared': 3.3.11
 
-  /@vue/runtime-dom@3.3.9:
-    resolution: {integrity: sha512-e7LIfcxYSWbV6BK1wQv9qJyxprC75EvSqF/kQKe6bdZEDNValzeRXEVgiX7AHI6hZ59HA4h7WT5CGvm69vzJTQ==}
+  /@vue/runtime-dom@3.3.11:
+    resolution: {integrity: sha512-OlhtV1PVpbgk+I2zl+Y5rQtDNcCDs12rsRg71XwaA2/Rbllw6mBLMi57VOn8G0AjOJ4Mdb4k56V37+g8ukShpQ==}
     dependencies:
-      '@vue/runtime-core': 3.3.9
-      '@vue/shared': 3.3.9
+      '@vue/runtime-core': 3.3.11
+      '@vue/shared': 3.3.11
       csstype: 3.1.2
 
-  /@vue/server-renderer@3.3.9(vue@3.3.9):
-    resolution: {integrity: sha512-w0zT/s5l3Oa3ZjtLW88eO4uV6AQFqU8X5GOgzq7SkQQu6vVr+8tfm+OI2kDBplS/W/XgCBuFXiPw6T5EdwXP0A==}
+  /@vue/server-renderer@3.3.11(vue@3.3.11):
+    resolution: {integrity: sha512-AIWk0VwwxCAm4wqtJyxBylRTXSy1wCLOKbWxHaHiu14wjsNYtiRCSgVuqEPVuDpErOlRdNnuRgipQfXRLjLN5A==}
     peerDependencies:
-      vue: 3.3.9
+      vue: 3.3.11
     dependencies:
-      '@vue/compiler-ssr': 3.3.9
-      '@vue/shared': 3.3.9
-      vue: 3.3.9(typescript@5.3.2)
+      '@vue/compiler-ssr': 3.3.11
+      '@vue/shared': 3.3.11
+      vue: 3.3.11(typescript@5.3.3)
+
+  /@vue/shared@3.3.11:
+    resolution: {integrity: sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw==}
 
   /@vue/shared@3.3.9:
     resolution: {integrity: sha512-ZE0VTIR0LmYgeyhurPTpy4KzKsuDyQbMSdM49eKkMnT5X4VfFBLysMzjIZhLEFQYjjOVVfbvUDHckwjDFiO2eA==}
 
-  /@vue/test-utils@2.4.1(vue@3.3.9):
+  /@vue/test-utils@2.4.1(vue@3.3.11):
     resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==}
     peerDependencies:
       '@vue/server-renderer': ^3.0.1
@@ -8819,7 +8801,7 @@ packages:
         optional: true
     dependencies:
       js-beautify: 1.14.9
-      vue: 3.3.9(typescript@5.3.2)
+      vue: 3.3.11(typescript@5.3.3)
       vue-component-type-helpers: 1.8.4
     dev: true
 
@@ -9261,15 +9243,6 @@ packages:
     resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
     engines: {node: '>=0.8'}
 
-  /assert@2.0.0:
-    resolution: {integrity: sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==}
-    dependencies:
-      es6-object-assign: 1.1.0
-      is-nan: 1.3.2
-      object-is: 1.1.5
-      util: 0.12.5
-    dev: true
-
   /assert@2.1.0:
     resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==}
     dependencies:
@@ -9439,7 +9412,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@babel/template': 7.22.5
-      '@babel/types': 7.22.17
+      '@babel/types': 7.23.5
       '@types/babel__core': 7.20.0
       '@types/babel__traverse': 7.20.0
     dev: true
@@ -9754,8 +9727,8 @@ packages:
     dependencies:
       node-gyp-build: 4.6.0
 
-  /bullmq@4.14.4:
-    resolution: {integrity: sha512-8tD3Zq4CP+2q+zZ1JSkKov5ra18Jhth8zdr2X1/V2MMOVpa8vfVCsQaRnKgDpiMpaF6AH9sucXUNtk4xamTEKw==}
+  /bullmq@4.15.2:
+    resolution: {integrity: sha512-VzmeiTP2ze7R3d/prOkwkoPyVxWm0N+Aj2lYQqljbWX7QqZgCaZCMiDVj97YpxFExdHDGKnmRPIS/ANY86GX2g==}
     dependencies:
       cron-parser: 4.8.1
       glob: 8.1.0
@@ -10002,45 +9975,45 @@ packages:
     resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
     dev: true
 
-  /chart.js@4.4.0:
-    resolution: {integrity: sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==}
+  /chart.js@4.4.1:
+    resolution: {integrity: sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==}
     engines: {pnpm: '>=7'}
     dependencies:
       '@kurkle/color': 0.3.2
     dev: false
 
-  /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.0)(date-fns@2.30.0):
+  /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.1)(date-fns@2.30.0):
     resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==}
     peerDependencies:
       chart.js: '>=2.8.0'
       date-fns: '>=2.0.0'
     dependencies:
-      chart.js: 4.4.0
+      chart.js: 4.4.1
       date-fns: 2.30.0
     dev: false
 
-  /chartjs-chart-matrix@2.0.1(chart.js@4.4.0):
+  /chartjs-chart-matrix@2.0.1(chart.js@4.4.1):
     resolution: {integrity: sha512-BGfeY+/PHnITyDlc7WfnKJ1RyOfgOzIqWp/gxzzl7pUjyoGzHDcw51qd2xJF9gdT9Def7ZwOnOMm8GJUXDxI0w==}
     peerDependencies:
       chart.js: '>=3.0.0'
     dependencies:
-      chart.js: 4.4.0
+      chart.js: 4.4.1
     dev: false
 
-  /chartjs-plugin-gradient@0.6.1(chart.js@4.4.0):
+  /chartjs-plugin-gradient@0.6.1(chart.js@4.4.1):
     resolution: {integrity: sha512-TGHNIh8KqQMLdb+UfY80cBHYRyOC47eeokmgkeajRdKGbFt462lJiyiq4ZJ25fiM7BGsmzoBLhmVyEw4B3gQxw==}
     peerDependencies:
       chart.js: '>=2.6.0'
     dependencies:
-      chart.js: 4.4.0
+      chart.js: 4.4.1
     dev: false
 
-  /chartjs-plugin-zoom@2.0.1(chart.js@4.4.0):
+  /chartjs-plugin-zoom@2.0.1(chart.js@4.4.1):
     resolution: {integrity: sha512-ogOmLu6e+Q7E1XWOCOz9YwybMslz9qNfGV2a+qjfmqJYpsw5ZMoRHZBUyW+NGhkpQ5PwwPA/+rikHpBZb7PZuA==}
     peerDependencies:
       chart.js: '>=3.2.0'
     dependencies:
-      chart.js: 4.4.0
+      chart.js: 4.4.1
       hammerjs: 2.0.8
     dev: false
 
@@ -10099,8 +10072,8 @@ packages:
     engines: {node: '>=10'}
     requiresBuild: true
 
-  /chromatic@9.1.0:
-    resolution: {integrity: sha512-9ib8k4LIfg/88kKufxz1N8vgCB7nlLhJqmx+Vx55cM/6DCB/M6oqroirVRXfdeC7qaZuhyUemPF2QHxBh7GXtQ==}
+  /chromatic@10.1.0:
+    resolution: {integrity: sha512-S+ztO8f1k/LckuzJKCqaTs6AfUQ0eLNT9kEoyCUwX7gkJnveQo5JStCfY55v30zogjRkHJpwqzEfSXl6AwO2tQ==}
     hasBin: true
     dev: false
 
@@ -10421,12 +10394,6 @@ packages:
     resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
     engines: {node: '>= 0.6'}
 
-  /core-js-compat@3.31.1:
-    resolution: {integrity: sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==}
-    dependencies:
-      browserslist: 4.21.9
-    dev: true
-
   /core-js-compat@3.33.3:
     resolution: {integrity: sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow==}
     dependencies:
@@ -10458,7 +10425,7 @@ packages:
       readable-stream: 3.6.0
     dev: false
 
-  /create-jest@29.7.0(@types/node@20.10.3):
+  /create-jest@29.7.0(@types/node@20.10.4):
     resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -10467,7 +10434,7 @@ packages:
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.10.3)
+      jest-config: 29.7.0(@types/node@20.10.4)
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -10670,8 +10637,8 @@ packages:
       uniq: 1.0.1
     dev: false
 
-  /cypress@13.6.0:
-    resolution: {integrity: sha512-quIsnFmtj4dBUEJYU4OH0H12bABJpSujvWexC24Ju1gTlKMJbeT6tTO0vh7WNfiBPPjoIXLN+OUqVtiKFs6SGw==}
+  /cypress@13.6.1:
+    resolution: {integrity: sha512-k1Wl5PQcA/4UoTffYKKaxA0FJKwg8yenYNYRzLt11CUR0Kln+h7Udne6mdU1cUIdXBDTVZWtmiUjzqGs7/pEpw==}
     engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
     hasBin: true
     requiresBuild: true
@@ -11320,10 +11287,6 @@ packages:
       is-symbol: 1.0.4
     dev: true
 
-  /es6-object-assign@1.1.0:
-    resolution: {integrity: sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==}
-    dev: true
-
   /es6-promise@4.2.8:
     resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==}
     requiresBuild: true
@@ -11477,7 +11440,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.13.1)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0):
+  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.13.2)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0):
     resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11498,7 +11461,7 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+      '@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
       debug: 3.2.7(supports-color@8.1.1)
       eslint: 8.55.0
       eslint-import-resolver-node: 0.3.9
@@ -11506,7 +11469,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.13.1)(eslint@8.55.0):
+  /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.13.2)(eslint@8.55.0):
     resolution: {integrity: sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11516,7 +11479,7 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.13.1(eslint@8.55.0)(typescript@5.3.2)
+      '@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
@@ -11525,7 +11488,7 @@ packages:
       doctrine: 2.1.0
       eslint: 8.55.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.13.1)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.13.2)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0)
       hasown: 2.0.0
       is-core-module: 2.13.1
       is-glob: 4.0.3
@@ -13586,8 +13549,8 @@ packages:
     resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
     engines: {node: '>=8'}
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/parser': 7.23.3
+      '@babel/core': 7.23.5
+      '@babel/parser': 7.23.5
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.0
       semver: 6.3.1
@@ -13599,8 +13562,8 @@ packages:
     resolution: {integrity: sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==}
     engines: {node: '>=10'}
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/parser': 7.23.3
+      '@babel/core': 7.23.5
+      '@babel/parser': 7.23.5
       '@istanbuljs/schema': 0.1.3
       istanbul-lib-coverage: 3.2.0
       semver: 7.5.4
@@ -13676,7 +13639,7 @@ packages:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.3.0
@@ -13697,7 +13660,7 @@ packages:
       - supports-color
     dev: true
 
-  /jest-cli@29.7.0(@types/node@20.10.3):
+  /jest-cli@29.7.0(@types/node@20.10.4):
     resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -13711,10 +13674,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.10.3)
+      create-jest: 29.7.0(@types/node@20.10.4)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.10.3)
+      jest-config: 29.7.0(@types/node@20.10.4)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.6.2
@@ -13725,7 +13688,7 @@ packages:
       - ts-node
     dev: true
 
-  /jest-config@29.7.0(@types/node@20.10.3):
+  /jest-config@29.7.0(@types/node@20.10.4):
     resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     peerDependencies:
@@ -13740,7 +13703,7 @@ packages:
       '@babel/core': 7.22.11
       '@jest/test-sequencer': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       babel-jest: 29.7.0(@babel/core@7.22.11)
       chalk: 4.1.2
       ci-info: 3.7.1
@@ -13820,7 +13783,7 @@ packages:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       jest-mock: 29.7.0
       jest-util: 29.7.0
     dev: true
@@ -13850,7 +13813,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.6
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -13911,7 +13874,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.10.3
+      '@types/node': 20.10.4
     dev: true
 
   /jest-mock@29.7.0:
@@ -13919,7 +13882,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       jest-util: 29.7.0
     dev: true
 
@@ -13974,7 +13937,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -14005,7 +13968,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       chalk: 4.1.2
       cjs-module-lexer: 1.2.2
       collect-v8-coverage: 1.0.1
@@ -14057,7 +14020,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       chalk: 4.1.2
       ci-info: 3.7.1
       graceful-fs: 4.2.11
@@ -14082,7 +14045,7 @@ packages:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -14101,13 +14064,13 @@ packages:
     resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.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.10.3):
+  /jest@29.7.0(@types/node@20.10.4):
     resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -14120,7 +14083,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.10.3)
+      jest-cli: 29.7.0(@types/node@20.10.4)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -14217,12 +14180,12 @@ packages:
         optional: true
     dependencies:
       '@babel/core': 7.23.5
-      '@babel/parser': 7.23.3
-      '@babel/plugin-transform-class-properties': 7.22.5(@babel/core@7.23.5)
+      '@babel/parser': 7.23.5
+      '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.5)
       '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.5)
       '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.5)
       '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.5)
-      '@babel/plugin-transform-private-methods': 7.22.5(@babel/core@7.23.5)
+      '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.5)
       '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
       '@babel/preset-flow': 7.23.3(@babel/core@7.23.5)
       '@babel/preset-typescript': 7.23.3(@babel/core@7.23.5)
@@ -14357,8 +14320,8 @@ packages:
       graceful-fs: 4.2.11
     dev: true
 
-  /jsonld@8.3.1:
-    resolution: {integrity: sha512-tYfKpWL56meSJCHS91Ph0+EUThHZOZ8bKuboME4998SF+Kkukp2PhCPdRCvA7tsGUKr9FvSoyIRqJPuImBcBuA==}
+  /jsonld@8.3.2:
+    resolution: {integrity: sha512-MwBbq95szLwt8eVQ1Bcfwmgju/Y5P2GdtlHE2ncyfuYjIdEhluUVyj1eudacf1mOkWIoS9GpDBTECqhmq7EOaA==}
     engines: {node: '>=14'}
     dependencies:
       '@digitalbazaar/http-client': 3.4.1
@@ -15134,10 +15097,10 @@ packages:
       msw: '>=0.35.0 <2.0.0'
     dependencies:
       is-node-process: 1.2.0
-      msw: 1.3.2(typescript@5.3.2)
+      msw: 1.3.2(typescript@5.3.3)
     dev: true
 
-  /msw@1.3.2(typescript@5.3.2):
+  /msw@1.3.2(typescript@5.3.3):
     resolution: {integrity: sha512-wKLhFPR+NitYTkQl5047pia0reNGgf0P6a1eTnA5aNlripmiz0sabMvvHcicE8kQ3/gZcI0YiPFWmYfowfm3lA==}
     engines: {node: '>=14'}
     hasBin: true
@@ -15166,7 +15129,7 @@ packages:
       path-to-regexp: 6.2.1
       strict-event-emitter: 0.4.6
       type-fest: 2.19.0
-      typescript: 5.3.2
+      typescript: 5.3.3
       yargs: 17.6.2
     transitivePeerDependencies:
       - encoding
@@ -15766,8 +15729,8 @@ packages:
     resolution: {integrity: sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==}
     dev: true
 
-  /otpauth@9.2.0:
-    resolution: {integrity: sha512-vbiHaeTJHrRG4fWRAZwVVrCnQz9SEzNINk2Hfx8BZY8UxTJEnqpOHxr11KfrRVAqWZdD6Y5jdyXW6Xp/ls9O/w==}
+  /otpauth@9.2.1:
+    resolution: {integrity: sha512-/MRvcm63pzK20NCsIOe8Btun42/yWNylPbUo/h5dMpSRJpoAJstWodEUjm4zUDeT1+Vbqif2E8IcP4trl1U4gQ==}
     dependencies:
       jssha: 3.3.1
     dev: false
@@ -17041,21 +17004,21 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /react-docgen-typescript@2.2.2(typescript@5.3.2):
+  /react-docgen-typescript@2.2.2(typescript@5.3.3):
     resolution: {integrity: sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==}
     peerDependencies:
       typescript: '>= 4.3.x'
     dependencies:
-      typescript: 5.3.2
+      typescript: 5.3.3
     dev: true
 
   /react-docgen@7.0.1:
     resolution: {integrity: sha512-rCz0HBIT0LWbIM+///LfRrJoTKftIzzwsYDf0ns5KwaEjejMHQRtphcns+IXFHDNY9pnz6G8l/JbbI6pD4EAIA==}
     engines: {node: '>=16.14.0'}
     dependencies:
-      '@babel/core': 7.22.11
-      '@babel/traverse': 7.22.11
-      '@babel/types': 7.22.17
+      '@babel/core': 7.23.5
+      '@babel/traverse': 7.23.5
+      '@babel/types': 7.23.5
       '@types/babel__core': 7.20.0
       '@types/babel__traverse': 7.20.0
       '@types/doctrine': 0.0.9
@@ -17252,29 +17215,18 @@ packages:
     resolution: {integrity: sha512-5AAx+mujtXijsEavc5lWXBPQqrM4+Dl5qNH96N2aNeuJFUzpiiToKPsxQD/zAIJHspz7zz0maX0PCtCTFVlixQ==}
     engines: {node: '>= 4'}
     dependencies:
-      assert: 2.0.0
+      assert: 2.1.0
       ast-types: 0.15.2
       esprima: 4.0.1
       source-map: 0.6.1
       tslib: 2.6.2
     dev: true
 
-  /recast@0.23.1:
-    resolution: {integrity: sha512-RokaBcoxSjXUDzz1TXSZmZsSW6ZpLmlA3GGqJ8uuTrQ9hZhEz+4Tpsc+gRvYRJ2BU4H+ZyUlg91eSGDw7bwy7g==}
-    engines: {node: '>= 4'}
-    dependencies:
-      assert: 2.0.0
-      ast-types: 0.16.1
-      esprima: 4.0.1
-      source-map: 0.6.1
-      tslib: 2.6.2
-    dev: true
-
   /recast@0.23.4:
     resolution: {integrity: sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==}
     engines: {node: '>= 4'}
     dependencies:
-      assert: 2.0.0
+      assert: 2.1.0
       ast-types: 0.16.1
       esprima: 4.0.1
       source-map: 0.6.1
@@ -17316,8 +17268,8 @@ packages:
       redis-errors: 1.2.0
     dev: false
 
-  /reflect-metadata@0.1.13:
-    resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}
+  /reflect-metadata@0.1.14:
+    resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
     dev: false
 
   /regenerate-unicode-properties@10.1.0:
@@ -17579,23 +17531,24 @@ packages:
       fsevents: 2.3.3
     dev: true
 
-  /rollup@4.6.1:
-    resolution: {integrity: sha512-jZHaZotEHQaHLgKr8JnQiDT1rmatjgKlMekyksz+yk9jt/8z9quNjnKNRoaM0wd9DC2QKXjmWWuDYtM3jfF8pQ==}
+  /rollup@4.7.0:
+    resolution: {integrity: sha512-7Kw0dUP4BWH78zaZCqF1rPyQ8D5DSU6URG45v1dqS/faNsx9WXyess00uTOZxKr7oR/4TOjO1CPudT8L1UsEgw==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.6.1
-      '@rollup/rollup-android-arm64': 4.6.1
-      '@rollup/rollup-darwin-arm64': 4.6.1
-      '@rollup/rollup-darwin-x64': 4.6.1
-      '@rollup/rollup-linux-arm-gnueabihf': 4.6.1
-      '@rollup/rollup-linux-arm64-gnu': 4.6.1
-      '@rollup/rollup-linux-arm64-musl': 4.6.1
-      '@rollup/rollup-linux-x64-gnu': 4.6.1
-      '@rollup/rollup-linux-x64-musl': 4.6.1
-      '@rollup/rollup-win32-arm64-msvc': 4.6.1
-      '@rollup/rollup-win32-ia32-msvc': 4.6.1
-      '@rollup/rollup-win32-x64-msvc': 4.6.1
+      '@rollup/rollup-android-arm-eabi': 4.7.0
+      '@rollup/rollup-android-arm64': 4.7.0
+      '@rollup/rollup-darwin-arm64': 4.7.0
+      '@rollup/rollup-darwin-x64': 4.7.0
+      '@rollup/rollup-linux-arm-gnueabihf': 4.7.0
+      '@rollup/rollup-linux-arm64-gnu': 4.7.0
+      '@rollup/rollup-linux-arm64-musl': 4.7.0
+      '@rollup/rollup-linux-riscv64-gnu': 4.7.0
+      '@rollup/rollup-linux-x64-gnu': 4.7.0
+      '@rollup/rollup-linux-x64-musl': 4.7.0
+      '@rollup/rollup-win32-arm64-msvc': 4.7.0
+      '@rollup/rollup-win32-ia32-msvc': 4.7.0
+      '@rollup/rollup-win32-x64-msvc': 4.7.0
       fsevents: 2.3.3
 
   /rrweb-cssom@0.6.0:
@@ -17846,8 +17799,8 @@ packages:
     resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
     engines: {node: '>=8'}
 
-  /shiki@0.14.5:
-    resolution: {integrity: sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==}
+  /shiki@0.14.6:
+    resolution: {integrity: sha512-R4koBBlQP33cC8cpzX0hAoOURBHJILp4Aaduh2eYi+Vj8ZBqtK/5SWNEHBS3qwUMu8dqOtI/ftno3ESfNeVW9g==}
     dependencies:
       ansi-sequence-parser: 1.1.1
       jsonc-parser: 3.2.0
@@ -18274,11 +18227,11 @@ packages:
     resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==}
     dev: true
 
-  /storybook@7.6.3:
-    resolution: {integrity: sha512-H3odxahMiR8vVW7ltlqcHhn3UVH5ta03weKlY7xvpv5DV+thZ+mEO2cDYfsufCSg0Ldb5LQ4qq3OyLVdpDBN8g==}
+  /storybook@7.6.4:
+    resolution: {integrity: sha512-nQhs9XkrroxjqMoBnnToyc6M8ndbmpkOb1qmULO4chtfMy4k0p9Un3K4TJvDaP8c3wPUFGd4ZaJ1hZNVmIl56Q==}
     hasBin: true
     dependencies:
-      '@storybook/cli': 7.6.3
+      '@storybook/cli': 7.6.4
     transitivePeerDependencies:
       - bufferutil
       - encoding
@@ -18865,13 +18818,13 @@ packages:
       escape-string-regexp: 5.0.0
     dev: false
 
-  /ts-api-utils@1.0.1(typescript@5.3.2):
+  /ts-api-utils@1.0.1(typescript@5.3.3):
     resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==}
     engines: {node: '>=16.13.0'}
     peerDependencies:
       typescript: '>=4.2.0'
     dependencies:
-      typescript: 5.3.2
+      typescript: 5.3.3
     dev: true
 
   /ts-case-convert@2.0.2:
@@ -19136,7 +19089,7 @@ packages:
       ioredis: 5.3.2
       mkdirp: 2.1.6
       pg: 8.11.3
-      reflect-metadata: 0.1.13
+      reflect-metadata: 0.1.14
       sha.js: 2.4.11
       tslib: 2.5.3
       uuid: 9.0.0
@@ -19151,8 +19104,8 @@ packages:
     hasBin: true
     dev: true
 
-  /typescript@5.3.2:
-    resolution: {integrity: sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==}
+  /typescript@5.3.3:
+    resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
     engines: {node: '>=14.17'}
     hasBin: true
 
@@ -19211,7 +19164,6 @@ packages:
     engines: {node: '>=14.0'}
     dependencies:
       '@fastify/busboy': 2.1.0
-    dev: true
 
   /unicode-canonical-property-names-ecmascript@2.0.0:
     resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==}
@@ -19433,7 +19385,7 @@ packages:
     resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
     hasBin: true
 
-  /v-code-diff@1.7.2(vue@3.3.9):
+  /v-code-diff@1.7.2(vue@3.3.11):
     resolution: {integrity: sha512-y+q8ZHf8GfphYLhcZbjAKcId/h6vZujS71Ryq5u+dI6Jg4ZLTdLrBNVSzYpHywHSSFFfBMdilm6XvVryEaH4+A==}
     requiresBuild: true
     peerDependencies:
@@ -19446,8 +19398,8 @@ packages:
       diff: 5.1.0
       diff-match-patch: 1.0.5
       highlight.js: 11.8.0
-      vue: 3.3.9(typescript@5.3.2)
-      vue-demi: 0.13.11(vue@3.3.9)
+      vue: 3.3.11(typescript@5.3.3)
+      vue-demi: 0.13.11(vue@3.3.11)
     dev: false
 
   /v8-to-istanbul@9.1.0:
@@ -19487,7 +19439,7 @@ packages:
       core-util-is: 1.0.2
       extsprintf: 1.3.0
 
-  /vite-node@0.34.6(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0):
+  /vite-node@0.34.6(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0):
     resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
@@ -19497,7 +19449,7 @@ packages:
       mlly: 1.4.0
       pathe: 1.1.1
       picocolors: 1.0.0
-      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -19513,8 +19465,8 @@ packages:
     resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
     dev: true
 
-  /vite@5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0):
-    resolution: {integrity: sha512-OekeWqR9Ls56f3zd4CaxzbbS11gqYkEiBtnWFFgYR2WV8oPJRRKq0mpskYy/XaoCL3L7VINDhqqOMNDiYdGvGg==}
+  /vite@5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0):
+    resolution: {integrity: sha512-B4T4rJCDPihrQo2B+h1MbeGL/k/GMAHzhQ8S0LjQ142s6/+l3hHTT095ORvsshj4QCkoWu3Xtmob5mazvakaOw==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
@@ -19541,10 +19493,10 @@ packages:
       terser:
         optional: true
     dependencies:
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       esbuild: 0.19.8
       postcss: 8.4.32
-      rollup: 4.6.1
+      rollup: 4.7.0
       sass: 1.69.5
       terser: 5.24.0
     optionalDependencies:
@@ -19595,7 +19547,7 @@ packages:
     dependencies:
       '@types/chai': 4.3.5
       '@types/chai-subset': 1.3.3
-      '@types/node': 20.10.3
+      '@types/node': 20.10.4
       '@vitest/expect': 0.34.6
       '@vitest/runner': 0.34.6
       '@vitest/snapshot': 0.34.6
@@ -19615,8 +19567,8 @@ packages:
       strip-literal: 1.0.1
       tinybench: 2.5.0
       tinypool: 0.7.0
-      vite: 5.0.5(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
-      vite-node: 0.34.6(@types/node@20.10.3)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite-node: 0.34.6(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
       why-is-node-running: 2.2.2
     transitivePeerDependencies:
       - less
@@ -19640,15 +19592,15 @@ packages:
     resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
     dev: false
 
-  /vue-component-type-helpers@1.8.24:
-    resolution: {integrity: sha512-lqWs/7fdRXoSBAlbouHBX+LNuaY6gI9xWW34m/ZIz9zVPYHEyw0b2/zaCBwlKx0NtKTeF/6pOpvrxVkh7nhIYg==}
+  /vue-component-type-helpers@1.8.25:
+    resolution: {integrity: sha512-NCA6sekiJIMnMs4DdORxATXD+/NRkQpS32UC+I1KQJUasx+Z7MZUb3Y+MsKsFmX+PgyTYSteb73JW77AibaCCw==}
     dev: true
 
   /vue-component-type-helpers@1.8.4:
     resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
     dev: true
 
-  /vue-demi@0.13.11(vue@3.3.9):
+  /vue-demi@0.13.11(vue@3.3.11):
     resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
     engines: {node: '>=12'}
     hasBin: true
@@ -19660,14 +19612,14 @@ packages:
       '@vue/composition-api':
         optional: true
     dependencies:
-      vue: 3.3.9(typescript@5.3.2)
+      vue: 3.3.11(typescript@5.3.3)
     dev: false
 
-  /vue-docgen-api@4.64.1(vue@3.3.9):
+  /vue-docgen-api@4.64.1(vue@3.3.11):
     resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==}
     dependencies:
-      '@babel/parser': 7.23.3
-      '@babel/types': 7.22.17
+      '@babel/parser': 7.23.5
+      '@babel/types': 7.23.5
       '@vue/compiler-dom': 3.3.9
       '@vue/compiler-sfc': 3.3.9
       ast-types: 0.14.2
@@ -19676,7 +19628,7 @@ packages:
       pug: 3.0.2
       recast: 0.22.0
       ts-map: 1.0.3
-      vue-inbrowser-compiler-independent-utils: 4.64.1(vue@3.3.9)
+      vue-inbrowser-compiler-independent-utils: 4.64.1(vue@3.3.11)
     transitivePeerDependencies:
       - vue
     dev: true
@@ -19699,12 +19651,12 @@ packages:
       - supports-color
     dev: true
 
-  /vue-inbrowser-compiler-independent-utils@4.64.1(vue@3.3.9):
+  /vue-inbrowser-compiler-independent-utils@4.64.1(vue@3.3.11):
     resolution: {integrity: sha512-Hn32n07XZ8j9W8+fmOXPQL+i+W2e/8i6mkH4Ju3H6nR0+cfvmWM95GhczYi5B27+Y8JlCKgAo04IUiYce4mKAw==}
     peerDependencies:
       vue: '>=2'
     dependencies:
-      vue: 3.3.9(typescript@5.3.2)
+      vue: 3.3.11(typescript@5.3.3)
     dev: true
 
   /vue-template-compiler@2.7.14:
@@ -19714,40 +19666,40 @@ packages:
       he: 1.2.0
     dev: true
 
-  /vue-tsc@1.8.24(typescript@5.3.2):
-    resolution: {integrity: sha512-eH1CSj231OzVEY5Hi7wS6ubzyOEwgr5jCptR0Ddf2SitGcaXIsPVDvrprm3eolCdyhDt3WS1Eb2F4fGX9BsUUw==}
+  /vue-tsc@1.8.25(typescript@5.3.3):
+    resolution: {integrity: sha512-lHsRhDc/Y7LINvYhZ3pv4elflFADoEOo67vfClAfF2heVHpHmVquLSjojgCSIwzA4F0Pc4vowT/psXCYcfk+iQ==}
     hasBin: true
     peerDependencies:
       typescript: '*'
     dependencies:
       '@volar/typescript': 1.11.1
-      '@vue/language-core': 1.8.24(typescript@5.3.2)
+      '@vue/language-core': 1.8.25(typescript@5.3.3)
       semver: 7.5.4
-      typescript: 5.3.2
+      typescript: 5.3.3
     dev: true
 
-  /vue@3.3.9(typescript@5.3.2):
-    resolution: {integrity: sha512-sy5sLCTR8m6tvUk1/ijri3Yqzgpdsmxgj6n6yl7GXXCXqVbmW2RCXe9atE4cEI6Iv7L89v5f35fZRRr5dChP9w==}
+  /vue@3.3.11(typescript@5.3.3):
+    resolution: {integrity: sha512-d4oBctG92CRO1cQfVBZp6WJAs0n8AK4Xf5fNjQCBeKCvMI1efGQ5E3Alt1slFJS9fZuPcFoiAiqFvQlv1X7t/w==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@vue/compiler-dom': 3.3.9
-      '@vue/compiler-sfc': 3.3.9
-      '@vue/runtime-dom': 3.3.9
-      '@vue/server-renderer': 3.3.9(vue@3.3.9)
-      '@vue/shared': 3.3.9
-      typescript: 5.3.2
+      '@vue/compiler-dom': 3.3.11
+      '@vue/compiler-sfc': 3.3.11
+      '@vue/runtime-dom': 3.3.11
+      '@vue/server-renderer': 3.3.11(vue@3.3.11)
+      '@vue/shared': 3.3.11
+      typescript: 5.3.3
 
-  /vuedraggable@4.1.0(vue@3.3.9):
+  /vuedraggable@4.1.0(vue@3.3.11):
     resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
     peerDependencies:
       vue: ^3.0.1
     dependencies:
       sortablejs: 1.14.0
-      vue: 3.3.9(typescript@5.3.2)
+      vue: 3.3.11(typescript@5.3.3)
     dev: false
 
   /w3c-xmlserializer@5.0.0:
@@ -20222,7 +20174,7 @@ packages:
       sharp: 0.31.3
     dev: false
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.3)(@storybook/components@7.5.3)(@storybook/core-events@7.6.3)(@storybook/manager-api@7.6.3)(@storybook/preview-api@7.6.3)(@storybook/theming@7.6.3)(@storybook/types@7.6.3)(react-dom@18.2.0)(react@18.2.0):
+  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.4)(@storybook/components@7.6.3)(@storybook/core-events@7.6.4)(@storybook/manager-api@7.6.4)(@storybook/preview-api@7.6.4)(@storybook/theming@7.6.4)(@storybook/types@7.6.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
@@ -20243,13 +20195,13 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/blocks': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.6.3
-      '@storybook/manager-api': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.6.3
-      '@storybook/theming': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.3
+      '@storybook/blocks': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/components': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 7.6.4
+      '@storybook/manager-api': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.6.4
+      '@storybook/theming': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.4
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     dev: true

From b7bdd45dba3a0c4fe07bcd2b60f43672891f0e95 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 9 Dec 2023 13:13:31 +0900
Subject: [PATCH 195/435] Fix/vue import error on intellij (#12612)

* Fix fix labeler config (#8)

* fix vue import error

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 packages/frontend/vue-shims.d.ts | 6 ++++++
 1 file changed, 6 insertions(+)
 create mode 100644 packages/frontend/vue-shims.d.ts

diff --git a/packages/frontend/vue-shims.d.ts b/packages/frontend/vue-shims.d.ts
new file mode 100644
index 0000000000..eba994772d
--- /dev/null
+++ b/packages/frontend/vue-shims.d.ts
@@ -0,0 +1,6 @@
+/* eslint-disable */
+declare module "*.vue" {
+	import { defineComponent } from "vue";
+	const component: ReturnType<typeof defineComponent>;
+	export default component;
+}

From dd332b3515fd5284c02e05cd7b5f02d82237b0e8 Mon Sep 17 00:00:00 2001
From: Yuriha <121590760+yuriha-chan@users.noreply.github.com>
Date: Sat, 9 Dec 2023 13:14:51 +0900
Subject: [PATCH 196/435] =?UTF-8?q?Misskey=20Play=E3=81=AE=E3=83=8E?=
 =?UTF-8?q?=E3=83=BC=E3=83=88=E6=8A=95=E7=A8=BF=E7=94=BB=E9=9D=A2=E3=81=A7?=
 =?UTF-8?q?=E3=80=8C=E5=86=85=E5=AE=B9=E3=82=92=E9=9A=A0=E3=81=99=E3=80=8D?=
 =?UTF-8?q?=E3=82=92=E8=A8=AD=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#12576)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Add the content warning option in AiScript UI postFormButton

* Fix initial CW in postFormButton

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 packages/frontend/src/components/MkAsUi.vue           | 2 ++
 packages/frontend/src/components/MkPostForm.vue       | 5 +++--
 packages/frontend/src/components/MkPostFormDialog.vue | 1 +
 packages/frontend/src/scripts/aiscript/ui.ts          | 8 ++++++++
 4 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue
index 7670b54f16..4239cc6091 100644
--- a/packages/frontend/src/components/MkAsUi.vue
+++ b/packages/frontend/src/components/MkAsUi.vue
@@ -43,6 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			fixed
 			:instant="true"
 			:initialText="c.form.text"
+			:initialCw="c.form.cw"
 		/>
 	</div>
 	<MkFolder v-else-if="c.type === 'folder'" :defaultOpen="c.opened">
@@ -97,6 +98,7 @@ function onSwitchUpdate(v) {
 function openPostForm() {
 	os.post({
 		initialText: c.form.text,
+		initialCw: c.form.cw,
 		instant: true,
 	});
 }
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 9e5c4ca3f1..e6d55ae982 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -135,6 +135,7 @@ const props = withDefaults(defineProps<{
 	mention?: Misskey.entities.User;
 	specified?: Misskey.entities.User;
 	initialText?: string;
+	initialCw?: string;
 	initialVisibility?: (typeof Misskey.noteVisibilities)[number];
 	initialFiles?: Misskey.entities.DriveFile[];
 	initialLocalOnly?: boolean;
@@ -177,10 +178,10 @@ const poll = ref<{
 	expiresAt: string | null;
 	expiredAfter: string | null;
 } | null>(null);
-const useCw = ref(false);
+const useCw = ref<boolean>(!!props.initialCw);
 const showPreview = ref(defaultStore.state.showPreview);
 watch(showPreview, () => defaultStore.set('showPreview', showPreview.value));
-const cw = ref<string | null>(null);
+const cw = ref<string | null>(props.initialCw ?? null);
 const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
 const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
 const visibleUsers = ref([]);
diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue
index a0fad1ab41..7734e5a6d1 100644
--- a/packages/frontend/src/components/MkPostFormDialog.vue
+++ b/packages/frontend/src/components/MkPostFormDialog.vue
@@ -22,6 +22,7 @@ const props = defineProps<{
 	mention?: Misskey.entities.User;
 	specified?: Misskey.entities.User;
 	initialText?: string;
+	initialCw?: string;
 	initialVisibility?: typeof Misskey.noteVisibilities;
 	initialFiles?: Misskey.entities.DriveFile[];
 	initialLocalOnly?: boolean;
diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts
index d326b956e8..75b9248432 100644
--- a/packages/frontend/src/scripts/aiscript/ui.ts
+++ b/packages/frontend/src/scripts/aiscript/ui.ts
@@ -121,6 +121,7 @@ export type AsUiPostFormButton = AsUiComponentBase & {
 	rounded?: boolean;
 	form?: {
 		text: string;
+		cw?: string;
 	};
 };
 
@@ -128,6 +129,7 @@ export type AsUiPostForm = AsUiComponentBase & {
 	type: 'postForm';
 	form?: {
 		text: string;
+		cw?: string;
 	};
 };
 
@@ -454,8 +456,11 @@ function getPostFormButtonOptions(def: values.Value | undefined, call: (fn: valu
 	const getForm = () => {
 		const text = form!.value.get('text');
 		utils.assertString(text);
+		const cw = form!.value.get('cw');
+		if (cw) utils.assertString(cw);
 		return {
 			text: text.value,
+			cw: cw?.value,
 		};
 	};
 
@@ -478,8 +483,11 @@ function getPostFormOptions(def: values.Value | undefined, call: (fn: values.VFn
 	const getForm = () => {
 		const text = form!.value.get('text');
 		utils.assertString(text);
+		const cw = form!.value.get('cw');
+		if (cw) utils.assertString(cw);
 		return {
 			text: text.value,
+			cw: cw?.value,
 		};
 	};
 

From b72f9186b56c1ee1017a6570aa8eb851dfa00c60 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 9 Dec 2023 13:15:30 +0900
Subject: [PATCH 197/435] 2023.12.0-beta.3

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

diff --git a/package.json b/package.json
index d2685c3203..8e96a07cd3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.12.0-beta.2",
+	"version": "2023.12.0-beta.3",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From 025afe88b415d2401f73c6d375a1d4601b9c5a49 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 9 Dec 2023 21:52:25 +0900
Subject: [PATCH 198/435] =?UTF-8?q?(dev)=20index.html=E3=81=ABmeta[name=3D?=
 =?UTF-8?q?viewport]=E3=81=8C=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F=E3=81=AE?=
 =?UTF-8?q?=E3=81=A7=E8=BF=BD=E5=8A=A0=EF=BC=86=E8=B6=B3=E3=82=8A=E3=81=A6?=
 =?UTF-8?q?=E3=81=AA=E3=81=84CSP=E8=BF=BD=E5=8A=A0=20(#12613)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (dev) index.htmlにmeta[name=viewport]がなかったので追加&足りてないCSP追加

* fix tab
---
 packages/frontend/src/index.html | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/index.html b/packages/frontend/src/index.html
index b93e32265e..49189914a0 100644
--- a/packages/frontend/src/index.html
+++ b/packages/frontend/src/index.html
@@ -17,12 +17,14 @@
 	<meta
 		http-equiv="Content-Security-Policy"
 		content="default-src 'self';
-		  script-src 'self';
-		  style-src 'self' 'unsafe-inline';
-		  img-src 'self' data: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
+			worker-src 'self';
+			script-src 'self';
+			style-src 'self' 'unsafe-inline';
+			img-src 'self' data: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
 			media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;"
 	/>
 	<meta property="og:site_name" content="[DEV BUILD] Misskey" />
+	<meta name="viewport" content="width=device-width, initial-scale=1">
 </head>
 
 <body>

From 4c135a5ca12c5e0efc343425905b995ecb0a5a90 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Sun, 10 Dec 2023 02:11:17 +0900
Subject: [PATCH 199/435] Fix indentation (#12615)

---
 .../src/components/MkEmojiPicker.section.vue  | 58 ++++++++--------
 .../frontend/src/components/MkEmojiPicker.vue | 22 +++----
 packages/frontend/src/pages/admin-user.vue    | 66 +++++++++----------
 .../frontend/src/pages/settings/general.vue   |  2 +-
 4 files changed, 74 insertions(+), 74 deletions(-)

diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index 35de9bbb5e..14f3f5770f 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -26,35 +26,35 @@ SPDX-License-Identifier: AGPL-3.0-only
 </section>
 <!-- フォルダの中にはカスタム絵文字やフォルダがある -->
 <section v-else v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);">
-  <header class="_acrylic" @click="shown = !shown">
-    <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-folder ti-fw"></i>:{{ customEmojiTree.length }} <i class="ti ti-icons ti-fw"></i>:{{ emojis.length }})
-  </header>
-  <div v-if="shown" style="padding-left: 9px;">
-      <MkEmojiPickerSection
-          v-for="child in customEmojiTree"
-          :key="`custom:${child.value}`"
-          :initialShown="initialShown"
-          :emojis="computed(() => customEmojis.filter(e => e.category === child.category).map(e => `:${e.name}:`))"
-          :hasChildSection="child.children.length !== 0"
-          :customEmojiTree="child.children"
-          @chosen="nestedChosen"
-      >
-          {{ child.value || i18n.ts.other }}
-      </MkEmojiPickerSection>
-  </div>
-  <div v-if="shown" class="body">
-    <button
-        v-for="emoji in emojis"
-        :key="emoji"
-        :data-emoji="emoji"
-        class="_button item"
-        @pointerenter="computeButtonTitle"
-        @click="emit('chosen', emoji, $event)"
-    >
-      <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/>
-      <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
-    </button>
-  </div>
+	<header class="_acrylic" @click="shown = !shown">
+		<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-folder ti-fw"></i>:{{ customEmojiTree.length }} <i class="ti ti-icons ti-fw"></i>:{{ emojis.length }})
+	</header>
+	<div v-if="shown" style="padding-left: 9px;">
+		<MkEmojiPickerSection
+			v-for="child in customEmojiTree"
+			:key="`custom:${child.value}`"
+			:initialShown="initialShown"
+			:emojis="computed(() => customEmojis.filter(e => e.category === child.category).map(e => `:${e.name}:`))"
+			:hasChildSection="child.children.length !== 0"
+			:customEmojiTree="child.children"
+			@chosen="nestedChosen"
+		>
+			{{ child.value || i18n.ts.other }}
+		</MkEmojiPickerSection>
+	</div>
+	<div v-if="shown" class="body">
+		<button
+			v-for="emoji in emojis"
+			:key="emoji"
+			:data-emoji="emoji"
+			class="_button item"
+			@pointerenter="computeButtonTitle"
+			@click="emit('chosen', emoji, $event)"
+		>
+			<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/>
+			<MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/>
+		</button>
+	</div>
 </section>
 </template>
 
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index b5e5a0260c..29a33a162f 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -77,8 +77,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 				:key="`custom:${child.value}`"
 				:initialShown="false"
 				:emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value).filter(filterAvailable).map(e => `:${e.name}:`))"
-        :hasChildSection="child.children.length !== 0"
-        :customEmojiTree="child.children"
+				:hasChildSection="child.children.length !== 0"
+				:customEmojiTree="child.children"
 				@chosen="chosen"
 			>
 				{{ child.value || i18n.ts.other }}
@@ -103,12 +103,12 @@ import { ref, shallowRef, computed, watch, onMounted } from 'vue';
 import * as Misskey from 'misskey-js';
 import XSection from '@/components/MkEmojiPicker.section.vue';
 import {
-  emojilist,
-  emojiCharByCategory,
-  UnicodeEmojiDef,
-  unicodeEmojiCategories as categories,
-  getEmojiName,
-  CustomEmojiFolderTree
+	emojilist,
+	emojiCharByCategory,
+	UnicodeEmojiDef,
+	unicodeEmojiCategories as categories,
+	getEmojiName,
+	CustomEmojiFolderTree
 } from '@/scripts/emojilist.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import * as os from '@/os.js';
@@ -176,9 +176,9 @@ function parseAndMergeCategories(input: string, root: CustomEmojiFolderTree): Cu
 }
 
 customEmojiCategories.value.forEach(ec => {
-  if (ec !== null) {
-    parseAndMergeCategories(ec, customEmojiFolderRoot);
-  }
+	if (ec !== null) {
+		parseAndMergeCategories(ec, customEmojiFolderRoot);
+	}
 });
 
 parseAndMergeCategories('', customEmojiFolderRoot);
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index fd839b4369..55ad1e2ab1 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -122,7 +122,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 							</template>
 						</MkFolder>
 
-                        <div>
+						<div>
 							<MkButton v-if="iAmModerator" inline danger style="margin-right: 8px;" @click="unsetUserAvatar"><i class="ti ti-user-circle"></i> {{ i18n.ts.unsetUserAvatar }}</MkButton>
 							<MkButton v-if="iAmModerator" inline danger @click="unsetUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.unsetUserBanner }}</MkButton>
 						</div>
@@ -325,41 +325,41 @@ async function toggleSuspend(v) {
 }
 
 async function unsetUserAvatar() {
-  const confirm = await os.confirm({
-    type: 'warning',
-    text: i18n.ts.unsetUserAvatarConfirm,
-  });
-  if (confirm.canceled) return;
-  const process = async () => {
-    await os.api('admin/unset-user-avatar', { userId: user.value.id });
-    os.success();
-  };
-  await process().catch(err => {
-    os.alert({
-      type: 'error',
-      text: err.toString(),
-    });
-  });
-  refreshUser();
+	const confirm = await os.confirm({
+		type: 'warning',
+		text: i18n.ts.unsetUserAvatarConfirm,
+	});
+	if (confirm.canceled) return;
+	const process = async () => {
+		await os.api('admin/unset-user-avatar', { userId: user.value.id });
+		os.success();
+	};
+	await process().catch(err => {
+		os.alert({
+			type: 'error',
+			text: err.toString(),
+		});
+	});
+	refreshUser();
 }
 
 async function unsetUserBanner() {
-  const confirm = await os.confirm({
-    type: 'warning',
-    text: i18n.ts.unsetUserBannerConfirm,
-  });
-  if (confirm.canceled) return;
-  const process = async () => {
-    await os.api('admin/unset-user-banner', { userId: user.value.id });
-    os.success();
-  };
-  await process().catch(err => {
-    os.alert({
-      type: 'error',
-      text: err.toString(),
-    });
-  });
-  refreshUser();
+	const confirm = await os.confirm({
+		type: 'warning',
+		text: i18n.ts.unsetUserBannerConfirm,
+	});
+	if (confirm.canceled) return;
+	const process = async () => {
+		await os.api('admin/unset-user-banner', { userId: user.value.id });
+		os.success();
+	};
+	await process().catch(err => {
+		os.alert({
+			type: 'error',
+			text: err.toString(),
+		});
+	});
+	refreshUser();
 }
 
 async function deleteAllFiles() {
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 98a07d907d..0c83abf7f6 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -293,7 +293,7 @@ const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroup
 watch(lang, () => {
 	miLocalStorage.setItem('lang', lang.value as string);
 	miLocalStorage.removeItem('locale');
-  miLocalStorage.removeItem('localeVersion');
+	miLocalStorage.removeItem('localeVersion');
 });
 
 watch(fontSize, () => {

From 2217d0c050d16aa195a65780ffd6c450ba202dc1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?=
 <root@acid-chicken.com>
Date: Sun, 10 Dec 2023 17:53:38 +0900
Subject: [PATCH 200/435] refactor(frontend): remove redundant class names
 (#12618)

---
 ...lugin-unwind-css-module-class-name.test.ts |   3 +-
 ...lup-plugin-unwind-css-module-class-name.ts | 223 +++++++++++++++++-
 .../frontend/src/components/MkCodeEditor.vue  |   2 +-
 .../src/components/MkNoteDetailed.vue         |   2 +-
 packages/frontend/src/components/MkSwitch.vue |   5 +-
 packages/frontend/src/pages/admin-user.vue    |   7 +-
 .../src/pages/admin/modlog.ModLog.vue         |   5 +-
 packages/frontend/src/ui/deck.vue             |   2 +-
 packages/frontend/src/ui/universal.vue        |   2 +-
 9 files changed, 222 insertions(+), 29 deletions(-)

diff --git a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts
index 759f270393..550e08d7f7 100644
--- a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts
+++ b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts
@@ -180,7 +180,7 @@ import './photoswipe-!~{003}~.js';
 const _hoisted_1 = createBaseVNode("i", {
   class: "ti ti-photo"
 }, null, -1);
-const _sfc_main = defineComponent({
+const index_photos = defineComponent({
   __name: "index.photos",
   props: {
     user: {}
@@ -261,7 +261,6 @@ const style0 = {
 const cssModules = {
   "$style": style0
 };
-const index_photos = _sfc_main;
 export {index_photos as default};
 `.slice(1));
 });
diff --git a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.ts b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.ts
index 18c817e0f5..68cdc0bc78 100644
--- a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.ts
+++ b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.ts
@@ -13,13 +13,13 @@ function isFalsyIdentifier(identifier: estree.Identifier): boolean {
 	return identifier.name === 'undefined' || identifier.name === 'NaN';
 }
 
-function normalizeClassWalker(tree: estree.Node): string | null {
+function normalizeClassWalker(tree: estree.Node, stack: string | undefined): string | null {
 	if (tree.type === 'Identifier') return isFalsyIdentifier(tree) ? '' : null;
 	if (tree.type === 'Literal') return typeof tree.value === 'string' ? tree.value : '';
 	if (tree.type === 'BinaryExpression') {
 		if (tree.operator !== '+') return null;
-		const left = normalizeClassWalker(tree.left);
-		const right = normalizeClassWalker(tree.right);
+		const left = normalizeClassWalker(tree.left, stack);
+		const right = normalizeClassWalker(tree.right, stack);
 		if (left === null || right === null) return null;
 		return `${left}${right}`;
 	}
@@ -33,15 +33,15 @@ function normalizeClassWalker(tree: estree.Node): string | null {
 	if (tree.type === 'ArrayExpression') {
 		const values = tree.elements.map((treeNode) => {
 			if (treeNode === null) return '';
-			if (treeNode.type === 'SpreadElement') return normalizeClassWalker(treeNode.argument);
-			return normalizeClassWalker(treeNode);
+			if (treeNode.type === 'SpreadElement') return normalizeClassWalker(treeNode.argument, stack);
+			return normalizeClassWalker(treeNode, stack);
 		});
 		if (values.some((x) => x === null)) return null;
 		return values.join(' ');
 	}
 	if (tree.type === 'ObjectExpression') {
 		const values = tree.properties.map((treeNode) => {
-			if (treeNode.type === 'SpreadElement') return normalizeClassWalker(treeNode.argument);
+			if (treeNode.type === 'SpreadElement') return normalizeClassWalker(treeNode.argument, stack);
 			let x = treeNode.value;
 			let inveted = false;
 			while (x.type === 'UnaryExpression' && x.operator === '!') {
@@ -67,18 +67,26 @@ function normalizeClassWalker(tree: estree.Node): string | null {
 		if (values.some((x) => x === null)) return null;
 		return values.join(' ');
 	}
-	console.error(`Unexpected node type: ${tree.type}`);
+	if (
+		tree.type !== 'CallExpression' &&
+		tree.type !== 'ChainExpression' &&
+		tree.type !== 'ConditionalExpression' &&
+		tree.type !== 'LogicalExpression' &&
+		tree.type !== 'MemberExpression') {
+		console.error(stack ? `Unexpected node type: ${tree.type} (in ${stack})` : `Unexpected node type: ${tree.type}`);
+	}
 	return null;
 }
 
-export function normalizeClass(tree: estree.Node): string | null {
-	const walked = normalizeClassWalker(tree);
+export function normalizeClass(tree: estree.Node, stack?: string): string | null {
+	const walked = normalizeClassWalker(tree, stack);
 	return walked && walked.replace(/^\s+|\s+(?=\s)|\s+$/g, '');
 }
 
 export function unwindCssModuleClassName(ast: estree.Node): void {
 	(walk as typeof estreeWalker.walk)(ast, {
 		enter(node, parent): void {
+			//#region
 			if (parent?.type !== 'Program') return;
 			if (node.type !== 'VariableDeclaration') return;
 			if (node.declarations.length !== 1) return;
@@ -102,6 +110,14 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
 				return true;
 			});
 			if (!~__cssModulesIndex) return;
+			/* This region assumeed that the entered node looks like the following code.
+			 *
+			 * ```ts
+			 * const SomeComponent = _export_sfc(_sfc_main, [["foo", bar], ["__cssModules", cssModules]]);
+			 * ```
+			 */
+			//#endregion
+			//#region
 			const cssModuleForestName = ((node.declarations[0].init.arguments[1].elements[__cssModulesIndex] as estree.ArrayExpression).elements[1] as estree.Identifier).name;
 			const cssModuleForestNode = parent.body.find((x) => {
 				if (x.type !== 'VariableDeclaration') return false;
@@ -117,6 +133,16 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
 				if (property.value.type !== 'Identifier') return [];
 				return [[property.key.value as string, property.value.name as string]];
 			}));
+			/* This region collected a VariableDeclaration node in the module that looks like the following code.
+			 *
+			 * ```ts
+			 * const cssModules = {
+			 *   "$style": style0,
+			 * };
+			 * ```
+			 */
+			//#endregion
+			//#region
 			const sfcMain = parent.body.find((x) => {
 				if (x.type !== 'VariableDeclaration') return false;
 				if (x.declarations.length !== 1) return false;
@@ -146,7 +172,22 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
 			if (ctx.type !== 'Identifier') return;
 			if (ctx.name !== '_ctx') return;
 			if (render.argument.body.type !== 'BlockStatement') return;
+			/* This region assumed that `sfcMain` looks like the following code.
+			 *
+			 * ```ts
+			 * const _sfc_main = defineComponent({
+			 *   setup(_props) {
+			 *     ...
+			 *     return (_ctx, _cache) => {
+			 *       ...
+			 *     };
+			 *   },
+			 * });
+			 * ```
+			 */
+			//#endregion
 			for (const [key, value] of moduleForest) {
+				//#region
 				const cssModuleTreeNode = parent.body.find((x) => {
 					if (x.type !== 'VariableDeclaration') return false;
 					if (x.declarations.length !== 1) return false;
@@ -172,6 +213,19 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
 					if (actualValue.declarations[0].init?.type !== 'Literal') return [];
 					return [[actualKey, actualValue.declarations[0].init.value as string]];
 				}));
+				/* This region collected VariableDeclaration nodes in the module that looks like the following code.
+				 *
+				 * ```ts
+				 * const foo = "bar";
+				 * const baz = "qux";
+				 * const style0 = {
+				 *   foo: foo,
+				 *   baz: baz,
+				 * };
+				 * ```
+				 */
+				//#endregion
+				//#region
 				(walk as typeof estreeWalker.walk)(render.argument.body, {
 					enter(childNode) {
 						if (childNode.type !== 'MemberExpression') return;
@@ -189,6 +243,39 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
 						});
 					},
 				});
+				/* This region inlined the reference identifier of the class name in the render function into the actual literal, as in the following code.
+				 *
+				 * ```ts
+				 * const _sfc_main = defineComponent({
+				 *   setup(_props) {
+				 *     ...
+				 *     return (_ctx, _cache) => {
+				 *       ...
+				 *       return openBlock(), createElementBlock("div", {
+				 *         class: normalizeClass(_ctx.$style.foo),
+				 *       }, null);
+				 *     };
+				 *   },
+				 * });
+				 * ```
+				 *
+				 * ↓
+				 *
+				 * ```ts
+				 * const _sfc_main = defineComponent({
+				 *   setup(_props) {
+				 *     ...
+				 *     return (_ctx, _cache) => {
+				 *       ...
+				 *       return openBlock(), createElementBlock("div", {
+				 *         class: normalizeClass("bar"),
+				 *       }, null);
+				 *     };
+				 *   },
+				 * });
+				 */
+				//#endregion
+				//#region
 				(walk as typeof estreeWalker.walk)(render.argument.body, {
 					enter(childNode) {
 						if (childNode.type !== 'MemberExpression') return;
@@ -205,13 +292,47 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
 						});
 					},
 				});
+				/* This region replaced the reference identifier of missing class names in the render function with `undefined`, as in the following code.
+				 *
+				 * ```ts
+				 * const _sfc_main = defineComponent({
+				 *   setup(_props) {
+				 *     ...
+				 *     return (_ctx, _cache) => {
+				 *       ...
+				 *       return openBlock(), createElementBlock("div", {
+				 *         class: normalizeClass(_ctx.$style.hoge),
+				 *       }, null);
+				 *     };
+				 *   },
+				 * });
+				 * ```
+				 *
+				 * ↓
+				 *
+				 * ```ts
+				 * const _sfc_main = defineComponent({
+				 *   setup(_props) {
+				 *     ...
+				 *     return (_ctx, _cache) => {
+				 *       ...
+				 *       return openBlock(), createElementBlock("div", {
+				 *         class: normalizeClass(undefined),
+				 *       }, null);
+				 *     };
+				 *   },
+				 * });
+				 * ```
+				 */
+				//#endregion
+				//#region
 				(walk as typeof estreeWalker.walk)(render.argument.body, {
 					enter(childNode) {
 						if (childNode.type !== 'CallExpression') return;
 						if (childNode.callee.type !== 'Identifier') return;
 						if (childNode.callee.name !== 'normalizeClass') return;
 						if (childNode.arguments.length !== 1) return;
-						const normalized = normalizeClass(childNode.arguments[0]);
+						const normalized = normalizeClass(childNode.arguments[0], name);
 						if (normalized === null) return;
 						this.replace({
 							type: 'Literal',
@@ -219,8 +340,60 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
 						});
 					},
 				});
+				/* This region compiled the `normalizeClass` call into a pseudo-AOT compilation, as in the following code.
+				 *
+				 * ```ts
+				 * const _sfc_main = defineComponent({
+				 *   setup(_props) {
+				 *     ...
+				 *     return (_ctx, _cache) => {
+				 *       ...
+				 *       return openBlock(), createElementBlock("div", {
+				 *         class: normalizeClass("bar"),
+				 *       }, null);
+				 *     };
+				 *   },
+				 * });
+				 * ```
+				 *
+				 * ↓
+				 *
+				 * ```ts
+				 * const _sfc_main = defineComponent({
+				 *   setup(_props) {
+				 *     ...
+				 *     return (_ctx, _cache) => {
+				 *       ...
+				 *       return openBlock(), createElementBlock("div", {
+				 *         class: "bar",
+				 *       }, null);
+				 *     };
+				 *   },
+				 * });
+				 * ```
+				 */
+				//#endregion
 			}
+			//#region
 			if (node.declarations[0].init.arguments[1].elements.length === 1) {
+				(walk as typeof estreeWalker.walk)(ast, {
+					enter(childNode) {
+						if (childNode.type !== 'Identifier') return;
+						if (childNode.name !== ident) return;
+						this.replace({
+							type: 'Identifier',
+							name: node.declarations[0].id.name,
+						});
+					},
+				});
+				this.remove();
+				/* NOTE: The above logic is valid as long as the following two conditions are met.
+				 *
+				 * - the uniqueness of `ident` is kept throughout the module
+				 * - `_export_sfc` is noop when the second argument is an empty array
+				 *
+				 * Otherwise, the below logic should be used instead.
+
 				this.replace({
 					type: 'VariableDeclaration',
 					declarations: [{
@@ -236,6 +409,7 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
 					}],
 					kind: 'const',
 				});
+				 */
 			} else {
 				this.replace({
 					type: 'VariableDeclaration',
@@ -263,6 +437,35 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
 					kind: 'const',
 				});
 			}
+			/* This region removed the `__cssModules` reference from the second argument of `_export_sfc`, as in the following code.
+			 *
+			 * ```ts
+			 * const SomeComponent = _export_sfc(_sfc_main, [["foo", bar], ["__cssModules", cssModules]]);
+			 * ```
+			 *
+			 * ↓
+			 *
+			 * ```ts
+			 * const SomeComponent = _export_sfc(_sfc_main, [["foo", bar]]);
+			 * ```
+			 *
+			 * When the declaration becomes noop, it is removed as follows.
+			 *
+			 * ```ts
+			 * const _sfc_main = defineComponent({
+			 *   ...
+			 * });
+			 * const SomeComponent = _export_sfc(_sfc_main, []);
+			 * ```
+			 *
+			 * ↓
+			 *
+			 * ```ts
+			 * const SomeComponent = defineComponent({
+			 *   ...
+			 * });
+			 */
+			//#endregion
 		},
 	});
 }
diff --git a/packages/frontend/src/components/MkCodeEditor.vue b/packages/frontend/src/components/MkCodeEditor.vue
index 03788af21e..60f16f285f 100644
--- a/packages/frontend/src/components/MkCodeEditor.vue
+++ b/packages/frontend/src/components/MkCodeEditor.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div :class="[$style.codeEditorRoot, { [$style.disabled]: disabled, [$style.focused]: focused }]">
+<div :class="[$style.codeEditorRoot, { [$style.focused]: focused }]">
 	<div :class="$style.codeEditorScroller">
 		<textarea
 			ref="inputEl"
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index a8ccf26c4c..48d90522c4 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -145,7 +145,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions' }]" @click="tab = 'reactions'"><i class="ti ti-icons"></i> {{ i18n.ts.reactions }}</button>
 	</div>
 	<div>
-		<div v-if="tab === 'replies'" :class="$style.tab_replies">
+		<div v-if="tab === 'replies'">
 			<div v-if="!repliesLoaded" style="padding: 16px">
 				<MkButton style="margin: 0 auto;" primary rounded @click="loadReplies">{{ i18n.ts.loadReplies }}</MkButton>
 			</div>
diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue
index 8e946e7437..2e2c0e15a2 100644
--- a/packages/frontend/src/components/MkSwitch.vue
+++ b/packages/frontend/src/components/MkSwitch.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div :class="[$style.root, { [$style.disabled]: disabled, [$style.checked]: checked }]">
+<div :class="[$style.root, { [$style.disabled]: disabled }]">
 	<input
 		ref="input"
 		type="checkbox"
@@ -64,9 +64,6 @@ const toggle = () => {
 		opacity: 0.6;
 		cursor: not-allowed;
 	}
-
-	//&.checked {
-	//}
 }
 
 .input {
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 55ad1e2ab1..4ad8cc58c5 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -134,10 +134,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div v-else-if="tab === 'roles'" class="_gaps">
 				<MkButton v-if="user.host == null" primary rounded @click="assignRole"><i class="ti ti-plus"></i> {{ i18n.ts.assign }}</MkButton>
 
-				<div v-for="role in info.roles" :key="role.id" :class="$style.roleItem">
+				<div v-for="role in info.roles" :key="role.id">
 					<div :class="$style.roleItemMain">
 						<MkRolePreview :class="$style.role" :role="role" :forModeration="true"/>
-						<button class="_button" :class="$style.roleToggle" @click="toggleRoleItem(role)"><i class="ti ti-chevron-down"></i></button>
+						<button class="_button" @click="toggleRoleItem(role)"><i class="ti ti-chevron-down"></i></button>
 						<button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="unassignRole(role, $event)"><i class="ti ti-x"></i></button>
 						<button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button>
 					</div>
@@ -621,9 +621,6 @@ definePageMetadata(computed(() => ({
 	}
 }
 
-.roleItem {
-}
-
 .roleItemMain {
 	display: flex;
 }
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index bceefcf6c8..fe825613fa 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkTime :time="log.createdAt"/>
 	</template>
 
-	<div :class="$style.root">
+	<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>
@@ -134,9 +134,6 @@ const props = defineProps<{
 </script>
 
 <style lang="scss" module>
-.root {
-}
-
 .avatar {
 	width: 18px;
 	height: 18px;
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index 10a073243b..3e3e2b949c 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<XSidebar v-if="!isMobile"/>
 
 	<div :class="$style.main">
-		<XAnnouncements v-if="$i" :class="$style.announcements"/>
+		<XAnnouncements v-if="$i"/>
 		<XStatusBars/>
 		<div ref="columnsEl" :class="[$style.sections, { [$style.center]: deckStore.reactiveState.columnAlign.value === 'center', [$style.snapScroll]: snapScroll }]" @contextmenu.self.prevent="onContextmenu" @wheel.self="onWheel">
 			<!-- sectionを利用しているのは、deck.vue側でcolumnに対してfirst-of-typeを効かせるため -->
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index cba7b82610..f46f55d988 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<MkStickyContainer ref="contents" :class="$style.contents" style="container-type: inline-size;" @contextmenu.stop="onContextmenu">
 		<template #header>
 			<div>
-				<XAnnouncements v-if="$i" :class="$style.announcements"/>
+				<XAnnouncements v-if="$i"/>
 				<XStatusBars :class="$style.statusbars"/>
 			</div>
 		</template>

From ebdb4431804cb23670054a0d37928ba92c84a0a4 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Mon, 11 Dec 2023 20:31:23 +0900
Subject: [PATCH 201/435] Fix trailing commas (#12628)

---
 packages/frontend/src/components/MkCode.core.vue    | 2 +-
 packages/frontend/src/components/MkEmojiPicker.vue  | 2 +-
 packages/frontend/src/components/MkFollowButton.vue | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/components/MkCode.core.vue b/packages/frontend/src/components/MkCode.core.vue
index eee08b2f02..8fca3bb15f 100644
--- a/packages/frontend/src/components/MkCode.core.vue
+++ b/packages/frontend/src/components/MkCode.core.vue
@@ -54,7 +54,7 @@ watch(() => props.lang, (to) => {
 	return new Promise((resolve) => {
 		fetchLanguage(to).then(() => resolve);
 	});
-}, { immediate: true, });
+}, { immediate: true });
 </script>
 
 <style scoped lang="scss">
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 29a33a162f..d84d298e23 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -108,7 +108,7 @@ import {
 	UnicodeEmojiDef,
 	unicodeEmojiCategories as categories,
 	getEmojiName,
-	CustomEmojiFolderTree
+	CustomEmojiFolderTree,
 } from '@/scripts/emojilist.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import * as os from '@/os.js';
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index 88d3188189..eb5c54de6b 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -104,7 +104,7 @@ async function onClick() {
 				});
 				emit('update:user', {
 					...props.user,
-					withReplies: defaultStore.state.defaultWithReplies
+					withReplies: defaultStore.state.defaultWithReplies,
 				});
 				hasPendingFollowRequestFromYou.value = true;
 

From b691126bff96aa9a15e0b66381ea734e34ef4531 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 12 Dec 2023 10:26:37 +0900
Subject: [PATCH 202/435] =?UTF-8?q?refactor(frontend):=20menu=E3=81=AEdivi?=
 =?UTF-8?q?der=E3=82=92null=E3=81=A7=E8=A1=A8=E7=8F=BE=E3=81=99=E3=82=8B?=
 =?UTF-8?q?=E3=81=AE=E3=82=92=E3=82=84=E3=82=81=E3=82=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkDrive.vue  |  4 ++--
 packages/frontend/src/components/MkMenu.vue   |  2 +-
 packages/frontend/src/components/MkNote.vue   |  4 ++--
 .../frontend/src/components/MkPopupMenu.vue   |  2 +-
 .../src/components/MkVisitorDashboard.vue     |  4 ++--
 packages/frontend/src/os.ts                   |  2 +-
 packages/frontend/src/pages/notifications.vue |  2 +-
 .../pages/settings/preferences-backups.vue    |  4 ++--
 packages/frontend/src/pages/user/home.vue     |  2 +-
 .../src/scripts/get-drive-file-menu.ts        |  8 ++++----
 .../frontend/src/scripts/get-note-menu.ts     | 19 +++++++++----------
 .../frontend/src/scripts/get-user-menu.ts     | 18 +++++++++---------
 packages/frontend/src/types/menu.ts           |  2 +-
 packages/frontend/src/ui/_common_/common.ts   |  6 +++---
 packages/frontend/src/ui/deck.vue             |  2 +-
 packages/frontend/src/ui/deck/column.vue      |  6 +++---
 .../frontend/src/widgets/WidgetTimeline.vue   |  2 +-
 17 files changed, 44 insertions(+), 45 deletions(-)

diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue
index 71bb4ecfef..8dff73d994 100644
--- a/packages/frontend/src/components/MkDrive.vue
+++ b/packages/frontend/src/components/MkDrive.vue
@@ -616,7 +616,7 @@ function getMenu() {
 		type: 'switch',
 		text: i18n.ts.keepOriginalUploading,
 		ref: keepOriginal,
-	}, null, {
+	}, { type: 'divider' }, {
 		text: i18n.ts.addFile,
 		type: 'label',
 	}, {
@@ -627,7 +627,7 @@ function getMenu() {
 		text: i18n.ts.fromUrl,
 		icon: 'ti ti-link',
 		action: () => { urlUpload(); },
-	}, null, {
+	}, { type: 'divider' }, {
 		text: folder.value ? folder.value.name : i18n.ts.drive,
 		type: 'label',
 	}, folder.value ? {
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 951a0b2815..af0f1ec91b 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		@contextmenu.self="e => e.preventDefault()"
 	>
 		<template v-for="(item, i) in items2">
-			<div v-if="item === null" role="separator" :class="$style.divider"></div>
+			<div v-if="item.type === 'divider'" role="separator" :class="$style.divider"></div>
 			<span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item]">
 				<span>{{ item.text }}</span>
 			</span>
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 36e3b253a2..e723198a17 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -464,7 +464,7 @@ function showRenoteMenu(viaKeyboard = false): void {
 		pleaseLogin();
 		os.popupMenu([
 			getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
-			null,
+			{ type: 'divider' },
 			getUnrenote(),
 		], renoteTime.value, {
 			viaKeyboard: viaKeyboard,
@@ -472,7 +472,7 @@ function showRenoteMenu(viaKeyboard = false): void {
 	} else {
 		os.popupMenu([
 			getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote),
-			null,
+			{ type: 'divider' },
 			getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote),
 			$i.isModerator || $i.isAdmin ? getUnrenote() : undefined,
 		], renoteTime.value, {
diff --git a/packages/frontend/src/components/MkPopupMenu.vue b/packages/frontend/src/components/MkPopupMenu.vue
index 67fac003bf..95ef2d9b75 100644
--- a/packages/frontend/src/components/MkPopupMenu.vue
+++ b/packages/frontend/src/components/MkPopupMenu.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, shallowRef } from 'vue';
 import MkModal from './MkModal.vue';
 import MkMenu from './MkMenu.vue';
-import { MenuItem } from '@/types/menu';
+import { MenuItem } from '@/types/menu.js';
 
 defineProps<{
 	items: MenuItem[];
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 102cb8d139..0678a7c09c 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -103,7 +103,7 @@ function showMenu(ev) {
 		action: () => {
 			os.pageWindow('/about-misskey');
 		},
-	}, null, (instance.impressumUrl) ? {
+	}, { type: 'divider' }, (instance.impressumUrl) ? {
 		text: i18n.ts.impressum,
 		icon: 'ti ti-file-invoice',
 		action: () => {
@@ -121,7 +121,7 @@ function showMenu(ev) {
 		action: () => {
 			window.open(instance.privacyPolicyUrl, '_blank', 'noopener');
 		},
-	} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : null, {
+	} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : { type: 'divider' }, {
 		text: i18n.ts.help,
 		icon: 'ti ti-help-circle',
 		action: () => {
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 8aed5797e1..57f705a726 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -546,7 +546,7 @@ export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea:
 	});
 }
 
-export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement, options?: {
+export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement | null, options?: {
 	align?: string;
 	width?: number;
 	viaKeyboard?: boolean;
diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue
index 71ce7c353b..d57bda41b5 100644
--- a/packages/frontend/src/pages/notifications.vue
+++ b/packages/frontend/src/pages/notifications.vue
@@ -60,7 +60,7 @@ function setFilter(ev) {
 		action: () => {
 			includeTypes.value = null;
 		},
-	}, null, ...typeItems] : typeItems;
+	}, { type: 'divider' }, ...typeItems] : typeItems;
 	os.popupMenu(items, ev.currentTarget ?? ev.target);
 }
 
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index 0362998855..66c549930b 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -406,7 +406,7 @@ function menu(ev: MouseEvent, profileId: string) {
 		icon: 'ti ti-download',
 		href: URL.createObjectURL(new Blob([JSON.stringify(profiles.value[profileId], null, 2)], { type: 'application/json' })),
 		download: `${profiles.value[profileId].name}.json`,
-	}, null, {
+	}, { type: 'divider' }, {
 		text: ts.rename,
 		icon: 'ti ti-forms',
 		action: () => rename(profileId),
@@ -414,7 +414,7 @@ function menu(ev: MouseEvent, profileId: string) {
 		text: ts._preferencesBackups.save,
 		icon: 'ti ti-device-floppy',
 		action: () => save(profileId),
-	}, null, {
+	}, { type: 'divider' }, {
 		text: ts.delete,
 		icon: 'ti ti-trash',
 		action: () => deleteProfile(profileId),
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index e2b205a401..ecc3bb36c1 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -232,7 +232,7 @@ const age = computed(() => {
 	return calcAge(props.user.birthday);
 });
 
-function menu(ev) {
+function menu(ev: MouseEvent) {
 	const { menu, cleanup } = getUserMenu(user.value, router);
 	os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup);
 }
diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts
index 23a1a77bfb..f8496f0711 100644
--- a/packages/frontend/src/scripts/get-drive-file-menu.ts
+++ b/packages/frontend/src/scripts/get-drive-file-menu.ts
@@ -82,7 +82,7 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss
 		to: `/my/drive/file/${file.id}`,
 		text: i18n.ts._fileViewer.title,
 		icon: 'ti ti-info-circle',
-	}, null, {
+	}, { type: 'divider' }, {
 		text: i18n.ts.rename,
 		icon: 'ti ti-forms',
 		action: () => rename(file),
@@ -101,7 +101,7 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss
 			aspectRatio: NaN,
 			uploadFolder: folder ? folder.id : folder,
 		}),
-	}] : [], null, {
+	}] : [], { type: 'divider' }, {
 		text: i18n.ts.createNoteFromTheFile,
 		icon: 'ti ti-pencil',
 		action: () => os.post({
@@ -118,7 +118,7 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss
 		text: i18n.ts.download,
 		icon: 'ti ti-download',
 		download: file.name,
-	}, null, {
+	}, { type: 'divider' }, {
 		text: i18n.ts.delete,
 		icon: 'ti ti-trash',
 		danger: true,
@@ -126,7 +126,7 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss
 	}];
 
 	if (defaultStore.state.devMode) {
-		menu = menu.concat([null, {
+		menu = menu.concat([{ type: 'divider' }, {
 			icon: 'ti ti-id',
 			text: i18n.ts.copyFileId,
 			action: () => {
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 14ada9b7f0..1f6cfffce1 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -61,7 +61,7 @@ export async function getNoteClipMenu(props: {
 				},
 			);
 		},
-	})), null, {
+	})), { type: 'divider' }, {
 		icon: 'ti ti-plus',
 		text: i18n.ts.createNew,
 		action: async () => {
@@ -94,7 +94,7 @@ export async function getNoteClipMenu(props: {
 	}];
 }
 
-export function getAbuseNoteMenu(note: misskey.entities.Note, text: string): MenuItem {
+export function getAbuseNoteMenu(note: Misskey.entities.Note, text: string): MenuItem {
 	return {
 		icon: 'ti ti-exclamation-circle',
 		text,
@@ -108,7 +108,7 @@ export function getAbuseNoteMenu(note: misskey.entities.Note, text: string): Men
 	};
 }
 
-export function getCopyNoteLinkMenu(note: misskey.entities.Note, text: string): MenuItem {
+export function getCopyNoteLinkMenu(note: Misskey.entities.Note, text: string): MenuItem {
 	return {
 		icon: 'ti ti-link',
 		text,
@@ -264,7 +264,7 @@ export function getNoteMenu(props: {
 					text: i18n.ts.unclip,
 					danger: true,
 					action: unclip,
-				}, null] : []
+				}, { type: 'divider' }] : []
 			), {
 				icon: 'ti ti-info-circle',
 				text: i18n.ts.details,
@@ -291,7 +291,7 @@ export function getNoteMenu(props: {
 				text: i18n.ts.translate,
 				action: translate,
 			} : undefined,
-			null,
+			{ type: 'divider' },
 			statePromise.then(state => state.isFavorited ? {
 				icon: 'ti ti-star-off',
 				text: i18n.ts.unfavorite,
@@ -338,7 +338,7 @@ export function getNoteMenu(props: {
 			},
 			/*
 		...($i.isModerator || $i.isAdmin ? [
-			null,
+			{ type: 'divider' },
 			{
 				icon: 'ti ti-speakerphone',
 				text: i18n.ts.promote,
@@ -347,13 +347,13 @@ export function getNoteMenu(props: {
 			: []
 		),*/
 			...(appearNote.userId !== $i.id ? [
-				null,
+				{ type: 'divider' },
 				appearNote.userId !== $i.id ? getAbuseNoteMenu(appearNote, i18n.ts.reportAbuse) : undefined,
 			]
 			: []
 			),
 			...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [
-				null,
+				{ type: 'divider' },
 				appearNote.userId === $i.id ? {
 					icon: 'ti ti-edit',
 					text: i18n.ts.deleteAndEdit,
@@ -528,10 +528,9 @@ export function getRenoteMenu(props: {
 		}]);
 	}
 
-	// nullを挟むことで区切り線を出せる
 	const renoteItems = [
 		...normalRenoteItems,
-		...(channelRenoteItems.length > 0 && normalRenoteItems.length > 0) ? [null] : [],
+		...(channelRenoteItems.length > 0 && normalRenoteItems.length > 0) ? [{ type: 'divider' }] : [],
 		...channelRenoteItems,
 	];
 
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index f62f737568..6e5c689d97 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -119,7 +119,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
 			userId: user.id,
 		});
 	}
-	
+
 	async function invalidateFollow() {
 		if (!await getConfirmed(i18n.ts.breakFollowConfirm)) return;
 
@@ -183,7 +183,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
 			const canonical = user.host === null ? `@${user.username}` : `@${user.username}@${user.host}`;
 			os.post({ specified: user, initialText: `${canonical} ` });
 		},
-	}, null, {
+	}, { type: 'divider' }, {
 		icon: 'ti ti-pencil',
 		text: i18n.ts.editMemo,
 		action: () => {
@@ -307,7 +307,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
 		}]);
 		//}
 
-		menu = menu.concat([null, {
+		menu = menu.concat([{ type: 'divider' }, {
 			icon: user.isMuted ? 'ti ti-eye' : 'ti ti-eye-off',
 			text: user.isMuted ? i18n.ts.unmute : i18n.ts.mute,
 			action: toggleMute,
@@ -329,7 +329,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
 			}]);
 		}
 
-		menu = menu.concat([null, {
+		menu = menu.concat([{ type: 'divider' }, {
 			icon: 'ti ti-exclamation-circle',
 			text: i18n.ts.reportAbuse,
 			action: reportAbuse,
@@ -337,15 +337,15 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
 	}
 
 	if (user.host !== null) {
-		menu = menu.concat([null, {
+		menu = menu.concat([{ type: 'divider' }, {
 			icon: 'ti ti-refresh',
 			text: i18n.ts.updateRemoteUser,
 			action: userInfoUpdate,
 		}]);
 	}
-	
+
 	if (defaultStore.state.devMode) {
-		menu = menu.concat([null, {
+		menu = menu.concat([{ type: 'divider' }, {
 			icon: 'ti ti-id',
 			text: i18n.ts.copyUserId,
 			action: () => {
@@ -355,7 +355,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
 	}
 
 	if ($i && meId === user.id) {
-		menu = menu.concat([null, {
+		menu = menu.concat([{ type: 'divider' }, {
 			icon: 'ti ti-pencil',
 			text: i18n.ts.editProfile,
 			action: () => {
@@ -365,7 +365,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
 	}
 
 	if (userActions.length > 0) {
-		menu = menu.concat([null, ...userActions.map(action => ({
+		menu = menu.concat([{ type: 'divider' }, ...userActions.map(action => ({
 			icon: 'ti ti-plug',
 			text: action.title,
 			action: () => {
diff --git a/packages/frontend/src/types/menu.ts b/packages/frontend/src/types/menu.ts
index fbe627176b..f4516bbe5b 100644
--- a/packages/frontend/src/types/menu.ts
+++ b/packages/frontend/src/types/menu.ts
@@ -8,7 +8,7 @@ import { Ref } from 'vue';
 
 export type MenuAction = (ev: MouseEvent) => void;
 
-export type MenuDivider = null;
+export type MenuDivider = { type: 'divider' };
 export type MenuNull = undefined;
 export type MenuLabel = { type: 'label', text: string };
 export type MenuLink = { type: 'link', to: string, text: string, icon?: string, indicate?: boolean, avatar?: Misskey.entities.User };
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index 48a1144df7..bfafe3dd96 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -64,7 +64,7 @@ export function openInstanceMenu(ev: MouseEvent) {
 		text: i18n.ts.charts,
 		icon: 'ti ti-chart-line',
 		to: '/about#charts',
-	}, null, {
+	}, { type: 'divider' }, {
 		type: 'link',
 		text: i18n.ts.ads,
 		icon: 'ti ti-ad',
@@ -79,7 +79,7 @@ export function openInstanceMenu(ev: MouseEvent) {
 		text: i18n.ts.tools,
 		icon: 'ti ti-tool',
 		children: toolsMenuItems(),
-	}, null, (instance.impressumUrl) ? {
+	}, { type: 'divider' }, (instance.impressumUrl) ? {
 		text: i18n.ts.impressum,
 		icon: 'ti ti-file-invoice',
 		action: () => {
@@ -97,7 +97,7 @@ export function openInstanceMenu(ev: MouseEvent) {
 		action: () => {
 			window.open(instance.privacyPolicyUrl, '_blank', 'noopener');
 		},
-	} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : null, {
+	} : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : { type: 'divider' }, {
 		text: i18n.ts.help,
 		icon: 'ti ti-help-circle',
 		action: () => {
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index 3e3e2b949c..1c459cbf3a 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -236,7 +236,7 @@ function changeProfile(ev: MouseEvent) {
 				deckStore.set('profile', k);
 				unisonReload();
 			},
-		}))), null, {
+		}))), { type: 'divider' }, {
 			text: i18n.ts._deck.newProfile,
 			icon: 'ti ti-plus',
 			action: async () => {
diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue
index 39a0279dea..f5463d6921 100644
--- a/packages/frontend/src/ui/deck/column.vue
+++ b/packages/frontend/src/ui/deck/column.vue
@@ -104,7 +104,7 @@ function toggleActive() {
 }
 
 function getMenu() {
-	let items = [{
+	let items: MenuItem[] = [{
 		icon: 'ti ti-settings',
 		text: i18n.ts._deck.configureColumn,
 		action: async () => {
@@ -170,7 +170,7 @@ function getMenu() {
 		action: () => {
 			popRightColumn(props.column.id);
 		},
-	} : undefined, null, {
+	} : undefined, { type: 'divider' }, {
 		icon: 'ti ti-trash',
 		text: i18n.ts.remove,
 		danger: true,
@@ -180,7 +180,7 @@ function getMenu() {
 	}];
 
 	if (props.menu) {
-		items.unshift(null);
+		items.unshift({ type: 'divider' });
 		items = props.menu.concat(items);
 	}
 
diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue
index b8842d7b52..a2d49f62af 100644
--- a/packages/frontend/src/widgets/WidgetTimeline.vue
+++ b/packages/frontend/src/widgets/WidgetTimeline.vue
@@ -130,7 +130,7 @@ const choose = async (ev) => {
 		text: i18n.ts._timelines.global,
 		icon: 'ti ti-whirl',
 		action: () => { setSrc('global'); },
-	}, antennaItems.length > 0 ? null : undefined, ...antennaItems, listItems.length > 0 ? null : undefined, ...listItems], ev.currentTarget ?? ev.target).then(() => {
+	}, antennaItems.length > 0 ? { type: 'divider' } : undefined, ...antennaItems, listItems.length > 0 ? { type: 'divider' } : undefined, ...listItems], ev.currentTarget ?? ev.target).then(() => {
 		menuOpened.value = false;
 	});
 };

From 564a23c0b5857500df82cf4f2731763dd8da38c3 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 12 Dec 2023 10:34:08 +0900
Subject: [PATCH 203/435] fix type

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

diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 57f705a726..b02f6aa640 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -546,7 +546,7 @@ export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea:
 	});
 }
 
-export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement | null, options?: {
+export function popupMenu(items: MenuItem[] | Ref<MenuItem[]>, src?: HTMLElement | EventTarget | null, options?: {
 	align?: string;
 	width?: number;
 	viaKeyboard?: boolean;

From 7f85d7a1f9b05e1d918dff189ccdfbfa219f2008 Mon Sep 17 00:00:00 2001
From: 1STEP621 <86859447+1STEP621@users.noreply.github.com>
Date: Tue, 12 Dec 2023 12:19:49 +0900
Subject: [PATCH 204/435] =?UTF-8?q?Enhance(frontend):=20=E3=83=AA=E3=82=B9?=
 =?UTF-8?q?=E3=83=88/=E3=82=A2=E3=83=B3=E3=83=86=E3=83=8A/=E3=83=81?=
 =?UTF-8?q?=E3=83=A3=E3=83=B3=E3=83=8D=E3=83=AB=E3=82=92=E3=82=BF=E3=82=A4?=
 =?UTF-8?q?=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=8B=E3=82=89=E6=96=B0?=
 =?UTF-8?q?=E8=A6=8F=E4=BD=9C=E6=88=90=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#12629)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* add short leads to lists, antennas, and channels

* remove unused import

* add CHANGELOG.md

* hide separator when there is no item

* fix mistakes

* Update timeline.vue

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                             |  1 +
 packages/frontend/src/pages/timeline.vue | 62 +++++++++++++++++-------
 2 files changed, 46 insertions(+), 17 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c7ee4b0388..9b0ccfdb30 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@
 - Enhance: ノートプレビューに「内容を隠す」が反映されるように
 - Enhance: データセーバーの適用範囲を個別で設定できるように
 	- 従来のデータセーバーの設定はリセットされます
+- Enhance: タイムライン上のタブからリスト、アンテナ、チャンネルの管理ページにジャンプできるように
 - Feat: センシティブと判断されたウェブサイトのサムネイルをぼかすように
   - ウェブサイトをセンシティブと判断する仕組みが動いていないため、summalyProxyを使用しないと機能しません。
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 942061efe3..f3213ad273 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -48,6 +48,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { miLocalStorage } from '@/local-storage.js';
 import { antennasCache, userListsCache } from '@/cache.js';
 import { deviceKind } from '@/scripts/device-kind.js';
+import { MenuItem } from '@/types/menu.js';
 
 provide('shouldOmitHeaderTitle', true);
 
@@ -83,22 +84,40 @@ function top(): void {
 
 async function chooseList(ev: MouseEvent): Promise<void> {
 	const lists = await userListsCache.fetch();
-	const items = lists.map(list => ({
-		type: 'link' as const,
-		text: list.name,
-		to: `/timeline/list/${list.id}`,
-	}));
+	const items: MenuItem[] = [
+		...lists.map(list => ({
+			type: 'link' as const,
+			text: list.name,
+			to: `/timeline/list/${list.id}`,
+		})),
+		(lists.length === 0 ? undefined : { type: 'divider' }),
+		{
+			type: 'link' as const,
+			icon: 'ti ti-plus',
+			text: i18n.ts.createNew,
+			to: '/my/lists',
+		},
+	];
 	os.popupMenu(items, ev.currentTarget ?? ev.target);
 }
 
 async function chooseAntenna(ev: MouseEvent): Promise<void> {
 	const antennas = await antennasCache.fetch();
-	const items = antennas.map(antenna => ({
-		type: 'link' as const,
-		text: antenna.name,
-		indicate: antenna.hasUnreadNote,
-		to: `/timeline/antenna/${antenna.id}`,
-	}));
+	const items: MenuItem[] = [
+		...antennas.map(antenna => ({
+			type: 'link' as const,
+			text: antenna.name,
+			indicate: antenna.hasUnreadNote,
+			to: `/timeline/antenna/${antenna.id}`,
+		})),
+		(antennas.length === 0 ? undefined : { type: 'divider' }),
+		{
+			type: 'link' as const,
+			icon: 'ti ti-plus',
+			text: i18n.ts.createNew,
+			to: '/my/antennas',
+		},
+	];
 	os.popupMenu(items, ev.currentTarget ?? ev.target);
 }
 
@@ -106,12 +125,21 @@ async function chooseChannel(ev: MouseEvent): Promise<void> {
 	const channels = await os.api('channels/my-favorites', {
 		limit: 100,
 	});
-	const items = channels.map(channel => ({
-		type: 'link' as const,
-		text: channel.name,
-		indicate: channel.hasUnreadNote,
-		to: `/channels/${channel.id}`,
-	}));
+	const items = [
+		...channels.map(channel => ({
+			type: 'link' as const,
+			text: channel.name,
+			indicate: channel.hasUnreadNote,
+			to: `/channels/${channel.id}`,
+		})),
+		(channels.length === 0 ? undefined : null),
+		{
+			type: 'link' as const,
+			icon: 'ti ti-plus',
+			text: i18n.ts.createNew,
+			to: '/channels',
+		},
+	];
 	os.popupMenu(items, ev.currentTarget ?? ev.target);
 }
 

From aad573a1d798ab6018747228417d967dbb61b1c4 Mon Sep 17 00:00:00 2001
From: Tassoman <tassoman@users.noreply.github.com>
Date: Wed, 13 Dec 2023 00:13:03 +0100
Subject: [PATCH 205/435] adding color-scheme light to WidgetAichan (#12638)

---
 packages/frontend/src/widgets/WidgetAichan.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/frontend/src/widgets/WidgetAichan.vue b/packages/frontend/src/widgets/WidgetAichan.vue
index 76b35f6fed..cf2012b74d 100644
--- a/packages/frontend/src/widgets/WidgetAichan.vue
+++ b/packages/frontend/src/widgets/WidgetAichan.vue
@@ -72,5 +72,6 @@ defineExpose<WidgetComponentExpose>({
 	height: 350px;
 	border: none;
 	pointer-events: none;
+	color-scheme: light;
 }
 </style>

From 06ca63f9c2816146d9b5fcc38f8877c3ee0313c1 Mon Sep 17 00:00:00 2001
From: Camilla Ett <camilla.ett@gmail.com>
Date: Wed, 13 Dec 2023 08:14:34 +0900
Subject: [PATCH 206/435] =?UTF-8?q?Fix(backend):=20inboxJobPerSec=E3=81=AE?=
 =?UTF-8?q?=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB=E3=83=88=E5=80=A4=E3=82=92?=
 =?UTF-8?q?16=E3=81=8B=E3=82=8932=E3=81=AB=20(#12631)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .config/docker_example.yml                    | 22 +++++++++----------
 .config/example.yml                           |  2 +-
 .devcontainer/devcontainer.yml                | 22 +++++++++----------
 chart/files/default.yml                       | 22 +++++++++----------
 .../src/queue/QueueProcessorService.ts        |  2 +-
 5 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/.config/docker_example.yml b/.config/docker_example.yml
index d1534486d3..acd169bf43 100644
--- a/.config/docker_example.yml
+++ b/.config/docker_example.yml
@@ -56,17 +56,17 @@ dbReplications: false
 # You can configure any number of replicas here
 #dbSlaves:
 #  -
-#    host: 
-#    port: 
-#    db: 
-#    user: 
-#    pass: 
+#    host:
+#    port:
+#    db:
+#    user:
+#    pass:
 #  -
-#    host: 
-#    port: 
-#    db: 
-#    user: 
-#    pass: 
+#    host:
+#    port:
+#    db:
+#    user:
+#    pass:
 
 #   ┌─────────────────────┐
 #───┘ Redis configuration └─────────────────────────────────────
@@ -151,7 +151,7 @@ id: 'aidx'
 
 # Job rate limiter
 # deliverJobPerSec: 128
-# inboxJobPerSec: 16
+# inboxJobPerSec: 32
 
 # Job attempts
 # deliverJobMaxAttempts: 12
diff --git a/.config/example.yml b/.config/example.yml
index 481c615587..df423c2c83 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -166,7 +166,7 @@ id: 'aidx'
 
 # Job rate limiter
 #deliverJobPerSec: 128
-#inboxJobPerSec: 16
+#inboxJobPerSec: 32
 #relashionshipJobPerSec: 64
 
 # Job attempts
diff --git a/.devcontainer/devcontainer.yml b/.devcontainer/devcontainer.yml
index 3d57d1245d..7ea0929469 100644
--- a/.devcontainer/devcontainer.yml
+++ b/.devcontainer/devcontainer.yml
@@ -56,17 +56,17 @@ dbReplications: false
 # You can configure any number of replicas here
 #dbSlaves:
 #  -
-#    host: 
-#    port: 
-#    db: 
-#    user: 
-#    pass: 
+#    host:
+#    port:
+#    db:
+#    user:
+#    pass:
 #  -
-#    host: 
-#    port: 
-#    db: 
-#    user: 
-#    pass: 
+#    host:
+#    port:
+#    db:
+#    user:
+#    pass:
 
 #   ┌─────────────────────┐
 #───┘ Redis configuration └─────────────────────────────────────
@@ -147,7 +147,7 @@ id: 'aidx'
 
 # Job rate limiter
 # deliverJobPerSec: 128
-# inboxJobPerSec: 16
+# inboxJobPerSec: 32
 
 # Job attempts
 # deliverJobMaxAttempts: 12
diff --git a/chart/files/default.yml b/chart/files/default.yml
index 87b2f677eb..4cc291e80a 100644
--- a/chart/files/default.yml
+++ b/chart/files/default.yml
@@ -77,17 +77,17 @@ dbReplications: false
 # You can configure any number of replicas here
 #dbSlaves:
 #  -
-#    host: 
-#    port: 
-#    db: 
-#    user: 
-#    pass: 
+#    host:
+#    port:
+#    db:
+#    user:
+#    pass:
 #  -
-#    host: 
-#    port: 
-#    db: 
-#    user: 
-#    pass: 
+#    host:
+#    port:
+#    db:
+#    user:
+#    pass:
 
 #   ┌─────────────────────┐
 #───┘ Redis configuration └─────────────────────────────────────
@@ -167,7 +167,7 @@ id: "aidx"
 
 # Job rate limiter
 # deliverJobPerSec: 128
-# inboxJobPerSec: 16
+# inboxJobPerSec: 32
 
 # Job attempts
 # deliverJobMaxAttempts: 12
diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts
index 5201bfed8e..ee081ccaad 100644
--- a/packages/backend/src/queue/QueueProcessorService.ts
+++ b/packages/backend/src/queue/QueueProcessorService.ts
@@ -226,7 +226,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
 			autorun: false,
 			concurrency: this.config.inboxJobConcurrency ?? 16,
 			limiter: {
-				max: this.config.inboxJobPerSec ?? 16,
+				max: this.config.inboxJobPerSec ?? 32,
 				duration: 1000,
 			},
 			settings: {

From daea5a39ade09bae7e416e6fb7ffb21c19303ef6 Mon Sep 17 00:00:00 2001
From: YAVIIGI <118232419+YAVIIGI@users.noreply.github.com>
Date: Wed, 13 Dec 2023 08:15:25 +0900
Subject: [PATCH 207/435] =?UTF-8?q?fix(frontend):=20=E3=83=8E=E3=83=BC?=
 =?UTF-8?q?=E3=83=88=E4=B8=AD=E3=81=AE=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=92?=
 =?UTF-8?q?=E3=82=BF=E3=83=83=E3=83=97=E3=81=97=E3=81=A6=E3=80=8C=E3=83=AA?=
 =?UTF-8?q?=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=99=E3=82=8B?=
 =?UTF-8?q?=E3=80=8D=E3=82=92=E6=8A=BC=E3=81=97=E3=81=9F=E3=81=A8=E3=81=8D?=
 =?UTF-8?q?=E3=81=AB=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3?=
 =?UTF-8?q?=E3=82=B5=E3=82=A6=E3=83=B3=E3=83=89=E3=81=8C=E9=B3=B4=E3=82=8B?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#12624)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Add sound.play() in copy reaction

* Update CHANGELOG.md

* fix lint error
---
 CHANGELOG.md                                              | 1 +
 packages/frontend/src/components/global/MkCustomEmoji.vue | 2 ++
 packages/frontend/src/components/global/MkEmoji.vue       | 2 ++
 3 files changed, 5 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b0ccfdb30..8d1434cde4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,7 @@
 - Fix: 通知のグルーピング設定を変更してもリロードされるまで表示が変わらない問題を修正 #12470
 - Fix: 長い名前のチャンネルにおける投稿フォームの表示が崩れる問題を修正
 - Fix: セキュリティ向上のためAiScriptの`Mk:apiExternal`を無効化
+- Fix: ノート中の絵文字をタップして「リアクションする」からリアクションした際にリアクションサウンドが鳴らない不具合を修正
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index a092497307..a9643d68ca 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -25,6 +25,7 @@ import { defaultStore } from '@/store.js';
 import { customEmojisMap } from '@/custom-emojis.js';
 import * as os from '@/os.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import * as sound from '@/scripts/sound.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -90,6 +91,7 @@ function onClick(ev: MouseEvent) {
 			icon: 'ti ti-plus',
 			action: () => {
 				react(`:${props.name}:`);
+				sound.play('reaction');
 			},
 		}] : [])], ev.currentTarget ?? ev.target);
 	}
diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue
index 0855f20b8d..76ca8688d1 100644
--- a/packages/frontend/src/components/global/MkEmoji.vue
+++ b/packages/frontend/src/components/global/MkEmoji.vue
@@ -16,6 +16,7 @@ import { defaultStore } from '@/store.js';
 import { getEmojiName } from '@/scripts/emojilist.js';
 import * as os from '@/os.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import * as sound from '@/scripts/sound.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -56,6 +57,7 @@ function onClick(ev: MouseEvent) {
 			icon: 'ti ti-plus',
 			action: () => {
 				react(props.emoji);
+				sound.play('reaction');
 			},
 		}] : [])], ev.currentTarget ?? ev.target);
 	}

From 5472f4b934c8ca8c702152a4a927b4ac94cf3fdb Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 13 Dec 2023 16:56:19 +0900
Subject: [PATCH 208/435] =?UTF-8?q?enhance:=20=E3=82=A2=E3=82=A4=E3=82=B3?=
 =?UTF-8?q?=E3=83=B3=E3=83=87=E3=82=B3=E3=83=AC=E3=83=BC=E3=82=B7=E3=83=A7?=
 =?UTF-8?q?=E3=83=B3=E3=82=92=E8=A4=87=E6=95=B0=E8=A8=AD=E5=AE=9A=E3=81=A7?=
 =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            |  5 ++
 locales/ja-JP.yml                             |  5 ++
 packages/backend/src/core/RoleService.ts      |  3 ++
 .../backend/src/models/json-schema/role.ts    |  1 +
 .../backend/src/models/json-schema/user.ts    |  4 ++
 .../src/server/api/endpoints/i/update.ts      | 10 ++--
 packages/frontend/src/account.ts              |  2 +-
 .../src/components/MkDrive.folder.vue         |  9 ++--
 packages/frontend/src/components/MkWindow.vue |  2 +-
 .../frontend/src/components/global/MkA.vue    |  2 +-
 .../src/components/global/MkAvatar.vue        | 53 +++++++------------
 packages/frontend/src/const.ts                |  1 +
 .../frontend/src/pages/admin/roles.editor.vue | 22 +++++++-
 packages/frontend/src/pages/admin/roles.vue   |  7 +++
 .../profile.avatar-decoration-dialog.vue      | 11 ++--
 .../frontend/src/pages/settings/profile.vue   | 39 ++++++++++----
 17 files changed, 115 insertions(+), 62 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d1434cde4..e5ff09edec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
 - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
 - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
 - Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加
+- Enhance: アイコンデコレーションを複数設定できるように
 - Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
 
 ### Client
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 846a6d503d..d32023f5ac 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -264,6 +264,7 @@ export interface Locale {
     "removeAreYouSure": string;
     "deleteAreYouSure": string;
     "resetAreYouSure": string;
+    "areYouSure": string;
     "saved": string;
     "messaging": string;
     "upload": string;
@@ -1160,6 +1161,7 @@ export interface Locale {
     "avatarDecorations": string;
     "attach": string;
     "detach": string;
+    "detachAll": string;
     "angle": string;
     "flip": string;
     "showAvatarDecorations": string;
@@ -1173,6 +1175,7 @@ export interface Locale {
     "doReaction": string;
     "code": string;
     "reloadRequiredToApplySettings": string;
+    "remainingN": string;
     "_announcement": {
         "forExistingUsers": string;
         "forExistingUsersDescription": string;
@@ -1701,6 +1704,7 @@ export interface Locale {
             "canHideAds": string;
             "canSearchNotes": string;
             "canUseTranslator": string;
+            "avatarDecorationLimit": string;
         };
         "_condition": {
             "isLocal": string;
@@ -2181,6 +2185,7 @@ export interface Locale {
         "changeAvatar": string;
         "changeBanner": string;
         "verifiedLinkDescription": string;
+        "avatarDecorationMax": string;
     };
     "_exportOrImport": {
         "allNotes": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0d84440bc8..2ac57fd311 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -261,6 +261,7 @@ removed: "削除しました"
 removeAreYouSure: "「{x}」を削除しますか?"
 deleteAreYouSure: "「{x}」を削除しますか?"
 resetAreYouSure: "リセットしますか?"
+areYouSure: "よろしいですか?"
 saved: "保存しました"
 messaging: "チャット"
 upload: "アップロード"
@@ -1157,6 +1158,7 @@ tosAndPrivacyPolicy: "利用規約・プライバシーポリシー"
 avatarDecorations: "アイコンデコレーション"
 attach: "付ける"
 detach: "外す"
+detachAll: "全て外す"
 angle: "角度"
 flip: "反転"
 showAvatarDecorations: "アイコンのデコレーションを表示"
@@ -1170,6 +1172,7 @@ cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述
 doReaction: "リアクションする"
 code: "コード"
 reloadRequiredToApplySettings: "設定の反映にはリロードが必要です。"
+remainingN: "残り: {n}"
 
 _announcement:
   forExistingUsers: "既存ユーザーのみ"
@@ -1610,6 +1613,7 @@ _role:
     canHideAds: "広告の非表示"
     canSearchNotes: "ノート検索の利用"
     canUseTranslator: "翻訳機能の利用"
+    avatarDecorationLimit: "アイコンデコレーションの最大取付個数"
   _condition:
     isLocal: "ローカルユーザー"
     isRemote: "リモートユーザー"
@@ -2084,6 +2088,7 @@ _profile:
   changeAvatar: "アイコン画像を変更"
   changeBanner: "バナー画像を変更"
   verifiedLinkDescription: "内容にURLを設定すると、リンク先のWebサイトに自分のプロフィールへのリンクが含まれている場合に所有者確認済みアイコンを表示させることができます。"
+  avatarDecorationMax: "最大{max}つまでデコレーションを付けられます。"
 
 _exportOrImport:
   allNotes: "全てのノート"
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 29e48aa8ca..4de719d6a0 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -47,6 +47,7 @@ export type RolePolicies = {
 	userListLimit: number;
 	userEachUserListsLimit: number;
 	rateLimitFactor: number;
+	avatarDecorationLimit: number;
 };
 
 export const DEFAULT_POLICIES: RolePolicies = {
@@ -73,6 +74,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
 	userListLimit: 10,
 	userEachUserListsLimit: 50,
 	rateLimitFactor: 1,
+	avatarDecorationLimit: 1,
 };
 
 @Injectable()
@@ -326,6 +328,7 @@ export class RoleService implements OnApplicationShutdown {
 			userListLimit: calc('userListLimit', vs => Math.max(...vs)),
 			userEachUserListsLimit: calc('userEachUserListsLimit', vs => Math.max(...vs)),
 			rateLimitFactor: calc('rateLimitFactor', vs => Math.max(...vs)),
+			avatarDecorationLimit: calc('avatarDecorationLimit', vs => Math.max(...vs)),
 		};
 	}
 
diff --git a/packages/backend/src/models/json-schema/role.ts b/packages/backend/src/models/json-schema/role.ts
index dd2f32b14d..b0c6804bb8 100644
--- a/packages/backend/src/models/json-schema/role.ts
+++ b/packages/backend/src/models/json-schema/role.ts
@@ -145,6 +145,7 @@ export const packedRoleSchema = {
 						userEachUserListsLimit: rolePolicyValue,
 						canManageAvatarDecorations: rolePolicyValue,
 						canUseTranslator: rolePolicyValue,
+						avatarDecorationLimit: rolePolicyValue,
 					},
 				},
 				usersCount: {
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index c6b2707b80..c6b96b85f0 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -672,6 +672,10 @@ export const packedMeDetailedOnlySchema = {
 					type: 'number',
 					nullable: false, optional: false,
 				},
+				avatarDecorationLimit: {
+					type: 'number',
+					nullable: false, optional: false,
+				},
 			},
 		},
 		//#region secrets
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index b045c01189..399e6b88cb 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -125,7 +125,7 @@ export const meta = {
 
 const muteWords = { type: 'array', items: { oneOf: [
 	{ type: 'array', items: { type: 'string' } },
-	{ type: 'string' }
+	{ type: 'string' },
 ] } } as const;
 
 export const paramDef = {
@@ -137,7 +137,7 @@ export const paramDef = {
 		birthday: { ...birthdaySchema, nullable: true },
 		lang: { type: 'string', enum: [null, ...Object.keys(langmap)] as string[], nullable: true },
 		avatarId: { type: 'string', format: 'misskey:id', nullable: true },
-		avatarDecorations: { type: 'array', maxItems: 1, items: {
+		avatarDecorations: { type: 'array', maxItems: 16, items: {
 			type: 'object',
 			properties: {
 				id: { type: 'string', format: 'misskey:id' },
@@ -251,7 +251,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			function validateMuteWordRegex(mutedWords: (string[] | string)[]) {
 				for (const mutedWord of mutedWords) {
-					if (typeof mutedWord !== "string") continue;
+					if (typeof mutedWord !== 'string') continue;
 
 					const regexp = mutedWord.match(/^\/(.+)\/(.*)$/);
 					if (!regexp) throw new ApiError(meta.errors.invalidRegexp);
@@ -329,12 +329,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			if (ps.avatarDecorations) {
 				const decorations = await this.avatarDecorationService.getAll(true);
-				const myRoles = await this.roleService.getUserRoles(user.id);
+				const [myRoles, myPolicies] = await Promise.all([this.roleService.getUserRoles(user.id), this.roleService.getUserPolicies(user.id)]);
 				const allRoles = await this.roleService.getRoles();
 				const decorationIds = decorations
 					.filter(d => d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id)))
 					.map(d => d.id);
 
+				if (ps.avatarDecorations.length > myPolicies.avatarDecorationLimit) throw new ApiError(meta.errors.restrictedByRole);
+
 				updates.avatarDecorations = ps.avatarDecorations.filter(d => decorationIds.includes(d.id)).map(d => ({
 					id: d.id,
 					angle: d.angle ?? 0,
diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts
index 0e4e4b50ff..a6af298024 100644
--- a/packages/frontend/src/account.ts
+++ b/packages/frontend/src/account.ts
@@ -284,7 +284,7 @@ export async function openAccountMenu(opts: {
 			text: i18n.ts.profile,
 			to: `/@${ $i.username }`,
 			avatar: $i,
-		}, null, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, {
+		}, { type: 'divider' }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, {
 			type: 'parent' as const,
 			icon: 'ti ti-plus',
 			text: i18n.ts.addAccount,
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index 5322664664..b0c14d1f0b 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -39,6 +39,7 @@ import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
 import { claimAchievement } from '@/scripts/achievements.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { MenuItem } from '@/types/menu.js';
 
 const props = withDefaults(defineProps<{
 	folder: Misskey.entities.DriveFolder;
@@ -250,7 +251,7 @@ function setAsUploadFolder() {
 }
 
 function onContextmenu(ev: MouseEvent) {
-	let menu;
+	let menu: MenuItem[];
 	menu = [{
 		text: i18n.ts.openInWindow,
 		icon: 'ti ti-app-window',
@@ -260,18 +261,18 @@ function onContextmenu(ev: MouseEvent) {
 			}, {
 			}, 'closed');
 		},
-	}, null, {
+	}, { type: 'divider' }, {
 		text: i18n.ts.rename,
 		icon: 'ti ti-forms',
 		action: rename,
-	}, null, {
+	}, { type: 'divider' }, {
 		text: i18n.ts.delete,
 		icon: 'ti ti-trash',
 		danger: true,
 		action: deleteFolder,
 	}];
 	if (defaultStore.state.devMode) {
-		menu = menu.concat([null, {
+		menu = menu.concat([{ type: 'divider' }, {
 			icon: 'ti ti-id',
 			text: i18n.ts.copyFolderId,
 			action: () => {
diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue
index 1150a29e03..7c8ffcccf9 100644
--- a/packages/frontend/src/components/MkWindow.vue
+++ b/packages/frontend/src/components/MkWindow.vue
@@ -56,7 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onBeforeUnmount, onMounted, provide, shallowRef, ref } from 'vue';
 import contains from '@/scripts/contains.js';
 import * as os from '@/os.js';
-import { MenuItem } from '@/types/menu';
+import { MenuItem } from '@/types/menu.js';
 import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
 
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index 809dae421a..5552e96ee0 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -57,7 +57,7 @@ function onContextmenu(ev) {
 		action: () => {
 			router.push(props.to, 'forcePage');
 		},
-	}, null, {
+	}, { type: 'divider' }, {
 		icon: 'ti ti-external-link',
 		text: i18n.ts.openInNewTab,
 		action: () => {
diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index c7e50e275a..6aa9a42037 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -23,16 +23,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</div>
 		</div>
 	</div>
-	<img
-		v-if="showDecoration && (decoration || user.avatarDecorations.length > 0)"
-		:class="[$style.decoration]"
-		:src="decoration?.url ?? user.avatarDecorations[0].url"
-		:style="{
-			rotate: getDecorationAngle(),
-			scale: getDecorationScale(),
-		}"
-		alt=""
-	>
+	<template v-if="showDecoration">
+		<img
+			v-for="decoration in decorations ?? user.avatarDecorations"
+			:class="[$style.decoration]"
+			:src="decoration.url"
+			:style="{
+				rotate: getDecorationAngle(decoration),
+				scale: getDecorationScale(decoration),
+			}"
+			alt=""
+		>
+	</template>
 </component>
 </template>
 
@@ -57,19 +59,14 @@ const props = withDefaults(defineProps<{
 	link?: boolean;
 	preview?: boolean;
 	indicator?: boolean;
-	decoration?: {
-		url: string;
-		angle?: number;
-		flipH?: boolean;
-		flipV?: boolean;
-	};
+	decorations?: Misskey.entities.UserDetailed['avatarDecorations'][number][];
 	forceShowDecoration?: boolean;
 }>(), {
 	target: null,
 	link: false,
 	preview: false,
 	indicator: false,
-	decoration: undefined,
+	decorations: undefined,
 	forceShowDecoration: false,
 });
 
@@ -92,27 +89,13 @@ function onClick(ev: MouseEvent): void {
 	emit('click', ev);
 }
 
-function getDecorationAngle() {
-	let angle;
-	if (props.decoration) {
-		angle = props.decoration.angle ?? 0;
-	} else if (props.user.avatarDecorations.length > 0) {
-		angle = props.user.avatarDecorations[0].angle ?? 0;
-	} else {
-		angle = 0;
-	}
+function getDecorationAngle(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) {
+	const angle = decoration.angle ?? 0;
 	return angle === 0 ? undefined : `${angle * 360}deg`;
 }
 
-function getDecorationScale() {
-	let scaleX;
-	if (props.decoration) {
-		scaleX = props.decoration.flipH ? -1 : 1;
-	} else if (props.user.avatarDecorations.length > 0) {
-		scaleX = props.user.avatarDecorations[0].flipH ? -1 : 1;
-	} else {
-		scaleX = 1;
-	}
+function getDecorationScale(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) {
+	const scaleX = decoration.flipH ? -1 : 1;
 	return scaleX === 1 ? undefined : `${scaleX} 1`;
 }
 
diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts
index 397f804822..f016b7aa02 100644
--- a/packages/frontend/src/const.ts
+++ b/packages/frontend/src/const.ts
@@ -81,6 +81,7 @@ export const ROLE_POLICIES = [
 	'userListLimit',
 	'userEachUserListsLimit',
 	'rateLimitFactor',
+	'avatarDecorationLimit',
 ] as const;
 
 // なんか動かない
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index a8e0e8bbd1..5ded8d6931 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -531,6 +531,26 @@ SPDX-License-Identifier: AGPL-3.0-only
 					</MkRange>
 				</div>
 			</MkFolder>
+
+			<MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])">
+				<template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template>
+				<template #suffix>
+					<span v-if="role.policies.avatarDecorationLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
+					<span v-else>{{ role.policies.avatarDecorationLimit.value }}</span>
+					<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.avatarDecorationLimit)"></i></span>
+				</template>
+				<div class="_gaps">
+					<MkSwitch v-model="role.policies.avatarDecorationLimit.useDefault" :readonly="readonly">
+						<template #label>{{ i18n.ts._role.useBaseValue }}</template>
+					</MkSwitch>
+					<MkInput v-model="role.policies.avatarDecorationLimit.value" type="number" :min="0">
+						<template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template>
+					</MkInput>
+					<MkRange v-model="role.policies.avatarDecorationLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
+						<template #label>{{ i18n.ts._role.priority }}</template>
+					</MkRange>
+				</div>
+			</MkFolder>
 		</div>
 	</FormSlot>
 </div>
@@ -549,7 +569,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import MkRange from '@/components/MkRange.vue';
 import FormSlot from '@/components/form/slot.vue';
 import { i18n } from '@/i18n.js';
-import { ROLE_POLICIES } from '@/const';
+import { ROLE_POLICIES } from '@/const.js';
 import { instance } from '@/instance.js';
 import { deepClone } from '@/scripts/clone.js';
 
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index db4595b150..1bb91a0a5b 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -192,6 +192,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 							</MkSwitch>
 						</MkFolder>
 
+						<MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])">
+							<template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template>
+							<template #suffix>{{ policies.avatarDecorationLimit }}</template>
+							<MkInput v-model="policies.avatarDecorationLimit" type="number" :min="0">
+							</MkInput>
+						</MkFolder>
+
 						<MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton>
 					</div>
 				</MkFolder>
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
index 4d571bc9ba..c27a21217b 100644
--- a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkSpacer :marginMin="20" :marginMax="28">
 			<div style="text-align: center;">
 				<div :class="$style.name">{{ decoration.name }}</div>
-				<MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decoration="{ url: decoration.url, angle, flipH }" forceShowDecoration/>
+				<MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decorations="[...$i.avatarDecorations, { url: decoration.url, angle, flipH }]" forceShowDecoration/>
 			</div>
 			<div class="_gaps_s">
 				<MkRange v-model="angle" continuousUpdate :min="-0.5" :max="0.5" :step="0.025" :textConverter="(v) => `${Math.floor(v * 360)}°`">
@@ -54,6 +54,7 @@ const props = defineProps<{
 	decoration: {
 		id: string;
 		url: string;
+		name: string;
 	}
 }>();
 
@@ -77,18 +78,18 @@ async function attach() {
 		flipH: flipH.value,
 	};
 	await os.apiWithDialog('i/update', {
-		avatarDecorations: [decoration],
+		avatarDecorations: [...$i.avatarDecorations, decoration],
 	});
-	$i.avatarDecorations = [decoration];
+	$i.avatarDecorations = [...$i.avatarDecorations, decoration];
 
 	dialog.value.close();
 }
 
 async function detach() {
 	await os.apiWithDialog('i/update', {
-		avatarDecorations: [],
+		avatarDecorations: $i.avatarDecorations.filter(x => x.id !== props.decoration.id),
 	});
-	$i.avatarDecorations = [];
+	$i.avatarDecorations = $i.avatarDecorations.filter(x => x.id !== props.decoration.id);
 
 	dialog.value.close();
 }
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index ba75b539e1..a5d3835b93 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -87,16 +87,22 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<template #icon><i class="ti ti-sparkles"></i></template>
 		<template #label>{{ i18n.ts.avatarDecorations }}</template>
 
-		<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); grid-gap: 12px;">
-			<div
-				v-for="avatarDecoration in avatarDecorations"
-				:key="avatarDecoration.id"
-				:class="[$style.avatarDecoration, { [$style.avatarDecorationActive]: $i.avatarDecorations.some(x => x.id === avatarDecoration.id) }]"
-				@click="openDecoration(avatarDecoration)"
-			>
-				<div :class="$style.avatarDecorationName"><MkCondensedLine :minScale="0.5">{{ avatarDecoration.name }}</MkCondensedLine></div>
-				<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decoration="{ url: avatarDecoration.url }" forceShowDecoration/>
-				<i v-if="avatarDecoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => avatarDecoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.avatarDecorationLock" class="ti ti-lock"></i>
+		<div class="_gaps">
+			<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i?.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i?.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
+
+			<MkButton v-if="$i.avatarDecorations.length > 0" danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
+
+			<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); grid-gap: 12px;">
+				<div
+					v-for="avatarDecoration in avatarDecorations"
+					:key="avatarDecoration.id"
+					:class="[$style.avatarDecoration, { [$style.avatarDecorationActive]: $i.avatarDecorations.some(x => x.id === avatarDecoration.id) }]"
+					@click="openDecoration(avatarDecoration)"
+				>
+					<div :class="$style.avatarDecorationName"><MkCondensedLine :minScale="0.5">{{ avatarDecoration.name }}</MkCondensedLine></div>
+					<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: avatarDecoration.url }]" forceShowDecoration/>
+					<i v-if="avatarDecoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => avatarDecoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.avatarDecorationLock" class="ti ti-lock"></i>
+				</div>
 			</div>
 		</div>
 	</MkFolder>
@@ -273,6 +279,19 @@ function openDecoration(avatarDecoration) {
 	}, {}, 'closed');
 }
 
+function detachAllDecorations() {
+	os.confirm({
+		type: 'warning',
+		text: i18n.ts.areYouSure,
+	}).then(async ({ canceled }) => {
+		if (canceled) return;
+		await os.apiWithDialog('i/update', {
+			avatarDecorations: [],
+		});
+		$i.avatarDecorations = [];
+	});
+}
+
 const headerActions = computed(() => []);
 
 const headerTabs = computed(() => []);

From 71bb1814726ed563c0d67a975d1942ad50dd43ca 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: Wed, 13 Dec 2023 18:14:43 +0900
Subject: [PATCH 209/435] =?UTF-8?q?fix(frontend):=20MkAnimBg=E3=82=92?=
 =?UTF-8?q?=E3=83=AA=E3=82=B5=E3=82=A4=E3=82=BA=E3=81=AB=E5=AF=BE=E5=BF=9C?=
 =?UTF-8?q?=E3=81=95=E3=81=9B=E3=82=8B=20(#12642)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (fix) MkAnimBgをリサイズに対応させる

* fix lint

* refactor
---
 packages/frontend/src/components/MkAnimBg.vue | 32 ++++++++++++++++---
 1 file changed, 27 insertions(+), 5 deletions(-)

diff --git a/packages/frontend/src/components/MkAnimBg.vue b/packages/frontend/src/components/MkAnimBg.vue
index 70d101a9d3..284ee8f3f8 100644
--- a/packages/frontend/src/components/MkAnimBg.vue
+++ b/packages/frontend/src/components/MkAnimBg.vue
@@ -21,8 +21,9 @@ const props = withDefaults(defineProps<{
 	focus: 1.0,
 });
 
-function loadShader(gl, type, source) {
+function loadShader(gl: WebGLRenderingContext, type: number, source: string) {
 	const shader = gl.createShader(type);
+	if (shader == null) return null;
 
 	gl.shaderSource(shader, source);
 	gl.compileShader(shader);
@@ -38,11 +39,13 @@ function loadShader(gl, type, source) {
 	return shader;
 }
 
-function initShaderProgram(gl, vsSource, fsSource) {
+function initShaderProgram(gl: WebGLRenderingContext, vsSource: string, fsSource: string) {
 	const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
 	const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
 
 	const shaderProgram = gl.createProgram();
+	if (shaderProgram == null || vertexShader == null || fragmentShader == null) return null;
+
 	gl.attachShader(shaderProgram, vertexShader);
 	gl.attachShader(shaderProgram, fragmentShader);
 	gl.linkProgram(shaderProgram);
@@ -63,8 +66,10 @@ let handle: ReturnType<typeof window['requestAnimationFrame']> | null = null;
 
 onMounted(() => {
 	const canvas = canvasEl.value!;
-	canvas.width = canvas.offsetWidth;
-	canvas.height = canvas.offsetHeight;
+	let width = canvas.offsetWidth;
+	let height = canvas.offsetHeight;
+	canvas.width = width;
+	canvas.height = height;
 
 	const gl = canvas.getContext('webgl', { premultipliedAlpha: true });
 	if (gl == null) return;
@@ -197,6 +202,7 @@ onMounted(() => {
 			gl_FragColor = vec4( color, max(max(color.x, color.y), color.z) );
 		}
 	`);
+	if (shaderProgram == null) return;
 
 	gl.useProgram(shaderProgram);
 	const u_resolution = gl.getUniformLocation(shaderProgram, 'u_resolution');
@@ -226,7 +232,23 @@ onMounted(() => {
 		gl!.uniform1f(u_time, 0);
 		gl!.drawArrays(gl!.TRIANGLE_STRIP, 0, 4);
 	} else {
-		function render(timeStamp) {
+		function render(timeStamp: number) {
+			let sizeChanged = false;
+			if (Math.abs(height - canvas.offsetHeight) > 2) {
+				height = canvas.offsetHeight;
+				canvas.height = height;
+				sizeChanged = true;
+			}
+			if (Math.abs(width - canvas.offsetWidth) > 2) {
+				width = canvas.offsetWidth;
+				canvas.width = width;
+				sizeChanged = true;
+			}
+			if (sizeChanged && gl) {
+				gl.uniform2fv(u_resolution, [width, height]);
+				gl.viewport(0, 0, width, height);
+			}
+
 			gl!.uniform1f(u_time, timeStamp);
 			gl!.drawArrays(gl!.TRIANGLE_STRIP, 0, 4);
 

From 17f894348fcd8fc02ec898079f3ba994074f872b Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 13 Dec 2023 18:21:17 +0900
Subject: [PATCH 210/435] fix(client): fix glitch when attach/detach avatar
 decoration

---
 .../settings/profile.avatar-decoration-dialog.vue      | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
index c27a21217b..b7ea4c1521 100644
--- a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
@@ -77,19 +77,21 @@ async function attach() {
 		angle: angle.value,
 		flipH: flipH.value,
 	};
+	const update = [...$i.avatarDecorations, decoration];
 	await os.apiWithDialog('i/update', {
-		avatarDecorations: [...$i.avatarDecorations, decoration],
+		avatarDecorations: update,
 	});
-	$i.avatarDecorations = [...$i.avatarDecorations, decoration];
+	$i.avatarDecorations = update;
 
 	dialog.value.close();
 }
 
 async function detach() {
+	const update = $i.avatarDecorations.filter(x => x.id !== props.decoration.id);
 	await os.apiWithDialog('i/update', {
-		avatarDecorations: $i.avatarDecorations.filter(x => x.id !== props.decoration.id),
+		avatarDecorations: update,
 	});
-	$i.avatarDecorations = $i.avatarDecorations.filter(x => x.id !== props.decoration.id);
+	$i.avatarDecorations = update;
 
 	dialog.value.close();
 }

From 37820ad57216048964edd98232630e311e51ed0f Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 13 Dec 2023 18:31:32 +0900
Subject: [PATCH 211/435] =?UTF-8?q?fix(backend):=20=E3=83=A2=E3=83=87?=
 =?UTF-8?q?=E3=83=AC=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=83=AD=E3=82=B0?=
 =?UTF-8?q?=E3=81=8C=E3=83=A2=E3=83=87=E3=83=AC=E3=83=BC=E3=82=BF=E3=83=BC?=
 =?UTF-8?q?=E3=81=AF=E9=96=B2=E8=A6=A7=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE=E6=AD=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix #12622
---
 CHANGELOG.md                                                    | 1 +
 .../src/server/api/endpoints/admin/show-moderation-logs.ts      | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e5ff09edec..84a6ce35ef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -67,6 +67,7 @@
 - Fix: チャンネルのノート一覧にてインスタンスミュートが効かない問題
 - Fix: 「みつける」が年越し時に壊れる問題を修正
 - Fix: アカウントをブロックした際に、自身のユーザーのページでノートが相手に表示される問題を修正
+- Fix: モデレーションログがモデレーターは閲覧できないように修正
 
 ## 2023.11.1
 
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 f87a5a3574..34c247343a 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
@@ -14,7 +14,7 @@ export const meta = {
 	tags: ['admin'],
 
 	requireCredential: true,
-	requireModerator: true,
+	requireAdmin: true,
 
 	res: {
 		type: 'array',

From c1c3793a02877a586b38bccb9d70a72d41a91c5b Mon Sep 17 00:00:00 2001
From: Insert5StarName <anime@shourai.de>
Date: Wed, 13 Dec 2023 21:23:52 +0100
Subject: [PATCH 212/435] chore: update contributers and add note to docker
 compose

---
 docker-compose.yml.example                    |  1 +
 packages/frontend/src/pages/about-sharkey.vue | 12 ++++++------
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/docker-compose.yml.example b/docker-compose.yml.example
index 1e94c35d47..b0d68ce88e 100644
--- a/docker-compose.yml.example
+++ b/docker-compose.yml.example
@@ -2,6 +2,7 @@ version: "3"
 
 services:
   web:
+#   replace image below with git.joinsharkey.org/sharkey/sharkey:stable on next release
 #   image: ghcr.io/transfem-org/sharkey:stable
     build: .
     restart: always
diff --git a/packages/frontend/src/pages/about-sharkey.vue b/packages/frontend/src/pages/about-sharkey.vue
index be9b04ee69..67401cfff4 100644
--- a/packages/frontend/src/pages/about-sharkey.vue
+++ b/packages/frontend/src/pages/about-sharkey.vue
@@ -44,13 +44,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<FormSection>
 					<template #label>{{ i18n.ts._aboutMisskey.projectMembers }}</template>
 					<div :class="$style.contributors" style="margin-bottom: 8px;">
-						<a href="https://github.com/Mar0xy" target="_blank" :class="$style.contributor">
-							<img src="https://avatars.githubusercontent.com/u/8841466?v=4" :class="$style.contributorAvatar">
-							<span :class="$style.contributorUsername">@Mar0xy</span>
+						<a href="https://git.joinsharkey.org/Marie" target="_blank" :class="$style.contributor">
+							<img src="https://git.joinsharkey.org/avatar/0d57abf583f5ed6cf37f47055a1e1aa4?size=512" :class="$style.contributorAvatar">
+							<span :class="$style.contributorUsername">@Marie</span>
 						</a>
-						<a href="https://github.com/Insert5StarName" target="_blank" :class="$style.contributor">
-							<img src="https://avatars.githubusercontent.com/u/123300075?v=4" :class="$style.contributorAvatar">
-							<span :class="$style.contributorUsername">@Insert5StarName</span>
+						<a href="https://git.joinsharkey.org/Amelia" target="_blank" :class="$style.contributor">
+							<img src="https://git.joinsharkey.org/avatars/0634b661b89d6e45137074b6ddcd0b9ffc4cf467f2188ec12416ec6f91bb9d42?size=512" :class="$style.contributorAvatar">
+							<span :class="$style.contributorUsername">@Amelia</span>
 						</a>
 					</div>
 					<template #caption><MkLink url="https://github.com/transfem-org/sharkey/graphs/contributors">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template>

From eeed67ecac8fca3d542c4d68c1eb13ca113c5053 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Thu, 14 Dec 2023 07:18:29 +0900
Subject: [PATCH 213/435] =?UTF-8?q?(fix)=20=E3=83=87=E3=83=95=E3=82=A9?=
 =?UTF-8?q?=E3=83=AB=E3=83=88=E8=A1=A8=E7=A4=BA=E6=99=82=E3=81=AE=E3=83=98?=
 =?UTF-8?q?=E3=83=83=E3=83=80=E3=81=AB=E3=81=82=E3=82=8B=E3=83=81=E3=83=A3?=
 =?UTF-8?q?=E3=83=B3=E3=83=8D=E3=83=AB=E3=83=9C=E3=82=BF=E3=83=B3=E3=81=8C?=
 =?UTF-8?q?=E5=8F=8D=E5=BF=9C=E3=81=97=E3=81=AA=E3=81=84=E7=8F=BE=E8=B1=A1?=
 =?UTF-8?q?=E3=81=AE=E4=BF=AE=E6=AD=A3=20(#12648)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* dividerの仕変に追従

* fix type
---
 packages/frontend/src/pages/timeline.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index f3213ad273..59c45a57ff 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -125,14 +125,14 @@ async function chooseChannel(ev: MouseEvent): Promise<void> {
 	const channels = await os.api('channels/my-favorites', {
 		limit: 100,
 	});
-	const items = [
+	const items: MenuItem[] = [
 		...channels.map(channel => ({
 			type: 'link' as const,
 			text: channel.name,
 			indicate: channel.hasUnreadNote,
 			to: `/channels/${channel.id}`,
 		})),
-		(channels.length === 0 ? undefined : null),
+		(channels.length === 0 ? undefined : { type: 'divider' }),
 		{
 			type: 'link' as const,
 			icon: 'ti ti-plus',

From 839b7483ac85dc358cf116594f0003ca42e9b021 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 14 Dec 2023 11:29:27 +0900
Subject: [PATCH 214/435] =?UTF-8?q?enhance(frontend):=20=E5=90=8C=E3=81=98?=
 =?UTF-8?q?=E7=A8=AE=E9=A1=9E=E3=81=AE=E3=83=87=E3=82=B3=E3=83=AC=E3=83=BC?=
 =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E8=A4=87=E6=95=B0=E4=BB=98?=
 =?UTF-8?q?=E3=81=91=E3=82=89=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/components/global/MkAvatar.vue        |   6 +-
 .../profile.avatar-decoration.decoration.vue  |  67 ++++++++++
 ...e => profile.avatar-decoration.dialog.vue} |  63 ++++++---
 .../settings/profile.avatar-decoration.vue    | 125 ++++++++++++++++++
 .../frontend/src/pages/settings/profile.vue   |  75 +----------
 5 files changed, 240 insertions(+), 96 deletions(-)
 create mode 100644 packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue
 rename packages/frontend/src/pages/settings/{profile.avatar-decoration-dialog.vue => profile.avatar-decoration.dialog.vue} (66%)
 create mode 100644 packages/frontend/src/pages/settings/profile.avatar-decoration.vue

diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index 6aa9a42037..9d13c03290 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -59,7 +59,7 @@ const props = withDefaults(defineProps<{
 	link?: boolean;
 	preview?: boolean;
 	indicator?: boolean;
-	decorations?: Misskey.entities.UserDetailed['avatarDecorations'][number][];
+	decorations?: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>[];
 	forceShowDecoration?: boolean;
 }>(), {
 	target: null,
@@ -89,12 +89,12 @@ function onClick(ev: MouseEvent): void {
 	emit('click', ev);
 }
 
-function getDecorationAngle(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) {
+function getDecorationAngle(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
 	const angle = decoration.angle ?? 0;
 	return angle === 0 ? undefined : `${angle * 360}deg`;
 }
 
-function getDecorationScale(decoration: Misskey.entities.UserDetailed['avatarDecorations'][number]) {
+function getDecorationScale(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
 	const scaleX = decoration.flipH ? -1 : 1;
 	return scaleX === 1 ? undefined : `${scaleX} 1`;
 }
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue
new file mode 100644
index 0000000000..c113868238
--- /dev/null
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue
@@ -0,0 +1,67 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div
+	:class="[$style.root, { [$style.active]: active }]"
+	@click="emit('click')"
+>
+	<div :class="$style.name"><MkCondensedLine :minScale="0.5">{{ decoration.name }}</MkCondensedLine></div>
+	<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: decoration.url, angle, flipH }]" forceShowDecoration/>
+	<i v-if="decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.lock" class="ti ti-lock"></i>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { } from 'vue';
+import { $i } from '@/account.js';
+
+const props = defineProps<{
+	active?: boolean;
+	decoration: {
+		id: string;
+		url: string;
+		name: string;
+		roleIdsThatCanBeUsedThisDecoration: string[];
+	};
+	angle?: number;
+	flipH?: boolean;
+}>();
+
+const emit = defineEmits<{
+	(ev: 'click'): void;
+}>();
+</script>
+
+<style lang="scss" module>
+.root {
+	cursor: pointer;
+	padding: 16px 16px 28px 16px;
+	border: solid 2px var(--divider);
+	border-radius: 8px;
+	text-align: center;
+	font-size: 90%;
+	overflow: clip;
+	contain: content;
+}
+
+.active {
+	background-color: var(--accentedBg);
+	border-color: var(--accent);
+}
+
+.name {
+	position: relative;
+	z-index: 10;
+	font-weight: bold;
+	margin-bottom: 20px;
+}
+
+.lock {
+	position: absolute;
+	bottom: 12px;
+	right: 12px;
+}
+</style>
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue
similarity index 66%
rename from packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
rename to packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue
index b7ea4c1521..a27b46aa3e 100644
--- a/packages/frontend/src/pages/settings/profile.avatar-decoration-dialog.vue
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkSpacer :marginMin="20" :marginMax="28">
 			<div style="text-align: center;">
 				<div :class="$style.name">{{ decoration.name }}</div>
-				<MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decorations="[...$i.avatarDecorations, { url: decoration.url, angle, flipH }]" forceShowDecoration/>
+				<MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decorations="decorationsForPreview" forceShowDecoration/>
 			</div>
 			<div class="_gaps_s">
 				<MkRange v-model="angle" continuousUpdate :min="-0.5" :max="0.5" :step="0.025" :textConverter="(v) => `${Math.floor(v * 360)}°`">
@@ -30,8 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</MkSpacer>
 
 		<div :class="$style.footer" class="_buttonsCenter">
-			<MkButton v-if="using" primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.update }}</MkButton>
-			<MkButton v-if="using" rounded @click="detach"><i class="ti ti-x"></i> {{ i18n.ts.detach }}</MkButton>
+			<MkButton v-if="usingIndex != null" primary rounded @click="update"><i class="ti ti-check"></i> {{ i18n.ts.update }}</MkButton>
+			<MkButton v-if="usingIndex != null" rounded @click="detach"><i class="ti ti-x"></i> {{ i18n.ts.detach }}</MkButton>
 			<MkButton v-else primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.attach }}</MkButton>
 		</div>
 	</div>
@@ -51,48 +51,69 @@ import MkRange from '@/components/MkRange.vue';
 import { $i } from '@/account.js';
 
 const props = defineProps<{
+	usingIndex: number | null;
 	decoration: {
 		id: string;
 		url: string;
 		name: string;
-	}
+	};
 }>();
 
 const emit = defineEmits<{
 	(ev: 'closed'): void;
+	(ev: 'attach', payload: {
+		angle: number;
+		flipH: boolean;
+	}): void;
+	(ev: 'update', payload: {
+		angle: number;
+		flipH: boolean;
+	}): void;
+	(ev: 'detach'): void;
 }>();
 
 const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
-const using = computed(() => $i.avatarDecorations.some(x => x.id === props.decoration.id));
-const angle = ref(using.value ? $i.avatarDecorations.find(x => x.id === props.decoration.id).angle ?? 0 : 0);
-const flipH = ref(using.value ? $i.avatarDecorations.find(x => x.id === props.decoration.id).flipH ?? false : false);
+const angle = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].angle : null) ?? 0);
+const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false);
+
+const decorationsForPreview = computed(() => {
+	const decoration = {
+		id: props.decoration.id,
+		url: props.decoration.url,
+		angle: angle.value,
+		flipH: flipH.value,
+	};
+	const decorations = [...$i.avatarDecorations];
+	if (props.usingIndex != null) {
+		decorations[props.usingIndex] = decoration;
+	} else {
+		decorations.push(decoration);
+	}
+	return decorations;
+});
 
 function cancel() {
 	dialog.value.close();
 }
 
-async function attach() {
-	const decoration = {
-		id: props.decoration.id,
+async function update() {
+	emit('update', {
 		angle: angle.value,
 		flipH: flipH.value,
-	};
-	const update = [...$i.avatarDecorations, decoration];
-	await os.apiWithDialog('i/update', {
-		avatarDecorations: update,
 	});
-	$i.avatarDecorations = update;
+	dialog.value.close();
+}
 
+async function attach() {
+	emit('attach', {
+		angle: angle.value,
+		flipH: flipH.value,
+	});
 	dialog.value.close();
 }
 
 async function detach() {
-	const update = $i.avatarDecorations.filter(x => x.id !== props.decoration.id);
-	await os.apiWithDialog('i/update', {
-		avatarDecorations: update,
-	});
-	$i.avatarDecorations = update;
-
+	emit('detach');
 	dialog.value.close();
 }
 </script>
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue
new file mode 100644
index 0000000000..90c2b75a4d
--- /dev/null
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue
@@ -0,0 +1,125 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div v-if="!loading" class="_gaps">
+	<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i?.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i?.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
+
+	<div v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s">
+		<div>{{ i18n.ts.inUse }}</div>
+
+		<div :class="$style.decorations">
+			<XDecoration
+				v-for="(avatarDecoration, i) in $i.avatarDecorations"
+				:decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)"
+				:angle="avatarDecoration.angle"
+				:flipH="avatarDecoration.flipH"
+				:active="true"
+				@click="openDecoration(avatarDecoration, i)"
+			/>
+		</div>
+
+		<MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
+	</div>
+
+	<div :class="$style.decorations">
+		<XDecoration
+			v-for="avatarDecoration in avatarDecorations"
+			:key="avatarDecoration.id"
+			:decoration="avatarDecoration"
+			@click="openDecoration(avatarDecoration)"
+		/>
+	</div>
+</div>
+<div v-else>
+	<MkLoading/>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { ref, defineAsyncComponent } from 'vue';
+import * as Misskey from 'misskey-js';
+import XDecoration from './profile.avatar-decoration.decoration.vue';
+import MkButton from '@/components/MkButton.vue';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import MkInfo from '@/components/MkInfo.vue';
+
+const loading = ref(true);
+const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse>([]);
+
+os.api('get-avatar-decorations').then(_avatarDecorations => {
+	avatarDecorations.value = _avatarDecorations;
+	loading.value = false;
+});
+
+function openDecoration(avatarDecoration, index?: number) {
+	os.popup(defineAsyncComponent(() => import('./profile.avatar-decoration.dialog.vue')), {
+		decoration: avatarDecoration,
+		usingIndex: index,
+	}, {
+		'attach': async (payload) => {
+			const decoration = {
+				id: avatarDecoration.id,
+				angle: payload.angle,
+				flipH: payload.flipH,
+			};
+			const update = [...$i.avatarDecorations, decoration];
+			await os.apiWithDialog('i/update', {
+				avatarDecorations: update,
+			});
+			$i.avatarDecorations = update;
+		},
+		'update': async (payload) => {
+			const decoration = {
+				id: avatarDecoration.id,
+				angle: payload.angle,
+				flipH: payload.flipH,
+			};
+			const update = [...$i.avatarDecorations];
+			update[index] = decoration;
+			await os.apiWithDialog('i/update', {
+				avatarDecorations: update,
+			});
+			$i.avatarDecorations = update;
+		},
+		'detach': async () => {
+			const update = [...$i.avatarDecorations];
+			update.splice(index, 1);
+			await os.apiWithDialog('i/update', {
+				avatarDecorations: update,
+			});
+			$i.avatarDecorations = update;
+		},
+	}, 'closed');
+}
+
+function detachAllDecorations() {
+	os.confirm({
+		type: 'warning',
+		text: i18n.ts.areYouSure,
+	}).then(async ({ canceled }) => {
+		if (canceled) return;
+		await os.apiWithDialog('i/update', {
+			avatarDecorations: [],
+		});
+		$i.avatarDecorations = [];
+	});
+}
+</script>
+
+<style lang="scss" module>
+.current {
+	padding: 16px;
+	border-radius: var(--radius);
+}
+
+.decorations {
+	display: grid;
+	grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
+	grid-gap: 12px;
+}
+</style>
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index a5d3835b93..5f0d1aee51 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -87,24 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<template #icon><i class="ti ti-sparkles"></i></template>
 		<template #label>{{ i18n.ts.avatarDecorations }}</template>
 
-		<div class="_gaps">
-			<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i?.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i?.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
-
-			<MkButton v-if="$i.avatarDecorations.length > 0" danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
-
-			<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); grid-gap: 12px;">
-				<div
-					v-for="avatarDecoration in avatarDecorations"
-					:key="avatarDecoration.id"
-					:class="[$style.avatarDecoration, { [$style.avatarDecorationActive]: $i.avatarDecorations.some(x => x.id === avatarDecoration.id) }]"
-					@click="openDecoration(avatarDecoration)"
-				>
-					<div :class="$style.avatarDecorationName"><MkCondensedLine :minScale="0.5">{{ avatarDecoration.name }}</MkCondensedLine></div>
-					<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: avatarDecoration.url }]" forceShowDecoration/>
-					<i v-if="avatarDecoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => avatarDecoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.avatarDecorationLock" class="ti ti-lock"></i>
-				</div>
-			</div>
-		</div>
+		<XAvatarDecoration/>
 	</MkFolder>
 
 	<MkFolder>
@@ -128,7 +111,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, reactive, ref, watch, defineAsyncComponent, onMounted, onUnmounted } from 'vue';
+import { computed, reactive, ref, watch, defineAsyncComponent } from 'vue';
+import XAvatarDecoration from './profile.avatar-decoration.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
@@ -150,7 +134,6 @@ import MkInfo from '@/components/MkInfo.vue';
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
 const reactionAcceptance = computed(defaultStore.makeGetterSetter('reactionAcceptance'));
-const avatarDecorations = ref<any[]>([]);
 
 const profile = reactive({
 	name: $i.name,
@@ -171,10 +154,6 @@ watch(() => profile, () => {
 const fields = ref($i?.fields.map(field => ({ id: Math.random().toString(), name: field.name, value: field.value })) ?? []);
 const fieldEditMode = ref(false);
 
-os.api('get-avatar-decorations').then(_avatarDecorations => {
-	avatarDecorations.value = _avatarDecorations;
-});
-
 function addField() {
 	fields.value.push({
 		id: Math.random().toString(),
@@ -273,25 +252,6 @@ function changeBanner(ev) {
 	});
 }
 
-function openDecoration(avatarDecoration) {
-	os.popup(defineAsyncComponent(() => import('./profile.avatar-decoration-dialog.vue')), {
-		decoration: avatarDecoration,
-	}, {}, 'closed');
-}
-
-function detachAllDecorations() {
-	os.confirm({
-		type: 'warning',
-		text: i18n.ts.areYouSure,
-	}).then(async ({ canceled }) => {
-		if (canceled) return;
-		await os.apiWithDialog('i/update', {
-			avatarDecorations: [],
-		});
-		$i.avatarDecorations = [];
-	});
-}
-
 const headerActions = computed(() => []);
 
 const headerTabs = computed(() => []);
@@ -386,33 +346,4 @@ definePageMetadata({
 .dragItemForm {
 	flex-grow: 1;
 }
-
-.avatarDecoration {
-	cursor: pointer;
-	padding: 16px 16px 28px 16px;
-	border: solid 2px var(--divider);
-	border-radius: 8px;
-	text-align: center;
-	font-size: 90%;
-	overflow: clip;
-	contain: content;
-}
-
-.avatarDecorationActive {
-	background-color: var(--accentedBg);
-	border-color: var(--accent);
-}
-
-.avatarDecorationName {
-	position: relative;
-	z-index: 10;
-	font-weight: bold;
-	margin-bottom: 20px;
-}
-
-.avatarDecorationLock {
-	position: absolute;
-	bottom: 12px;
-	right: 12px;
-}
 </style>

From 2cfe64e9e658ccd47b4122f8d798c3e78e73259c Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 14 Dec 2023 11:35:17 +0900
Subject: [PATCH 215/435] fix swcrc

---
 packages/backend/.swcrc    | 2 +-
 packages/misskey-js/.swcrc | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/backend/.swcrc b/packages/backend/.swcrc
index d9f047b6ac..0504a2d389 100644
--- a/packages/backend/.swcrc
+++ b/packages/backend/.swcrc
@@ -11,7 +11,7 @@
 			"decoratorMetadata": true
 		},
 		"experimental": {
-			"keepImportAttributes": true
+			"keepImportAssertions": true
 		},
 		"baseUrl": "src",
 		"paths": {
diff --git a/packages/misskey-js/.swcrc b/packages/misskey-js/.swcrc
index d9f047b6ac..0504a2d389 100644
--- a/packages/misskey-js/.swcrc
+++ b/packages/misskey-js/.swcrc
@@ -11,7 +11,7 @@
 			"decoratorMetadata": true
 		},
 		"experimental": {
-			"keepImportAttributes": true
+			"keepImportAssertions": true
 		},
 		"baseUrl": "src",
 		"paths": {

From 5cee481083262d08ddc79eff78c6f23667c48ecc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Thu, 14 Dec 2023 12:26:02 +0900
Subject: [PATCH 216/435] =?UTF-8?q?refactor(frontend)=20$i=20=E3=81=AE?=
 =?UTF-8?q?=E5=9E=8B=E6=83=85=E5=A0=B1=E3=81=ABtoken=E3=82=92=E8=BF=BD?=
 =?UTF-8?q?=E5=8A=A0=20(#12649)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts
index a6af298024..f23fb804c5 100644
--- a/packages/frontend/src/account.ts
+++ b/packages/frontend/src/account.ts
@@ -16,7 +16,7 @@ import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js';
 
 // TODO: 他のタブと永続化されたstateを同期
 
-type Account = Misskey.entities.MeDetailed;
+type Account = Misskey.entities.MeDetailed & { token: string };
 
 const accountData = miLocalStorage.getItem('account');
 

From b33fe530476f89282e1e554aecf0cfe82e6d6edd Mon Sep 17 00:00:00 2001
From: 1STEP621 <86859447+1STEP621@users.noreply.github.com>
Date: Thu, 14 Dec 2023 13:11:23 +0900
Subject: [PATCH 217/435] =?UTF-8?q?Enhance(frontend):=20MFM=E3=82=84?=
 =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=8C=E4=BD=BF=E3=81=88=E3=82=8B?=
 =?UTF-8?q?=E5=85=A5=E5=8A=9B=E3=83=9C=E3=83=83=E3=82=AF=E3=82=B9=E3=81=A7?=
 =?UTF-8?q?=E3=82=AA=E3=83=BC=E3=83=88=E3=82=B3=E3=83=B3=E3=83=97=E3=83=AA?=
 =?UTF-8?q?=E3=83=BC=E3=83=88=E3=82=92=E4=BD=BF=E3=81=88=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#12643)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* rich autocomplete for use in profiles, announcements, and channel descriptions

* implementation omissions

* add tab, apply to page editor, and fix something

* componentization

* fix nyaize doesn't working in profile preview

* detach autocomplete instance when unmounted

* fix: mismatched camelCase

* remove unused / unnecessary styles

* update CHANGELOG.md

* fix lint

* remove dump.rdb

* props.richAutocomplete -> autocomplete

* Update packages/frontend/src/scripts/autocomplete.ts

* clarify namings
メンションなども「MFM」に含まれるのか自信がなかったのでrichSyntaxなどとぼかしていましたが、含むようなので変更しました

* tweak

* Update MkFormDialog.vue

* rename

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                  |  2 ++
 .../frontend/src/components/MkFormDialog.vue  |  4 +--
 packages/frontend/src/components/MkInput.vue  | 15 +++++++++-
 .../frontend/src/components/MkTextarea.vue    | 29 ++++++++++++++++++-
 .../global/MkMisskeyFlavoredMarkdown.ts       |  2 +-
 .../src/pages/admin/announcements.vue         |  4 +--
 .../frontend/src/pages/channel-editor.vue     |  4 +--
 packages/frontend/src/pages/clip.vue          |  1 +
 .../frontend/src/pages/my-clips/index.vue     |  1 +
 .../page-editor/els/page-editor.el.text.vue   | 16 ++++++++--
 .../frontend/src/pages/settings/profile.vue   |  7 +++--
 packages/frontend/src/scripts/autocomplete.ts | 14 +++++----
 12 files changed, 80 insertions(+), 19 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 84a6ce35ef..972c876518 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,8 @@
 - Enhance: データセーバーの適用範囲を個別で設定できるように
 	- 従来のデータセーバーの設定はリセットされます
 - Enhance: タイムライン上のタブからリスト、アンテナ、チャンネルの管理ページにジャンプできるように
+- Enhance: ユーザー名、プロフィール、お知らせ、ページの編集画面でMFMや絵文字のオートコンプリートが使用できるように
+- Enhance: プロフィール、お知らせの編集画面でMFMのプレビューを表示できるように
 - Feat: センシティブと判断されたウェブサイトのサムネイルをぼかすように
   - ウェブサイトをセンシティブと判断する仕組みが動いていないため、summalyProxyを使用しないと機能しません。
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue
index 24404728ca..6f882cfab7 100644
--- a/packages/frontend/src/components/MkFormDialog.vue
+++ b/packages/frontend/src/components/MkFormDialog.vue
@@ -26,11 +26,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
 					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
 				</MkInput>
-				<MkInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text">
+				<MkInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text" :mfmAutocomplete="form[item].treatAsMfm">
 					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
 					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
 				</MkInput>
-				<MkTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]">
+				<MkTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]" :mfmAutocomplete="form[item].treatAsMfm" :mfmPreview="form[item].treatAsMfm">
 					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
 					<template v-if="form[item].description" #caption>{{ form[item].description }}</template>
 				</MkTextarea>
diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue
index 72babfac76..ae797eb7d2 100644
--- a/packages/frontend/src/components/MkInput.vue
+++ b/packages/frontend/src/components/MkInput.vue
@@ -43,11 +43,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
+import { onMounted, onUnmounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
 import { debounce } from 'throttle-debounce';
 import MkButton from '@/components/MkButton.vue';
 import { useInterval } from '@/scripts/use-interval.js';
 import { i18n } from '@/i18n.js';
+import { Autocomplete, SuggestionType } from '@/scripts/autocomplete.js';
 
 const props = defineProps<{
 	modelValue: string | number | null;
@@ -59,6 +60,7 @@ const props = defineProps<{
 	placeholder?: string;
 	autofocus?: boolean;
 	autocomplete?: string;
+	mfmAutocomplete?: boolean | SuggestionType[],
 	autocapitalize?: string;
 	spellcheck?: boolean;
 	step?: any;
@@ -93,6 +95,7 @@ const height =
 	props.small ? 33 :
 	props.large ? 39 :
 	36;
+let autocomplete: Autocomplete;
 
 const focus = () => inputEl.value.focus();
 const onInput = (ev: KeyboardEvent) => {
@@ -160,6 +163,16 @@ onMounted(() => {
 			focus();
 		}
 	});
+	
+	if (props.mfmAutocomplete) {
+		autocomplete = new Autocomplete(inputEl.value, v, props.mfmAutocomplete === true ? null : props.mfmAutocomplete);
+	}
+});
+
+onUnmounted(() => {
+	if (autocomplete) {
+		autocomplete.detach();
+	}
 });
 
 defineExpose({
diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue
index 7c1ddcbbed..23fdd5bfe1 100644
--- a/packages/frontend/src/components/MkTextarea.vue
+++ b/packages/frontend/src/components/MkTextarea.vue
@@ -26,16 +26,21 @@ SPDX-License-Identifier: AGPL-3.0-only
 		></textarea>
 	</div>
 	<div :class="$style.caption"><slot name="caption"></slot></div>
+	<button style="font-size: 0.85em;" class="_textButton" type="button" @click="preview = !preview">{{ i18n.ts.preview }}</button>
+	<div v-show="preview" v-panel :class="$style.mfmPreview">
+		<Mfm :text="v"/>
+	</div>
 
 	<MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
 </div>
 </template>
 
 <script lang="ts" setup>
-import { onMounted, nextTick, ref, watch, computed, toRefs, shallowRef } from 'vue';
+import { onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, shallowRef } from 'vue';
 import { debounce } from 'throttle-debounce';
 import MkButton from '@/components/MkButton.vue';
 import { i18n } from '@/i18n.js';
+import { Autocomplete, SuggestionType } from '@/scripts/autocomplete.js';
 
 const props = defineProps<{
 	modelValue: string | null;
@@ -46,6 +51,8 @@ const props = defineProps<{
 	placeholder?: string;
 	autofocus?: boolean;
 	autocomplete?: string;
+	mfmAutocomplete?: boolean | SuggestionType[],
+	mfmPreview?: boolean;
 	spellcheck?: boolean;
 	debounce?: boolean;
 	manualSave?: boolean;
@@ -68,6 +75,8 @@ const changed = ref(false);
 const invalid = ref(false);
 const filled = computed(() => v.value !== '' && v.value != null);
 const inputEl = shallowRef<HTMLTextAreaElement>();
+const preview = ref(false);
+let autocomplete: Autocomplete;
 
 const focus = () => inputEl.value.focus();
 const onInput = (ev) => {
@@ -113,6 +122,16 @@ onMounted(() => {
 			focus();
 		}
 	});
+
+	if (props.mfmAutocomplete) {
+		autocomplete = new Autocomplete(inputEl.value, v, props.mfmAutocomplete === true ? null : props.mfmAutocomplete);
+	}
+});
+
+onUnmounted(() => {
+	if (autocomplete) {
+		autocomplete.detach();
+	}
 });
 </script>
 
@@ -194,4 +213,12 @@ onMounted(() => {
 .save {
 	margin: 8px 0 0 0;
 }
+
+.mfmPreview {
+  padding: 12px;
+  border-radius: var(--radius);
+  box-sizing: border-box;
+  min-height: 130px;
+	pointer-events: none;
+}
 </style>
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index fe599dcead..28293b287c 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -37,7 +37,7 @@ type MfmProps = {
 	isNote?: boolean;
 	emojiUrls?: string[];
 	rootScale?: number;
-	nyaize: boolean | 'respect';
+	nyaize?: boolean | 'respect';
 	parsedNodes?: mfm.MfmNode[] | null;
 	enableEmojiMenu?: boolean;
 	enableEmojiMenuReaction?: boolean;
diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue
index 92070dc6c6..e4bbe15955 100644
--- a/packages/frontend/src/pages/admin/announcements.vue
+++ b/packages/frontend/src/pages/admin/announcements.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<MkInput v-model="announcement.title">
 						<template #label>{{ i18n.ts.title }}</template>
 					</MkInput>
-					<MkTextarea v-model="announcement.text">
+					<MkTextarea v-model="announcement.text" mfmAutocomplete :mfmPreview="true">
 						<template #label>{{ i18n.ts.text }}</template>
 					</MkTextarea>
 					<MkInput v-model="announcement.imageUrl" type="url">
@@ -75,7 +75,6 @@ import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
-import MkTextarea from '@/components/MkTextarea.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkInfo from '@/components/MkInfo.vue';
@@ -83,6 +82,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkFolder from '@/components/MkFolder.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
 
 const announcements = ref<any[]>([]);
 
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index af382bb137..f16b8709f3 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<template #label>{{ i18n.ts.name }}</template>
 			</MkInput>
 
-			<MkTextarea v-model="description">
+			<MkTextarea v-model="description" mfmAutocomplete :mfmPreview="true">
 				<template #label>{{ i18n.ts.description }}</template>
 			</MkTextarea>
 
@@ -70,7 +70,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref, watch, defineAsyncComponent } from 'vue';
-import MkTextarea from '@/components/MkTextarea.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkColorInput from '@/components/MkColorInput.vue';
@@ -81,6 +80,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 import MkFolder from '@/components/MkFolder.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index 2ea0312c7e..3c94db82d7 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -101,6 +101,7 @@ const headerActions = computed(() => clip.value && isOwned.value ? [{
 				type: 'string',
 				required: false,
 				multiline: true,
+				treatAsMfm: true,
 				label: i18n.ts.description,
 				default: clip.value.description,
 			},
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index 2390617954..85c016187d 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -60,6 +60,7 @@ async function create() {
 			type: 'string',
 			required: false,
 			multiline: true,
+			treatAsMfm: true,
 			label: i18n.ts.description,
 		},
 		isPublic: {
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
index 4f47a77bdd..643e8ecdad 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
@@ -9,16 +9,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<template #header><i class="ti ti-align-left"></i> {{ i18n.ts._pages.blocks.text }}</template>
 
 	<section>
-		<textarea v-model="text" :class="$style.textarea"></textarea>
+		<textarea ref="inputEl" v-model="text" :class="$style.textarea"></textarea>
 	</section>
 </XContainer>
 </template>
 
 <script lang="ts" setup>
 /* eslint-disable vue/no-mutating-props */
-import { watch, ref } from 'vue';
+import { watch, ref, shallowRef, onMounted, onUnmounted } from 'vue';
 import XContainer from '../page-editor.container.vue';
 import { i18n } from '@/i18n.js';
+import { Autocomplete } from '@/scripts/autocomplete.js';
 
 const props = defineProps<{
 	modelValue: any
@@ -28,7 +29,10 @@ const emit = defineEmits<{
 	(ev: 'update:modelValue', value: any): void;
 }>();
 
+let autocomplete: Autocomplete;
+
 const text = ref(props.modelValue.text ?? '');
+const inputEl = shallowRef<HTMLTextAreaElement | null>(null);
 
 watch(text, () => {
 	emit('update:modelValue', {
@@ -36,6 +40,14 @@ watch(text, () => {
 		text: text.value,
 	});
 });
+
+onMounted(() => {
+	autocomplete = new Autocomplete(inputEl.value, text);
+});
+
+onUnmounted(() => {
+	autocomplete.detach();
+});
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index 5f0d1aee51..1381042c39 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -13,11 +13,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkButton primary rounded :class="$style.bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton>
 	</div>
 
-	<MkInput v-model="profile.name" :max="30" manualSave>
+	<MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']">
 		<template #label>{{ i18n.ts._profile.name }}</template>
 	</MkInput>
 
-	<MkTextarea v-model="profile.description" :max="500" tall manualSave>
+	<MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true" :nyaize="$i?.isCat ? 'respect' : undefined" :author="($i as Misskey.entities.UserLite)">
 		<template #label>{{ i18n.ts._profile.description }}</template>
 		<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
 	</MkTextarea>
@@ -112,10 +112,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, reactive, ref, watch, defineAsyncComponent } from 'vue';
+import Misskey from 'misskey-js';
 import XAvatarDecoration from './profile.avatar-decoration.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
-import MkTextarea from '@/components/MkTextarea.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import FormSplit from '@/components/form/split.vue';
@@ -130,6 +130,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { claimAchievement } from '@/scripts/achievements.js';
 import { defaultStore } from '@/store.js';
 import MkInfo from '@/components/MkInfo.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts
index 0b4ebb4410..2a9a42ace5 100644
--- a/packages/frontend/src/scripts/autocomplete.ts
+++ b/packages/frontend/src/scripts/autocomplete.ts
@@ -8,6 +8,8 @@ import getCaretCoordinates from 'textarea-caret';
 import { toASCII } from 'punycode/';
 import { popup } from '@/os.js';
 
+export type SuggestionType = 'user' | 'hashtag' | 'emoji' | 'mfmTag';
+
 export class Autocomplete {
 	private suggestion: {
 		x: Ref<number>;
@@ -19,6 +21,7 @@ export class Autocomplete {
 	private currentType: string;
 	private textRef: Ref<string>;
 	private opening: boolean;
+	private onlyType: SuggestionType[];
 
 	private get text(): string {
 		// Use raw .value to get the latest value
@@ -35,7 +38,7 @@ export class Autocomplete {
 	/**
 	 * 対象のテキストエリアを与えてインスタンスを初期化します。
 	 */
-	constructor(textarea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) {
+	constructor(textarea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>, onlyType?: SuggestionType[]) {
 		//#region BIND
 		this.onInput = this.onInput.bind(this);
 		this.complete = this.complete.bind(this);
@@ -46,6 +49,7 @@ export class Autocomplete {
 		this.textarea = textarea;
 		this.textRef = textRef;
 		this.opening = false;
+		this.onlyType = onlyType ?? ['user', 'hashtag', 'emoji', 'mfmTag'];
 
 		this.attach();
 	}
@@ -95,7 +99,7 @@ export class Autocomplete {
 
 		let opened = false;
 
-		if (isMention) {
+		if (isMention && this.onlyType.includes('user')) {
 			const username = text.substring(mentionIndex + 1);
 			if (username !== '' && username.match(/^[a-zA-Z0-9_]+$/)) {
 				this.open('user', username);
@@ -106,7 +110,7 @@ export class Autocomplete {
 			}
 		}
 
-		if (isHashtag && !opened) {
+		if (isHashtag && !opened && this.onlyType.includes('hashtag')) {
 			const hashtag = text.substring(hashtagIndex + 1);
 			if (!hashtag.includes(' ')) {
 				this.open('hashtag', hashtag);
@@ -114,7 +118,7 @@ export class Autocomplete {
 			}
 		}
 
-		if (isEmoji && !opened) {
+		if (isEmoji && !opened && this.onlyType.includes('emoji')) {
 			const emoji = text.substring(emojiIndex + 1);
 			if (!emoji.includes(' ')) {
 				this.open('emoji', emoji);
@@ -122,7 +126,7 @@ export class Autocomplete {
 			}
 		}
 
-		if (isMfmTag && !opened) {
+		if (isMfmTag && !opened && this.onlyType.includes('mfmTag')) {
 			const mfmTag = text.substring(mfmTagIndex + 1);
 			if (!mfmTag.includes(' ')) {
 				this.open('mfmTag', mfmTag.replace('[', ''));

From aedc1d0ee958f4ae31a56844643b6f54e4d6301c Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 14 Dec 2023 13:22:12 +0900
Subject: [PATCH 218/435] update deps

---
 package.json                                  |   4 +-
 packages/backend/package.json                 |  12 +-
 .../backend/src/server/web/views/base.pug     |   2 +-
 .../frontend/.storybook/preview-head.html     |   2 +-
 packages/frontend/package.json                |  18 +-
 packages/misskey-js/package.json              |   4 +-
 packages/sw/package.json                      |   2 +-
 pnpm-lock.yaml                                | 526 ++++++++----------
 8 files changed, 239 insertions(+), 331 deletions(-)

diff --git a/package.json b/package.json
index 8e96a07cd3..b0a5e137b8 100644
--- a/package.json
+++ b/package.json
@@ -53,8 +53,8 @@
 		"typescript": "5.3.3"
 	},
 	"devDependencies": {
-		"@typescript-eslint/eslint-plugin": "6.13.2",
-		"@typescript-eslint/parser": "6.13.2",
+		"@typescript-eslint/eslint-plugin": "6.14.0",
+		"@typescript-eslint/parser": "6.14.0",
 		"cross-env": "7.0.3",
 		"cypress": "13.6.1",
 		"eslint": "8.55.0",
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 85bdfbebf4..c68966de44 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -89,7 +89,7 @@
 		"bcryptjs": "2.4.3",
 		"blurhash": "2.0.5",
 		"body-parser": "1.20.2",
-		"bullmq": "4.15.2",
+		"bullmq": "4.15.3",
 		"cacheable-lookup": "7.0.0",
 		"cbor": "9.0.1",
 		"chalk": "5.3.0",
@@ -100,7 +100,7 @@
 		"content-disposition": "0.5.4",
 		"date-fns": "2.30.0",
 		"deep-email-validator": "0.1.21",
-		"fastify": "4.24.3",
+		"fastify": "4.25.0",
 		"fastify-raw-body": "4.3.0",
 		"feed": "4.2.2",
 		"file-type": "18.7.0",
@@ -148,7 +148,7 @@
 		"ratelimiter": "3.4.1",
 		"re2": "1.20.9",
 		"redis-lock": "0.1.4",
-		"reflect-metadata": "0.1.14",
+		"reflect-metadata": "0.2.0",
 		"rename": "1.0.4",
 		"rss-parser": "3.13.0",
 		"rxjs": "7.8.1",
@@ -171,7 +171,7 @@
 		"ulid": "2.3.0",
 		"vary": "1.1.2",
 		"web-push": "3.6.6",
-		"ws": "8.14.2",
+		"ws": "8.15.1",
 		"xev": "3.0.2"
 	},
 	"devDependencies": {
@@ -217,8 +217,8 @@
 		"@types/vary": "1.1.3",
 		"@types/web-push": "3.6.3",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "6.13.2",
-		"@typescript-eslint/parser": "6.13.2",
+		"@typescript-eslint/eslint-plugin": "6.14.0",
+		"@typescript-eslint/parser": "6.14.0",
 		"aws-sdk-client-mock": "3.0.0",
 		"cross-env": "7.0.3",
 		"eslint": "8.55.0",
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 2cb3fd4738..d167afe1e8 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -36,7 +36,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.37.0')
+		link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.44.0')
 		link(rel='modulepreload' href=`/vite/${clientEntry.file}`)
 
 		if !config.clientManifestExists
diff --git a/packages/frontend/.storybook/preview-head.html b/packages/frontend/.storybook/preview-head.html
index 36ff34b48a..30f3ebfb64 100644
--- a/packages/frontend/.storybook/preview-head.html
+++ b/packages/frontend/.storybook/preview-head.html
@@ -1,6 +1,6 @@
 <link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
 <link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
-<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.37.0/tabler-icons.min.css">
+<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.44.0/tabler-icons.min.css">
 <link rel="stylesheet" href="https://unpkg.com/@fontsource/m-plus-rounded-1c/index.css">
 <style>
 	html {
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index b78cfb2395..e566f86d1c 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -20,13 +20,13 @@
 		"@discordapp/twemoji": "14.1.2",
 		"@github/webauthn-json": "2.1.1",
 		"@rollup/plugin-alias": "5.1.0",
-		"@rollup/plugin-json": "6.0.1",
+		"@rollup/plugin-json": "6.1.0",
 		"@rollup/plugin-replace": "5.0.5",
 		"@rollup/pluginutils": "5.1.0",
 		"@syuilo/aiscript": "0.16.0",
-		"@tabler/icons-webfont": "2.37.0",
-		"@vitejs/plugin-vue": "4.5.1",
-		"@vue/compiler-sfc": "3.3.9",
+		"@tabler/icons-webfont": "2.44.0",
+		"@vitejs/plugin-vue": "4.5.2",
+		"@vue/compiler-sfc": "3.3.11",
 		"astring": "1.8.6",
 		"autosize": "6.0.1",
 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
@@ -57,7 +57,7 @@
 		"photoswipe": "5.4.3",
 		"punycode": "2.3.1",
 		"querystring": "0.2.1",
-		"rollup": "4.7.0",
+		"rollup": "4.9.0",
 		"sanitize-html": "2.11.0",
 		"shiki": "0.14.6",
 		"sass": "1.69.5",
@@ -73,7 +73,7 @@
 		"uuid": "9.0.1",
 		"v-code-diff": "1.7.2",
 		"vanilla-tilt": "1.8.1",
-		"vite": "5.0.7",
+		"vite": "5.0.8",
 		"vue": "3.3.11",
 		"vuedraggable": "next"
 	},
@@ -109,8 +109,8 @@
 		"@types/uuid": "9.0.7",
 		"@types/websocket": "1.0.10",
 		"@types/ws": "8.5.10",
-		"@typescript-eslint/eslint-plugin": "6.13.2",
-		"@typescript-eslint/parser": "6.13.2",
+		"@typescript-eslint/eslint-plugin": "6.14.0",
+		"@typescript-eslint/parser": "6.14.0",
 		"@vitest/coverage-v8": "0.34.6",
 		"@vue/runtime-core": "3.3.11",
 		"acorn": "8.11.2",
@@ -125,7 +125,7 @@
 		"msw": "1.3.2",
 		"msw-storybook-addon": "1.10.0",
 		"nodemon": "3.0.2",
-		"prettier": "3.1.0",
+		"prettier": "3.1.1",
 		"react": "18.2.0",
 		"react-dom": "18.2.0",
 		"start-server-and-test": "2.0.3",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 9c917e1b95..6a7712af90 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -25,8 +25,8 @@
 		"@swc/jest": "0.2.29",
 		"@types/jest": "29.5.11",
 		"@types/node": "20.10.4",
-		"@typescript-eslint/eslint-plugin": "6.13.2",
-		"@typescript-eslint/parser": "6.13.2",
+		"@typescript-eslint/eslint-plugin": "6.14.0",
+		"@typescript-eslint/parser": "6.14.0",
 		"eslint": "8.55.0",
 		"jest": "29.7.0",
 		"jest-fetch-mock": "3.0.3",
diff --git a/packages/sw/package.json b/packages/sw/package.json
index ffd7bfd8ba..039a6887aa 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -14,7 +14,7 @@
 		"misskey-js": "workspace:*"
 	},
 	"devDependencies": {
-		"@typescript-eslint/parser": "6.13.2",
+		"@typescript-eslint/parser": "6.14.0",
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
 		"eslint": "8.55.0",
 		"eslint-plugin-import": "2.29.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c2e3aff9b3..80b892652f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -36,11 +36,11 @@ importers:
         version: 4.4.0
     devDependencies:
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.13.2
-        version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3)
+        specifier: 6.14.0
+        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.13.2
-        version: 6.13.2(eslint@8.55.0)(typescript@5.3.3)
+        specifier: 6.14.0
+        version: 6.14.0(eslint@8.55.0)(typescript@5.3.3)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
@@ -103,10 +103,10 @@ importers:
         version: 8.2.0
       '@nestjs/common':
         specifier: 10.2.10
-        version: 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
+        version: 10.2.10(reflect-metadata@0.2.0)(rxjs@7.8.1)
       '@nestjs/core':
         specifier: 10.2.10
-        version: 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1)
+        version: 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.2.0)(rxjs@7.8.1)
       '@nestjs/testing':
         specifier: 10.2.10
         version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
@@ -150,8 +150,8 @@ importers:
         specifier: 1.20.2
         version: 1.20.2
       bullmq:
-        specifier: 4.15.2
-        version: 4.15.2
+        specifier: 4.15.3
+        version: 4.15.3
       cacheable-lookup:
         specifier: 7.0.0
         version: 7.0.0
@@ -183,8 +183,8 @@ importers:
         specifier: 0.1.21
         version: 0.1.21
       fastify:
-        specifier: 4.24.3
-        version: 4.24.3
+        specifier: 4.25.0
+        version: 4.25.0
       fastify-raw-body:
         specifier: 4.3.0
         version: 4.3.0
@@ -327,8 +327,8 @@ importers:
         specifier: 0.1.4
         version: 0.1.4
       reflect-metadata:
-        specifier: 0.1.14
-        version: 0.1.14
+        specifier: 0.2.0
+        version: 0.2.0
       rename:
         specifier: 1.0.4
         version: 1.0.4
@@ -396,8 +396,8 @@ importers:
         specifier: 3.6.6
         version: 3.6.6
       ws:
-        specifier: 8.14.2
-        version: 8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+        specifier: 8.15.1
+        version: 8.15.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       xev:
         specifier: 3.0.2
         version: 3.0.2
@@ -617,11 +617,11 @@ importers:
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.13.2
-        version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3)
+        specifier: 6.14.0
+        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.13.2
-        version: 6.13.2(eslint@8.55.0)(typescript@5.3.3)
+        specifier: 6.14.0
+        version: 6.14.0(eslint@8.55.0)(typescript@5.3.3)
       aws-sdk-client-mock:
         specifier: 3.0.0
         version: 3.0.0
@@ -633,7 +633,7 @@ importers:
         version: 8.55.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)
+        version: 2.29.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -657,28 +657,28 @@ importers:
         version: 2.1.1
       '@rollup/plugin-alias':
         specifier: 5.1.0
-        version: 5.1.0(rollup@4.7.0)
+        version: 5.1.0(rollup@4.9.0)
       '@rollup/plugin-json':
-        specifier: 6.0.1
-        version: 6.0.1(rollup@4.7.0)
+        specifier: 6.1.0
+        version: 6.1.0(rollup@4.9.0)
       '@rollup/plugin-replace':
         specifier: 5.0.5
-        version: 5.0.5(rollup@4.7.0)
+        version: 5.0.5(rollup@4.9.0)
       '@rollup/pluginutils':
         specifier: 5.1.0
-        version: 5.1.0(rollup@4.7.0)
+        version: 5.1.0(rollup@4.9.0)
       '@syuilo/aiscript':
         specifier: 0.16.0
         version: 0.16.0
       '@tabler/icons-webfont':
-        specifier: 2.37.0
-        version: 2.37.0
+        specifier: 2.44.0
+        version: 2.44.0
       '@vitejs/plugin-vue':
-        specifier: 4.5.1
-        version: 4.5.1(vite@5.0.7)(vue@3.3.11)
+        specifier: 4.5.2
+        version: 4.5.2(vite@5.0.8)(vue@3.3.11)
       '@vue/compiler-sfc':
-        specifier: 3.3.9
-        version: 3.3.9
+        specifier: 3.3.11
+        version: 3.3.11
       aiscript-vscode:
         specifier: github:aiscript-dev/aiscript-vscode#v0.0.6
         version: github.com/aiscript-dev/aiscript-vscode/b5a8aa0ad927831a0b867d1c183460a14e6c48cd
@@ -770,8 +770,8 @@ importers:
         specifier: 0.2.1
         version: 0.2.1
       rollup:
-        specifier: 4.7.0
-        version: 4.7.0
+        specifier: 4.9.0
+        version: 4.9.0
       sanitize-html:
         specifier: 2.11.0
         version: 2.11.0
@@ -818,8 +818,8 @@ importers:
         specifier: 1.8.1
         version: 1.8.1
       vite:
-        specifier: 5.0.7
-        version: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+        specifier: 5.0.8
+        version: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
       vue:
         specifier: 3.3.11
         version: 3.3.11(typescript@5.3.3)
@@ -865,7 +865,7 @@ importers:
         version: 7.6.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
       '@storybook/react-vite':
         specifier: 7.6.4
-        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)(rollup@4.7.0)(typescript@5.3.3)(vite@5.0.7)
+        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.0)(typescript@5.3.3)(vite@5.0.8)
       '@storybook/testing-library':
         specifier: 0.2.2
         version: 0.2.2
@@ -877,13 +877,13 @@ importers:
         version: 7.6.4
       '@storybook/vue3':
         specifier: 7.6.4
-        version: 7.6.4(@vue/compiler-core@3.3.9)(vue@3.3.11)
+        version: 7.6.4(@vue/compiler-core@3.3.11)(vue@3.3.11)
       '@storybook/vue3-vite':
         specifier: 7.6.4
-        version: 7.6.4(@vue/compiler-core@3.3.9)(typescript@5.3.3)(vite@5.0.7)(vue@3.3.11)
+        version: 7.6.4(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.8)(vue@3.3.11)
       '@testing-library/vue':
         specifier: 8.0.1
-        version: 8.0.1(@vue/compiler-sfc@3.3.9)(vue@3.3.11)
+        version: 8.0.1(@vue/compiler-sfc@3.3.11)(vue@3.3.11)
       '@types/escape-regexp':
         specifier: 0.0.3
         version: 0.0.3
@@ -921,11 +921,11 @@ importers:
         specifier: 8.5.10
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.13.2
-        version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3)
+        specifier: 6.14.0
+        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.13.2
-        version: 6.13.2(eslint@8.55.0)(typescript@5.3.3)
+        specifier: 6.14.0
+        version: 6.14.0(eslint@8.55.0)(typescript@5.3.3)
       '@vitest/coverage-v8':
         specifier: 0.34.6
         version: 0.34.6(vitest@0.34.6)
@@ -946,7 +946,7 @@ importers:
         version: 8.55.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)
+        version: 2.29.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)
       eslint-plugin-vue:
         specifier: 9.19.2
         version: 9.19.2(eslint@8.55.0)
@@ -969,8 +969,8 @@ importers:
         specifier: 3.0.2
         version: 3.0.2
       prettier:
-        specifier: 3.1.0
-        version: 3.1.0
+        specifier: 3.1.1
+        version: 3.1.1
       react:
         specifier: 18.2.0
         version: 18.2.0
@@ -985,7 +985,7 @@ importers:
         version: 7.6.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.6.4)(@storybook/components@7.6.3)(@storybook/core-events@7.6.4)(@storybook/manager-api@7.6.4)(@storybook/preview-api@7.6.4)(@storybook/theming@7.6.4)(@storybook/types@7.6.4)(react-dom@18.2.0)(react@18.2.0)
+        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.4)(@storybook/components@7.6.4)(@storybook/core-events@7.6.4)(@storybook/manager-api@7.6.4)(@storybook/preview-api@7.6.4)(@storybook/theming@7.6.4)(@storybook/types@7.6.4)(react-dom@18.2.0)(react@18.2.0)
       summaly:
         specifier: github:misskey-dev/summaly
         version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8
@@ -1033,11 +1033,11 @@ importers:
         specifier: 20.10.4
         version: 20.10.4
       '@typescript-eslint/eslint-plugin':
-        specifier: 6.13.2
-        version: 6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3)
+        specifier: 6.14.0
+        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
-        specifier: 6.13.2
-        version: 6.13.2(eslint@8.55.0)(typescript@5.3.3)
+        specifier: 6.14.0
+        version: 6.14.0(eslint@8.55.0)(typescript@5.3.3)
       eslint:
         specifier: 8.55.0
         version: 8.55.0
@@ -1109,8 +1109,8 @@ importers:
         version: link:../misskey-js
     devDependencies:
       '@typescript-eslint/parser':
-        specifier: 6.13.2
-        version: 6.13.2(eslint@8.55.0)(typescript@5.3.3)
+        specifier: 6.14.0
+        version: 6.14.0(eslint@8.55.0)(typescript@5.3.3)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: /@types/serviceworker@0.0.67
@@ -1119,7 +1119,7 @@ importers:
         version: 8.55.0
       eslint-plugin-import:
         specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)
+        version: 2.29.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)
       typescript:
         specifier: 5.3.3
         version: 5.3.3
@@ -2184,6 +2184,7 @@ packages:
     hasBin: true
     dependencies:
       '@babel/types': 7.22.17
+    dev: true
 
   /@babel/parser@7.23.5:
     resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==}
@@ -4146,7 +4147,7 @@ packages:
       '@fastify/reply-from': 9.0.1
       fast-querystring: 1.1.2
       fastify-plugin: 4.5.0
-      ws: 8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      ws: 8.15.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - bufferutil
       - utf-8-validate
@@ -4592,7 +4593,7 @@ packages:
       chalk: 4.1.2
     dev: true
 
-  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.0.7):
+  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.0.8):
     resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==}
     peerDependencies:
       typescript: '>= 4.3.x'
@@ -4606,7 +4607,7 @@ packages:
       magic-string: 0.27.0
       react-docgen-typescript: 2.2.2(typescript@5.3.3)
       typescript: 5.3.3
-      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
     dev: true
 
   /@jridgewell/gen-mapping@0.3.2:
@@ -4832,7 +4833,7 @@ packages:
       tar-fs: 2.1.1
     dev: true
 
-  /@nestjs/common@10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1):
+  /@nestjs/common@10.2.10(reflect-metadata@0.2.0)(rxjs@7.8.1):
     resolution: {integrity: sha512-fwAk931rjW8CNH2Mgwawq/7HWHH1dxkOLdcgs7U52ddLk8CtHXjejm1cbNahewlSbNhvlOl7y1STLHutE6sUqw==}
     peerDependencies:
       class-transformer: '*'
@@ -4846,13 +4847,13 @@ packages:
         optional: true
     dependencies:
       iterare: 1.2.1
-      reflect-metadata: 0.1.14
+      reflect-metadata: 0.2.0
       rxjs: 7.8.1
       tslib: 2.6.2
       uid: 2.0.2
     dev: false
 
-  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1):
+  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.2.0)(rxjs@7.8.1):
     resolution: {integrity: sha512-+ckOI6BPi2ZMHikT9MCG4ctHDc4OnjhoIytrn7f2AYMMXI4bnutJhqyQKc30VDka5x3Wq6QAD57pgSP7y+JjJg==}
     requiresBuild: true
     peerDependencies:
@@ -4870,12 +4871,12 @@ packages:
       '@nestjs/websockets':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      '@nestjs/common': 10.2.10(reflect-metadata@0.2.0)(rxjs@7.8.1)
       '@nuxtjs/opencollective': 0.3.2
       fast-safe-stringify: 2.1.1
       iterare: 1.2.1
       path-to-regexp: 3.2.0
-      reflect-metadata: 0.1.14
+      reflect-metadata: 0.2.0
       rxjs: 7.8.1
       tslib: 2.6.2
       uid: 2.0.2
@@ -4896,8 +4897,8 @@ packages:
       '@nestjs/platform-express':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
-      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      '@nestjs/common': 10.2.10(reflect-metadata@0.2.0)(rxjs@7.8.1)
+      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.2.0)(rxjs@7.8.1)
       tslib: 2.6.2
     dev: false
 
@@ -5555,7 +5556,7 @@ packages:
       '@babel/runtime': 7.23.2
     dev: true
 
-  /@rollup/plugin-alias@5.1.0(rollup@4.7.0):
+  /@rollup/plugin-alias@5.1.0(rollup@4.9.0):
     resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5564,12 +5565,12 @@ packages:
       rollup:
         optional: true
     dependencies:
-      rollup: 4.7.0
+      rollup: 4.9.0
       slash: 4.0.0
     dev: false
 
-  /@rollup/plugin-json@6.0.1(rollup@4.7.0):
-    resolution: {integrity: sha512-RgVfl5hWMkxN1h/uZj8FVESvPuBJ/uf6ly6GTj0GONnkfoBN5KC0MSz+PN2OLDgYXMhtG0mWpTrkiOjoxAIevw==}
+  /@rollup/plugin-json@6.1.0(rollup@4.9.0):
+    resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
       rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
@@ -5577,11 +5578,11 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.7.0)
-      rollup: 4.7.0
+      '@rollup/pluginutils': 5.1.0(rollup@4.9.0)
+      rollup: 4.9.0
     dev: false
 
-  /@rollup/plugin-replace@5.0.5(rollup@4.7.0):
+  /@rollup/plugin-replace@5.0.5(rollup@4.9.0):
     resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5590,12 +5591,12 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.7.0)
+      '@rollup/pluginutils': 5.1.0(rollup@4.9.0)
       magic-string: 0.30.5
-      rollup: 4.7.0
+      rollup: 4.9.0
     dev: false
 
-  /@rollup/pluginutils@5.1.0(rollup@4.7.0):
+  /@rollup/pluginutils@5.1.0(rollup@4.9.0):
     resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5607,94 +5608,94 @@ packages:
       '@types/estree': 1.0.5
       estree-walker: 2.0.2
       picomatch: 2.3.1
-      rollup: 4.7.0
+      rollup: 4.9.0
 
-  /@rollup/rollup-android-arm-eabi@4.7.0:
-    resolution: {integrity: sha512-rGku10pL1StFlFvXX5pEv88KdGW6DHUghsxyP/aRYb9eH+74jTGJ3U0S/rtlsQ4yYq1Hcc7AMkoJOb1xu29Fxw==}
+  /@rollup/rollup-android-arm-eabi@4.9.0:
+    resolution: {integrity: sha512-+1ge/xmaJpm1KVBuIH38Z94zj9fBD+hp+/5WLaHgyY8XLq1ibxk/zj6dTXaqM2cAbYKq8jYlhHd6k05If1W5xA==}
     cpu: [arm]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-android-arm64@4.7.0:
-    resolution: {integrity: sha512-/EBw0cuJ/KVHiU2qyVYUhogXz7W2vXxBzeE9xtVIMC+RyitlY2vvaoysMUqASpkUtoNIHlnKTu/l7mXOPgnKOA==}
+  /@rollup/rollup-android-arm64@4.9.0:
+    resolution: {integrity: sha512-im6hUEyQ7ZfoZdNvtwgEJvBWZYauC9KVKq1w58LG2Zfz6zMd8gRrbN+xCVoqA2hv/v6fm9lp5LFGJ3za8EQH3A==}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-arm64@4.7.0:
-    resolution: {integrity: sha512-4VXG1bgvClJdbEYYjQ85RkOtwN8sqI3uCxH0HC5w9fKdqzRzgG39K7GAehATGS8jghA7zNoS5CjSKkDEqWmNZg==}
+  /@rollup/rollup-darwin-arm64@4.9.0:
+    resolution: {integrity: sha512-u7aTMskN6Dmg1lCT0QJ+tINRt+ntUrvVkhbPfFz4bCwRZvjItx2nJtwJnJRlKMMaQCHRjrNqHRDYvE4mBm3DlQ==}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-x64@4.7.0:
-    resolution: {integrity: sha512-/ImhO+T/RWJ96hUbxiCn2yWI0/MeQZV/aeukQQfhxiSXuZJfyqtdHPUPrc84jxCfXTxbJLmg4q+GBETeb61aNw==}
+  /@rollup/rollup-darwin-x64@4.9.0:
+    resolution: {integrity: sha512-8FvEl3w2ExmpcOmX5RJD0yqXcVSOqAJJUJ29Lca29Ik+3zPS1yFimr2fr5JSZ4Z5gt8/d7WqycpgkX9nocijSw==}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm-gnueabihf@4.7.0:
-    resolution: {integrity: sha512-zhye8POvTyUXlKbfPBVqoHy3t43gIgffY+7qBFqFxNqVtltQLtWeHNAbrMnXiLIfYmxcoL/feuLDote2tx+Qbg==}
+  /@rollup/rollup-linux-arm-gnueabihf@4.9.0:
+    resolution: {integrity: sha512-lHoKYaRwd4gge+IpqJHCY+8Vc3hhdJfU6ukFnnrJasEBUvVlydP8PuwndbWfGkdgSvZhHfSEw6urrlBj0TSSfg==}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-gnu@4.7.0:
-    resolution: {integrity: sha512-RAdr3OJnUum6Vs83cQmKjxdTg31zJnLLTkjhcFt0auxM6jw00GD6IPFF42uasYPr/wGC6TRm7FsQiJyk0qIEfg==}
+  /@rollup/rollup-linux-arm64-gnu@4.9.0:
+    resolution: {integrity: sha512-JbEPfhndYeWHfOSeh4DOFvNXrj7ls9S/2omijVsao+LBPTPayT1uKcK3dHW3MwDJ7KO11t9m2cVTqXnTKpeaiw==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-musl@4.7.0:
-    resolution: {integrity: sha512-nhWwYsiJwZGq7SyR3afS3EekEOsEAlrNMpPC4ZDKn5ooYSEjDLe9W/xGvoIV8/F/+HNIY6jY8lIdXjjxfxopXw==}
+  /@rollup/rollup-linux-arm64-musl@4.9.0:
+    resolution: {integrity: sha512-ahqcSXLlcV2XUBM3/f/C6cRoh7NxYA/W7Yzuv4bDU1YscTFw7ay4LmD7l6OS8EMhTNvcrWGkEettL1Bhjf+B+w==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-riscv64-gnu@4.7.0:
-    resolution: {integrity: sha512-rlfy5RnQG1aop1BL/gjdH42M2geMUyVQqd52GJVirqYc787A/XVvl3kQ5NG/43KXgOgE9HXgCaEH05kzQ+hLoA==}
+  /@rollup/rollup-linux-riscv64-gnu@4.9.0:
+    resolution: {integrity: sha512-uwvOYNtLw8gVtrExKhdFsYHA/kotURUmZYlinH2VcQxNCQJeJXnkmWgw2hI9Xgzhgu7J9QvWiq9TtTVwWMDa+w==}
     cpu: [riscv64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-gnu@4.7.0:
-    resolution: {integrity: sha512-cCkoGlGWfBobdDtiiypxf79q6k3/iRVGu1HVLbD92gWV5WZbmuWJCgRM4x2N6i7ljGn1cGytPn9ZAfS8UwF6vg==}
+  /@rollup/rollup-linux-x64-gnu@4.9.0:
+    resolution: {integrity: sha512-m6pkSwcZZD2LCFHZX/zW2aLIISyzWLU3hrLLzQKMI12+OLEzgruTovAxY5sCZJkipklaZqPy/2bEEBNjp+Y7xg==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-musl@4.7.0:
-    resolution: {integrity: sha512-R2oBf2p/Arc1m+tWmiWbpHBjEcJnHVnv6bsypu4tcKdrYTpDfl1UT9qTyfkIL1iiii5D4WHxUHCg5X0pzqmxFg==}
+  /@rollup/rollup-linux-x64-musl@4.9.0:
+    resolution: {integrity: sha512-VFAC1RDRSbU3iOF98X42KaVicAfKf0m0OvIu8dbnqhTe26Kh6Ym9JrDulz7Hbk7/9zGc41JkV02g+p3BivOdAg==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-arm64-msvc@4.7.0:
-    resolution: {integrity: sha512-CPtgaQL1aaPc80m8SCVEoxFGHxKYIt3zQYC3AccL/SqqiWXblo3pgToHuBwR8eCP2Toa+X1WmTR/QKFMykws7g==}
+  /@rollup/rollup-win32-arm64-msvc@4.9.0:
+    resolution: {integrity: sha512-9jPgMvTKXARz4inw6jezMLA2ihDBvgIU9Ml01hjdVpOcMKyxFBJrn83KVQINnbeqDv0+HdO1c09hgZ8N0s820Q==}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-ia32-msvc@4.7.0:
-    resolution: {integrity: sha512-pmioUlttNh9GXF5x2CzNa7Z8kmRTyhEzzAC+2WOOapjewMbl+3tGuAnxbwc5JyG8Jsz2+hf/QD/n5VjimOZ63g==}
+  /@rollup/rollup-win32-ia32-msvc@4.9.0:
+    resolution: {integrity: sha512-WE4pT2kTXQN2bAv40Uog0AsV7/s9nT9HBWXAou8+++MBCnY51QS02KYtm6dQxxosKi1VIz/wZIrTQO5UP2EW+Q==}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-x64-msvc@4.7.0:
-    resolution: {integrity: sha512-SeZzC2QhhdBQUm3U0c8+c/P6UlRyBcLL2Xp5KX7z46WXZxzR8RJSIWL9wSUeBTgxog5LTPJuPj0WOT9lvrtP7Q==}
+  /@rollup/rollup-win32-x64-msvc@4.9.0:
+    resolution: {integrity: sha512-aPP5Q5AqNGuT0tnuEkK/g4mnt3ZhheiXrDIiSVIHN9mcN21OyXDVbEMqmXPE7e2OplNLDkcvV+ZoGJa2ZImFgw==}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
@@ -6527,7 +6528,7 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-vite@7.6.4(typescript@5.3.3)(vite@5.0.7):
+  /@storybook/builder-vite@7.6.4(typescript@5.3.3)(vite@5.0.8):
     resolution: {integrity: sha512-eqb3mLUfuXd4a7+46cWevQ9qH81FvHy1lrAbZGwp4bQ/Tj0YF8Ej7lKBbg7zoIwiu2zDci+BbMiaDOY1kPtILw==}
     peerDependencies:
       '@preact/preset-vite': '*'
@@ -6559,23 +6560,12 @@ packages:
       magic-string: 0.30.5
       rollup: 3.29.4
       typescript: 5.3.3
-      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - encoding
       - supports-color
     dev: true
 
-  /@storybook/channels@7.6.3:
-    resolution: {integrity: sha512-o9J0TBbFon16tUlU5V6kJgzAlsloJcS1cTHWqh3VWczohbRm+X1PLNUihJ7Q8kBWXAuuJkgBu7RQH7Ib46WyYg==}
-    dependencies:
-      '@storybook/client-logger': 7.6.3
-      '@storybook/core-events': 7.6.3
-      '@storybook/global': 5.0.0
-      qs: 6.11.1
-      telejson: 7.2.0
-      tiny-invariant: 1.3.1
-    dev: true
-
   /@storybook/channels@7.6.4:
     resolution: {integrity: sha512-Z4PY09/Czl70ap4ObmZ4bgin+EQhPaA3HdrEDNwpnH7A9ttfEO5u5KThytIjMq6kApCCihmEPDaYltoVrfYJJA==}
     dependencies:
@@ -6639,12 +6629,6 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/client-logger@7.6.3:
-    resolution: {integrity: sha512-BpsCnefrBFdxD6ukMjAblm1D6zB4U5HR1I85VWw6LOqZrfzA6l/1uBxItz0XG96HTjngbvAabWf5k7ZFCx5UCg==}
-    dependencies:
-      '@storybook/global': 5.0.0
-    dev: true
-
   /@storybook/client-logger@7.6.4:
     resolution: {integrity: sha512-vJwMShC98tcoFruRVQ4FphmFqvAZX1FqZqjFyk6IxtFumPKTVSnXJjlU1SnUIkSK2x97rgdUMqkdI+wAv/tugQ==}
     dependencies:
@@ -6672,29 +6656,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/components@7.6.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-UNV0WoUo+W0huOLvoEMuqRN/VB4p0CNswrXN1mi/oGWvAFJ8idu63lSuV4uQ/LKxAZ6v3Kpdd+oK/o+OeOoL6w==}
-    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.6.3
-      '@storybook/csf': 0.1.2
-      '@storybook/global': 5.0.0
-      '@storybook/theming': 7.6.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.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.6.4(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-K5RvEObJAnX+SbGJbkM1qrZEk+VR2cUhRCSrFnlfMwsn8/60T3qoH7U8bCXf8krDgbquhMwqev5WzDB+T1VV8g==}
     peerDependencies:
@@ -6756,12 +6717,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/core-events@7.6.3:
-    resolution: {integrity: sha512-Vu3JX1mjtR8AX84lyqWsi2s2lhD997jKRWVznI3wx+UpTk8t7TTMLFk2rGYJRjaornhrqwvLYpnmtxRSxW9BOQ==}
-    dependencies:
-      ts-dedent: 2.2.0
-    dev: true
-
   /@storybook/core-events@7.6.4:
     resolution: {integrity: sha512-i3xzcJ19ILSy4oJL5Dz9y0IlyApynn5RsGhAMIsW+mcfri+hGfeakq1stNCo0o7jW4Y3A7oluFTtIoK8DOxQdQ==}
     dependencies:
@@ -6811,7 +6766,7 @@ packages:
       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)
+      ws: 8.15.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
     transitivePeerDependencies:
       - bufferutil
       - encoding
@@ -6964,7 +6919,7 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/react-vite@7.6.4(react-dom@18.2.0)(react@18.2.0)(rollup@4.7.0)(typescript@5.3.3)(vite@5.0.7):
+  /@storybook/react-vite@7.6.4(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.0)(typescript@5.3.3)(vite@5.0.8):
     resolution: {integrity: sha512-1NYzCJRO6k/ZyoMzpu1FQiaUaiLNjAvTAB1x3HE7oY/tEIT8kGpzXGYH++LJVWvyP/5dSWlUnRSy2rJvySraiw==}
     engines: {node: '>=16'}
     peerDependencies:
@@ -6972,16 +6927,16 @@ packages:
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
       vite: ^3.0.0 || ^4.0.0 || ^5.0.0
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.0.7)
-      '@rollup/pluginutils': 5.1.0(rollup@4.7.0)
-      '@storybook/builder-vite': 7.6.4(typescript@5.3.3)(vite@5.0.7)
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.0.8)
+      '@rollup/pluginutils': 5.1.0(rollup@4.9.0)
+      '@storybook/builder-vite': 7.6.4(typescript@5.3.3)(vite@5.0.8)
       '@storybook/react': 7.6.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
-      '@vitejs/plugin-react': 3.1.0(vite@5.0.7)
+      '@vitejs/plugin-react': 3.1.0(vite@5.0.8)
       magic-string: 0.30.5
       react: 18.2.0
       react-docgen: 7.0.1
       react-dom: 18.2.0(react@18.2.0)
-      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - encoding
@@ -7073,20 +7028,6 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/theming@7.6.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-9ToNU2LM6a2kVBjOXitXEeEOuMurVLhn+uaZO1dJjv8NGnJVYiLwNPwrLsImiUD8/XXNuil972aanBR6+Aj9jw==}
-    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.6.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.6.4(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-Z/dcC5EpkIXelYCkt9ojnX6D7qGOng8YHxV/OWlVE9TrEGYVGPOEfwQryR0RhmGpDha1TYESLYrsDb4A8nJ1EA==}
     peerDependencies:
@@ -7101,15 +7042,6 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/types@7.6.3:
-    resolution: {integrity: sha512-vj9Jzg5eR52l8O9512QywbQpNdo67Z6BQWR8QoZRcG+/Bhzt08YI8IZMPQLFMKzcmWDPK0blQ4GfyKDYplMjPA==}
-    dependencies:
-      '@storybook/channels': 7.6.3
-      '@types/babel__core': 7.20.0
-      '@types/express': 4.17.17
-      file-system-cache: 2.3.0
-    dev: true
-
   /@storybook/types@7.6.4:
     resolution: {integrity: sha512-qyiiXPCvol5uVgfubcIMzJBA0awAyFPU+TyUP1mkPYyiTHnsHYel/mKlSdPjc8a97N3SlJXHOCx41Hde4IyJgg==}
     dependencies:
@@ -7119,18 +7051,18 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.6.4(@vue/compiler-core@3.3.9)(typescript@5.3.3)(vite@5.0.7)(vue@3.3.11):
+  /@storybook/vue3-vite@7.6.4(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.8)(vue@3.3.11):
     resolution: {integrity: sha512-M1cT6lZsRqwws+H+pv2K/jJGiXUfGHAz7nc0DwK/YYT/w0OOVp5hinh/IvNwIYW5zUmwIyQVEoGrrxQEi0S79w==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
       vite: ^3.0.0 || ^4.0.0 || ^5.0.0
     dependencies:
-      '@storybook/builder-vite': 7.6.4(typescript@5.3.3)(vite@5.0.7)
+      '@storybook/builder-vite': 7.6.4(typescript@5.3.3)(vite@5.0.8)
       '@storybook/core-server': 7.6.4
-      '@storybook/vue3': 7.6.4(@vue/compiler-core@3.3.9)(vue@3.3.11)
-      '@vitejs/plugin-vue': 4.5.1(vite@5.0.7)(vue@3.3.11)
+      '@storybook/vue3': 7.6.4(@vue/compiler-core@3.3.11)(vue@3.3.11)
+      '@vitejs/plugin-vue': 4.5.2(vite@5.0.8)(vue@3.3.11)
       magic-string: 0.30.5
-      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
       vue-docgen-api: 4.64.1(vue@3.3.11)
     transitivePeerDependencies:
       - '@preact/preset-vite'
@@ -7144,7 +7076,7 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.6.4(@vue/compiler-core@3.3.9)(vue@3.3.11):
+  /@storybook/vue3@7.6.4(@vue/compiler-core@3.3.11)(vue@3.3.11):
     resolution: {integrity: sha512-9BYxqj9C30/7UBspP4sE0UE/4fgE17jymJHWkxFbEE95MfbLkDFRnJ3Y09VPK0OnmbsBW5xBciMyjGV9Lq2pmg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
@@ -7156,7 +7088,7 @@ packages:
       '@storybook/global': 5.0.0
       '@storybook/preview-api': 7.6.4
       '@storybook/types': 7.6.4
-      '@vue/compiler-core': 3.3.9
+      '@vue/compiler-core': 3.3.11
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
@@ -7439,14 +7371,14 @@ packages:
     dependencies:
       defer-to-connect: 2.0.1
 
-  /@tabler/icons-webfont@2.37.0:
-    resolution: {integrity: sha512-0pdo3V9l1gT0tUg+0L2CM6Xa+yTQf14XHNpXgANWBsk6iyDDrGmbW1Qq6FkQ3mVAU0D+Gd4dp34uB0s4pIZpOw==}
+  /@tabler/icons-webfont@2.44.0:
+    resolution: {integrity: sha512-E5+CYnZXKTUGFhpb0KGiRR4s+Tylbsq6/CA88QKn44xaBfU3ui/zVwKc1vaTKBL9kxZIxNz7qLkne+lUd/Hvzw==}
     dependencies:
-      '@tabler/icons': 2.37.0
+      '@tabler/icons': 2.44.0
     dev: false
 
-  /@tabler/icons@2.37.0:
-    resolution: {integrity: sha512-Qo/aRhs2xXcD8IDSuePonR/39GSvQUdwv6RXdbcAhMfpJZP+pHRn2TQFPNGJND9B5uUBez694j4Nx71539XHGA==}
+  /@tabler/icons@2.44.0:
+    resolution: {integrity: sha512-WPPtihDcAwEm1QZM9MXQw6+r/R2/qx7KMU1eegsi9DsqBLAb0W2kbt6e/syvd6j9c+6XNpRVBW1ziGqSWQAWOg==}
     dev: false
 
   /@tensorflow/tfjs-backend-cpu@4.4.0(@tensorflow/tfjs-core@4.4.0):
@@ -7635,7 +7567,7 @@ packages:
       '@testing-library/dom': 9.2.0
     dev: true
 
-  /@testing-library/vue@8.0.1(@vue/compiler-sfc@3.3.9)(vue@3.3.11):
+  /@testing-library/vue@8.0.1(@vue/compiler-sfc@3.3.11)(vue@3.3.11):
     resolution: {integrity: sha512-l51ZEpjTQ6glq3wM+asQ1GbKJMGcxwgHEygETx0aCRN4TjFEGvMZy4YdWKs/y7bu4bmLrxcxhbEPP7iPSW/2OQ==}
     engines: {node: '>=14'}
     peerDependencies:
@@ -7644,7 +7576,7 @@ packages:
     dependencies:
       '@babel/runtime': 7.23.2
       '@testing-library/dom': 9.3.3
-      '@vue/compiler-sfc': 3.3.9
+      '@vue/compiler-sfc': 3.3.11
       '@vue/test-utils': 2.4.1(vue@3.3.11)
       vue: 3.3.11(typescript@5.3.3)
     transitivePeerDependencies:
@@ -8311,8 +8243,8 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/eslint-plugin@6.13.2(@typescript-eslint/parser@6.13.2)(eslint@8.55.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-3+9OGAWHhk4O1LlcwLBONbdXsAhLjyCFogJY/cWy2lxdVJ2JrcTF2pTGMaLl2AE7U1l31n8Py4a8bx5DLf/0dQ==}
+  /@typescript-eslint/eslint-plugin@6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-1ZJBykBCXaSHG94vMMKmiHoL0MhNHKSVlcHVYZNw+BKxufhqQVTOawNpwwI1P5nIFZ/4jLVop0mcY6mJJDFNaw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
@@ -8323,11 +8255,11 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
-      '@typescript-eslint/scope-manager': 6.13.2
-      '@typescript-eslint/type-utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
-      '@typescript-eslint/visitor-keys': 6.13.2
+      '@typescript-eslint/parser': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/scope-manager': 6.14.0
+      '@typescript-eslint/type-utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/visitor-keys': 6.14.0
       debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.55.0
       graphemer: 1.4.0
@@ -8361,8 +8293,8 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.13.2(eslint@8.55.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-MUkcC+7Wt/QOGeVlM8aGGJZy1XV5YKjTpq9jK6r6/iLsGXhBVaGP5N0UYvFsu9BFlSpwY9kMretzdBH01rkRXg==}
+  /@typescript-eslint/parser@6.14.0(eslint@8.55.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-QjToC14CKacd4Pa7JK4GeB/vHmWFJckec49FR4hmIRf97+KXole0T97xxu9IFiPxVQ1DBWrQ5wreLwAGwWAVQA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
@@ -8371,10 +8303,10 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/scope-manager': 6.13.2
-      '@typescript-eslint/types': 6.13.2
-      '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3)
-      '@typescript-eslint/visitor-keys': 6.13.2
+      '@typescript-eslint/scope-manager': 6.14.0
+      '@typescript-eslint/types': 6.14.0
+      '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3)
+      '@typescript-eslint/visitor-keys': 6.14.0
       debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.55.0
       typescript: 5.3.3
@@ -8390,12 +8322,12 @@ packages:
       '@typescript-eslint/visitor-keys': 6.11.0
     dev: true
 
-  /@typescript-eslint/scope-manager@6.13.2:
-    resolution: {integrity: sha512-CXQA0xo7z6x13FeDYCgBkjWzNqzBn8RXaE3QVQVIUm74fWJLkJkaHmHdKStrxQllGh6Q4eUGyNpMe0b1hMkXFA==}
+  /@typescript-eslint/scope-manager@6.14.0:
+    resolution: {integrity: sha512-VT7CFWHbZipPncAZtuALr9y3EuzY1b1t1AEkIq2bTXUPKw+pHoXflGNG5L+Gv6nKul1cz1VH8fz16IThIU0tdg==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dependencies:
-      '@typescript-eslint/types': 6.13.2
-      '@typescript-eslint/visitor-keys': 6.13.2
+      '@typescript-eslint/types': 6.14.0
+      '@typescript-eslint/visitor-keys': 6.14.0
     dev: true
 
   /@typescript-eslint/type-utils@6.11.0(eslint@8.53.0)(typescript@5.3.3):
@@ -8418,8 +8350,8 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/type-utils@6.13.2(eslint@8.55.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-Qr6ssS1GFongzH2qfnWKkAQmMUyZSyOr0W54nZNU1MDfo+U4Mv3XveeLZzadc/yq8iYhQZHYT+eoXJqnACM1tw==}
+  /@typescript-eslint/type-utils@6.14.0(eslint@8.55.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-x6OC9Q7HfYKqjnuNu5a7kffIYs3No30isapRBJl1iCHLitD8O0lFbRcVGiOcuyN837fqXzPZ1NS10maQzZMKqw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
@@ -8428,8 +8360,8 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
       debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.55.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
@@ -8443,8 +8375,8 @@ packages:
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
-  /@typescript-eslint/types@6.13.2:
-    resolution: {integrity: sha512-7sxbQ+EMRubQc3wTfTsycgYpSujyVbI1xw+3UMRUcrhSy+pN09y/lWzeKDbvhoqcRbHdc+APLs/PWYi/cisLPg==}
+  /@typescript-eslint/types@6.14.0:
+    resolution: {integrity: sha512-uty9H2K4Xs8E47z3SnXEPRNDfsis8JO27amp2GNCnzGETEW3yTqEIVg5+AI7U276oGF/tw6ZA+UesxeQ104ceA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dev: true
 
@@ -8469,8 +8401,8 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/typescript-estree@6.13.2(typescript@5.3.3):
-    resolution: {integrity: sha512-SuD8YLQv6WHnOEtKv8D6HZUzOub855cfPnPMKvdM/Bh1plv1f7Q/0iFUDLKKlxHcEstQnaUU4QZskgQq74t+3w==}
+  /@typescript-eslint/typescript-estree@6.14.0(typescript@5.3.3):
+    resolution: {integrity: sha512-yPkaLwK0yH2mZKFE/bXkPAkkFgOv15GJAUzgUVonAbv0Hr4PK/N2yaA/4XQbTZQdygiDkpt5DkxPELqHguNvyw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       typescript: '*'
@@ -8478,8 +8410,8 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@typescript-eslint/types': 6.13.2
-      '@typescript-eslint/visitor-keys': 6.13.2
+      '@typescript-eslint/types': 6.14.0
+      '@typescript-eslint/visitor-keys': 6.14.0
       debug: 4.3.4(supports-color@5.5.0)
       globby: 11.1.0
       is-glob: 4.0.3
@@ -8509,8 +8441,8 @@ packages:
       - typescript
     dev: true
 
-  /@typescript-eslint/utils@6.13.2(eslint@8.55.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-b9Ptq4eAZUym4idijCRzl61oPCwwREcfDI8xGk751Vhzig5fFZR9CyzDz4Sp/nxSLBYxUPyh4QdIDqWykFhNmQ==}
+  /@typescript-eslint/utils@6.14.0(eslint@8.55.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-XwRTnbvRr7Ey9a1NT6jqdKX8y/atWG+8fAIu3z73HSP8h06i3r/ClMhmaF/RGWGW1tHJEwij1uEg2GbEmPYvYg==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
       eslint: ^7.0.0 || ^8.0.0
@@ -8518,9 +8450,9 @@ packages:
       '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.6
-      '@typescript-eslint/scope-manager': 6.13.2
-      '@typescript-eslint/types': 6.13.2
-      '@typescript-eslint/typescript-estree': 6.13.2(typescript@5.3.3)
+      '@typescript-eslint/scope-manager': 6.14.0
+      '@typescript-eslint/types': 6.14.0
+      '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3)
       eslint: 8.55.0
       semver: 7.5.4
     transitivePeerDependencies:
@@ -8536,11 +8468,11 @@ packages:
       eslint-visitor-keys: 3.4.3
     dev: true
 
-  /@typescript-eslint/visitor-keys@6.13.2:
-    resolution: {integrity: sha512-OGznFs0eAQXJsp+xSd6k/O1UbFi/K/L7WjqeRoFE7vadjAF9y0uppXhYNQNEqygjou782maGClOoZwPqF0Drlw==}
+  /@typescript-eslint/visitor-keys@6.14.0:
+    resolution: {integrity: sha512-fB5cw6GRhJUz03MrROVuj5Zm/Q+XWlVdIsFj+Zb1Hvqouc8t+XP2H5y53QYU/MGtd2dPg6/vJJlhoX3xc2ehfw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     dependencies:
-      '@typescript-eslint/types': 6.13.2
+      '@typescript-eslint/types': 6.14.0
       eslint-visitor-keys: 3.4.3
     dev: true
 
@@ -8548,7 +8480,7 @@ packages:
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
     dev: true
 
-  /@vitejs/plugin-react@3.1.0(vite@5.0.7):
+  /@vitejs/plugin-react@3.1.0(vite@5.0.8):
     resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
@@ -8559,19 +8491,19 @@ packages:
       '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.23.5)
       magic-string: 0.27.0
       react-refresh: 0.14.0
-      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@vitejs/plugin-vue@4.5.1(vite@5.0.7)(vue@3.3.11):
-    resolution: {integrity: sha512-DaUzYFr+2UGDG7VSSdShKa9sIWYBa1LL8KC0MNOf2H5LjcTPjob0x8LbkqXWmAtbANJCkpiQTj66UVcQkN2s3g==}
+  /@vitejs/plugin-vue@4.5.2(vite@5.0.8)(vue@3.3.11):
+    resolution: {integrity: sha512-UGR3DlzLi/SaVBPX0cnSyE37vqxU3O6chn8l0HJNzQzDia6/Au2A4xKv+iIJW8w2daf80G7TYHhi1pAUjdZ0bQ==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
       vite: ^4.0.0 || ^5.0.0
       vue: ^3.2.25
     dependencies:
-      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
       vue: 3.3.11(typescript@5.3.3)
 
   /@vitest/coverage-v8@0.34.6(vitest@0.34.6):
@@ -8667,6 +8599,7 @@ packages:
       '@vue/shared': 3.3.9
       estree-walker: 2.0.2
       source-map-js: 1.0.2
+    dev: true
 
   /@vue/compiler-dom@3.3.11:
     resolution: {integrity: sha512-zoAiUIqSKqAJ81WhfPXYmFGwDRuO+loqLxvXmfUdR5fOitPoUiIeFI9cTTyv9MU5O1+ZZglJVTusWzy+wfk5hw==}
@@ -8679,6 +8612,7 @@ packages:
     dependencies:
       '@vue/compiler-core': 3.3.9
       '@vue/shared': 3.3.9
+    dev: true
 
   /@vue/compiler-sfc@3.3.11:
     resolution: {integrity: sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==}
@@ -8694,32 +8628,12 @@ packages:
       postcss: 8.4.32
       source-map-js: 1.0.2
 
-  /@vue/compiler-sfc@3.3.9:
-    resolution: {integrity: sha512-wy0CNc8z4ihoDzjASCOCsQuzW0A/HP27+0MDSSICMjVIFzk/rFViezkR3dzH+miS2NDEz8ywMdbjO5ylhOLI2A==}
-    dependencies:
-      '@babel/parser': 7.23.3
-      '@vue/compiler-core': 3.3.9
-      '@vue/compiler-dom': 3.3.9
-      '@vue/compiler-ssr': 3.3.9
-      '@vue/reactivity-transform': 3.3.9
-      '@vue/shared': 3.3.9
-      estree-walker: 2.0.2
-      magic-string: 0.30.5
-      postcss: 8.4.32
-      source-map-js: 1.0.2
-
   /@vue/compiler-ssr@3.3.11:
     resolution: {integrity: sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==}
     dependencies:
       '@vue/compiler-dom': 3.3.11
       '@vue/shared': 3.3.11
 
-  /@vue/compiler-ssr@3.3.9:
-    resolution: {integrity: sha512-NO5oobAw78R0G4SODY5A502MGnDNiDjf6qvhn7zD7TJGc8XDeIEw4fg6JU705jZ/YhuokBKz0A5a/FL/XZU73g==}
-    dependencies:
-      '@vue/compiler-dom': 3.3.9
-      '@vue/shared': 3.3.9
-
   /@vue/language-core@1.8.25(typescript@5.3.3):
     resolution: {integrity: sha512-NJk/5DnAZlpvXX8BdWmHI45bWGLViUaS3R/RMrmFSvFMSbJKuEODpM4kR0F0Ofv5SFzCWuNiMhxameWpVdQsnA==}
     peerDependencies:
@@ -8749,15 +8663,6 @@ packages:
       estree-walker: 2.0.2
       magic-string: 0.30.5
 
-  /@vue/reactivity-transform@3.3.9:
-    resolution: {integrity: sha512-HnUFm7Ry6dFa4Lp63DAxTixUp8opMtQr6RxQCpDI1vlh12rkGIeYqMvJtK+IKyEfEOa2I9oCkD1mmsPdaGpdVg==}
-    dependencies:
-      '@babel/parser': 7.23.3
-      '@vue/compiler-core': 3.3.9
-      '@vue/shared': 3.3.9
-      estree-walker: 2.0.2
-      magic-string: 0.30.5
-
   /@vue/reactivity@3.3.11:
     resolution: {integrity: sha512-D5tcw091f0nuu+hXq5XANofD0OXnBmaRqMYl5B3fCR+mX+cXJIGNw/VNawBqkjLNWETrFW0i+xH9NvDbTPVh7g==}
     dependencies:
@@ -8790,6 +8695,7 @@ packages:
 
   /@vue/shared@3.3.9:
     resolution: {integrity: sha512-ZE0VTIR0LmYgeyhurPTpy4KzKsuDyQbMSdM49eKkMnT5X4VfFBLysMzjIZhLEFQYjjOVVfbvUDHckwjDFiO2eA==}
+    dev: true
 
   /@vue/test-utils@2.4.1(vue@3.3.11):
     resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==}
@@ -9727,8 +9633,8 @@ packages:
     dependencies:
       node-gyp-build: 4.6.0
 
-  /bullmq@4.15.2:
-    resolution: {integrity: sha512-VzmeiTP2ze7R3d/prOkwkoPyVxWm0N+Aj2lYQqljbWX7QqZgCaZCMiDVj97YpxFExdHDGKnmRPIS/ANY86GX2g==}
+  /bullmq@4.15.3:
+    resolution: {integrity: sha512-UvvM61cGGoT6Ish+gjmPYzCoqOnkmdC9OMufcO41ijya8Evk6zqXm3PNYK8CLCkvb3jrWieDo1SFag9Tg3RiPQ==}
     dependencies:
       cron-parser: 4.8.1
       glob: 8.1.0
@@ -11440,7 +11346,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.13.2)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0):
+  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.14.0)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0):
     resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11461,7 +11367,7 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
       debug: 3.2.7(supports-color@8.1.1)
       eslint: 8.55.0
       eslint-import-resolver-node: 0.3.9
@@ -11469,7 +11375,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.13.2)(eslint@8.55.0):
+  /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0):
     resolution: {integrity: sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11479,7 +11385,7 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.13.2(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
@@ -11488,7 +11394,7 @@ packages:
       doctrine: 2.1.0
       eslint: 8.55.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.13.2)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.14.0)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0)
       hasown: 2.0.0
       is-core-module: 2.13.1
       is-glob: 4.0.3
@@ -11941,12 +11847,6 @@ packages:
     resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
     dev: true
 
-  /fast-querystring@1.1.0:
-    resolution: {integrity: sha512-LWkjBCZlxjnSanuPpZ6mHswjy8hQv3VcPJsQB3ltUF2zjvrycr0leP3TSTEEfvQ1WEMSRl5YNsGqaft9bjLqEw==}
-    dependencies:
-      fast-decode-uri-component: 1.0.1
-    dev: false
-
   /fast-querystring@1.1.2:
     resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==}
     dependencies:
@@ -11986,8 +11886,8 @@ packages:
       secure-json-parse: 2.7.0
     dev: false
 
-  /fastify@4.24.3:
-    resolution: {integrity: sha512-6HHJ+R2x2LS3y1PqxnwEIjOTZxFl+8h4kSC/TuDPXtA+v2JnV9yEtOsNSKK1RMD7sIR2y1ZsA4BEFaid/cK5pg==}
+  /fastify@4.25.0:
+    resolution: {integrity: sha512-2XANZZDadl/PccnVbmrIC8BmUtb16MO5Hyfnqv1cZIslvf7GYTVwlPuVxLKL23NNxWRc5BikY8HyhWj+NJvokA==}
     dependencies:
       '@fastify/ajv-compiler': 3.5.0
       '@fastify/error': 3.4.0
@@ -11998,8 +11898,8 @@ packages:
       fast-json-stringify: 5.8.0
       find-my-way: 7.7.0
       light-my-request: 5.11.0
-      pino: 8.16.0
-      process-warning: 2.2.0
+      pino: 8.17.0
+      process-warning: 3.0.0
       proxy-addr: 2.0.7
       rfdc: 1.3.0
       secure-json-parse: 2.7.0
@@ -12145,7 +12045,7 @@ packages:
     engines: {node: '>=14'}
     dependencies:
       fast-deep-equal: 3.1.3
-      fast-querystring: 1.1.0
+      fast-querystring: 1.1.2
       safe-regex2: 2.0.0
     dev: false
 
@@ -14232,7 +14132,7 @@ packages:
       whatwg-encoding: 3.1.1
       whatwg-mimetype: 4.0.0
       whatwg-url: 14.0.0
-      ws: 8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3)
+      ws: 8.15.1(bufferutil@4.0.7)(utf-8-validate@6.0.3)
       xml-name-validator: 5.0.0
     transitivePeerDependencies:
       - bufferutil
@@ -16088,8 +15988,8 @@ packages:
     resolution: {integrity: sha512-KO0m2f1HkrPe9S0ldjx7za9BJjeHqBku5Ch8JyxETxT8dEFGz1PwgrHaOQupVYitpzbFSYm7nnljxD8dik2c+g==}
     dev: false
 
-  /pino@8.16.0:
-    resolution: {integrity: sha512-UUmvQ/7KTZt/vHjhRrnyS7h+J7qPBQnpG80V56xmIC+o9IqYmQOw/UIny9S9zYDfRBR0ClouCr464EkBMIT7Fw==}
+  /pino@8.17.0:
+    resolution: {integrity: sha512-ey+Mku+PVPhvxglLXMg1l1zQMwSHuNrKC3MD40EDZbkckJmmuY7DYZLIOwwjZ8ix/Nvhe9dZt5H99cgkot9bAw==}
     hasBin: true
     dependencies:
       atomic-sleep: 1.0.0
@@ -16557,8 +16457,8 @@ packages:
     hasBin: true
     dev: true
 
-  /prettier@3.1.0:
-    resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==}
+  /prettier@3.1.1:
+    resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==}
     engines: {node: '>=14'}
     hasBin: true
     dev: true
@@ -16631,6 +16531,10 @@ packages:
     resolution: {integrity: sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==}
     dev: false
 
+  /process-warning@3.0.0:
+    resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==}
+    dev: false
+
   /process@0.11.10:
     resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
     engines: {node: '>= 0.6.0'}
@@ -17272,6 +17176,10 @@ packages:
     resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
     dev: false
 
+  /reflect-metadata@0.2.0:
+    resolution: {integrity: sha512-vUN0wuk3MuhSVMfU/ImnPQAK8QZcXJ339DtVsP3jDscxCe6dT+PsOe3J1BYS9Ec2Fd4oC6ry6bCBebzTya0IYw==}
+    dev: false
+
   /regenerate-unicode-properties@10.1.0:
     resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
     engines: {node: '>=4'}
@@ -17531,24 +17439,24 @@ packages:
       fsevents: 2.3.3
     dev: true
 
-  /rollup@4.7.0:
-    resolution: {integrity: sha512-7Kw0dUP4BWH78zaZCqF1rPyQ8D5DSU6URG45v1dqS/faNsx9WXyess00uTOZxKr7oR/4TOjO1CPudT8L1UsEgw==}
+  /rollup@4.9.0:
+    resolution: {integrity: sha512-bUHW/9N21z64gw8s6tP4c88P382Bq/L5uZDowHlHx6s/QWpjJXivIAbEw6LZthgSvlEizZBfLC4OAvWe7aoF7A==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.7.0
-      '@rollup/rollup-android-arm64': 4.7.0
-      '@rollup/rollup-darwin-arm64': 4.7.0
-      '@rollup/rollup-darwin-x64': 4.7.0
-      '@rollup/rollup-linux-arm-gnueabihf': 4.7.0
-      '@rollup/rollup-linux-arm64-gnu': 4.7.0
-      '@rollup/rollup-linux-arm64-musl': 4.7.0
-      '@rollup/rollup-linux-riscv64-gnu': 4.7.0
-      '@rollup/rollup-linux-x64-gnu': 4.7.0
-      '@rollup/rollup-linux-x64-musl': 4.7.0
-      '@rollup/rollup-win32-arm64-msvc': 4.7.0
-      '@rollup/rollup-win32-ia32-msvc': 4.7.0
-      '@rollup/rollup-win32-x64-msvc': 4.7.0
+      '@rollup/rollup-android-arm-eabi': 4.9.0
+      '@rollup/rollup-android-arm64': 4.9.0
+      '@rollup/rollup-darwin-arm64': 4.9.0
+      '@rollup/rollup-darwin-x64': 4.9.0
+      '@rollup/rollup-linux-arm-gnueabihf': 4.9.0
+      '@rollup/rollup-linux-arm64-gnu': 4.9.0
+      '@rollup/rollup-linux-arm64-musl': 4.9.0
+      '@rollup/rollup-linux-riscv64-gnu': 4.9.0
+      '@rollup/rollup-linux-x64-gnu': 4.9.0
+      '@rollup/rollup-linux-x64-musl': 4.9.0
+      '@rollup/rollup-win32-arm64-msvc': 4.9.0
+      '@rollup/rollup-win32-ia32-msvc': 4.9.0
+      '@rollup/rollup-win32-x64-msvc': 4.9.0
       fsevents: 2.3.3
 
   /rrweb-cssom@0.6.0:
@@ -19449,7 +19357,7 @@ packages:
       mlly: 1.4.0
       pathe: 1.1.1
       picocolors: 1.0.0
-      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -19465,8 +19373,8 @@ packages:
     resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
     dev: true
 
-  /vite@5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0):
-    resolution: {integrity: sha512-B4T4rJCDPihrQo2B+h1MbeGL/k/GMAHzhQ8S0LjQ142s6/+l3hHTT095ORvsshj4QCkoWu3Xtmob5mazvakaOw==}
+  /vite@5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0):
+    resolution: {integrity: sha512-jYMALd8aeqR3yS9xlHd0OzQJndS9fH5ylVgWdB+pxTwxLKdO1pgC5Dlb398BUxpfaBxa4M9oT7j1g503Gaj5IQ==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
@@ -19496,7 +19404,7 @@ packages:
       '@types/node': 20.10.4
       esbuild: 0.19.8
       postcss: 8.4.32
-      rollup: 4.7.0
+      rollup: 4.9.0
       sass: 1.69.5
       terser: 5.24.0
     optionalDependencies:
@@ -19567,7 +19475,7 @@ packages:
       strip-literal: 1.0.1
       tinybench: 2.5.0
       tinypool: 0.7.0
-      vite: 5.0.7(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
       vite-node: 0.34.6(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
       why-is-node-running: 2.2.2
     transitivePeerDependencies:
@@ -19621,7 +19529,7 @@ packages:
       '@babel/parser': 7.23.5
       '@babel/types': 7.23.5
       '@vue/compiler-dom': 3.3.9
-      '@vue/compiler-sfc': 3.3.9
+      '@vue/compiler-sfc': 3.3.11
       ast-types: 0.14.2
       hash-sum: 2.0.0
       lru-cache: 8.0.4
@@ -19977,8 +19885,8 @@ packages:
       async-limiter: 1.0.1
     dev: true
 
-  /ws@8.14.2(bufferutil@4.0.7)(utf-8-validate@6.0.3):
-    resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==}
+  /ws@8.15.1(bufferutil@4.0.7)(utf-8-validate@6.0.3):
+    resolution: {integrity: sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==}
     engines: {node: '>=10.0.0'}
     peerDependencies:
       bufferutil: ^4.0.1
@@ -20174,7 +20082,7 @@ packages:
       sharp: 0.31.3
     dev: false
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.4)(@storybook/components@7.6.3)(@storybook/core-events@7.6.4)(@storybook/manager-api@7.6.4)(@storybook/preview-api@7.6.4)(@storybook/theming@7.6.4)(@storybook/types@7.6.4)(react-dom@18.2.0)(react@18.2.0):
+  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.4)(@storybook/components@7.6.4)(@storybook/core-events@7.6.4)(@storybook/manager-api@7.6.4)(@storybook/preview-api@7.6.4)(@storybook/theming@7.6.4)(@storybook/types@7.6.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
@@ -20196,7 +20104,7 @@ packages:
         optional: true
     dependencies:
       '@storybook/blocks': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/components': 7.6.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/components': 7.6.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/core-events': 7.6.4
       '@storybook/manager-api': 7.6.4(react-dom@18.2.0)(react@18.2.0)
       '@storybook/preview-api': 7.6.4

From 364efbe58bd85ed29e919ca095547ebeec49297c Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 14 Dec 2023 13:22:54 +0900
Subject: [PATCH 219/435] 2023.12.0-beta.4

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

diff --git a/package.json b/package.json
index b0a5e137b8..bb44951e8a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.12.0-beta.3",
+	"version": "2023.12.0-beta.4",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From a92795d90f9e55c7b7726725dceea979fd8940a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Thu, 14 Dec 2023 14:11:20 +0900
Subject: [PATCH 220/435] =?UTF-8?q?feat(frontend):=20=E7=B5=B5=E6=96=87?=
 =?UTF-8?q?=E5=AD=97=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC=E3=81=AE=E5=AE=9F?=
 =?UTF-8?q?=E8=A3=85=20(#12617)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 絵文字デッキの作成

* 細かい不備を修正

* fix lint

* fix

* fix CHANGELOG.md

* fix setTimeout -> nextTick

* fix https://github.com/misskey-dev/misskey/pull/12617#issuecomment-1848952862

* fix bug

* fix CHANGELOG.md

* fix CHANGELOG.md

* wip

* Update CHANGELOG.md

* Update CHANGELOG.md

* wip

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                  |  14 +-
 locales/index.d.ts                            |   7 +-
 locales/ja-JP.yml                             |   7 +-
 .../frontend/src/components/MkEmojiPicker.vue |  23 +-
 .../src/components/MkEmojiPickerDialog.vue    |   5 +-
 .../frontend/src/components/MkPostForm.vue    |   2 +-
 .../frontend/src/components/form/section.vue  |   9 +-
 .../src/pages/settings/emoji-picker.vue       | 274 ++++++++++++++++++
 .../frontend/src/pages/settings/index.vue     |   8 +-
 .../pages/settings/preferences-backups.vue    |   8 +-
 .../frontend/src/pages/settings/reaction.vue  | 159 ----------
 packages/frontend/src/router.ts               |   6 +-
 packages/frontend/src/scripts/emoji-picker.ts |   9 +-
 .../frontend/src/scripts/reaction-picker.ts   |   9 +-
 packages/frontend/src/store.ts                |  12 +-
 15 files changed, 354 insertions(+), 198 deletions(-)
 create mode 100644 packages/frontend/src/pages/settings/emoji-picker.vue
 delete mode 100644 packages/frontend/src/pages/settings/reaction.vue

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 972c876518..7fbc1e06de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,17 @@
 
 ## 2023.x.x (unreleased)
 
+### Note
+- 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。  
+
+	**影響:**  
+	それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された投稿用のピン留め絵文字が使われるため)。   
+	投稿用のピン留め絵文字をアップデート前の状態にするには、以下の手順で操作します。
+
+	1. 「設定」メニューに移動し、「絵文字ピッカー」タブを選択します。
+	2. 「ピン留 (全般)」のタブを選択します。
+	3. 「リアクション設定からコピーする」ボタンを押すことで、アップデート前の状態に戻すことができます。
+
 ### General
 - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
 - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
@@ -25,7 +36,8 @@
 ### Client
 - Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加
 - Feat: データセーバーでコードハイライトの読み込みを削減できるように
-- Enhance: 投稿フォームの絵文字ピッカーをリアクション時に使用するものと同じのを使用するように #12336
+- Enhance: 投稿フォームの絵文字ピッカーをリアクション時に使用するものと同じのを使用するように #12336 #12560
+- Enhance: リアクション用ピン留め絵文字と投稿時の絵文字入力用ピン留め絵文字を分けて設定できるように #12560
 - Enhance: 絵文字のオートコンプリート機能強化 #12364
 - Enhance: ユーザーのRawデータを表示するページが復活
 - Enhance: リアクション選択時に音を鳴らせるように
diff --git a/locales/index.d.ts b/locales/index.d.ts
index d32023f5ac..40837b05a2 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -124,7 +124,12 @@ export interface Locale {
     "add": string;
     "reaction": string;
     "reactions": string;
-    "reactionSetting": string;
+    "emojiPicker": string;
+    "pinnedEmojisForReactionSettingDescription": string;
+    "pinnedEmojisSettingDescription": string;
+    "emojiPickerDisplay": string;
+    "copyFromPinnedEmojisForReaction": string;
+    "copyFromPinnedEmojis": string;
     "reactionSettingDescription2": string;
     "rememberNoteVisibility": string;
     "attachCancel": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 2ac57fd311..3ad27910ef 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -121,7 +121,12 @@ sensitive: "センシティブ"
 add: "追加"
 reaction: "リアクション"
 reactions: "リアクション"
-reactionSetting: "ピッカーに表示するリアクション"
+emojiPicker: "絵文字ピッカー"
+pinnedEmojisForReactionSettingDescription: "リアクション時にピン留め表示する絵文字を設定できます"
+pinnedEmojisSettingDescription: "絵文字入力時にピン留め表示する絵文字を設定できます"
+emojiPickerDisplay: "ピッカーの表示"
+copyFromPinnedEmojisForReaction: "リアクション設定からコピーする"
+copyFromPinnedEmojis: "絵文字設定からコピーする"
 reactionSettingDescription2: "ドラッグして並び替え、クリックして削除、+を押して追加します。"
 rememberNoteVisibility: "公開範囲を記憶する"
 attachCancel: "添付取り消し"
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index d84d298e23..f36d46506f 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -121,10 +121,11 @@ import { $i } from '@/account.js';
 
 const props = withDefaults(defineProps<{
 	showPinned?: boolean;
-	asReactionPicker?: boolean;
+  pinnedEmojis?: string[];
 	maxHeight?: number;
 	asDrawer?: boolean;
 	asWindow?: boolean;
+	asReactionPicker?: boolean; // 今は使われてないが将来的に使いそう
 }>(), {
 	showPinned: true,
 });
@@ -137,24 +138,22 @@ const searchEl = shallowRef<HTMLInputElement>();
 const emojisEl = shallowRef<HTMLDivElement>();
 
 const {
-	reactions: pinnedReactions,
-	reactionPickerSize,
-	reactionPickerWidth,
-	reactionPickerHeight,
-	disableShowingAnimatedImages,
+	emojiPickerScale,
+	emojiPickerWidth,
+	emojiPickerHeight,
 	recentlyUsedEmojis,
 } = defaultStore.reactiveState;
 
-const pinned = computed(() => props.asReactionPicker ? pinnedReactions.value : []); // TODO: 非リアクションの絵文字ピッカー用のpinned絵文字を設定可能にする?
-const size = computed(() => props.asReactionPicker ? reactionPickerSize.value : 1);
-const width = computed(() => props.asReactionPicker ? reactionPickerWidth.value : 3);
-const height = computed(() => props.asReactionPicker ? reactionPickerHeight.value : 2);
+const pinned = computed(() => props.pinnedEmojis);
+const size = computed(() => emojiPickerScale.value);
+const width = computed(() => emojiPickerWidth.value);
+const height = computed(() => emojiPickerHeight.value);
 const q = ref<string>('');
 const searchResultCustom = ref<Misskey.entities.EmojiSimple[]>([]);
 const searchResultUnicode = ref<UnicodeEmojiDef[]>([]);
 const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index');
 
-const customEmojiFolderRoot: CustomEmojiFolderTree = { value: "", category: "", children: [] };
+const customEmojiFolderRoot: CustomEmojiFolderTree = { value: '', category: '', children: [] };
 
 function parseAndMergeCategories(input: string, root: CustomEmojiFolderTree): CustomEmojiFolderTree {
 	const parts = input.split('/').map(p => p.trim());
@@ -368,7 +367,7 @@ function chosen(emoji: any, ev?: MouseEvent) {
 	emit('chosen', key);
 
 	// 最近使った絵文字更新
-	if (!pinned.value.includes(key)) {
+	if (!pinned.value?.includes(key)) {
 		let recents = defaultStore.state.recentlyUsedEmojis;
 		recents = recents.filter((emoji: any) => emoji !== key);
 		recents.unshift(key);
diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue
index 2cce1f5520..6660dcf1ed 100644
--- a/packages/frontend/src/components/MkEmojiPickerDialog.vue
+++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	ref="modal"
 	v-slot="{ type, maxHeight }"
 	:zPriority="'middle'"
-	:preferType="asReactionPicker && defaultStore.state.reactionPickerUseDrawerForMobile === false ? 'popup' : 'auto'"
+	:preferType="defaultStore.state.emojiPickerUseDrawerForMobile === false ? 'popup' : 'auto'"
 	:transparentBg="true"
 	:manualShowing="manualShowing"
 	:src="src"
@@ -22,6 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		class="_popup _shadow"
 		:class="{ [$style.drawer]: type === 'drawer' }"
 		:showPinned="showPinned"
+		:pinnedEmojis="pinnedEmojis"
 		:asReactionPicker="asReactionPicker"
 		:asDrawer="type === 'drawer'"
 		:max-height="maxHeight"
@@ -40,11 +41,13 @@ const props = withDefaults(defineProps<{
 	manualShowing?: boolean | null;
 	src?: HTMLElement;
 	showPinned?: boolean;
+  pinnedEmojis?: string[],
 	asReactionPicker?: boolean;
   choseAndClose?: boolean;
 }>(), {
 	manualShowing: null,
 	showPinned: true,
+	pinnedEmojis: undefined,
 	asReactionPicker: false,
 	choseAndClose: true,
 });
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index e6d55ae982..4a1930ac0b 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -857,7 +857,7 @@ async function insertEmoji(ev: MouseEvent) {
 		},
 		() => {
 			textAreaReadOnly.value = false;
-			focus();
+			nextTick(() => focus());
 		},
 	);
 }
diff --git a/packages/frontend/src/components/form/section.vue b/packages/frontend/src/components/form/section.vue
index 095b24604a..6af63d1ec6 100644
--- a/packages/frontend/src/components/form/section.vue
+++ b/packages/frontend/src/components/form/section.vue
@@ -6,6 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div :class="[$style.root, { [$style.rootFirst]: first }]">
 	<div :class="[$style.label, { [$style.labelFirst]: first }]"><slot name="label"></slot></div>
+	<div :class="[$style.description]"><slot name="description"></slot></div>
 	<div :class="$style.main">
 		<slot></slot>
 	</div>
@@ -31,7 +32,7 @@ defineProps<{
 .label {
 	font-weight: bold;
 	padding: 1.5em 0 0 0;
-	margin: 0 0 16px 0;
+	margin: 0 0 8px 0;
 
 	&:empty {
 		display: none;
@@ -45,4 +46,10 @@ defineProps<{
 .main {
 	margin: 1.5em 0 0 0;
 }
+
+.description {
+	font-size: 0.85em;
+	color: var(--fgTransparentWeak);
+	margin: 0 0 8px 0;
+}
 </style>
diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue
new file mode 100644
index 0000000000..f3f974a96f
--- /dev/null
+++ b/packages/frontend/src/pages/settings/emoji-picker.vue
@@ -0,0 +1,274 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div class="_gaps_m">
+	<MkFolder :defaultOpen="true">
+		<template #icon><i class="ti ti-pin"></i></template>
+		<template #label>{{ i18n.ts.pinned }} ({{ i18n.ts.reaction }})</template>
+		<template #caption>{{ i18n.ts.pinnedEmojisForReactionSettingDescription }}</template>
+
+		<div class="_gaps">
+			<div>
+				<div v-panel style="border-radius: 6px;">
+					<Sortable
+						v-model="pinnedEmojisForReaction"
+						:class="$style.emojis"
+						:itemKey="item => item"
+						:animation="150"
+						:delay="100"
+						:delayOnTouchOnly="true"
+					>
+						<template #item="{element}">
+							<button class="_button" :class="$style.emojisItem" @click="removeReaction(element, $event)">
+								<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true"/>
+								<MkEmoji v-else :emoji="element" :normal="true"/>
+							</button>
+						</template>
+						<template #footer>
+							<button class="_button" :class="$style.emojisAdd" @click="chooseReaction">
+								<i class="ti ti-plus"></i>
+							</button>
+						</template>
+					</Sortable>
+				</div>
+				<div :class="$style.editorCaption">{{ i18n.ts.reactionSettingDescription2 }}</div>
+			</div>
+
+			<div class="_buttons">
+				<MkButton inline @click="previewReaction"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton>
+				<MkButton inline danger @click="setDefaultReaction"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton>
+				<MkButton inline danger @click="copyFromPinnedEmojis"><i class="ti ti-copy"></i> {{ i18n.ts.copyFromPinnedEmojis }}</MkButton>
+			</div>
+		</div>
+	</MkFolder>
+
+	<MkFolder>
+		<template #icon><i class="ti ti-pin"></i></template>
+		<template #label>{{ i18n.ts.pinned }} ({{ i18n.ts.general }})</template>
+		<template #caption>{{ i18n.ts.pinnedEmojisSettingDescription }}</template>
+
+		<div class="_gaps">
+			<div>
+				<div v-panel style="border-radius: 6px;">
+					<Sortable
+						v-model="pinnedEmojis"
+						:class="$style.emojis"
+						:itemKey="item => item"
+						:animation="150"
+						:delay="100"
+						:delayOnTouchOnly="true"
+					>
+						<template #item="{element}">
+							<button class="_button" :class="$style.emojisItem" @click="removeEmoji(element, $event)">
+								<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true"/>
+								<MkEmoji v-else :emoji="element" :normal="true"/>
+							</button>
+						</template>
+						<template #footer>
+							<button class="_button" :class="$style.emojisAdd" @click="chooseEmoji">
+								<i class="ti ti-plus"></i>
+							</button>
+						</template>
+					</Sortable>
+				</div>
+				<div :class="$style.editorCaption">{{ i18n.ts.reactionSettingDescription2 }}</div>
+			</div>
+
+			<div class="_buttons">
+				<MkButton inline @click="previewEmoji"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton>
+				<MkButton inline danger @click="setDefaultEmoji"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton>
+				<MkButton inline danger @click="copyFromPinnedEmojisForReaction"><i class="ti ti-copy"></i> {{ i18n.ts.copyFromPinnedEmojisForReaction }}</MkButton>
+			</div>
+		</div>
+	</MkFolder>
+
+	<FormSection>
+		<template #label>{{ i18n.ts.emojiPickerDisplay }}</template>
+
+		<div class="_gaps_m">
+			<MkRadios v-model="emojiPickerScale">
+				<template #label>{{ i18n.ts.size }}</template>
+				<option :value="1">{{ i18n.ts.small }}</option>
+				<option :value="2">{{ i18n.ts.medium }}</option>
+				<option :value="3">{{ i18n.ts.large }}</option>
+			</MkRadios>
+
+			<MkRadios v-model="emojiPickerWidth">
+				<template #label>{{ i18n.ts.numberOfColumn }}</template>
+				<option :value="1">5</option>
+				<option :value="2">6</option>
+				<option :value="3">7</option>
+				<option :value="4">8</option>
+				<option :value="5">9</option>
+			</MkRadios>
+
+			<MkRadios v-model="emojiPickerHeight">
+				<template #label>{{ i18n.ts.height }}</template>
+				<option :value="1">{{ i18n.ts.small }}</option>
+				<option :value="2">{{ i18n.ts.medium }}</option>
+				<option :value="3">{{ i18n.ts.large }}</option>
+				<option :value="4">{{ i18n.ts.large }}+</option>
+			</MkRadios>
+
+			<MkSwitch v-model="emojiPickerUseDrawerForMobile">
+				{{ i18n.ts.useDrawerReactionPickerForMobile }}
+				<template #caption>{{ i18n.ts.needReloadToApply }}</template>
+			</MkSwitch>
+		</div>
+	</FormSection>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref, Ref, watch } from 'vue';
+import Sortable from 'vuedraggable';
+import MkRadios from '@/components/MkRadios.vue';
+import MkButton from '@/components/MkButton.vue';
+import FormSection from '@/components/form/section.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { deepClone } from '@/scripts/clone.js';
+import { reactionPicker } from '@/scripts/reaction-picker.js';
+import { emojiPicker } from '@/scripts/emoji-picker.js';
+import MkCustomEmoji from '@/components/global/MkCustomEmoji.vue';
+import MkEmoji from '@/components/global/MkEmoji.vue';
+import MkFolder from '@/components/MkFolder.vue';
+
+const pinnedEmojisForReaction: Ref<string[]> = ref(deepClone(defaultStore.state.reactions));
+const pinnedEmojis: Ref<string[]> = ref(deepClone(defaultStore.state.pinnedEmojis));
+
+const emojiPickerScale = computed(defaultStore.makeGetterSetter('emojiPickerScale'));
+const emojiPickerWidth = computed(defaultStore.makeGetterSetter('emojiPickerWidth'));
+const emojiPickerHeight = computed(defaultStore.makeGetterSetter('emojiPickerHeight'));
+const emojiPickerUseDrawerForMobile = computed(defaultStore.makeGetterSetter('emojiPickerUseDrawerForMobile'));
+
+const removeReaction = (reaction: string, ev: MouseEvent) => remove(pinnedEmojisForReaction, reaction, ev);
+const chooseReaction = (ev: MouseEvent) => pickEmoji(pinnedEmojisForReaction, ev);
+const setDefaultReaction = () => setDefault(pinnedEmojisForReaction);
+
+const removeEmoji = (reaction: string, ev: MouseEvent) => remove(pinnedEmojis, reaction, ev);
+const chooseEmoji = (ev: MouseEvent) => pickEmoji(pinnedEmojis, ev);
+const setDefaultEmoji = () => setDefault(pinnedEmojis);
+
+function previewReaction(ev: MouseEvent) {
+	reactionPicker.show(getHTMLElement(ev));
+}
+
+function previewEmoji(ev: MouseEvent) {
+	emojiPicker.show(getHTMLElement(ev));
+}
+
+async function copyFromPinnedEmojis() {
+	const { canceled } = await os.confirm({
+		type: 'warning',
+		text: 'a',
+	});
+
+	if (canceled) {
+		return;
+	}
+
+	pinnedEmojisForReaction.value = [...pinnedEmojis.value];
+}
+
+async function copyFromPinnedEmojisForReaction() {
+	const { canceled } = await os.confirm({
+		type: 'warning',
+		text: 'a',
+	});
+
+	if (canceled) {
+		return;
+	}
+
+	pinnedEmojis.value = [...pinnedEmojisForReaction.value];
+}
+
+function remove(itemsRef: Ref<string[]>, reaction: string, ev: MouseEvent) {
+	os.popupMenu([{
+		text: i18n.ts.remove,
+		action: () => {
+			itemsRef.value = itemsRef.value.filter(x => x !== reaction);
+		},
+	}], getHTMLElement(ev));
+}
+
+async function setDefault(itemsRef: Ref<string[]>) {
+	const { canceled } = await os.confirm({
+		type: 'warning',
+		text: i18n.ts.resetAreYouSure,
+	});
+	if (canceled) return;
+
+	itemsRef.value = deepClone(defaultStore.def.reactions.default);
+}
+
+async function pickEmoji(itemsRef: Ref<string[]>, ev: MouseEvent) {
+	os.pickEmoji(getHTMLElement(ev), {
+		showPinned: false,
+	}).then(it => {
+		const emoji = it as string;
+		if (!itemsRef.value.includes(emoji)) {
+			itemsRef.value.push(emoji);
+		}
+	});
+}
+
+function getHTMLElement(ev: MouseEvent): HTMLElement {
+	const target = ev.currentTarget ?? ev.target;
+	return target as HTMLElement;
+}
+
+watch(pinnedEmojisForReaction, () => {
+	defaultStore.set('reactions', pinnedEmojisForReaction.value);
+}, {
+	deep: true,
+});
+
+watch(pinnedEmojis, () => {
+	defaultStore.set('pinnedEmojis', pinnedEmojis.value);
+}, {
+	deep: true,
+});
+
+definePageMetadata({
+	title: i18n.ts.emojiPicker,
+	icon: 'ti ti-mood-happy',
+});
+</script>
+
+<style lang="scss" module>
+.tab {
+	margin: calc(var(--margin) / 2) 0;
+	padding: calc(var(--margin) / 2) 0;
+	background: var(--bg);
+}
+
+.emojis {
+  padding: 12px;
+  font-size: 1.1em;
+}
+
+.emojisItem {
+  display: inline-block;
+  padding: 8px;
+  cursor: move;
+}
+
+.emojisAdd {
+  display: inline-block;
+  padding: 8px;
+}
+
+.editorCaption {
+	font-size: 0.85em;
+	padding: 8px 0 0 0;
+	color: var(--fgTransparentWeak);
+}
+</style>
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index 633ee894a9..e533f4420b 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -74,9 +74,9 @@ const menuDef = computed(() => [{
 		active: currentPage.value?.route.name === 'privacy',
 	}, {
 		icon: 'ti ti-mood-happy',
-		text: i18n.ts.reaction,
-		to: '/settings/reaction',
-		active: currentPage.value?.route.name === 'reaction',
+		text: i18n.ts.emojiPicker,
+		to: '/settings/emoji-picker',
+		active: currentPage.value?.route.name === 'emojiPicker',
 	}, {
 		icon: 'ti ti-cloud',
 		text: i18n.ts.drive,
@@ -236,7 +236,7 @@ provideMetadataReceiver((info) => {
 		childInfo.value = null;
 	} else {
 		childInfo.value = info;
-		INFO.value.needWideArea = info.value?.needWideArea ?? undefined;
+		INFO.value.needWideArea = info.value.needWideArea ?? undefined;
 	}
 });
 
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index 66c549930b..cc6223218f 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -83,10 +83,10 @@ const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
 	'useReactionPickerForContextMenu',
 	'showGapBetweenNotesInTimeline',
 	'instanceTicker',
-	'reactionPickerSize',
-	'reactionPickerWidth',
-	'reactionPickerHeight',
-	'reactionPickerUseDrawerForMobile',
+	'emojiPickerScale',
+	'emojiPickerWidth',
+	'emojiPickerHeight',
+	'emojiPickerUseDrawerForMobile',
 	'defaultSideView',
 	'menuDisplay',
 	'reportError',
diff --git a/packages/frontend/src/pages/settings/reaction.vue b/packages/frontend/src/pages/settings/reaction.vue
deleted file mode 100644
index fe5d9fc443..0000000000
--- a/packages/frontend/src/pages/settings/reaction.vue
+++ /dev/null
@@ -1,159 +0,0 @@
-<!--
-SPDX-FileCopyrightText: syuilo and other misskey contributors
-SPDX-License-Identifier: AGPL-3.0-only
--->
-
-<template>
-<div class="_gaps_m">
-	<FromSlot>
-		<template #label>{{ i18n.ts.reactionSettingDescription }}</template>
-		<div v-panel style="border-radius: 6px;">
-			<Sortable v-model="reactions" :class="$style.reactions" :itemKey="item => item" :animation="150" :delay="100" :delayOnTouchOnly="true">
-				<template #item="{element}">
-					<button class="_button" :class="$style.reactionsItem" @click="remove(element, $event)">
-						<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true"/>
-						<MkEmoji v-else :emoji="element" :normal="true"/>
-					</button>
-				</template>
-				<template #footer>
-					<button class="_button" :class="$style.reactionsAdd" @click="chooseEmoji"><i class="ti ti-plus"></i></button>
-				</template>
-			</Sortable>
-		</div>
-		<template #caption>{{ i18n.ts.reactionSettingDescription2 }} <button class="_textButton" @click="preview">{{ i18n.ts.preview }}</button></template>
-	</FromSlot>
-
-	<MkRadios v-model="reactionPickerSize">
-		<template #label>{{ i18n.ts.size }}</template>
-		<option :value="1">{{ i18n.ts.small }}</option>
-		<option :value="2">{{ i18n.ts.medium }}</option>
-		<option :value="3">{{ i18n.ts.large }}</option>
-	</MkRadios>
-	<MkRadios v-model="reactionPickerWidth">
-		<template #label>{{ i18n.ts.numberOfColumn }}</template>
-		<option :value="1">5</option>
-		<option :value="2">6</option>
-		<option :value="3">7</option>
-		<option :value="4">8</option>
-		<option :value="5">9</option>
-	</MkRadios>
-	<MkRadios v-model="reactionPickerHeight">
-		<template #label>{{ i18n.ts.height }}</template>
-		<option :value="1">{{ i18n.ts.small }}</option>
-		<option :value="2">{{ i18n.ts.medium }}</option>
-		<option :value="3">{{ i18n.ts.large }}</option>
-		<option :value="4">{{ i18n.ts.large }}+</option>
-	</MkRadios>
-
-	<MkSwitch v-model="reactionPickerUseDrawerForMobile">
-		{{ i18n.ts.useDrawerReactionPickerForMobile }}
-		<template #caption>{{ i18n.ts.needReloadToApply }}</template>
-	</MkSwitch>
-
-	<FormSection>
-		<div class="_buttons">
-			<MkButton inline @click="preview"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton>
-			<MkButton inline danger @click="setDefault"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton>
-		</div>
-	</FormSection>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { defineAsyncComponent, watch, ref, computed } from 'vue';
-import Sortable from 'vuedraggable';
-import MkRadios from '@/components/MkRadios.vue';
-import FromSlot from '@/components/form/slot.vue';
-import MkButton from '@/components/MkButton.vue';
-import FormSection from '@/components/form/section.vue';
-import MkSwitch from '@/components/MkSwitch.vue';
-import * as os from '@/os.js';
-import { defaultStore } from '@/store.js';
-import { i18n } from '@/i18n.js';
-import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { deepClone } from '@/scripts/clone.js';
-
-const reactions = ref(deepClone(defaultStore.state.reactions));
-
-const reactionPickerSize = computed(defaultStore.makeGetterSetter('reactionPickerSize'));
-const reactionPickerWidth = computed(defaultStore.makeGetterSetter('reactionPickerWidth'));
-const reactionPickerHeight = computed(defaultStore.makeGetterSetter('reactionPickerHeight'));
-const reactionPickerUseDrawerForMobile = computed(defaultStore.makeGetterSetter('reactionPickerUseDrawerForMobile'));
-
-function save() {
-	defaultStore.set('reactions', reactions.value);
-}
-
-function remove(reaction, ev: MouseEvent) {
-	os.popupMenu([{
-		text: i18n.ts.remove,
-		action: () => {
-			reactions.value = reactions.value.filter(x => x !== reaction);
-		},
-	}], ev.currentTarget ?? ev.target);
-}
-
-function preview(ev: MouseEvent) {
-	os.popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), {
-		asReactionPicker: true,
-		src: ev.currentTarget ?? ev.target,
-	}, {}, 'closed');
-}
-
-async function setDefault() {
-	const { canceled } = await os.confirm({
-		type: 'warning',
-		text: i18n.ts.resetAreYouSure,
-	});
-	if (canceled) return;
-
-	reactions.value = deepClone(defaultStore.def.reactions.default);
-}
-
-function chooseEmoji(ev: MouseEvent) {
-	os.pickEmoji(ev.currentTarget ?? ev.target, {
-		showPinned: false,
-	}).then(emoji => {
-		if (!reactions.value.includes(emoji)) {
-			reactions.value.push(emoji);
-		}
-	});
-}
-
-watch(reactions, () => {
-	save();
-}, {
-	deep: true,
-});
-
-const headerActions = computed(() => []);
-
-const headerTabs = computed(() => []);
-
-definePageMetadata({
-	title: i18n.ts.reaction,
-	icon: 'ti ti-mood-happy',
-	action: {
-		icon: 'ti ti-eye',
-		handler: preview,
-	},
-});
-</script>
-
-<style lang="scss" module>
-.reactions {
-	padding: 12px;
-	font-size: 1.1em;
-}
-
-.reactionsItem {
-	display: inline-block;
-	padding: 8px;
-	cursor: move;
-}
-
-.reactionsAdd {
-	display: inline-block;
-	padding: 8px;
-}
-</style>
diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts
index b81811d2e7..a7a53e97e6 100644
--- a/packages/frontend/src/router.ts
+++ b/packages/frontend/src/router.ts
@@ -63,9 +63,9 @@ export const routes = [{
 		name: 'privacy',
 		component: page(() => import('./pages/settings/privacy.vue')),
 	}, {
-		path: '/reaction',
-		name: 'reaction',
-		component: page(() => import('./pages/settings/reaction.vue')),
+		path: '/emoji-picker',
+		name: 'emojiPicker',
+		component: page(() => import('./pages/settings/emoji-picker.vue')),
 	}, {
 		path: '/drive',
 		name: 'drive',
diff --git a/packages/frontend/src/scripts/emoji-picker.ts b/packages/frontend/src/scripts/emoji-picker.ts
index d6d6bf1245..3cf653ea1b 100644
--- a/packages/frontend/src/scripts/emoji-picker.ts
+++ b/packages/frontend/src/scripts/emoji-picker.ts
@@ -3,8 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { defineAsyncComponent, Ref, ref } from 'vue';
+import { defineAsyncComponent, Ref, ref, computed, ComputedRef } from 'vue';
 import { popup } from '@/os.js';
+import { defaultStore } from '@/store.js';
 
 /**
  * 絵文字ピッカーを表示する。
@@ -23,8 +24,10 @@ class EmojiPicker {
 	}
 
 	public async init() {
+		const emojisRef = defaultStore.reactiveState.pinnedEmojis;
 		await popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), {
 			src: this.src,
+			pinnedEmojis: emojisRef,
 			asReactionPicker: false,
 			manualShowing: this.manualShowing,
 			choseAndClose: false,
@@ -44,8 +47,8 @@ class EmojiPicker {
 
 	public show(
 		src: HTMLElement,
-		onChosen: EmojiPicker['onChosen'],
-		onClosed: EmojiPicker['onClosed'],
+		onChosen?: EmojiPicker['onChosen'],
+		onClosed?: EmojiPicker['onClosed'],
 	) {
 		this.src.value = src;
 		this.manualShowing.value = true;
diff --git a/packages/frontend/src/scripts/reaction-picker.ts b/packages/frontend/src/scripts/reaction-picker.ts
index 19e1bfba2c..9b13e794f5 100644
--- a/packages/frontend/src/scripts/reaction-picker.ts
+++ b/packages/frontend/src/scripts/reaction-picker.ts
@@ -5,6 +5,7 @@
 
 import { defineAsyncComponent, Ref, ref } from 'vue';
 import { popup } from '@/os.js';
+import { defaultStore } from '@/store.js';
 
 class ReactionPicker {
 	private src: Ref<HTMLElement | null> = ref(null);
@@ -17,25 +18,27 @@ class ReactionPicker {
 	}
 
 	public async init() {
+		const reactionsRef = defaultStore.reactiveState.reactions;
 		await popup(defineAsyncComponent(() => import('@/components/MkEmojiPickerDialog.vue')), {
 			src: this.src,
+			pinnedEmojis: reactionsRef,
 			asReactionPicker: true,
 			manualShowing: this.manualShowing,
 		}, {
 			done: reaction => {
-				this.onChosen!(reaction);
+				if (this.onChosen) this.onChosen(reaction);
 			},
 			close: () => {
 				this.manualShowing.value = false;
 			},
 			closed: () => {
 				this.src.value = null;
-				this.onClosed!();
+				if (this.onClosed) this.onClosed();
 			},
 		});
 	}
 
-	public show(src: HTMLElement, onChosen: ReactionPicker['onChosen'], onClosed: ReactionPicker['onClosed']) {
+	public show(src: HTMLElement, onChosen?: ReactionPicker['onChosen'], onClosed?: ReactionPicker['onClosed']) {
 		this.src.value = src;
 		this.manualShowing.value = true;
 		this.onChosen = onChosen;
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 8459a5721a..c7e501aa84 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -119,6 +119,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'account',
 		default: ['👍', '❤️', '😆', '🤔', '😮', '🎉', '💢', '😥', '😇', '🍮'],
 	},
+	pinnedEmojis: {
+		where: 'account',
+		default: [],
+	},
 	reactionAcceptance: {
 		where: 'account',
 		default: 'nonSensitiveOnly' as 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null,
@@ -271,19 +275,19 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: 'remote' as 'none' | 'remote' | 'always',
 	},
-	reactionPickerSize: {
+	emojiPickerScale: {
 		where: 'device',
 		default: 1,
 	},
-	reactionPickerWidth: {
+	emojiPickerWidth: {
 		where: 'device',
 		default: 1,
 	},
-	reactionPickerHeight: {
+	emojiPickerHeight: {
 		where: 'device',
 		default: 2,
 	},
-	reactionPickerUseDrawerForMobile: {
+	emojiPickerUseDrawerForMobile: {
 		where: 'device',
 		default: true,
 	},

From 8ff87176f843e4e7ff3e1432c1e090867c8c2535 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 14 Dec 2023 14:23:18 +0900
Subject: [PATCH 221/435] tweak profile.avatar-decoration.dialog.vue

---
 .../src/pages/settings/profile.avatar-decoration.dialog.vue    | 3 ++-
 .../frontend/src/pages/settings/profile.avatar-decoration.vue  | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue
index a27b46aa3e..26cacf3c37 100644
--- a/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div :class="$style.footer" class="_buttonsCenter">
 			<MkButton v-if="usingIndex != null" primary rounded @click="update"><i class="ti ti-check"></i> {{ i18n.ts.update }}</MkButton>
 			<MkButton v-if="usingIndex != null" rounded @click="detach"><i class="ti ti-x"></i> {{ i18n.ts.detach }}</MkButton>
-			<MkButton v-else primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.attach }}</MkButton>
+			<MkButton v-else :disabled="exceeded" primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.attach }}</MkButton>
 		</div>
 	</div>
 </MkModalWindow>
@@ -73,6 +73,7 @@ const emit = defineEmits<{
 }>();
 
 const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
+const exceeded = computed(() => ($i.policies.avatarDecorationLimit - $i.avatarDecorations.length) <= 0);
 const angle = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].angle : null) ?? 0);
 const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false);
 
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue
index 90c2b75a4d..bfef6e0325 100644
--- a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div v-if="!loading" class="_gaps">
-	<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i?.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i?.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
+	<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
 
 	<div v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s">
 		<div>{{ i18n.ts.inUse }}</div>

From 8416329f409de63944e34c16f72bf9eb73d0f3e9 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 14 Dec 2023 14:23:54 +0900
Subject: [PATCH 222/435] New Crowdin updates (#12352)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Indonesian)

* 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 (Swedish)

* 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 (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 (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Indonesian)

* 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 (Swedish)

* 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 (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 (Russian)

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

* New translations ja-jp.yml (Romanian)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (French)

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

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

* New translations ja-jp.yml (French)

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

* New translations ja-jp.yml (French)

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

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

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

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (English)

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

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

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

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Italian)

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

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Spanish)

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

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

* New translations ja-jp.yml (Thai)

* New translations ja-jp.yml (French)

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

* New translations ja-jp.yml (Italian)

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

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

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

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

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Russian)

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

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

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

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Romanian)

* 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 (Greek)

* 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 (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 (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 (Chinese Traditional)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Italian)

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

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

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (German)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (English)

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

* New translations ja-jp.yml (Korean (Gyeongsang))

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

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

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

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

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

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Russian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

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

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Russian)

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

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Catalan)

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

* New translations ja-jp.yml (Chinese Traditional)
---
 locales/ar-SA.yml |   1 -
 locales/bn-BD.yml |   1 -
 locales/ca-ES.yml |   5 +
 locales/cs-CZ.yml |   1 -
 locales/de-DE.yml |  29 ++-
 locales/el-GR.yml |   1 -
 locales/en-US.yml |  49 +++-
 locales/es-ES.yml | 116 ++++++++-
 locales/fr-FR.yml | 118 ++++++++--
 locales/id-ID.yml |  42 +++-
 locales/it-IT.yml |  66 +++++-
 locales/ja-KS.yml | 581 +++++++++++++++++++++++++++-------------------
 locales/ko-GS.yml | 528 +++++++++++++++++++++++++++++++++++++++++
 locales/ko-KR.yml | 190 +++++++++------
 locales/lo-LA.yml |   1 -
 locales/nl-NL.yml |   1 -
 locales/pl-PL.yml |   1 -
 locales/pt-PT.yml |   1 -
 locales/ro-RO.yml |  22 +-
 locales/ru-RU.yml |  25 +-
 locales/sk-SK.yml |   1 -
 locales/th-TH.yml |  58 ++++-
 locales/uk-UA.yml |   1 -
 locales/uz-UZ.yml |   1 -
 locales/vi-VN.yml |   1 -
 locales/zh-CN.yml |  10 +-
 locales/zh-TW.yml | 105 ++++++---
 27 files changed, 1562 insertions(+), 394 deletions(-)
 create mode 100644 locales/ko-GS.yml

diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index d62990b7b7..6ac56ffc29 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -418,7 +418,6 @@ share: "شارِك"
 notFound: "غير موجود"
 notFoundDescription: "تعذر العثور على صفحة يقود إليها هذا الرابط."
 uploadFolder: "المجلد الافتراضي للرفع"
-cacheClear: "مسح ذاكرة التخزين المؤقت"
 markAsReadAllNotifications: "وضع جميع الإشعارات كأنها مقروءة"
 markAsReadAllUnreadNotes: "علّم جميع الملاحظات كمقروءة"
 markAsReadAllTalkMessages: "علّم جميع الرسائل كمقروءة"
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index 31f2b948ed..c6784269c4 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -393,7 +393,6 @@ share: "শেয়ার"
 notFound: "পাওয়া যায়নি"
 notFoundDescription: "এই URL-এর সাথে সম্পর্কিত কোনো পৃষ্ঠা নেই।"
 uploadFolder: "আপলোডের জন্য ডিফল্ট ফোল্ডার"
-cacheClear: "ক্যাশ পরিষ্কার করুন"
 markAsReadAllNotifications: "সমস্ত বিজ্ঞপ্তিগুলি পঠিত হিসাবে চিহ্নিত করুন"
 markAsReadAllUnreadNotes: "সমস্ত নোটগুলি পঠিত হিসাবে চিহ্নিত করুন"
 markAsReadAllTalkMessages: "সমস্ত মেসেজ পঠিত হিসাবে চিহ্নিত করুন"
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index 62ef511bc7..018645768d 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -157,6 +157,9 @@ addEmoji: "Afegeix un emoji"
 settingGuide: "Configuració recomanada"
 cacheRemoteFiles: "Emmagatzemar fitxers remots"
 cacheRemoteFilesDescription: "Quan aquesta opció està desactivada, els fitxers remots es carreguen directament des del servidor remot. Si desactiveu això, es reduirà l'ús d'emmagatzematge, però augmentarà el trànsit, ja que no es generaran miniatures."
+youCanCleanRemoteFilesCache: "Pots netejar la memòria cau fent clic al botó de la paperera🗑️ a l'administrador d'arxius."
+cacheRemoteSensitiveFiles: "Posar a la memòria cau arxius remots sensibles"
+cacheRemoteSensitiveFilesDescription: "Quan aquesta opció és desactiva, els arxius remots sensibles es carregant directament del servidor d'origen sense que es guardin a la memòria cau."
 flagAsBot: "Marca aquest compte com a bot"
 flagAsBotDescription: "Marca aquest compte com a bot"
 flagAsCat: "Marca aquest compte com a gat"
@@ -165,6 +168,7 @@ flagShowTimelineReplies: "Mostra les respostes a la línia de temps"
 flagShowTimelineRepliesDescription: "Mostra les respostes a la línia de temps"
 autoAcceptFollowed: "Aprova automàticament les sol·licituds de seguiment dels usuaris que segueixes"
 addAccount: "Afegeix un compte"
+reloadAccountsList: "Recarregar la llista de contactes"
 loginFailed: "S'ha produït un error al accedir."
 showOnRemote: "Navega més en el perfil original"
 general: "General"
@@ -191,6 +195,7 @@ perHour: "Per hora"
 perDay: "Per dia"
 stopActivityDelivery: "Deixa d'enviar activitats"
 blockThisInstance: "Deixa d'enviar activitats"
+silenceThisInstance: "Silencia aquesta instància "
 operations: "Accions"
 software: "Programari"
 version: "Versió"
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 5d6487d6df..8221da44ea 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -428,7 +428,6 @@ share: "Sdílet"
 notFound: "Nenalezeno"
 notFoundDescription: "Nebyla nalezená žádná stránka korespondující se zadanou URL."
 uploadFolder: "Výchozí lokace pro upload"
-cacheClear: "Vymazat cache"
 markAsReadAllNotifications: "Označit všechna oznámení za přečtená"
 markAsReadAllUnreadNotes: "Označit všechny příspěvky za přečtené"
 markAsReadAllTalkMessages: "Označit všechny zprávy za přečtené"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index af927586d0..db6aea29c4 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -311,6 +311,7 @@ folderName: "Ordnername"
 createFolder: "Ordner erstellen"
 renameFolder: "Ordner umbenennen"
 deleteFolder: "Ordner löschen"
+folder: "Ordner"
 addFile: "Datei hinzufügen"
 emptyDrive: "Deine Drive ist leer"
 emptyFolder: "Dieser Ordner ist leer"
@@ -437,7 +438,6 @@ share: "Teilen"
 notFound: "Nicht gefunden"
 notFoundDescription: "Es konnte keine Seite unter dieser URL gefunden werden."
 uploadFolder: "Standardordner für Uploads"
-cacheClear: "Cache leeren"
 markAsReadAllNotifications: "Alle Benachrichtigungen als gelesen markieren"
 markAsReadAllUnreadNotes: "Alle Notizen als gelesen markieren"
 markAsReadAllTalkMessages: "Alle Chats als gelesen markieren"
@@ -544,6 +544,8 @@ showInPage: "In einer Seite anzeigen"
 popout: "Pop-Up"
 volume: "Lautstärke"
 masterVolume: "Gesamtlautstärke"
+notUseSound: "Gebe kein Ton aus"
+useSoundOnlyWhenActive: "Gebe nur Ton aus, wenn Misskey aktiv ist"
 details: "Details"
 chooseEmoji: "Emoji auswählen"
 unableToProcess: "Der Vorgang konnte nicht abgeschlossen werden"
@@ -564,6 +566,10 @@ output: "Ausgabe"
 script: "Skript"
 disablePagesScript: "AiScript auf Seiten deaktivieren"
 updateRemoteUser: "Benutzerinformationen aktualisieren"
+unsetUserAvatar: "Entferne Profilbild"
+unsetUserAvatarConfirm: "Möchtest du dein Profilbild entfernen?"
+unsetUserBanner: "Entferne Profilbanner"
+unsetUserBannerConfirm: "Möchtest du dein Profilbanner entfernen?"
 deleteAllFiles: "Alle Dateien löschen"
 deleteAllFilesConfirm: "Möchtest du wirklich alle Dateien löschen?"
 removeAllFollowing: "Allen gefolgten Benutzern entfolgen"
@@ -1020,6 +1026,8 @@ resetPasswordConfirm: "Wirklich Passwort zurücksetzen?"
 sensitiveWords: "Sensible Wörter"
 sensitiveWordsDescription: "Die Notizsichtbarkeit aller Notizen, die diese Wörter enthalten, wird automatisch auf \"Startseite\" gesetzt. Durch Zeilenumbrüche können mehrere konfiguriert werden."
 sensitiveWordsDescription2: "Durch die Verwendung von Leerzeichen können AND-Verknüpfungen angegeben werden und durch das Umgeben von Schrägstrichen können reguläre Ausdrücke verwendet werden."
+hiddenTags: "Ausgeblendete Hashtags"
+hiddenTagsDescription: "Die hier eingestellten Tags werden nicht mehr in den Trends angezeigt. Mit der Umschalttaste können mehrere ausgewählt werden."
 notesSearchNotAvailable: "Die Notizsuche ist nicht verfügbar."
 license: "Lizenz"
 unfavoriteConfirm: "Wirklich aus Favoriten entfernen?"
@@ -1032,6 +1040,7 @@ enableChartsForRemoteUser: "Diagramme für Nutzer fremder Instanzen erstellen"
 enableChartsForFederatedInstances: "Diagramme für fremde Instanzen erstellen"
 showClipButtonInNoteFooter: "\"Clip\" zum Notizmenu hinzufügen"
 reactionsDisplaySize: "Reaktionsanzeigegröße"
+limitWidthOfReaction: "Begrenze die Breite der Reaktion und zeige sie verkleinert an"
 noteIdOrUrl: "Notiz-ID oder URL"
 video: "Video"
 videos: "Videos"
@@ -1155,7 +1164,10 @@ refreshing: "Wird aktualisiert..."
 pullDownToRefresh: "Zum Aktualisieren ziehen"
 disableStreamingTimeline: "Echtzeitaktualisierung der Chronik deaktivieren"
 useGroupedNotifications: "Benachrichtigungen gruppieren"
+signupPendingError: "Beim Überprüfen der Mailadresse ist etwas schiefgelaufen. Der Link könnte abgelaufen sein."
 cwNotationRequired: "Ist \"Inhaltswarnung verwenden\" aktiviert, muss eine Beschreibung gegeben werden."
+doReaction: "Reagieren"
+code: "Code"
 _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."
@@ -1165,6 +1177,9 @@ _announcement:
   tooManyActiveAnnouncementDescription: "Zu viele aktive Ankündigungen können die Benutzerfreundlichkeit verschlechtern. Es wird empfohlen, veraltete Ankündigungen zu archivieren."
   readConfirmTitle: "Als gelesen markieren?"
   readConfirmText: "Dies markiert den Inhalt von \"{title}\" als gelesen."
+  dialogAnnouncementUxWarn: "Bei der Verwendung von mehr als zwei Meldungen im Dialog-Format wird um Vorsicht geboten, da dies negative Auswirkungen auf die UX haben kann."
+  silence: "Keine Benachrichtigung"
+  silenceDescription: "Wenn aktiviert, gibt diese Meldung keine Nachricht aus und muss nicht als \"gelesen\" markiert werden."
 _initialAccountSetting:
   accountCreated: "Dein Konto wurde erfolgreich erstellt!"
   letsStartAccountSetup: "Lass uns nun dein Konto einrichten."
@@ -1177,8 +1192,20 @@ _initialAccountSetting:
   pushNotificationDescription: "Durch die Aktivierung von Push-Benachrichtigungen kannst du von {name} Benachrichtigungen direkt auf dein Gerät erhalten."
   initialAccountSettingCompleted: "Kontoeinrichtung abgeschlossen!"
   haveFun: "Viel Spaß mit {name}!"
+  youCanContinueTutorial: "Du kannst mit dem Tutorial von {name}(Misskey) fortfahren, oder auch abbrechen und gleich anfangen Misskey zu benutzen."
+  startTutorial: "Fange mit dem Tutorial an"
   skipAreYouSure: "Die Kontoeinrichtung wirklich überspringen?"
   laterAreYouSure: "Die Kontoeinrichtung wirklich später erledigen?"
+_initialTutorial:
+  launchTutorial: "Tutorial ansehen"
+  title: "Tutorial"
+  wellDone: "Gut gemacht!"
+  skipAreYouSure: "Möchtest du das Tutorial verlassen?"
+  _landing:
+    title: "Willkommen zum Tutorial"
+    description: "Hier kannst du sehen, wie Misskey funktioniert"
+  _note:
+    title: "Was sind Notizen?"
 _serverRules:
   description: "Eine Reihe von Regeln, die vor der Registrierung angezeigt werden. Eine Zusammenfassung der Nutzungsbedingungen anzuzeigen ist empfohlen."
 _serverSettings:
diff --git a/locales/el-GR.yml b/locales/el-GR.yml
index 9392fd12fe..a1c2d25391 100644
--- a/locales/el-GR.yml
+++ b/locales/el-GR.yml
@@ -228,7 +228,6 @@ userList: "Λίστες"
 about: "Πληροφορίες"
 moderator: "Συντονιστής"
 moderation: "Συντονισμός"
-cacheClear: "Εκκαθάριση προσωρινής μνήμης"
 markAsReadAllNotifications: "Όλες οι ειδοποιήσεις διαβάστηκαν"
 members: "Μέλη"
 transfer: "Μεταφορά"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index b14592b20a..da9b0fb7bc 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -311,6 +311,7 @@ folderName: "Folder name"
 createFolder: "Create a folder"
 renameFolder: "Rename this folder"
 deleteFolder: "Delete this folder"
+folder: "Folder"
 addFile: "Add a file"
 emptyDrive: "Your Drive is empty"
 emptyFolder: "This folder is empty"
@@ -437,7 +438,6 @@ share: "Share"
 notFound: "Not found"
 notFoundDescription: "No page corresponding to this URL could be found."
 uploadFolder: "Default folder for uploads"
-cacheClear: "Clear cache"
 markAsReadAllNotifications: "Mark all notifications as read"
 markAsReadAllUnreadNotes: "Mark all notes as read"
 markAsReadAllTalkMessages: "Mark all messages as read"
@@ -544,6 +544,8 @@ showInPage: "Show in page"
 popout: "Pop-out"
 volume: "Volume"
 masterVolume: "Master volume"
+notUseSound: "No sounds output."
+useSoundOnlyWhenActive: "Output sounds only if Misskey is active."
 details: "Details"
 chooseEmoji: "Select an emoji"
 unableToProcess: "The operation could not be completed"
@@ -564,10 +566,10 @@ output: "Output"
 script: "Script"
 disablePagesScript: "Disable AiScript on Pages"
 updateRemoteUser: "Update remote user information"
-unsetUserAvatar: "Delete user icon"
-unsetUserAvatarConfirm: "Are you sure that you want to delete this user's icon?"
-unsetUserBanner: "Delete user banner"
-unsetUserBannerConfirm: "Are you sure that you want to delete this user's banner?"
+unsetUserAvatar: "Unset avatar"
+unsetUserAvatarConfirm: "Are you sure you want to unset the avatar?"
+unsetUserBanner: "Unset banner"
+unsetUserBannerConfirm: "Are you sure you want to unset the banner?"
 deleteAllFiles: "Delete all files"
 deleteAllFilesConfirm: "Are you sure that you want to delete all files?"
 removeAllFollowing: "Unfollow all followed users"
@@ -639,6 +641,7 @@ smtpSecure: "Use implicit SSL/TLS for SMTP connections"
 smtpSecureInfo: "Turn this off when using STARTTLS"
 testEmail: "Test email delivery"
 wordMute: "Word mute"
+hardWordMute: "Hard word mute"
 regexpError: "Regular Expression error"
 regexpErrorDescription: "An error occurred in the regular expression on line {line} of your {tab} word mutes:"
 instanceMute: "Instance Mutes"
@@ -1024,6 +1027,8 @@ resetPasswordConfirm: "Really reset your password?"
 sensitiveWords: "Sensitive words"
 sensitiveWordsDescription: "The visibility of all notes containing any of the configured words will be set to \"Home\" automatically. You can list multiple by separating them via line breaks."
 sensitiveWordsDescription2: "Using spaces will create AND expressions and surrounding keywords with slashes will turn them into a regular expression."
+hiddenTags: "Hidden hashtags"
+hiddenTagsDescription: "Select tags which will not shown on trend list.\nMultiple tags could be registered by lines."
 notesSearchNotAvailable: "Note search is unavailable."
 license: "License"
 unfavoriteConfirm: "Really remove from favorites?"
@@ -1036,6 +1041,7 @@ enableChartsForRemoteUser: "Generate remote user data charts"
 enableChartsForFederatedInstances: "Generate remote instance data charts"
 showClipButtonInNoteFooter: "Add \"Clip\" to note action menu"
 reactionsDisplaySize: "Reaction display size"
+limitWidthOfReaction: "Limits the maximum width of reactions and display them in reduced size."
 noteIdOrUrl: "Note ID or URL"
 video: "Video"
 videos: "Videos"
@@ -1162,6 +1168,8 @@ useGroupedNotifications: "Display grouped notifications"
 signupPendingError: "There was a problem verifying the email address. The link may have expired."
 cwNotationRequired: "If \"Hide content\" is enabled, a description must be provided."
 doReaction: "Add reaction"
+code: "Code"
+reloadRequiredToApplySettings: "Reloading is required to apply the settings."
 _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."
@@ -1270,6 +1278,8 @@ _serverSettings:
   shortName: "Short name"
   shortNameDescription: "A shorthand for the instance's name that can be displayed if the full official name is long."
   fanoutTimelineDescription: "Greatly increases performance of timeline retrieval and reduces load on the database when enabled. In exchange, memory usage of Redis will increase. Consider disabling this in case of low server memory or server instability."
+  fanoutTimelineDbFallback: "Fallback to database"
+  fanoutTimelineDbFallbackDescription: "When enabled, the timeline will fall back to the database for additional queries if the timeline is not cached. Disabling it further reduces the server load by eliminating the fallback process, but limits the range of timelines that can be retrieved."
 _accountMigration:
   moveFrom: "Migrate another account to this one"
   moveFromSub: "Create alias to another account"
@@ -1540,7 +1550,9 @@ _role:
   assignTarget: "Assignment type"
   descriptionOfAssignTarget: "<b>Manual</b> to manually change who is part of this role and who is not.\n<b>Conditional</b> to have users be automatically assigned and removed from this role based on a condition."
   manual: "Manual"
+  manualRoles: "Manual roles"
   conditional: "Conditional"
+  conditionalRoles: "Conditional roles"
   condition: "Condition"
   isConditionalRole: "This is a conditional role."
   isPublic: "Public role"
@@ -1810,6 +1822,14 @@ _sfx:
   notification: "Notifications"
   antenna: "Antennas"
   channel: "Channel notifications"
+  reaction: "On choosing a reaction"
+_soundSettings:
+  driveFile: "Use an audio file in Drive."
+  driveFileWarn: "Select an audio file from Drive."
+  driveFileTypeWarn: "This file is not supported"
+  driveFileTypeWarnDescription: "Select an audio file"
+  driveFileDurationWarn: "The audio is too long."
+  driveFileDurationWarnDescription: "Long audio may disrupt using Misskey. Still continue?"
 _ago:
   future: "Future"
   justNow: "Just now"
@@ -1821,6 +1841,14 @@ _ago:
   monthsAgo: "{n}mo ago"
   yearsAgo: "{n}y ago"
   invalid: "None"
+_timeIn:
+  seconds: "In {n}s"
+  minutes: "In {n}m"
+  hours: "In {n}h"
+  days: "In {n}d"
+  weeks: "In {n}w"
+  months: "In {n}mo"
+  years: "In {n}y"
 _time:
   second: "Second(s)"
   minute: "Minute(s)"
@@ -2244,6 +2272,8 @@ _moderationLogTypes:
   createAvatarDecoration: "Avatar decoration created"
   updateAvatarDecoration: "Avatar decoration updated"
   deleteAvatarDecoration: "Avatar decoration deleted"
+  unsetUserAvatar: "Unset this user's avatar"
+  unsetUserBanner: "Unset this user's banner"
 _fileViewer:
   title: "File details"
   type: "File type"
@@ -2293,3 +2323,12 @@ _externalResourceInstaller:
     _themeInstallFailed:
       title: "Failed to install theme"
       description: "A problem occurred during theme installation. Please try again. Error details can be viewed in the Javascript console."
+_dataSaver:
+  _media:
+    title: "Loading Media"
+  _avatar:
+    title: "Avatar image"
+    description: "Stop avatar image animation. Animated images can be larger in file size than normal  images, potentially leading to further reductions in data traffic."
+  _code:
+    title: "Code highlighting"
+    description: "If code highlighting notations are used in MFM, etc., they will not load until tapped. Syntax highlighting requires downloading the highlight definition files for each programming language. Therefore, disabling the automatic loading of these files is expected to reduce the amount of communication data."
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index df611c2350..3c36e2b29f 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -311,6 +311,7 @@ folderName: "Nombre de la carpeta"
 createFolder: "Crear carpeta"
 renameFolder: "Renombrar carpeta"
 deleteFolder: "Borrar carpeta"
+folder: "Carpeta"
 addFile: "Agregar archivo"
 emptyDrive: "El drive está vacío"
 emptyFolder: "La carpeta está vacía"
@@ -437,7 +438,6 @@ share: "Compartir"
 notFound: "No se encuentra"
 notFoundDescription: "No se encontró la página correspondiente a la URL elegida"
 uploadFolder: "Carpeta de subidas por defecto"
-cacheClear: "Borrar caché"
 markAsReadAllNotifications: "Marcar todas las notificaciones como leídas"
 markAsReadAllUnreadNotes: "Marcar todas las notas como leídas"
 markAsReadAllTalkMessages: "Marcar todos los chats como leídos"
@@ -544,6 +544,8 @@ showInPage: "Mostrar en la página"
 popout: "Popout"
 volume: "Volumen"
 masterVolume: "Volumen principal"
+notUseSound: "Sin sonido"
+useSoundOnlyWhenActive: "Sonar solo cuando Misskey esté activo"
 details: "Detalles"
 chooseEmoji: "Elije un emoji"
 unableToProcess: "La operación no se puede llevar a cabo"
@@ -564,6 +566,10 @@ output: "Salida"
 script: "Script"
 disablePagesScript: "Deshabilitar AiScript en Páginas"
 updateRemoteUser: "Actualizar información de usuario remoto"
+unsetUserAvatar: "Quitar avatar"
+unsetUserAvatarConfirm: "¿Confirmas que quieres quitar tu avatar?"
+unsetUserBanner: "Quitar banner"
+unsetUserBannerConfirm: "¿Confirmas que quieres quitar tu banner?"
 deleteAllFiles: "Borrar todos los archivos"
 deleteAllFilesConfirm: "¿Desea borrar todos los archivos?"
 removeAllFollowing: "Retener todos los siguientes"
@@ -979,6 +985,7 @@ assign: "Asignar"
 unassign: "Quitar"
 color: "Color"
 manageCustomEmojis: "Administrar emojis personalizados"
+manageAvatarDecorations: "Administrar decoraciones de avatar"
 youCannotCreateAnymore: "Has llegado al límite de creaciones."
 cannotPerformTemporary: "Temporalmente no disponible"
 cannotPerformTemporaryDescription: "Esta acción no se puede realizar porque se excedió el límite de ejecución. Espera un poco y prueba de nuevo."
@@ -1019,6 +1026,7 @@ resetPasswordConfirm: "¿Realmente quieres cambiar la contraseña?"
 sensitiveWords: "Palabras sensibles"
 sensitiveWordsDescription: "La visibilidad de todas las notas que contienen cualquiera de las palabras configuradas serán puestas en \"Inicio\" automáticamente. Puedes enumerás varias separándolas con saltos de línea"
 sensitiveWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares."
+hiddenTags: "Hashtags ocultos"
 notesSearchNotAvailable: "No se puede buscar una nota"
 license: "Licencia"
 unfavoriteConfirm: "¿Desea quitar de favoritos?"
@@ -1031,6 +1039,7 @@ enableChartsForRemoteUser: "Generar gráficas de usuarios remotos."
 enableChartsForFederatedInstances: "Generar gráficos de servidores remotos"
 showClipButtonInNoteFooter: "Añadir \"Clip\" al menú de notas"
 reactionsDisplaySize: "Tamaño de las reacciones"
+limitWidthOfReaction: "Limitar ancho de las reacciones"
 noteIdOrUrl: "ID o URL de la nota"
 video: "Video"
 videos: "Video"
@@ -1132,6 +1141,10 @@ mutualFollow: "Os seguís mutuamente"
 fileAttachedOnly: "Solo notas con archivos"
 showRepliesToOthersInTimeline: "Mostrar respuestas a otros en la línea de tiempo"
 hideRepliesToOthersInTimeline: "Ocultar respuestas a otros en la línea de tiempo"
+showRepliesToOthersInTimelineAll: "Muestra tus respuestas a otros usuarios que sigues en la línea de tiempo"
+hideRepliesToOthersInTimelineAll: "Ocultar tus respuestas a otros usuarios que sigues en la línea de tiempo"
+confirmShowRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres mostrar tus respuestas a otros usuarios que sigues en tu línea de tiempo?"
+confirmHideRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres ocultar tus respuestas a otros usuarios que sigues en tu línea de tiempo?"
 externalServices: "Servicios Externos"
 impressum: "Impressum"
 impressumUrl: "Impressum URL"
@@ -1139,7 +1152,22 @@ impressumDescription: "En algunos países, como Alemania, la inclusión del oper
 privacyPolicy: "Política de Privacidad"
 privacyPolicyUrl: "URL de la Política de Privacidad"
 tosAndPrivacyPolicy: "Condiciones de Uso y Política de Privacidad"
+avatarDecorations: "Decoraciones de avatar"
+attach: "Acoplar"
+detach: "Quitar"
+angle: "Ángulo"
 flip: "Echar de un capirotazo"
+showAvatarDecorations: "Mostrar decoraciones de avatar"
+releaseToRefresh: "Soltar para recargar"
+refreshing: "Recargando..."
+pullDownToRefresh: "Tira hacia abajo para recargar"
+disableStreamingTimeline: "Desactivar actualizaciones en tiempo real de la línea de tiempo"
+useGroupedNotifications: "Mostrar notificaciones agrupadas"
+signupPendingError: "Ha habido un problema al verificar tu dirección de correo electrónico. Es posible que el enlace haya caducado."
+cwNotationRequired: "Si se ha activado \"ocultar contenido\", es necesario proporcionar una descripción."
+doReaction: "Añadir reacción"
+code: "Código"
+reloadRequiredToApplySettings: "Es necesario recargar para que se aplique la configuración."
 _announcement:
   forExistingUsers: "Solo para usuarios registrados"
   forExistingUsersDescription: "Este anuncio solo se mostrará a aquellos usuarios registrados en el momento de su publicación. Si se deshabilita esta opción, aquellos usuarios que se registren tras su publicación también lo verán."
@@ -1149,6 +1177,10 @@ _announcement:
   tooManyActiveAnnouncementDescription: "Tener demasiados anuncios activos empeora la experiencia de usuario. Por favor, considera archivar aquellos anuncios que hayan quedado obsoletos."
   readConfirmTitle: "¿Marcar como leído?"
   readConfirmText: "Esto marcará el contenido de \"{title}\" como leído."
+  shouldNotBeUsedToPresentPermanentInfo: "Dado que puede impactar en la experiencia de usuario de forma significativa, es recomendable usar notificaciones en el flujo de información en vez de información persistente."
+  dialogAnnouncementUxWarn: "Mostrar dos o más notificaciones en formato diálogo a la vez puede impactar en la experiencia de usuario de forma significativa, úsalos con cuidado."
+  silence: "Silenciar notificaciones"
+  silenceDescription: "Si lo activas, no enviarás notificación sobre este anuncio y el usuario no tendrá que leerlo."
 _initialAccountSetting:
   accountCreated: "¡La cuenta ha sido creada!"
   letsStartAccountSetup: "Para empezar, creemos tu perfil."
@@ -1161,8 +1193,38 @@ _initialAccountSetting:
   pushNotificationDescription: "Habilitar las notificaciones push te permitirá recibir notificaciones de {name} directamente en tu dispositivo."
   initialAccountSettingCompleted: "¡Configuración del perfil completada!"
   haveFun: "¡Disfruta de {name}!"
+  youCanContinueTutorial: "Puedes proceder a un tutorial sobre cómo usar {name} (Misskey) o puedes terminar la instalación aquí y empezar a usarlo ya mismo."
+  startTutorial: "Comenzar tutorial"
   skipAreYouSure: "¿Realmente quieres saltarte la configuración del perfil?"
   laterAreYouSure: "¿Realmente quieres configurar tu perfil después?"
+_initialTutorial:
+  launchTutorial: "Comenzar tutorial"
+  title: "Tutorial"
+  wellDone: "¡Bien hecho!"
+  skipAreYouSure: "¿Salir del tutorial?"
+  _landing:
+    title: "Bienvenid@ al tutorial"
+    description: "Aquí podrás aprender las nociones básicas sobre cómo usar Misskey y sus funciones."
+  _note:
+    title: "¿Qué es una nota?"
+    description: "Las publicaciones en Misskey se llaman 'Notas'. Las notas se ordenan de forma cronológica en la línea de tiempo y se actualizan en tiempo real."
+    reply: "Pulsa en este botón para contestar a un mensaje. También es posible contestar a otras contestaciones, continuando así la conversación como un hilo."
+    renote: "Puedes compartir esa nota en tu propia línea de tiempo. También puedes añadir una cita con tus comentarios."
+    reaction: "Puedes añadir reacciones a la Nota. Se explicarán más detalles en la siguiente página."
+    menu: "Puedes ver los detalles de la Nota, copiar enlaces, y realizar otras acciones."
+  _reaction:
+    title: "¿Qué son las reacciones?"
+    description: "Se puede reaccionar a las Notas con diferentes emojis. Las reacciones te permiten expresar matices que no se pueden transmitir con un simple 'me gusta'."
+    letsTryReacting: "Puedes añadir reacciones pulsando en el botón '+' de la nota. ¡Intenta reaccionar a esta nota de ejemplo!"
+    reactToContinue: "Añade una reacción para continuar."
+    reactNotification: "Recibirás notificaciones en tiempo real cuando alguien reaccione a tu nota."
+    reactDone: "Puedes deshacer una reacción pulsando en el botón '-'."
+  _timeline:
+    title: "El concepto de Línea de tiempo"
+    description1: "Misskey proporciona múltiples líneas de tiempo basadas en su uso (algunas pueden no estar disponibles dependiendo de las políticas de la instancia)."
+    home: "Puedes ver los posts de las cuentas que sigues."
+    local: "Puedes ver los posts de todos los usuarios de este servidor."
+    social: "Se ven los posts de la línea de tiempo de inicio junto con los de la línea de tiempo local."
 _serverRules:
   description: "Un conjunto de reglas que serán mostradas antes del registro. Configurar un sumario de términos de servicio es recomendado."
 _serverSettings:
@@ -1474,6 +1536,7 @@ _role:
     inviteLimitCycle: "Enfriamiento del límite de invitaciones"
     inviteExpirationTime: "Intervalo de caducidad de invitaciones"
     canManageCustomEmojis: "Administrar emojis personalizados"
+    canManageAvatarDecorations: "Administrar decoraciones de avatar"
     driveCapacity: "Capacidad del drive"
     alwaysMarkNsfw: "Siempre marcar archivos como NSFW"
     pinMax: "Máximo de notas fijadas"
@@ -1719,6 +1782,8 @@ _ago:
   monthsAgo: "Hace {n} meses"
   yearsAgo: "Hace {n} años"
   invalid: "No hay nada que ver aqui"
+_timeIn:
+  years: "En {n} años"
 _time:
   second: "Segundos"
   minute: "Minutos"
@@ -2033,6 +2098,9 @@ _notification:
   checkNotificationBehavior: "Comprobar comportamiento de la notificación"
   sendTestNotification: "Enviar notificación de prueba"
   notificationWillBeDisplayedLikeThis: "Las notificaciones tendrán este aspecto"
+  reactedBySomeUsers: "{n} usuarios han reaccionado"
+  renotedBySomeUsers: "{n} usuarios han renotado"
+  followedBySomeUsers: "Seguido por {n} usuarios"
   _types:
     all: "Todo"
     note: "Nuevas notas"
@@ -2136,6 +2204,11 @@ _moderationLogTypes:
   createAd: "Anuncio creado"
   deleteAd: "Anuncio eliminado"
   updateAd: "Anuncio actualizado"
+  createAvatarDecoration: "Decoración de avatar creada"
+  updateAvatarDecoration: "Decoración de avatar actualizada"
+  deleteAvatarDecoration: "Decoración de avatar eliminada"
+  unsetUserAvatar: "Quitar decoración de avatar de este usuario"
+  unsetUserBanner: "Quitar banner de este usuario"
 _fileViewer:
   title: "Detalles del archivo"
   type: "Tipo de archivo"
@@ -2144,3 +2217,44 @@ _fileViewer:
   uploadedAt: "Subido el"
   attachedNotes: "Notas adjuntas"
   thisPageCanBeSeenFromTheAuthor: "Esta página solo puede ser vista por el autor."
+_externalResourceInstaller:
+  title: "Instalar desde sitio externo"
+  checkVendorBeforeInstall: "Asegúrate de que el distribuidor de este recurso es de confianza antes de proceder a la instalación."
+  _plugin:
+    title: "¿Quieres instalar este plugin?"
+    metaTitle: "Información del plugin"
+  _theme:
+    title: "¿Quieres instalar este tema?"
+    metaTitle: "Información del tema"
+  _meta:
+    base: "Esquema de color base"
+  _vendorInfo:
+    title: "Información del distribuidor"
+    endpoint: "Terminal referenciada"
+    hashVerify: "Verificación de hash"
+  _errors:
+    _invalidParams:
+      title: "Parámetros inválidos"
+      description: "No hay información suficiente para cargar datos de un sitio externo. Por favor, confirma la URL introducida."
+    _resourceTypeNotSupported:
+      title: "Este recurso externo no es compatible"
+      description: "El tipo de este recurso externo no es compatible. Por favor, contacta con el administrador del sitio."
+    _failedToFetch:
+      title: "No se pudo obtener los datos"
+      fetchErrorDescription: "Ha ocurrido un error al comunicarse con el sitio externo. Si no se soluciona tras intentarlo otra vez, por favor, contacta con el administrador del sitio."
+      parseErrorDescription: "Ha ocurrido un error al procesar los datos obtenidos del sitio externo. Por favor, contacta con el administrador del sitio."
+    _hashUnmatched:
+      title: "Verificación de datos fallida"
+      description: "Ha ocurrido un error al verificar la integridad de los datos obtenidos. Por seguridad, la instalación no se puede realizar. Por favor, contacta con el administrador del sitio."
+    _pluginParseFailed:
+      title: "Error de AiScript"
+      description: "Los datos se han obtenido correctamente, pero ha ocurrido un error de AiScript al procesarlos. Por favor, contacta con el autor del plugin. Se pueden ver más detalles del error en la consola de Javascript."
+    _pluginInstallFailed:
+      title: "Instalación del plugin fallida."
+      description: "Ha ocurrido un problema al instalar el plugin. Por favor, inténtalo de nuevo. Se pueden ver más detalles del error en la consola de Javascript."
+    _themeParseFailed:
+      title: "Análisis del tema fallido"
+      description: "Los datos se han obtenido correctamente, pero ha ocurrido un error al analizar el tema. Por favor, contacta con el autor. Se pueden ver más detalles del error en la consola de Javascript."
+    _themeInstallFailed:
+      title: "Instalación de tema fallida"
+      description: "Ha ocurrido un problema al instalar el tema. Por favor, inténtalo de nuevo. Se pueden ver más detalles del error en la consola de Javascript."
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 94254fd998..bc0676eb0a 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -75,7 +75,7 @@ import: "Importer"
 export: "Exporter"
 files: "Fichiers"
 download: "Télécharger"
-driveFileDeleteConfirm: "Êtes-vous sûr de vouloir supprimer le fichier \"{name}\" ? Les notes liées à ce fichier seront aussi supprimées."
+driveFileDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer le fichier « {name} » ? Les notes avec ce fichier joint seront aussi supprimées."
 unfollowConfirm: "Désirez-vous vous désabonner de {name} ?"
 exportRequested: "Vous avez demandé une exportation. L’opération pourrait prendre un peu de temps. Une fois terminée, le fichier sera ajouté au Drive."
 importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps."
@@ -258,14 +258,15 @@ imageUrl: "URL de l’image"
 remove: "Supprimer"
 removed: "Supprimé"
 removeAreYouSure: "Êtes-vous sûr·e de vouloir supprimer « {x} » ?"
-deleteAreYouSure: "Êtes-vous sûr·e de vouloir supprimer「{x}」?"
+deleteAreYouSure: "Êtes-vous sûr·e de vouloir supprimer « {x} » ?"
 resetAreYouSure: "Voulez-vous réinitialiser ?"
+areYouSure: "Êtes-vous sûr·e ?"
 saved: "Enregistré"
 messaging: "Discuter"
 upload: "Téléverser"
 keepOriginalUploading: "Garder l’image d’origine"
 keepOriginalUploadingDescription: "Conserve la version originale lors du téléchargement d'images. S'il est désactivé, le navigateur génère l'image pour la publication web lors du téléchargement."
-fromDrive: "Depuis le Drive"
+fromDrive: "Depuis le Disque"
 fromUrl: "Depuis une URL"
 uploadFromUrl: "Téléverser via une URL"
 uploadFromUrlDescription: "URL du fichier que vous souhaitez téléverser"
@@ -299,7 +300,7 @@ dark: "Sombre"
 lightThemes: "Thèmes clairs"
 darkThemes: "Thèmes sombres"
 syncDeviceDarkMode: "Utiliser le mode sombre de votre appareil"
-drive: "Drive"
+drive: "Disque"
 fileName: "Nom du fichier"
 selectFile: "Choisir le fichier"
 selectFiles: "Choisir les fichiers"
@@ -310,8 +311,9 @@ folderName: "Nom du dossier"
 createFolder: "Créer un dossier"
 renameFolder: "Renommer le dossier"
 deleteFolder: "Supprimer le dossier"
+folder: "Dossier"
 addFile: "Ajouter un fichier"
-emptyDrive: "Le Drive est vide"
+emptyDrive: "Le Disque est vide"
 emptyFolder: "Le dossier est vide"
 unableToDelete: "Suppression impossible"
 inputNewFileName: "Entrez un nouveau nom de fichier"
@@ -355,8 +357,8 @@ disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur
 registration: "S’inscrire"
 enableRegistration: "Autoriser les nouvelles inscriptions"
 invite: "Inviter"
-driveCapacityPerLocalAccount: "Volume du Drive par utilisateur local"
-driveCapacityPerRemoteAccount: "Volume du Drive par utilisateur distant"
+driveCapacityPerLocalAccount: "Capacité de stockage du Disque par utilisateur local"
+driveCapacityPerRemoteAccount: "Capacité de stockage du Disque par utilisateur distant"
 inMb: "en mégaoctets"
 bannerUrl: "URL de l’image de la bannière"
 backgroundImageUrl: "URL de l'image d'arrière-plan"
@@ -428,6 +430,7 @@ lastUsed: "Dernier utilisé"
 lastUsedAt: "Dernière utilisation : {t}"
 unregister: "Se désinscrire"
 passwordLessLogin: "Se connecter sans mot de passe"
+passwordLessLoginDescription: "Se connecter uniquement avec une clé de sécurité ou une clé d'accès sans utiliser de mot de passe"
 resetPassword: "Réinitialiser le mot de passe"
 newPasswordIs: "Votre nouveau mot de passe est \"{password}\""
 reduceUiAnimation: "Réduire les animations dans l’interface"
@@ -435,7 +438,6 @@ share: "Partager"
 notFound: "Non trouvé"
 notFoundDescription: "Aucune page ne correspond à l’URL spécifiée."
 uploadFolder: "Emplacement de téléversement par défaut"
-cacheClear: "Vider le cache"
 markAsReadAllNotifications: "Marquer toutes les notifications comme lues"
 markAsReadAllUnreadNotes: "Marquer toutes les notes comme lues"
 markAsReadAllTalkMessages: "Marquer toutes les discussions comme lues"
@@ -483,6 +485,7 @@ showNoteActionsOnlyHover: "Afficher les actions de note uniquement au survol"
 noHistory: "Pas d'historique"
 signinHistory: "Historique de connexion"
 enableAdvancedMfm: "Activer la MFM avancée"
+enableAnimatedMfm: "Activer le MFM animé"
 doing: "En cours..."
 category: "Catégorie"
 tags: "Étiquettes"
@@ -491,6 +494,7 @@ createAccount: "Créer un compte"
 existingAccount: "Compte existant"
 regenerate: "Générer à nouveau"
 fontSize: "Taille de la police"
+mediaListWithOneImageAppearance: "Hauteur des listes de médias n'ayant qu'une image "
 limitTo: "Limiter à {x}"
 noFollowRequests: "Vous n’avez aucune demande d’abonnement en attente"
 openImageInNewTab: "Ouvrir les images dans un nouvel onglet"
@@ -528,6 +532,7 @@ objectStorageSetPublicRead: "Régler sur « public » lors de l'envoi"
 serverLogs: "Journal du serveur"
 deleteAll: "Supprimer tout"
 showFixedPostForm: "Afficher le formulaire de publication en haut du fil d'actualité"
+showFixedPostFormInChannel: "Afficher le formulaire de publication en haut du fil (canaux)"
 withRepliesByDefaultForNewlyFollowed: "Afficher les réponses des nouvelles personnes que vous suivez dans le fil par défaut"
 newNoteRecived: "Voir les nouvelles notes"
 sounds: "Sons"
@@ -538,6 +543,8 @@ showInPage: "Afficher dans la page"
 popout: "Fenêtre contextuelle"
 volume: "Volume"
 masterVolume: "Volume principal"
+notUseSound: "Ne pas émettre de son"
+useSoundOnlyWhenActive: "Émettre des sons uniquement quand Misskey est active"
 details: "Détails"
 chooseEmoji: "Choisissez un émoji"
 unableToProcess: "L’opération n’a pas pu être complétée."
@@ -558,9 +565,13 @@ output: "Sortie"
 script: "Script"
 disablePagesScript: "Désactiver AiScript sur les Pages"
 updateRemoteUser: "Mettre à jour les informations de l’utilisateur·rice distant·e"
+unsetUserAvatar: "Supprimer l’avatar"
+unsetUserAvatarConfirm: "Êtes-vous sûr·e de vouloir supprimer l'avatar ?"
+unsetUserBanner: "Supprimer la bannière"
+unsetUserBannerConfirm: "Êtes-vous sûr·e de vouloir supprimer la bannière ?"
 deleteAllFiles: "Supprimer tous les fichiers"
 deleteAllFilesConfirm: "Êtes-vous sûr·e de vouloir supprimer tous les fichiers ?"
-removeAllFollowing: "Retenir tous les abonnements"
+removeAllFollowing: "Se désabonner de tous les utilisateurs auxquels vous êtes abonné·e"
 removeAllFollowingDescription: "Se désabonner de tous les comptes de {host}. Veuillez lancer cette action dans les cas où l’instance n’existe plus, etc."
 userSuspended: "Cet·te utilisateur·rice a été suspendu·e."
 userSilenced: "Cette utilisateur·trice a été mis·e en sourdine."
@@ -629,6 +640,7 @@ smtpSecure: "Utiliser SSL/TLS implicitement dans les connexions SMTP"
 smtpSecureInfo: "Désactiver cette option lorsque STARTTLS est utilisé"
 testEmail: "Tester la distribution de courriel"
 wordMute: "Filtre de mots"
+hardWordMute: "Filtre de mots dur"
 regexpError: "Erreur d’expression régulière"
 regexpErrorDescription: "Une erreur s'est produite dans l'expression régulière sur la ligne {ligne} de votre mot muet {tab} :"
 instanceMute: "Instance en sourdine"
@@ -701,8 +713,8 @@ pollVotesCount: "Nombre de votes envoyés"
 pollVotedCount: "Nombre de votes reçus"
 yes: "Oui"
 no: "Non"
-driveFilesCount: "Nombre de fichiers dans le Drive"
-driveUsage: "Utilisation du Drive"
+driveFilesCount: "Nombre de fichiers sur le Disque"
+driveUsage: "Utilisation du Disque"
 noCrawle: "Refuser l'indexation par les robots"
 noCrawleDescription: "Demandez aux moteurs de recherche de ne pas indexer votre page de profil, vos notes, vos pages, etc."
 lockedAccountInfo: "À moins que vous ne définissiez la visibilité de votre note sur \"Abonné-e-s\", vos notes sont visibles par tous, même si vous exigez que les demandes d'abonnement soient approuvées manuellement."
@@ -845,7 +857,7 @@ pubSub: "Comptes Pub/Sub"
 lastCommunication: "Dernière communication"
 resolved: "Résolu"
 unresolved: "En attente"
-breakFollow: "Ne plus suivre"
+breakFollow: "Supprimer l'abonné·e"
 breakFollowConfirm: "Êtes-vous sûr de vouloir vous désabonner ?"
 itsOn: "Activé"
 itsOff: "Désactivé"
@@ -904,7 +916,7 @@ noEmailServerWarning: "Serveur de courrier non configuré."
 thereIsUnresolvedAbuseReportWarning: "Il n’y a aucun rapport non résolu."
 recommended: "Recommandé"
 check: "Vérifier"
-driveCapOverrideLabel: "Modifier la capacité de stockage du drive de cet·te utilisateur·rice"
+driveCapOverrideLabel: "Modifier la capacité de stockage du Disque de cet·te utilisateur·rice"
 driveCapOverrideCaption: "Si une valeur inférieure à 0 est spécifiée, elle est annulée."
 requireAdminForView: "Vous devez être connecté avec un compte administrateur pour les visualiser."
 isSystemAccount: "Ces comptes sont automatiquement créés et gérés par le système."
@@ -980,6 +992,7 @@ preset: "Préréglage"
 selectFromPresets: "Sélectionner à partir des préréglages"
 achievements: "Accomplissements"
 gotInvalidResponseError: "Réponse du serveur invalide"
+gotInvalidResponseErrorDescription: "Il se peut que le serveur soit hors ligne ou en maintenance. Veuillez réessayer plus tard."
 thisPostMayBeAnnoying: "Cette note peut gêner d'autres personnes."
 thisPostMayBeAnnoyingHome: "Publier vers le fil principal"
 thisPostMayBeAnnoyingCancel: "Annuler"
@@ -989,16 +1002,31 @@ internalServerError: "Erreur interne du serveur"
 copyErrorInfo: "Copier les détails de l’erreur"
 joinThisServer: "S'inscrire à cette instance"
 exploreOtherServers: "Trouver une autre instance"
+letsLookAtTimeline: "Jetez un coup d'œil au fil"
+disableFederationConfirm: "Voulez-vous vraiment désactiver la fédération ?"
+disableFederationConfirmWarn: "Même sans fédération, la note ne sera pas privée. Dans la plupart des cas, ce n'est pas nécessaire de désactiver la fédération."
 disableFederationOk: "Désactiver"
+invitationRequiredToRegister: "Actuellement, cette instance est uniquement sur invitation. Seuls ceux qui ont un code d'invitation peuvent s'inscrire."
+emailNotSupported: "Cette instance ne prend pas en charge l'envoi de courriels"
 postToTheChannel: "Publier au canal"
+cannotBeChangedLater: "Cela ne peut pas être modifié plus tard."
+reactionAcceptance: "Acceptation des réactions"
 likeOnly: "Les favoris uniquement"
+likeOnlyForRemote: "Toutes (mentions j'aime seulement pour les instances distantes)"
+nonSensitiveOnly: "Non sensibles seulement"
+nonSensitiveOnlyForLocalLikeOnlyForRemote: "Non sensibles seulement (mentions j'aime seulement pour les instances distantes)"
+rolesAssignedToMe: "Rôles attribués à moi"
 sensitiveWords: "Mots sensibles"
+hiddenTags: "Hashtags cachés"
+hiddenTagsDescription: "Les hashtags définis ne s'afficheront pas dans les tendances. Vous pouvez définir plusieurs hashtags en faisant un saut de ligne."
 notesSearchNotAvailable: "La recherche de notes n'est pas disponible."
 license: "Licence"
 myClips: "Mes clips"
+drivecleaner: "Nettoyeur du Disque"
 retryAllQueuesConfirmText: "Cela peut augmenter temporairement la charge du serveur."
 showClipButtonInNoteFooter: "Ajouter « Clip » au menu d'action de la note"
 reactionsDisplaySize: "Taille de l'affichage des réactions"
+limitWidthOfReaction: "Limiter la largeur maximale des réactions et les afficher en taille réduite"
 noteIdOrUrl: "Identifiant de la note ou URL"
 video: "Vidéo"
 videos: "Vidéos"
@@ -1009,6 +1037,7 @@ accountMovedShort: "Ce compte a migré"
 operationForbidden: "Opération non autorisée"
 forceShowAds: "Toujours afficher les publicités"
 addMemo: "Ajouter un mémo"
+editMemo: "Éditer le mémo"
 reactionsList: "Réactions"
 renotesList: "Liste de renotes"
 notificationDisplay: "Style des notifications"
@@ -1021,10 +1050,13 @@ vertical: "Vertical"
 horizontal: "Latéral"
 position: "Position"
 serverRules: "Règles du serveur"
+pleaseConfirmBelowBeforeSignup: "Pour vous inscrire sur cette instance, vous devez confirmer et accepter le contenu suivant."
 pleaseAgreeAllToContinue: "Pour continuer, veuillez accepter tous les champs ci-dessus."
 continue: "Continuer"
 preservedUsernames: "Noms d'utilisateur·rice réservés"
+createNoteFromTheFile: "Rédiger une note de ce fichier"
 archive: "Archive"
+channelArchiveConfirmTitle: "Voulez-vous vraiment archiver {name} ?"
 thisChannelArchived: "Ce canal a été archivé."
 displayOfNote: "Affichage de la note"
 initialAccountSetting: "Configuration initiale du profil"
@@ -1037,7 +1069,9 @@ failedToPreviewUrl: "Aperçu d'URL échoué"
 update: "Mettre à jour"
 later: "Plus tard"
 goToMisskey: "Retour vers Misskey"
+additionalEmojiDictionary: "Dictionnaires d'émojis additionnels"
 installed: "Installé"
+branding: "Image de marque"
 expirationDate: "Date d’expiration"
 waitingForMailAuth: "En attente de la vérification de l'adresse courriel"
 usedAt: "Utilisé le"
@@ -1080,6 +1114,7 @@ tosAndPrivacyPolicy: "Conditions d'utilisation et politique de confidentialité"
 avatarDecorations: "Décorations d'avatar"
 attach: "Mettre"
 detach: "Enlever"
+detachAll: "Tout enlever"
 angle: "Angle"
 flip: "Inverser"
 showAvatarDecorations: "Afficher les décorations d'avatar"
@@ -1091,6 +1126,9 @@ useGroupedNotifications: "Grouper les notifications"
 signupPendingError: "Un problème est survenu lors de la vérification de votre adresse e-mail. Le lien a peut-être expiré."
 cwNotationRequired: "Si « Masquer le contenu » est activé, une description doit être fournie."
 doReaction: "Réagir"
+code: "Code"
+reloadRequiredToApplySettings: "Le rafraîchissement est nécessaire pour que les paramètres prennent effet."
+remainingN: "Restants : {n}"
 _announcement:
   readConfirmTitle: "Marquer comme lu ?"
   shouldNotBeUsedToPresentPermanentInfo: "Puisque cela pourrait nuire considérablement à l'expérience utilisateur pour les nouveaux utilisateurs, il est recommandé d'utiliser les annonces pour afficher des informations temporaires plutôt que des informations persistantes."
@@ -1160,7 +1198,7 @@ _initialTutorial:
     tryThisFile: "Essayez de marquer l'image jointe à ce formulaire de publication comme sensible !"
     _exampleNote:
       note: "Oups, j'ai échoué à ouvrir le couvercle du natto..."
-    method: "Pour marquer un fichier joint comme sensible, cliquez sur la vignette du fichier, ouvrez le menu et cliquez sur « marquer comme sensible » ."
+    method: "Pour marquer un fichier joint comme sensible, cliquez sur la vignette du fichier pour ouvrir le menu et cliquez sur « marquer comme sensible » ."
     sensitiveSucceeded: "Quand vous joignez des fichiers, veuillez indiquer la sensibilité selon les règles du serveur."
     doItToContinue: "Marquez le fichier joint comme sensible pour procéder."
   _done:
@@ -1324,6 +1362,8 @@ _role:
   description: "Description du rôle"
   permission: "Rôle et autorisations"
   assignTarget: "Attribuer"
+  manualRoles: "Rôles manuels"
+  conditionalRoles: "Rôles conditionnels"
   condition: "Condition"
   isPublic: "Rôle public"
   options: "Options"
@@ -1341,8 +1381,10 @@ _role:
   _options:
     canManageCustomEmojis: "Gestion des émojis personnalisés"
     canManageAvatarDecorations: "Gestion des décorations d'avatar"
+    driveCapacity: "Capacité de stockage du Disque"
     wordMuteMax: "Nombre maximal de caractères dans le filtre de mots"
     canUseTranslator: "Usage de la fonctionnalité de traduction"
+    avatarDecorationLimit: "Nombre maximal de décorations d'avatar"
 _sensitiveMediaDetection:
   description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement."
   sensitivity: "Sensibilité de la détection"
@@ -1410,7 +1452,7 @@ _preferencesBackups:
   nameAlreadyExists: "Le nom de sauvegarde \"{name}\" existe déjà. Veuillez spécifier un autre nom."
   applyConfirm: "Voulez-vous appliquer la sauvegarde '{name}' au dispositif actuel ? La configuration actuelle de l'appareil sera perdue."
   saveConfirm: "Voulez-vous écraser {name} ?"
-  deleteConfirm: "Voulez-vous supprimer {name} ?"
+  deleteConfirm: "Êtes-vous sûr·e de vouloir supprimer {name} ?"
   renameConfirm: "Voulez-vous remplacer \"{old}\" par \"{new}\" ?"
   noBackups: "Aucune sauvegarde n'est disponible. L'option \"Nouvelle sauvegarde\" vous permet de sauvegarder la configuration actuelle du client sur le serveur."
   createdAt: "Créé : {date} {time}"
@@ -1547,6 +1589,14 @@ _sfx:
   notification: "Notifications"
   antenna: "Réception de l’antenne"
   channel: "Notifications de canal"
+  reaction: "Lors de la sélection de la réaction"
+_soundSettings:
+  driveFile: "Utiliser un effet sonore sur le Disque"
+  driveFileWarn: "Veuillez sélectionner le fichier sur le Disque"
+  driveFileTypeWarn: "Ce fichier n'est pas pris en charge"
+  driveFileTypeWarnDescription: "Veuillez sélectionner un fichier audio"
+  driveFileDurationWarn: "L'effet sonore est trop long"
+  driveFileDurationWarnDescription: "Utiliser un effet sonore long peut affecter l'utilisation de Misskey. Voulez-vous encore continuer ?"
 _ago:
   future: "Futur"
   justNow: "à l’instant"
@@ -1558,6 +1608,14 @@ _ago:
   monthsAgo: "Il y a {n} mois"
   yearsAgo: "Il y a {n} ans"
   invalid: "Il n'y a rien à voir ici"
+_timeIn:
+  seconds: "Dans {n}s"
+  minutes: "Dans {n}min"
+  hours: "Dans {n}h"
+  days: "Dans {n}j"
+  weeks: "Dans {n} sem."
+  months: "Dans {n} mois"
+  years: "Dans {n}a"
 _time:
   second: "s"
   minute: "min"
@@ -1575,7 +1633,7 @@ _2fa:
   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} ?"
+  removeKeyConfirm: "Êtes-vous sûr·e de vouloir supprimer {name} ?"
   renewTOTPOk: "Reconfigurer"
   renewTOTPCancel: "Pas maintenant"
   backupCodes: "Codes de Secours"
@@ -1584,8 +1642,8 @@ _permissions:
   "write:account": "Mettre à jour les informations de votre compte"
   "read:blocks": "Voir les comptes bloqués"
   "write:blocks": "Gérer les comptes bloqués"
-  "read:drive": "Parcourir le Drive"
-  "write:drive": "Écrire sur le Drive"
+  "read:drive": "Parcourir le Disque"
+  "write:drive": "Modifier le Disque"
   "read:favorites": "Afficher les favoris"
   "write:favorites": "Gérer les favoris"
   "read:following": "Voir les informations de vos abonnements"
@@ -1605,7 +1663,7 @@ _permissions:
   "read:page-likes": "Voir les mentions « J'aime » des pages"
   "write:page-likes": "Gérer les mentions « J'aime » sur les pages"
   "read:user-groups": "Voir les groupes d'utilisateur·rice·s"
-  "write:user-groups": "Éditer les groupes des utilisateur·rice·s"
+  "write:user-groups": "Éditer les groupes d'utilisateur·rice·s"
   "read:channels": "Lire les canaux"
   "write:channels": "Gérer les canaux"
   "read:gallery": "Voir la galerie"
@@ -1659,6 +1717,7 @@ _widgets:
   userList: "Liste utilisateur"
   _userList:
     chooseList: "Sélectionner une liste"
+  birthdayFollowings: "Utilisateurs qui fêtent l'anniversaire aujourd'hui"
 _cw:
   hide: "Masquer"
   show: "Afficher le contenu"
@@ -1716,8 +1775,9 @@ _profile:
   metadataDescription: "Vous pouvez afficher jusqu'à quatre informations supplémentaires dans votre profil."
   metadataLabel: "Étiquette"
   metadataContent: "Contenu"
-  changeAvatar: "Changer l'image de profil"
+  changeAvatar: "Changer l'avatar"
   changeBanner: "Changer de bannière"
+  avatarDecorationMax: "Vous pouvez mettre au plus {max} décorations d'avatar."
 _exportOrImport:
   allNotes: "Toutes les notes"
   followingList: "Abonnements"
@@ -1878,6 +1938,9 @@ _deck:
     channel: "Canal"
     mentions: "Mentions"
     direct: "Direct"
+_drivecleaner:
+  orderBySizeDesc: "Taille descendante"
+  orderByCreatedAtAsc: "Date d'ajout ascendante"
 _webhookSettings:
   name: "Nom"
   active: "Activé"
@@ -1915,6 +1978,8 @@ _moderationLogTypes:
   createAvatarDecoration: "Décoration d'avatar créée"
   updateAvatarDecoration: "Décoration d'avatar mise à jour"
   deleteAvatarDecoration: "Décoration d'avatar supprimée"
+  unsetUserAvatar: "Supprimer l'avatar de l'utilisateur·rice"
+  unsetUserBanner: "Supprimer la bannière de l'utilisateur·rice"
 _fileViewer:
   title: "Détails du fichier"
   type: "Type du fichier"
@@ -1964,3 +2029,16 @@ _externalResourceInstaller:
     _themeInstallFailed:
       title: "Échec d'installation du thème"
       description: "Il y a eu un problème lors de l'installation du thème. Veuillez réessayer. Pour plus de détails sur l'erreur, veuillez consulter la console JavaScript."
+_dataSaver:
+  _media:
+    title: "Chargement des médias"
+    description: "Empêche le chargement automatique des images et des vidéos. Appuyez sur les images et les vidéos cachées pour les charger."
+  _avatar:
+    title: "Animation d'avatars"
+    description: "Arrête l'animation d'avatars. Comme les images animées peuvent être plus volumineuses que les images normales, cela permet de réduire davantage le trafic de données."
+  _urlPreview:
+    title: "Vignettes d'aperçu des URL"
+    description: "Les vignettes d'aperçu des URL ne seront plus chargées."
+  _code:
+    title: "Mise en évidence du code"
+    description: "Si la notation de mise en évidence du code est utilisée, par exemple dans la MFM, elle ne sera pas chargée tant qu'elle n'aura pas été tapée. La mise en évidence du code nécessite le chargement du fichier de définition de chaque langue à mettre en évidence, mais comme ces fichiers ne sont plus chargés automatiquement, on peut s'attendre à une réduction du trafic de données."
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index 041c55cc2d..eebdf90646 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -437,7 +437,6 @@ share: "Bagikan"
 notFound: "Tidak dapat ditemukan"
 notFoundDescription: "Tidak ada halaman sesuai dengan URL yang ditentukan."
 uploadFolder: "Lokasi unggah folder bawaan"
-cacheClear: "Bersihkan tembolok"
 markAsReadAllNotifications: "Tandai semua notifikasi telah dibaca"
 markAsReadAllUnreadNotes: "Tandai semua catatan telah dibaca"
 markAsReadAllTalkMessages: "Tandai semua pesan telah dibaca"
@@ -564,6 +563,10 @@ output: "Keluaran"
 script: "Script"
 disablePagesScript: "Nonaktifkan script pada halaman"
 updateRemoteUser: "Perbaharui informasi pengguna instansi luar"
+unsetUserAvatar: "Hapus avatar"
+unsetUserAvatarConfirm: "Apakah kamu yakin ingin menghapus avatar?"
+unsetUserBanner: "Hapus banner"
+unsetUserBannerConfirm: "Apakah kamu yakin ingin menghapus banner?"
 deleteAllFiles: "Hapus semua berkas"
 deleteAllFilesConfirm: "Apakah kamu yakin ingin menghapus semua berkas?"
 removeAllFollowing: "Batalkan mengikuti semua pengguna"
@@ -979,6 +982,7 @@ assign: "Tetapkan\n"
 unassign: "Batalkan penetapan"
 color: "Warna"
 manageCustomEmojis: "Kelola Emoji Kustom"
+manageAvatarDecorations: "Kelola dekorasi avatar"
 youCannotCreateAnymore: "Kamu melewati batas pembuatan."
 cannotPerformTemporary: "Sementara Tidak Tersedia"
 cannotPerformTemporaryDescription: "Aksi ini tidak dapat dilakukan sementara karena melewati batas eksekusi. Mohon tunggu sejenak dan coba lagi."
@@ -1132,6 +1136,10 @@ mutualFollow: "Saling mengikuti"
 fileAttachedOnly: "Hanya catatan dengan berkas"
 showRepliesToOthersInTimeline: "Tampilkan balasan ke pengguna lain dalam lini masa"
 hideRepliesToOthersInTimeline: "Sembunyikan balasan ke orang lain dari lini masa"
+showRepliesToOthersInTimelineAll: "Tampilkan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa"
+hideRepliesToOthersInTimelineAll: "Sembuyikan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa"
+confirmShowRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk menampilkan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa?"
+confirmHideRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk menyembunyikan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa?"
 externalServices: "Layanan eksternal"
 impressum: "Impressum"
 impressumUrl: "Tautan Impressum"
@@ -1139,7 +1147,20 @@ impressumDescription: "Pada beberapa negara seperti Jerman, inklusi dari informa
 privacyPolicy: "Kebijakan Privasi"
 privacyPolicyUrl: "Tautan Kebijakan Privasi"
 tosAndPrivacyPolicy: "Syarat dan Ketentuan serta Kebijakan Privasi"
+avatarDecorations: "Dekorasi avatar"
+attach: "Lampirkan"
+detach: "Hapus"
+angle: "Sudut"
 flip: "Balik"
+showAvatarDecorations: "Tampilkan dekorasi avatar"
+releaseToRefresh: "Lepaskan untuk memuat ulang"
+refreshing: "Sedang memuat ulang..."
+pullDownToRefresh: "Tarik ke bawah untuk memuat ulang"
+disableStreamingTimeline: "Nonaktifkan pembaharuan lini masa real-time"
+useGroupedNotifications: "Tampilkan notifikasi secara dikelompokkan"
+signupPendingError: "Terdapat masalah ketika memverifikasi alamat surel. Tautan kemungkinan telah kedaluwarsa."
+cwNotationRequired: "Jika \"Sembunyikan konten\" diaktifkan, deskripsi harus disediakan."
+doReaction: "Tambahkan reaksi"
 _announcement:
   forExistingUsers: "Hanya pengguna yang telah ada"
   forExistingUsersDescription: "Pengumuman ini akan dimunculkan ke pengguna yang sudah ada dari titik waktu publikasi jika dinyalakan. Apabila dimatikan, mereka yang baru mendaftar setelah publikasi ini akan juga melihatnya."
@@ -1149,6 +1170,7 @@ _announcement:
   tooManyActiveAnnouncementDescription: "Terlalu banyak pengumuman dapat memperburuk pengalaman pengguna. Mohon pertimbangkan untuk mengarsipkan pengumuman yang sudah usang/tidak relevan."
   readConfirmTitle: "Tandai telah dibaca?"
   readConfirmText: "Aksi ini akan menandai konten dari \"{title}\" telah dibaca."
+  silence: "Tiada notifikasi"
 _initialAccountSetting:
   accountCreated: "Akun kamu telah sukses dibuat!"
   letsStartAccountSetup: "Untuk pemula, ayo atur profilmu dulu."
@@ -1161,8 +1183,25 @@ _initialAccountSetting:
   pushNotificationDescription: "Menyalakan notifikasi dorong akan membuatmu menerima notifikasi dari {name} secara langsung ke perangkatmu."
   initialAccountSettingCompleted: "Pengaturan profil selesai!"
   haveFun: "Selamat menikmati, {name}!"
+  startTutorial: "Mulai Tutorial"
   skipAreYouSure: "Yakin melewati atur profil?"
   laterAreYouSure: "Yakin banget untuk atur profil nanti?"
+_initialTutorial:
+  launchTutorial: "Lihat Tutorial"
+  title: "Tutorial"
+  skipAreYouSure: "Berhenti dari Tutorial?"
+  _landing:
+    title: "Selamat datang di Tutorial"
+    description: "Di sini kamu dapat mempelajari dasar-dasar dari penggunaan Misskey dan fitur-fiturnya."
+  _note:
+    title: "Apa itu Catatan?"
+  _postNote:
+    title: "Pengaturan posting Catatan"
+    _visibility:
+      public: "Perlihatkan catatan ke semua pengguna."
+      home: "Hanya publik ke lini masa Beranda. Pengguna yang mengunjungi profilmu melalui pengikut dan renote dapat melihatnya."
+      followers: "Perlihatkan ke pengikut saja. Hanya pengikut yang dapat melihat postinganmu dan tidak dapat direnote oleh siapapun."
+      direct: "Hanya perlihatkan ke pengguna spesifik dan penerima akan diberi tahu. Dapat juga digunakan sebagai alternatif dari pesan langsung."
 _serverRules:
   description: "Daftar peraturan akan ditampilkan sebelum pendaftaran. Mengatur ringkasan dari Syarat dan Ketentuan sangat direkomendasikan."
 _serverSettings:
@@ -1474,6 +1513,7 @@ _role:
     inviteLimitCycle: "Interval Penerbitan Kode Undangan"
     inviteExpirationTime: "Interval kedaluwarsa undangan"
     canManageCustomEmojis: "Dapat mengelola Emoji kustom"
+    canManageAvatarDecorations: "Kelola dekorasi avatar"
     driveCapacity: "Kapasitas Drive"
     alwaysMarkNsfw: "Selalu tandai berkas sebagai NSFW"
     pinMax: "Jumlah maksimal catatan yang disematkan"
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index dd0abf2287..8c233dd66a 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -15,7 +15,7 @@ gotIt: "ok!"
 cancel: "Annulla"
 noThankYou: "No grazie"
 enterUsername: "Inserisci un nome utente"
-renotedBy: "Rinotato da {user}"
+renotedBy: "Rinotata da {user}"
 noNotes: "Nessuna nota!"
 noNotifications: "Nessuna notifica"
 instance: "Istanza"
@@ -102,12 +102,12 @@ defaultNoteVisibility: "Privacy predefinita delle note"
 follow: "Segui"
 followRequest: "Richiesta di follow"
 followRequests: "Richieste di follow"
-unfollow: "Non seguire"
+unfollow: "Interrompi following"
 followRequestPending: "Richiesta in approvazione"
 enterEmoji: "Inserisci emoji"
 renote: "Rinota"
 unrenote: "Elimina la Rinota"
-renoted: "Rinotato!"
+renoted: "Rinotata!"
 cantRenote: "È impossibile rinotare questa nota."
 cantReRenote: "È impossibile rinotare una Rinota."
 quote: "Citazione"
@@ -311,6 +311,7 @@ folderName: "Nome della cartella"
 createFolder: "Nuova cartella"
 renameFolder: "Rinomina cartella"
 deleteFolder: "Elimina cartella"
+folder: "Cartella"
 addFile: "Allega"
 emptyDrive: "Il Drive è vuoto"
 emptyFolder: "La cartella è vuota"
@@ -437,7 +438,6 @@ share: "Condividi"
 notFound: "Non trovato"
 notFoundDescription: "Nessuna pagina corrisponde all'URL indicata."
 uploadFolder: "Destinazione caricamento predefinita"
-cacheClear: "Svuota cache"
 markAsReadAllNotifications: "Segna tutte le notifiche come lette"
 markAsReadAllUnreadNotes: "Segna tutte le note come lette"
 markAsReadAllTalkMessages: "Segna tutte le chat come lette"
@@ -544,6 +544,8 @@ showInPage: "Visualizza in pagina"
 popout: "Finestra pop-out"
 volume: "Volume"
 masterVolume: "Volume principale"
+notUseSound: "Non emettere suoni"
+useSoundOnlyWhenActive: "Emetti suoni solo quando Misskey è in attività"
 details: "Dettagli"
 chooseEmoji: "Scegli emoji"
 unableToProcess: "Impossibile compiere l'operazione"
@@ -564,6 +566,10 @@ output: "Uscita"
 script: "Script"
 disablePagesScript: "Disabilita AiScript nelle pagine"
 updateRemoteUser: "Aggiorna le informazioni dal profilo remoto"
+unsetUserAvatar: "Rimozione foto profilo"
+unsetUserAvatarConfirm: "Vuoi davvero rimuovere la foto profilo?"
+unsetUserBanner: "Rimuovi intestazione profilo"
+unsetUserBannerConfirm: "Vuoi davvero rimuovere l'intestazione dal profilo?"
 deleteAllFiles: "Elimina tutti i file"
 deleteAllFilesConfirm: "Vuoi davvero eliminare tutti i file?"
 removeAllFollowing: "Annulla tutti i follow"
@@ -635,6 +641,7 @@ smtpSecure: "Usare SSL/TLS implicito per le connessioni SMTP"
 smtpSecureInfo: "Disabilitare quando è attivo STARTTLS."
 testEmail: "Verifica il funzionamento"
 wordMute: "Filtri parole"
+hardWordMute: "Filtro parole forte"
 regexpError: "errore regex"
 regexpErrorDescription: "Si è verificato un errore nell'espressione regolare alla riga {line} della parola muta {tab}:"
 instanceMute: "Silenzia l'istanza"
@@ -814,7 +821,7 @@ configure: "Imposta"
 postToGallery: "Pubblicare nella galleria"
 postToHashtag: "Pubblica a questo hashtag"
 gallery: "Galleria"
-recentPosts: "Le più recenti"
+recentPosts: "Pubblicazioni recenti"
 popularPosts: "Le più visualizzate"
 shareWithNote: "Condividere in nota"
 ads: "Banner"
@@ -852,7 +859,7 @@ pubSub: "Publish/Subscribe del profilo"
 lastCommunication: "La comunicazione più recente"
 resolved: "Risolto"
 unresolved: "Non risolto"
-breakFollow: "Non farti più seguire"
+breakFollow: "Interrompi follow"
 breakFollowConfirm: "Vuoi davvero che questo profilo smetta di seguirti?"
 itsOn: "Abilitato"
 itsOff: "Disabilitato"
@@ -1020,6 +1027,8 @@ resetPasswordConfirm: "Vuoi davvero ripristinare la password?"
 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."
+hiddenTags: "Hashtag nascosti"
+hiddenTagsDescription: "Impedire la visualizzazione del tag impostato nei trend. Puoi impostare più valori, uno per riga."
 notesSearchNotAvailable: "Non è possibile cercare tra le Note."
 license: "Licenza"
 unfavoriteConfirm: "Vuoi davvero rimuovere la preferenza?"
@@ -1032,6 +1041,7 @@ enableChartsForRemoteUser: "Abilita i grafici per i profili remoti"
 enableChartsForFederatedInstances: "Abilita i grafici per le istanze federate"
 showClipButtonInNoteFooter: "Aggiungi il bottone Clip tra le azioni delle Note"
 reactionsDisplaySize: "Grandezza delle reazioni"
+limitWidthOfReaction: "Limita la larghezza delle reazioni e ridimensionale"
 noteIdOrUrl: "ID della Nota o URL"
 video: "Video"
 videos: "Video"
@@ -1114,7 +1124,7 @@ currentAnnouncements: "Annunci attuali"
 pastAnnouncements: "Annunci precedenti"
 youHaveUnreadAnnouncements: "Ci sono Annunci non letti"
 useSecurityKey: "Per utilizzare la chiave di sicurezza o la passkey, segui le indicazioni del dispositivo"
-replies: "Rispondi"
+replies: "Risposte"
 renotes: "Rinota"
 loadReplies: "Leggi le risposte"
 loadConversation: "Leggi la conversazione"
@@ -1158,6 +1168,8 @@ useGroupedNotifications: "Mostra le notifiche raggruppate"
 signupPendingError: "Si è verificato un problema durante la verifica del tuo indirizzo email. Potrebbe essere scaduto il collegamento temporaneo."
 cwNotationRequired: "Devi indicare perché il contenuto è indicato come esplicito."
 doReaction: "Reagisci"
+code: "Codice"
+reloadRequiredToApplySettings: "Per applicare le impostazioni, occorre ricaricare."
 _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."
@@ -1266,6 +1278,8 @@ _serverSettings:
   shortName: "Abbreviazione"
   shortNameDescription: "Un'abbreviazione o un nome comune che può essere visualizzato al posto del nome ufficiale lungo del server."
   fanoutTimelineDescription: "Attivando questa funzionalità migliori notevolmente la capacità delle Timeline di collezionare Note, riducendo il carico sul database. Tuttavia, aumenterà l'impiego di memoria RAM per Redis. Disattiva se il tuo server ha poca RAM o la funzionalità è irregolare."
+  fanoutTimelineDbFallback: "Elaborazione dati alternativa"
+  fanoutTimelineDbFallbackDescription: "Attivando l'elaborazione alternativa, verrà interrogato ulteriormente il database se la timeline non è nella cache. \nDisattivando, si può ridurre ulteriormente il carico del server, evitando l'elaborazione alternativa, ma limitando l'intervallo recuperabile delle timeline."
 _accountMigration:
   moveFrom: "Migra un altro profilo dentro a questo"
   moveFromSub: "Crea un alias verso un altro profilo remoto"
@@ -1536,7 +1550,9 @@ _role:
   assignTarget: "Modalità di assegnazione del ruolo"
   descriptionOfAssignTarget: "<b>Manuale</b>: per assegnare manualmente questo ruolo ai profili.\n<b>Condizionale</b>: per assegnare o rimuovere automaticamente questo ruolo ai profili, a precise condizioni."
   manual: "Manuale"
+  manualRoles: "Ruoli assegnati manualmente"
   conditional: "Condizionale"
+  conditionalRoles: "Ruoli condizionati"
   condition: "Condizioni"
   isConditionalRole: "Questo è un ruolo condizionato"
   isPublic: "Ruolo pubblico"
@@ -1660,7 +1676,7 @@ _preferencesBackups:
   list: "Elenco di impostazioni salvate in precedenza"
   saveNew: "Nuovo salvataggio"
   loadFile: "Carica da file"
-  apply: "Applicabile a questo dispositivo"
+  apply: "Applica a questo dispositivo"
   save: "Sovrascrivi il backup"
   inputName: "Inserire il nome del backup."
   cannotSave: "Impossibile salvare."
@@ -1806,6 +1822,14 @@ _sfx:
   notification: "Notifiche"
   antenna: "Ricezione dell'antenna"
   channel: "Notifiche di canale"
+  reaction: "Quando seleziono una reazione"
+_soundSettings:
+  driveFile: "Suoni del Drive"
+  driveFileWarn: "Seleziona file dal dispositivo"
+  driveFileTypeWarn: "Formato file non supportato"
+  driveFileTypeWarnDescription: "Per favore, scegli un file di tipo audio"
+  driveFileDurationWarn: "La durata dell'audio è troppo lunga"
+  driveFileDurationWarnDescription: "Scegliere un audio lungo potrebbe interferire con l'uso di Misskey. Vuoi continuare lo stesso?"
 _ago:
   future: "Futuro"
   justNow: "Adesso"
@@ -1817,6 +1841,14 @@ _ago:
   monthsAgo: "{n} mesi fa"
   yearsAgo: "{n} anni fa"
   invalid: "Niente da visualizzare"
+_timeIn:
+  seconds: "Dopo {n} secondi"
+  minutes: "Dopo {n} minuti"
+  hours: "Dopo {n} ore"
+  days: "Dopo {n} giorni"
+  weeks: "Dopo {n} settimane"
+  months: "Dopo {n} mesi"
+  years: "Dopo {n} anni"
 _time:
   second: "s"
   minute: "min"
@@ -1861,7 +1893,7 @@ _permissions:
   "read:favorites": "Visualizza i tuoi preferiti"
   "write:favorites": "Gestisci i tuoi preferiti"
   "read:following": "Vedi le informazioni di follow"
-  "write:following": "Seguire / Non seguire altri profili"
+  "write:following": "Following di altri profili"
   "read:messaging": "Visualizzare la chat"
   "write:messaging": "Gestire la chat"
   "read:mutes": "Vedi i profili silenziati"
@@ -1942,6 +1974,7 @@ _widgets:
   _userList:
     chooseList: "Seleziona una lista"
   clicker: "Cliccaggio"
+  birthdayFollowings: "Chi nacque oggi"
 _cw:
   hide: "Nascondere"
   show: "Continua la lettura..."
@@ -2240,6 +2273,8 @@ _moderationLogTypes:
   createAvatarDecoration: "Creazione decorazione della foto profilo"
   updateAvatarDecoration: "Aggiornamento decorazione foto profilo"
   deleteAvatarDecoration: "Eliminazione decorazione della foto profilo"
+  unsetUserAvatar: "Rimossa foto profilo"
+  unsetUserBanner: "Rimossa intestazione profilo"
 _fileViewer:
   title: "Dettagli del file"
   type: "Tipo di file"
@@ -2289,3 +2324,16 @@ _externalResourceInstaller:
     _themeInstallFailed:
       title: "Impossibile installare la variazione grafica"
       description: "Si è verificato un impedimento durante l'installazione della variazione grafica. Per favore riprova e consulta la console di Javascript per ottenere dettagli aggiuntivi."
+_dataSaver:
+  _media:
+    title: "Caricamento dei media"
+    description: "Impedire il caricamento automatico di immagini e video. Devi toccare le immagini o i video nascosti per caricarli."
+  _avatar:
+    title: "Immagine del profilo"
+    description: "Impedire l'animazione per l'immagine del profilo. Le immagini animate possono avere dimensioni file maggiori rispetto a quelle normali, puoi ridurre ulteriormente l'utilizzo dei dati."
+  _urlPreview:
+    title: "Anteprime delle URL"
+    description: "Impedire il caricamento delle anteprime URL."
+  _code:
+    title: "Codice evidenziato"
+    description: "Impedire che il codice sorgente sia automaticamente evidenziato. Evidenziare il codice richiede il caricamento di un file per ogni linguaggio. Puoi evidenziare soltanto il codice che intendi leggere e ridurre il traffico inutilizzato."
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index aa68def1da..c1d60d8a4c 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -15,7 +15,7 @@ gotIt: "ほい"
 cancel: "やめとく"
 noThankYou: "やめとく"
 enterUsername: "ユーザー名を入れてや"
-renotedBy: "{user}がRenoteしたで"
+renotedBy: "{user}がリノートしたで"
 noNotes: "ノートはあらへん"
 noNotifications: "通知はあらへん"
 instance: "サーバー"
@@ -38,7 +38,7 @@ addUser: "ユーザーを追加や"
 favorite: "お気に入り"
 favorites: "お気に入り"
 unfavorite: "やっぱ気に入らん"
-favorited: "お気に入りに入れたで"
+favorited: "お気に入りに入れたで。"
 alreadyFavorited: "もうお気に入りに入れとるがな。"
 cantFavorite: "アカン、お気に入りに入れれんかったわ。"
 pin: "ピン留めしとく"
@@ -48,9 +48,9 @@ copyLink: "リンクをコピー"
 copyLinkRenote: "リノートのリンクをコピーするで?"
 delete: "ほかす"
 deleteAndEdit: "ほかして直す"
-deleteAndEditConfirm: "このノートをほかしてもっかい直す?このノートへのツッコミ、Renote、返信も全部消えるんやけどそれでもええん?"
+deleteAndEditConfirm: "このノートをほかしてもっかい直す?このノートへのツッコミ、リノート、返信も全部消えるんやけどそれでもええん?"
 addToList: "リストに入れたる"
-addToAntenna: "アンテナに追加"
+addToAntenna: "アンテナに入れる"
 sendMessage: "メッセージを送る"
 copyRSS: "RSSをコピー"
 copyUsername: "ユーザー名をコピー"
@@ -59,7 +59,7 @@ copyNoteId: "ノートIDをコピー"
 copyFileId: "ファイルIDをコピー"
 copyFolderId: "フォルダーIDをコピー"
 copyProfileUrl: "プロフィールURLをコピー"
-searchUser: "ユーザーを検索"
+searchUser: "ユーザーを探す"
 reply: "返事"
 loadMore: "まだまだあるで!"
 showMore: "まだまだあるで!"
@@ -68,7 +68,7 @@ youGotNewFollower: "フォローされたで"
 receiveFollowRequest: "フォローリクエストされたで"
 followRequestAccepted: "フォローが承認されたで"
 mention: "メンション"
-mentions: "自分宛て"
+mentions: "あんた宛て"
 directNotes: "ダイレクト投稿"
 importAndExport: "インポートとエクスポート"
 import: "インポート"
@@ -88,7 +88,7 @@ followers: "フォロワー"
 followsYou: "フォローされとるで"
 createList: "リスト作る"
 manageLists: "リストの管理"
-error: "エラー"
+error: "おかしなったで"
 somethingHappened: "なんかあかんわ"
 retry: "もっぺんやる?"
 pageLoadError: "ページが読み込めんかったわ。"
@@ -105,13 +105,13 @@ followRequests: "フォロー申請"
 unfollow: "フォローやめる"
 followRequestPending: "フォロー許してくれるん待っとる"
 enterEmoji: "絵文字を入れてや"
-renote: "Renote"
-unrenote: "Renoteやめる"
-renoted: "Renoteしたで。"
-cantRenote: "この投稿はRenoteできへんらしい。"
-cantReRenote: "Renote自体はRenoteできへんで。"
+renote: "リノート"
+unrenote: "リノートやめる"
+renoted: "リノートしたで。"
+cantRenote: "この投稿はリノートできへんっぽい。"
+cantReRenote: "リノート自体はリノートできへんで。"
 quote: "引用"
-inChannelRenote: "チャンネル内Renote"
+inChannelRenote: "チャンネルの中でリノート"
 inChannelQuote: "チャンネル内引用"
 pinnedNote: "ピン留めされとるノート"
 pinned: "ピン留めしとく"
@@ -130,8 +130,8 @@ unmarkAsSensitive: "そこまでアカンことないやろ"
 enterFileName: "ファイル名を入れてや"
 mute: "ミュート"
 unmute: "ミュートやめたる"
-renoteMute: "Renoteは見いひん"
-renoteUnmute: "Renoteもやっぱ見るわ"
+renoteMute: "リノートは見いひん"
+renoteUnmute: "リノートもやっぱ見るわ"
 block: "ブロック"
 unblock: "ブロックやめたる"
 suspend: "凍結"
@@ -141,13 +141,13 @@ unblockConfirm: "ブロックやめたるってほんまか?"
 suspendConfirm: "凍結してしもうてええか?"
 unsuspendConfirm: "解凍するけどええか?"
 selectList: "リストを選ぶ"
-editList: "リスト直すで"
+editList: "リストいじる"
 selectChannel: "チャンネルを選ぶ"
 selectAntenna: "アンテナを選ぶ"
-editAntenna: "アンテナを編集"
+editAntenna: "アンテナいじる"
 selectWidget: "ウィジェットを選ぶ"
 editWidgets: "ウィジェットをいじる"
-editWidgetsExit: "編集終ったで"
+editWidgetsExit: "いじるのをやめる"
 customEmojis: "カスタム絵文字"
 emoji: "絵文字"
 emojis: "絵文字"
@@ -156,14 +156,14 @@ emojiUrl: "絵文字画像URL"
 addEmoji: "絵文字を追加"
 settingGuide: "ええ感じの設定"
 cacheRemoteFiles: "リモートのファイルをキャッシュする"
-cacheRemoteFilesDescription: "この設定を切っとったら、リモートファイルをキャッシュせんと直リンクするようになるで。サーバーの容量は節約できるけど、サムネイルを作らんなるから通信量が増えるで。"
+cacheRemoteFilesDescription: "この設定を入れとったら、リモートのファイルを端から端までこのサーバーのキャッシュん中突っ込むようになるで。画像映し出すんがめっちゃ速うなるけど、サーバーの容量をやたらと食うようになるで。リモートの人がどんだけ長くキャッシュを持っとくかはドライブ容量の制限で決めとくで。制限を超えたら古いのから順々に消してって、かわりにリンクになるで。この設定を切ったら、リモートのファイルは最初っからリンクとして扱うことにするけど、画像のサムネ作るのとかみんなのプライバシー守るために、default.ymlのproxyRemoteFilesをtrueにしといたほうがええよ。"
 youCanCleanRemoteFilesCache: "ファイル管理にある🗑️ボタンでキャッシュ全部ほかすで。"
-cacheRemoteSensitiveFiles: "リモートのセンシティブなファイルをキャッシュする"
-cacheRemoteSensitiveFilesDescription: "この設定を無効にすると、リモートのセンシティブなファイルはキャッシュせず直リンクするようになるで。"
+cacheRemoteSensitiveFiles: "リモートのきわどいファイルをキャッシュに突っ込む"
+cacheRemoteSensitiveFilesDescription: "この設定を切ると、リモートのきわどいファイルはキャッシュせず直でリンクするようになるで。"
 flagAsBot: "Botにするで"
 flagAsBotDescription: "もしこのアカウントをプログラム使うて運用するんやったら、このフラグをオンにしてや。オンにすれば、反応がバーッて連鎖せんように開発者が使うたり、Misskeyのシステム上での扱いがBotに合ったもんになるからな。"
-flagAsCat: "Catやで"
-flagAsCatDescription: "ワレ、猫ちゃんならこのフラグをつけてみ?"
+flagAsCat: "猫や。かわええな。"
+flagAsCatDescription: "ネコになりたいんならこれつけとき。"
 flagShowTimelineReplies: "タイムラインにノートへの返信を表示するで"
 flagShowTimelineRepliesDescription: "オンにしたら、タイムラインにユーザーのノートの他にもそのユーザーの他のノートへの返信を表示するで。"
 autoAcceptFollowed: "フォローしとるユーザーからのフォローリクエストを勝手に許可しとく"
@@ -214,12 +214,12 @@ clearQueueConfirmText: "未配達の投稿は配送されんなるで。ふつ
 clearCachedFiles: "キャッシュをほかす"
 clearCachedFilesConfirm: "キャッシュされとるリモートファイルをみんなほかしてええか?"
 blockedInstances: "ブロックしたサーバー"
-blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定してな。ブロックされてもうたサーバーとはもう金輪際やり取りできひんくなるで。ついでにそのサブドメインもブロックするで。"
+blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定してな。ブロックされてもうたサーバーとはもう金輪際やり取りできひんくなるで。"
 silencedInstances: "サーバーサイレンスされてんねん"
 silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定すんで。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになり、フォロワーでないローカルアカウントにはメンションできなくなんねん。ブロックしたインスタンスには影響せーへんで。"
 muteAndBlock: "ミュートとブロック"
-mutedUsers: "ミュートしたユーザー"
-blockedUsers: "ブロックしたユーザー"
+mutedUsers: "ミュートしとるユーザー"
+blockedUsers: "ブロックしとるユーザー"
 noUsers: "ユーザーはおらん"
 editProfile: "プロフィールをいじる"
 noteDeleteConfirm: "このノートをほかしてええか?"
@@ -246,7 +246,7 @@ changePassword: "パスワードをいじる"
 security: "セキュリティ"
 retypedNotMatch: "入れたやつ合うてへんわ。"
 currentPassword: "今のパスワード"
-newPassword: "次のパスワード"
+newPassword: "今度のパスワード"
 newPasswordRetype: "今度のパスワード(もっぺん入れて)"
 attachFile: "ファイルのっける"
 more: "他のん"
@@ -311,6 +311,7 @@ folderName: "フォルダー名"
 createFolder: "フォルダー作る"
 renameFolder: "フォルダー名を変える"
 deleteFolder: "フォルダーをほかす"
+folder: "フォルダー"
 addFile: "ファイルを追加"
 emptyDrive: "ドライブは空っぽや"
 emptyFolder: "このフォルダーは空や"
@@ -324,7 +325,7 @@ copyUrl: "URLをコピー"
 rename: "名前を変えるで"
 avatar: "アイコン"
 banner: "バナー"
-displayOfSensitiveMedia: "センシティブなメディアの表示"
+displayOfSensitiveMedia: "きわどいやつの表示"
 whenServerDisconnected: "サーバーとの接続が失くなってしもうたとき"
 disconnectedFromServer: "サーバーが機嫌悪いねん"
 reload: "リロード"
@@ -412,7 +413,7 @@ userList: "リスト"
 about: "情報"
 aboutMisskey: "Misskeyってなんや?"
 administrator: "管理者"
-token: "トークン"
+token: "確認コード"
 2fa: "二要素認証"
 setupOf2fa: "二要素認証のセットアップ"
 totp: "認証アプリ"
@@ -425,7 +426,7 @@ moderationLogs: "モデログ"
 nUsersMentioned: "{n}人が投稿"
 securityKeyAndPasskey: "セキュリティキー・パスキー"
 securityKey: "セキュリティキー"
-lastUsed: "最後につこうた日"
+lastUsed: "最後に使うた日"
 lastUsedAt: "最後に使うたんは: {t}"
 unregister: "登録やめる"
 passwordLessLogin: "パスワード無くてもログインできるようにする"
@@ -437,7 +438,6 @@ share: "わけわけ"
 notFound: "見つからへんね"
 notFoundDescription: "言われたURLにはまるページはなかったで。"
 uploadFolder: "とりあえずアップロードしたやつ置いとく所"
-cacheClear: "キャッシュをほかす"
 markAsReadAllNotifications: "通知はもう全て読んだわっ"
 markAsReadAllUnreadNotes: "投稿は全て読んだわっ"
 markAsReadAllTalkMessages: "チャットはもうぜんぶ読んだわっ"
@@ -471,7 +471,7 @@ weakPassword: "へぼいパスワード"
 normalPassword: "ぼちぼちのパスワード"
 strongPassword: "ええ感じのパスワード"
 passwordMatched: "よし!一致や!"
-passwordNotMatched: "一致しとらんで?"
+passwordNotMatched: "ちゃうで?"
 signinWith: "{x}でログイン"
 signinFailed: "ログインできんかったで。もっかいユーザー名とパスワードを確認してみてや。"
 or: "それか"
@@ -525,7 +525,7 @@ objectStorageEndpointDesc: "S3のときは空、それ以外は各サービス
 objectStorageRegion: "Region"
 objectStorageRegionDesc: "'xx-east-1'みたいなregionを指定したってやー。使ってるサービスにregionの概念がないときは、空か'us-east-1'にするんやで。"
 objectStorageUseSSL: "SSLを使う"
-objectStorageUseSSLDesc: "API接続にhttpsを使わん場合はオフにするんやで"
+objectStorageUseSSLDesc: "API接続にhttpsを使わんのやったら消しといて"
 objectStorageUseProxy: "Proxyを使う"
 objectStorageUseProxyDesc: "API接続にproxy使わんのやったら切ってくれへん?"
 objectStorageSetPublicRead: "アップロードした時に'public-read'を設定してや"
@@ -536,14 +536,16 @@ showFixedPostForm: "タイムラインの上の方で投稿できるようにや
 showFixedPostFormInChannel: "タイムラインの上の方で投稿できるようにするわ(チャンネル)"
 withRepliesByDefaultForNewlyFollowed: "フォローする時、デフォルトで返信をタイムラインに含むようにしよか"
 newNoteRecived: "新しいノートがあるで"
-sounds: "サウンド"
-sound: "サウンド"
+sounds: "音"
+sound: "音"
 listen: "聴く"
 none: "なし"
 showInPage: "ページで表示"
 popout: "ポップアウト"
 volume: "やかましさ"
 masterVolume: "全体のやかましさ"
+notUseSound: "音出さへん"
+useSoundOnlyWhenActive: "Misskeyがアクティブなときだけ音出す"
 details: "もっと"
 chooseEmoji: "絵文字を選ぶ"
 unableToProcess: "なんか奥の方で詰まってもうた"
@@ -564,6 +566,10 @@ output: "出力"
 script: "スクリプト"
 disablePagesScript: "Pagesのスクリプトを無効にしてや"
 updateRemoteUser: "リモートユーザー情報の更新してくれん?"
+unsetUserAvatar: "アイコン戻す"
+unsetUserAvatarConfirm: "アイコンを元に戻すで?"
+unsetUserBanner: "バナー戻す"
+unsetUserBannerConfirm: "バナー元に戻すで?"
 deleteAllFiles: "ファイルを全部ほかす"
 deleteAllFilesConfirm: "ホンマにファイル全部ほかすんか?消したもんはもう戻ってこんのやで?"
 removeAllFollowing: "フォローを全解除"
@@ -575,7 +581,7 @@ yourAccountSuspendedDescription: "あんたのアカウントは、サーバー
 tokenRevoked: "トークンが無効やで"
 tokenRevokedDescription: "ログイントークンが失効しとるで。もっかいログインしてもろてもええか?"
 accountDeleted: "アカウントは削除されとるで"
-accountDeletedDescription: "このアカウントは削除されとるで。"
+accountDeletedDescription: "このアカウントはもう消えとる。"
 menu: "メニュー"
 divider: "分割線"
 addItem: "項目を追加"
@@ -591,9 +597,9 @@ enableInfiniteScroll: "自動でもっと見る"
 visibility: "公開範囲"
 poll: "アンケート"
 useCw: "内容を隠す"
-enablePlayer: "プレイヤーを開く"
-disablePlayer: "プレイヤーを閉じる"
-expandTweet: "ポストを展開する"
+enablePlayer: "プレイヤー開く"
+disablePlayer: "プレイヤー閉じる"
+expandTweet: "ポスト展開しとく"
 themeEditor: "テーマエディター"
 description: "説明"
 describeFile: "キャプションを付ける"
@@ -606,7 +612,7 @@ preferencesBackups: "設定のバックアップ"
 deck: "デッキ"
 undeck: "デッキ解除"
 useBlurEffectForModal: "モーダルにぼかし効果を使用"
-useFullReactionPicker: "フル機能の突っ込みピッカーを使用"
+useFullReactionPicker: "フルフルのツッコミピッカーを使う"
 width: "幅"
 height: "高さ"
 large: "大"
@@ -635,6 +641,7 @@ smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する"
 smtpSecureInfo: "STARTTLS使っとる時はオフにしてや。"
 testEmail: "配信テスト"
 wordMute: "ワードミュート"
+hardWordMute: "ハードワードミュート"
 regexpError: "正規表現エラー"
 regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが出てきたで:"
 instanceMute: "サーバーミュート"
@@ -650,7 +657,7 @@ database: "データベース"
 channel: "チャンネル"
 create: "作成"
 notificationSetting: "通知設定"
-notificationSettingDesc: "表示する通知の種類えらんでや。"
+notificationSettingDesc: "出す通知の種類えらんでや。"
 useGlobalSetting: "グローバル設定を使ってや"
 useGlobalSettingDesc: "オンにすると、アカウントの通知設定が使われるで。オフにすると、別々に設定できるようになるで。"
 other: "その他"
@@ -687,18 +694,18 @@ clip: "クリップ"
 createNew: "新しく作るで"
 optional: "任意"
 createNewClip: "新しいクリップを作るで"
-unclip: "クリップ解除するで"
-confirmToUnclipAlreadyClippedNote: "このノートはすでにクリップ「{name}」に含まれとるで。ノートをこのクリップから除外しよか?"
+unclip: "クリップやめとく"
+confirmToUnclipAlreadyClippedNote: "このノートはもう「{name}」に含まれとるで。ノート、このクリップから外そか?"
 public: "パブリック"
 private: "非公開"
-i18nInfo: "Misskeyは有志によっていろんな言語に翻訳されとるで。{link}で翻訳に協力したってやー。"
+i18nInfo: "Misskeyは有志がいろんな言語に訳しとるで。{link}で翻訳に協力したってやー。"
 manageAccessTokens: "アクセストークンの管理"
 accountInfo: "アカウント情報"
 notesCount: "ノートの数やで"
 repliesCount: "返信した数やで"
-renotesCount: "Renoteした数やで"
+renotesCount: "リノートした数やで"
 repliedCount: "返信された数やで"
-renotedCount: "Renoteされた数やで"
+renotedCount: "リノートされた数やで"
 followingCount: "フォロー数やで"
 followersCount: "フォロワー数やで"
 sentReactionsCount: "ツッコんだ数"
@@ -715,7 +722,7 @@ lockedAccountInfo: "フォローを承認制にしとっても、ノートの公
 alwaysMarkSensitive: "デフォルトでメディアを閲覧注意にするで"
 loadRawImages: "添付画像のサムネイルをオリジナル画質にするで"
 disableShowingAnimatedImages: "アニメーション画像を再生せんとくで"
-highlightSensitiveMedia: "メディアがセンシティブなことをめっっちゃわかりやすく表紙"
+highlightSensitiveMedia: "きわどいことをめっっちゃわかりやすくする"
 verificationEmailSent: "無事確認のメールを送れたで。メールに書いてあるリンクにアクセスして、設定を完了してなー。"
 notSet: "未設定"
 emailVerified: "メールアドレスは確認されたで"
@@ -727,7 +734,7 @@ useSystemFont: "システムのデフォルトのフォントを使うで"
 clips: "クリップ"
 experimentalFeatures: "おためし機能やで"
 experimental: "実験的"
-thisIsExperimentalFeature: "これは実験的な機能やで。仕様が変更になったりちゃんと動かなかったりするかもやで。"
+thisIsExperimentalFeature: "これは実験的な機能やから、仕様が変わったりちゃんと動かんかったりするかもしれん。"
 developer: "開発者やで"
 makeExplorable: "アカウントを見つけやすくするで"
 makeExplorableDescription: "オフにすると、「みつける」にアカウントが載らんくなるで。"
@@ -745,7 +752,7 @@ onlineUsersCount: "{n}人が起きとるで"
 nUsers: "{n}ユーザー"
 nNotes: "{n}ノート"
 sendErrorReports: "エラーリポートを送る"
-sendErrorReportsDescription: "オンにしたら、変なことが起きたときにエラーの詳細がMisskeyに送られて、ソフトウェアの品質向上に使えるようになるで。エラー情報には、OSのバージョン、ブラウザの種類、行動履歴なんかが含まれるで。"
+sendErrorReportsDescription: "オンにしたら、なんか変なことが起きたとき、詳しいのが全部Misskeyに送られて、ソフトウェアをもっと良うするで。エラー情報には、OSのバージョン、ブラウザの種類、行動履歴なんかが含まれるな。"
 myTheme: "マイテーマ"
 backgroundColor: "背景"
 accentColor: "アクセント"
@@ -761,8 +768,8 @@ deleteConfirm: "ホンマにほかすで?"
 invalidValue: "有効な値じゃないみたいやで。"
 registry: "レジストリ"
 closeAccount: "アカウントを閉鎖する"
-currentVersion: "現在のバージョン"
-latestVersion: "最新のバージョン"
+currentVersion: "今のやつ"
+latestVersion: "いっちゃん新しいやつ"
 youAreRunningUpToDateClient: "今使ってるクライアントが最新やで!"
 newVersionOfClientAvailable: "新しいバージョンのクライアントが使えるで。"
 usageAmount: "使用量"
@@ -784,9 +791,9 @@ goBack: "戻る"
 unlikeConfirm: "いいね解除するんか?"
 fullView: "フルビュー"
 quitFullView: "フルビュー解除"
-addDescription: "説明を追加するで"
-userPagePinTip: "個々のノートのメニューから「ピン留め」を選んどくと、ここにノートを表示しておけるで。"
-notSpecifiedMentionWarning: "宛先に含まれてへんメンションがあるで"
+addDescription: "説明を入れるで"
+userPagePinTip: "ノートのメニューから「ピン留め」を選んどいたら、ここにノートを置いとけるで。"
+notSpecifiedMentionWarning: "宛先にないメンションがあるで"
 info: "情報"
 userInfo: "ユーザー情報やで"
 unknown: "不明"
@@ -798,7 +805,7 @@ active: "アクティブ"
 offline: "オフライン"
 notRecommended: "あんま推奨しやんで"
 botProtection: "Botプロテクション"
-instanceBlocking: "サーバーブロック"
+instanceBlocking: "サーバーブロック・サイレンス"
 selectAccount: "アカウントを選んでなー"
 switchAccount: "アカウントを変えるで"
 enabled: "有効"
@@ -858,7 +865,7 @@ itsOn: "オンになっとるよ"
 itsOff: "オフになってるで"
 on: "オン"
 off: "オフ"
-emailRequiredForSignup: "アカウント登録にメールアドレスを必須にするで"
+emailRequiredForSignup: "アカウント作るのにメールアドレスを必須にするで"
 unread: "未読"
 filter: "フィルタ"
 controlPanel: "コントロールパネル"
@@ -872,7 +879,7 @@ ffVisibility: "つながりの公開範囲"
 ffVisibilityDescription: "あんたのフォロー/フォロワー情報の公開範囲を設定できるで。"
 continueThread: "さらにスレッドを見るで"
 deleteAccountConfirm: "アカウントを消すで?ええんか?"
-incorrectPassword: "パスワードがちゃうで。"
+incorrectPassword: "パスワードがちゃうわ。"
 voteConfirm: "「{choice}」に投票するんか?"
 hide: "隠す"
 useDrawerReactionPickerForMobile: "ケータイとかのときドロワーで表示するで"
@@ -900,8 +907,8 @@ oneMonth: "1ヶ月"
 reflectMayTakeTime: "反映されるまで時間がかかることがあるで"
 failedToFetchAccountInformation: "アカウントの取得に失敗したみたいや…"
 rateLimitExceeded: "レート制限が超えたみたいやで"
-cropImage: "画像のクロップ"
-cropImageAsk: "画像をクロップしてもええか?"
+cropImage: "画像切り取り"
+cropImageAsk: "画像を切り取ってもええか?"
 cropYes: "切り抜いたる"
 cropNo: "切り抜かへん"
 file: "ファイル"
@@ -912,18 +919,18 @@ thereIsUnresolvedAbuseReportWarning: "未対応の通報があるみたいやで
 recommended: "推奨"
 check: "チェック"
 driveCapOverrideLabel: "このユーザーのドライブ容量上限を変更するで"
-driveCapOverrideCaption: "0以下を指定すると解除されるで。"
-requireAdminForView: "これを見るには管理者アカウントでログインしとらなあかんで。"
+driveCapOverrideCaption: "0以下にしたら解除されるで。"
+requireAdminForView: "これ見たいんなら管理者じゃないとアカンわ。"
 isSystemAccount: "システムが自動で作成・管理しとるアカウントやで。"
-typeToConfirm: "この操作をやるんなら {x} と入力してなー"
+typeToConfirm: "これやるんなら {x} って入力してなー"
 deleteAccount: "アカウント削除するで"
 document: "ドキュメント"
 numberOfPageCache: "ページ、どんだけキャッシュすんの?"
-numberOfPageCacheDescription: "増やすと使いやすくなる、負荷とメモリ使用量が増えてくで。一長一短やな。"
+numberOfPageCacheDescription: "増やすと使いやすくなるけど、負荷とメモリ使用量が増えてくで。一長一短やな。"
 logoutConfirm: "ログアウトしまっか?"
 lastActiveDate: "最後に使った日時"
 statusbar: "ステータスバー"
-pleaseSelect: "選択したってやー"
+pleaseSelect: "選んだってやー"
 reverse: "反転"
 colored: "色付き"
 refreshInterval: "更新間隔"
@@ -932,28 +939,28 @@ type: "タイプ"
 speed: "速度"
 slow: "遅い"
 fast: "速い"
-sensitiveMediaDetection: "センシティブなメディアの検出"
-localOnly: "ローカルのみ"
-remoteOnly: "リモートのみ"
+sensitiveMediaDetection: "きわどいやつの検出"
+localOnly: "ローカルだけ"
+remoteOnly: "リモートだけ"
 failedToUpload: "アップロードに失敗してもうたわ…"
-cannotUploadBecauseInappropriate: "不適切な内容を含むかもしれへんって判定されたからアップロードできへんわ。"
-cannotUploadBecauseNoFreeSpace: "ドライブの空き容量が無いからアップロードできへんわ。"
+cannotUploadBecauseInappropriate: "きわどい内容を含むかもしれへんって言われたからアップロードできへんわ。"
+cannotUploadBecauseNoFreeSpace: "ドライブがもうパンパンやからアップロードできへんわ。"
 cannotUploadBecauseExceedsFileSizeLimit: "ファイルが思うたよりも大きいさかいアップロードできへんでこれ。"
 beta: "ベータ"
-enableAutoSensitive: "自動NSFW判定"
+enableAutoSensitive: "自動できわどいか判断する"
 enableAutoSensitiveDescription: "使える時は、機械学習を使って自動でメディアにNSFWフラグを設定するで。この機能をオフにしても、サーバーによっては自動で設定されることがあるで。"
-activeEmailValidationDescription: "ユーザーのメールアドレスのバリデーションを、捨てアドかどうかや実際に通信可能かどうかとかを判定して積極的に行うで。オフにすると単に文字列として正しいかどうかだけチェックするで。"
+activeEmailValidationDescription: "ユーザーのメアドのバリデーションを、捨てアドかどうかとか、ちゃんと通信できるかとかを見るで。切ったら単に文字列として合っとるかどうかだけ見るわ。"
 navbar: "ナビゲーションバー"
 shuffle: "シャッフルするで"
 account: "アカウント"
-move: "移動するで"
+move: "移すで"
 pushNotification: "プッシュ通知"
 subscribePushNotification: "プッシュ通知をオンにするで"
 unsubscribePushNotification: "プッシュ通知を止めるで"
 pushNotificationAlreadySubscribed: "プッシュ通知はオンになってるで"
 pushNotificationNotSupported: "ブラウザかサーバーがプッシュ通知に対応してないみたいやで。"
 sendPushNotificationReadMessage: "通知やメッセージが既読になったらプッシュ通知を消すで"
-sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」っていう表示が一瞬表示されるようになるで。端末の電池使用量が増える可能性があるで。"
+sendPushNotificationReadMessageCaption: "あんたの端末の電池使う量が増えるかもしれん。"
 windowMaximize: "最大化"
 windowMinimize: "最小化"
 windowRestore: "元に戻す"
@@ -963,30 +970,30 @@ tools: "ツール"
 cannotLoad: "読み込めへんで"
 numberOfProfileView: "プロフィール表示回数"
 like: "ええやん!"
-unlike: "いいねを解除"
+unlike: "いいねやめる"
 numberOfLikes: "いいね数"
 show: "表示"
 neverShow: "今後表示しない"
 remindMeLater: "また後で"
 didYouLikeMisskey: "Misskey気に入ってくれた?"
-pleaseDonate: "Misskeyは{host}が使用している無料のソフトウェアやで。これからも開発を続けれるように、寄付したってな~。"
+pleaseDonate: "Misskeyは{host}が使うとる無料のソフトウェアやで。これからも開発を続けれるように、寄付したってな~。"
 roles: "ロール"
 role: "ロール"
 noRole: "ロールはありまへん"
 normalUser: "一般ユーザー"
 undefined: "未定義"
 assign: "アサイン"
-unassign: "アサインを解除"
+unassign: "アサインやめる"
 color: "色"
 manageCustomEmojis: "カスタム絵文字の管理"
 manageAvatarDecorations: "アバターを飾るモンの管理"
 youCannotCreateAnymore: "これ以上作れなさそうやわ"
-cannotPerformTemporary: "一時的に利用できへんで"
-cannotPerformTemporaryDescription: "操作回数が制限を超えたから一時的に利用できへんくなったで。ちょっと時間置いてからもう一回やってやー。"
+cannotPerformTemporary: "ちょっといまは使えへんで"
+cannotPerformTemporaryDescription: "操作し過ぎてちょっと今は使えへんくしとるで。ちょっと待ってからもっかいやってや。"
 invalidParamError: "パラメータがエラー言うとりますわ"
-invalidParamErrorDescription: "リクエストパラメータに問題があんねん。普通はバグやねんけど、もしかすると入力した文字数が多すぎるとかの可能性もあるから確認してや〜"
+invalidParamErrorDescription: "リクエストパラメータが変やわ。だいたいはバグやねんけど、もしかしたら入れた文字が多すぎるとかかもしれんから確認してや〜"
 permissionDeniedError: "操作が拒否されてもうた。"
-permissionDeniedErrorDescription: "自分のアカウントにはこの操作を行う権限があらへんねん"
+permissionDeniedErrorDescription: "このアカウントはこれやったらアカンって。"
 preset: "プリセット"
 selectFromPresets: "プリセットから選ぶ"
 achievements: "実績"
@@ -996,15 +1003,15 @@ thisPostMayBeAnnoying: "この投稿は迷惑かもしらんで。"
 thisPostMayBeAnnoyingHome: "ホームに投稿"
 thisPostMayBeAnnoyingCancel: "やめとく"
 thisPostMayBeAnnoyingIgnore: "このまま投稿"
-collapseRenotes: "見たことあるRenoteは飛ばして表示するで"
+collapseRenotes: "見たことあるリノートは飛ばして表示するで"
 internalServerError: "サーバー内部エラー"
-internalServerErrorDescription: "サーバー内部でよう分からんエラーやわ"
-copyErrorInfo: "エラー情報をコピー"
+internalServerErrorDescription: "サーバーでなんか変なこと起こっとるわ。"
+copyErrorInfo: "エラー情報をコピるで"
 joinThisServer: "このサーバーに登録するわ"
 exploreOtherServers: "他のサーバー見てみる"
 letsLookAtTimeline: "タイムライン見てみーや"
 disableFederationConfirm: "連合なしにしとくか?"
-disableFederationConfirmWarn: "連合なしにしても投稿は非公開にはならへんで。大体の場合は連合なしにする必要はないで。"
+disableFederationConfirmWarn: "連合なしにしても投稿が非公開になるわけちゃうで。大体の場合は連合なしにする必要はないで。"
 disableFederationOk: "連合なしにしとく"
 invitationRequiredToRegister: "今このサーバー招待制になってもうてんねん。招待コードを持っとるんやったら登録できるで。"
 emailNotSupported: "このサーバーはメール配信がサポートされてへんみたいやわ"
@@ -1013,14 +1020,16 @@ cannotBeChangedLater: "後からは変えられへんで。"
 reactionAcceptance: "ツッコミの受け入れ"
 likeOnly: "いいねだけ"
 likeOnlyForRemote: "リモートからはいいねだけな"
-nonSensitiveOnly: "センシティブじゃないやつだけ"
-nonSensitiveOnlyForLocalLikeOnlyForRemote: "センシティブじゃないやつだけ (リモートはいいねだけ)"
+nonSensitiveOnly: "いつ見ても大丈夫なやつだけ"
+nonSensitiveOnlyForLocalLikeOnlyForRemote: "いつ見ても大丈夫なやつだけ (リモートはいいねだけ)"
 rolesAssignedToMe: "自分に割り当てられたロール"
 resetPasswordConfirm: "パスワード作り直すんでええな?"
 sensitiveWords: "けったいな単語"
 sensitiveWordsDescription: "設定した単語が入っとるノートの公開範囲をホームにしたるわ。改行で区切ったら複数設定できるで。"
 sensitiveWordsDescription2: "スペースで区切るとAND指定、キーワードをスラッシュで囲んだら正規表現や。"
-notesSearchNotAvailable: "ノート検索は使われへんで。"
+hiddenTags: "見えてへんハッシュタグ"
+hiddenTagsDescription: "設定したタグを最近流行りのとこに見えんようにすんで。複数設定するときは改行で区切ってな。"
+notesSearchNotAvailable: "なんかノート探せへん。"
 license: "ライセンス"
 unfavoriteConfirm: "ほんまに気に入らんの?"
 myClips: "自分のクリップ"
@@ -1031,20 +1040,21 @@ retryAllQueuesConfirmText: "一時的にサーバー重なるかもしれへん
 enableChartsForRemoteUser: "リモートユーザーのチャートを作る"
 enableChartsForFederatedInstances: "リモートサーバーのチャートを作る"
 showClipButtonInNoteFooter: "ノートのアクションにクリップを追加"
-reactionsDisplaySize: "リアクションの表示のでかさ"
+reactionsDisplaySize: "ツッコミの表示のでかさ"
+limitWidthOfReaction: "ツッコミの最大横幅を制限して、ちっさく表示するで"
 noteIdOrUrl: "ノートIDかURL"
 video: "動画"
 videos: "動画"
 dataSaver: "データケチケチ"
 accountMigration: "アカウントのお引っ越し"
 accountMoved: "このユーザーはさらのアカウントに引っ越したで:"
-accountMovedShort: "このアカウントは移行されとるで"
+accountMovedShort: "このアカウントは引っ越し済みや"
 operationForbidden: "この操作はできまへん"
-forceShowAds: "常に広告を表示しとく"
+forceShowAds: "いっつも広告を映す"
 addMemo: "メモを足す"
 editMemo: "メモをいらう"
 reactionsList: "ツッコミ一覧"
-renotesList: "Renote一覧"
+renotesList: "リノート一覧"
 notificationDisplay: "通知見せる"
 leftTop: "左上"
 rightTop: "右上"
@@ -1056,7 +1066,7 @@ horizontal: "横"
 position: "位置"
 serverRules: "サーバールール"
 pleaseConfirmBelowBeforeSignup: "このサーバーに登録する前に、下に書いてること確認してな。"
-pleaseAgreeAllToContinue: "続けるんやったら、全ての「せやな」にチェック入れてる必要があるで。"
+pleaseAgreeAllToContinue: "続けるんやったら、全部にチェック入れとかなアカンで。"
 continue: "続けるで"
 preservedUsernames: "予約ユーザー名"
 preservedUsernamesDescription: "予約しとくユーザー名を行ごとに挙げるで。ここで指定されたユーザー名はアカウント作るときに使えへんくなるけど、管理者は例外や。あと、もうあるアカウントも例外やな。"
@@ -1082,29 +1092,29 @@ changeReactionConfirm: "ツッコミを別のに変えるか?"
 later: "あとで"
 goToMisskey: "Misskeyへ"
 additionalEmojiDictionary: "絵文字の追加辞書"
-installed: "インストール済み"
+installed: "インストールしとる"
 branding: "ブランディング"
 enableServerMachineStats: "サーバーのマシン情報見せびらかすで"
 enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする"
 turnOffToImprovePerformance: "オフにしたらえらい軽うなるで。"
-createInviteCode: "招待コードを作成"
-createWithOptions: "オプションを指定して作成"
-createCount: "作成数"
+createInviteCode: "招待コード作る"
+createWithOptions: "オプション決めて作る"
+createCount: "作った数"
 inviteCodeCreated: "招待コード作ったで"
 inviteLimitExceeded: "招待コード作りすぎやで。"
-createLimitRemaining: "作成できる招待コード: 残り {limit} 個やで"
-inviteLimitResetCycle: "{time}で最大 {limit} 個の招待コードを作成できるで。"
+createLimitRemaining: "作れる招待コードは残り {limit} 個や"
+inviteLimitResetCycle: "{time}で最大 {limit} 個の招待コードを作れるで。"
 expirationDate: "有効期限"
-noExpirationDate: "有効期限を設けへん"
-inviteCodeUsedAt: "招待コードが使用された日時"
-registeredUserUsingInviteCode: "招待コードを使用したユーザー"
+noExpirationDate: "期限なし"
+inviteCodeUsedAt: "招待コードが使われた時"
+registeredUserUsingInviteCode: "招待コードを使うた人"
 waitingForMailAuth: "メール認証待ち"
-inviteCodeCreator: "招待コードを作成したユーザー"
-usedAt: "使用日時"
+inviteCodeCreator: "招待コードを作った人"
+usedAt: "使った時"
 unused: "つこてへん"
 used: "もうつこてる"
 expired: "期限切れ"
-doYouAgree: "同意するんか?"
+doYouAgree: "ええんか?"
 beSureToReadThisAsItIsImportant: "重要やから絶対読んでや。"
 iHaveReadXCarefullyAndAgree: "「{x}」の内容をよう読んで、同意するで。"
 dialog: "ダイアログ"
@@ -1115,119 +1125,176 @@ pastAnnouncements: "過去のお知らせやで"
 youHaveUnreadAnnouncements: "あんたまだこのお知らせ読んどらんやろ。"
 useSecurityKey: "ブラウザまたはデバイスの言う通りに、セキュリティキーまたはパスキーを使ってや。"
 replies: "返事"
-renotes: "Renote"
+renotes: "リノート"
 loadReplies: "返信を見るで"
 loadConversation: "会話を見るで"
 pinnedList: "ピン留めしはったリスト"
 keepScreenOn: "デバイスの画面を常にオンにすんで"
-verifiedLink: "このリンク先の所有者であることが確認されたで。"
+verifiedLink: "このリンク先の所有者ってわかったわ。"
 notifyNotes: "投稿を通知"
-unnotifyNotes: "投稿の通知を解除すんで"
+unnotifyNotes: "投稿の通知やめる"
 authentication: "認証"
-authenticationRequiredToContinue: "続けるには認証をやってや。"
+authenticationRequiredToContinue: "続けるんなら認証してや。"
 dateAndTime: "日時"
-showRenotes: "リノートを表示"
-edited: "編集し終わってる"
-notificationRecieveConfig: "通知を受け取るかの設定"
+showRenotes: "リノート出す"
+edited: "いじったやつ"
+notificationRecieveConfig: "通知もらうかの設定"
 mutualFollow: "お互いフォローしてんで"
-fileAttachedOnly: "ファイル付きのみ"
-showRepliesToOthersInTimeline: "タイムラインに他の人への返信とかも含めんで"
-hideRepliesToOthersInTimeline: "タイムラインに他の人への返信とかは見ーへんで"
-showRepliesToOthersInTimelineAll: ""
-hideRepliesToOthersInTimelineAll: ""
-confirmShowRepliesAll: ""
-confirmHideRepliesAll: ""
+fileAttachedOnly: "ファイルのっけてあるやつだけ"
+showRepliesToOthersInTimeline: "タイムラインに他の人への返信とかも入れるで"
+hideRepliesToOthersInTimeline: "タイムラインに他の人への返信とかは入れへん"
+showRepliesToOthersInTimelineAll: "タイムラインに今フォローしとる人全員の返信入れるで"
+hideRepliesToOthersInTimelineAll: "タイムラインに今フォローしとる人の返信入れへん"
+confirmShowRepliesAll: "これは元に戻せへんから慎重に決めてや。本当にタイムラインに今フォローしとる全員の返信を入れるか?"
+confirmHideRepliesAll: "これは元に戻せへんから慎重に決めてや。本当にタイムラインに今フォローしとる全員の返信を入れへんのか?"
 externalServices: "他のサイトのサービス"
 impressum: "運営者の情報"
 impressumUrl: "運営者の情報URL"
-impressumDescription: "ドイツなどのほんま1部の国と地域ではな、表示が義務付けられててん。(Impressum)"
+impressumDescription: "ドイツとかの一部んところではな、表示が義務付けられてんねん(Impressum)。"
 privacyPolicy: "プライバシーポリシー"
 privacyPolicyUrl: "プライバシーポリシーURL"
 tosAndPrivacyPolicy: "利用規約・プライバシーポリシー"
 avatarDecorations: "アイコンデコレーション"
-attach: ""
-detach: ""
-angle: ""
+attach: "のっける"
+detach: "取る"
+angle: "角度"
 flip: "反転"
-showAvatarDecorations: ""
-releaseToRefresh: "離してリロード"
-refreshing: "リロード中"
+showAvatarDecorations: "アイコンのデコレーション映す"
+releaseToRefresh: "離したらリロード"
+refreshing: "リロードしとる"
 pullDownToRefresh: "引っ張ってリロードするで"
 disableStreamingTimeline: "タイムラインのリアルタイム更新をやめるで"
-useGroupedNotifications: "通知をグルーピングしてだすで"
-signupPendingError: "メールアドレスの確認中に問題が起こってえらいこっちゃ。リンクの有効期限が切れてるかもやで"
-cwNotationRequired: "「内容を隠す」がオンの場合は注釈の記述が必要やで。"
-doReaction: "ツッコミすんで"
+useGroupedNotifications: "通知をグループ分けして出すで"
+signupPendingError: "メアド確認してたらなんか変なことなったわ。リンクの期限切れてるかもしれん。"
+cwNotationRequired: "「内容を隠す」んやったら注釈書かなアカンで。"
+doReaction: "ツッコむで"
+code: "コード"
+reloadRequiredToApplySettings: "設定を見るんにはリロードが必要やで。"
 _announcement:
   forExistingUsers: "もうおるユーザーのみ"
-  forExistingUsersDescription: "有効にすると、このお知らせ作成時点でおるユーザーにのみお知らせが表示されます。無効にすると、このお知らせ作成後にアカウントを作成したユーザーにもお知らせが表示されます。"
-  needConfirmationToRead: "既読にするのに確認が必要やで"
-  needConfirmationToReadDescription: "有効にすると、このお知らせを既読にする際に確認ダイアログが表示されます。また、一括既読操作の対象にもなりません。"
-  end: "お知らせを終了"
-  tooManyActiveAnnouncementDescription: "アクティブなお知らせが多いため、UXが低下する可能性があります。終了したお知らせはアーカイブすることを検討した方がええよ。"
+  forExistingUsersDescription: "オンにしたらこのお知らせができた時点でおる人らにだけお知らせが行くで。切ったらこの知らせが行ったあとにアカウント作った人にもちゃんとお知らせが行くで。"
+  needConfirmationToRead: "既読にするんやったら確認してや"
+  needConfirmationToReadDescription: "オンにしたら、このお知らせを既読にする時に確認するで。ついでに、一括既読しても既読扱いにならへんで。"
+  end: "お知らせやめる"
+  tooManyActiveAnnouncementDescription: "お知らせが多すぎてUXが落ちそうや。終わったお知らせはアーカイブに突っ込んだほうがええかも。"
   readConfirmTitle: "既読にしてええんやな?"
-  readConfirmText: "「{title}」の内容を読み、既読にします。"
+  readConfirmText: "「{title}」はもう読んだから既読にするで。"
   shouldNotBeUsedToPresentPermanentInfo: "新規ユーザーのUXを損ねやすいから、お知らせはストック情報やのうてフロー情報の掲示に使った方がええで。"
-  dialogAnnouncementUxWarn: "ダイアログ形式のお知らせが同時に2つ以上ある場合、UXに悪影響を及ぼす可能性が高くなるから、使用は慎重にすんのがおすすめやで。"
+  dialogAnnouncementUxWarn: "ダイアログ形式のお知らせがいっぺんに2コ以上ある場合、UXに良うないことが多いから、使うんは慎重にすんのがおすすめやで。"
   silence: "通知せんで"
-  silenceDescription: "オンにすると、このお知らせは通知されないで、既読にする必要もなくなるで。"
+  silenceDescription: "オンにすると、このお知らせは通知されへんし、既読にする必要もなくなるで。"
 _initialAccountSetting:
   accountCreated: "アカウント作り終わったで。"
   letsStartAccountSetup: "アカウントの初期設定をしよか。"
-  letsFillYourProfile: "最初はあんたのプロフィールを設定しよか。"
+  letsFillYourProfile: "最初はあんたのプロフィールを設定するで。"
   profileSetting: "プロフィール設定"
   privacySetting: "プライバシー設定"
   theseSettingsCanEditLater: "この設定はあとから変えれるで。"
   youCanEditMoreSettingsInSettingsPageLater: "これ以外にもいろんな設定を「設定」ページからできるで。後で確認してみてな。"
   followUsers: "タイムラインを構築するために、気になるユーザーをフォローしてみ。"
   pushNotificationDescription: "プッシュ通知を有効にすると{name}の通知をあんたのデバイスで受け取れるで。"
-  initialAccountSettingCompleted: "初期設定が終わったで。"
+  initialAccountSettingCompleted: "初期設定終わりや!"
   haveFun: "{name}、楽しんでな~"
-  youCanContinueTutorial: "このまま{name}(Misskey)の使い方のチュートリアルに進めるけど、ここで中断してすぐに使い始めることもできるで。"
-  startTutorial: "チュートリアルを開始するで"
+  youCanContinueTutorial: "こんまま{name}(Misskey)の使い方のチュートリアルにも行けるけど、ここでやめてすぐに使い始めてもええで。"
+  startTutorial: "チュートリアルはじめる"
   skipAreYouSure: "初期設定飛ばすか?"
   laterAreYouSure: "初期設定あとでやり直すん?"
 _initialTutorial:
-  launchTutorial: "チュートリアルを見るで"
+  launchTutorial: "チュートリアル見るで"
   title: "チュートリアルやで"
   wellDone: "やるやん"
-  skipAreYouSure: "チュートリアルをやめるか?"
+  skipAreYouSure: "チュートリアルやめるか?"
   _landing:
     title: "チュートリアルによう来たな"
-    description: "ここでは、Misskeyの基本的な使い方や機能を確認できるで。"
+    description: "ここでは、Misskeyのカンタンな使い方とか機能を確かめれんで。"
   _note:
     title: "ノートってなんや?"
-    description: "Misskeyでの投稿は「ノート」って呼ばれてるで。ノートはタイムラインに時系列で並んでいて、リアルタイムで更新されてるで。"
-    reply: "返信することもできるで。返信に対しての返信も可能で、スレッドのように会話を続けることもできるで。"
-    renote: "そのノートを自分のタイムラインに流して共有することもできるで。テキストを追加して引用することもできるで。"
-    reaction: "ツッコミをつけることもできるで。細かいことは次のページで解説するで。"
+    description: "Misskeyでの投稿は「ノート」って呼ばれてんで。ノートは順々にタイムラインに載ってて、リアルタイムで新しくなってってんで。"
+    reply: "返信もできるで。返信の返信もできるから、スレッドっぽく会話をそのまま続けれもするで。"
+    renote: "そのノートを自分のタイムラインに流して共有できるで。テキスト入れて引用してもええな。"
+    reaction: "ツッコミをつけることもできるで。細かいことは次のページや。"
+    menu: "ノートの詳細を出したり、リンクをコピーしたり、いろいろできんねん。"
+  _reaction:
+    title: "ツッコミってなんや?"
+    description: "ノートには「ツッコミ」できんねん。「いいね」とか何言っとるかわからんし、簡単に表現できるのはええことやん?"
+    letsTryReacting: "ノートの「+」ボタンでツッコめるわ。試しに下のノートにツッコんでみ。"
+    reactToContinue: "ツッコんだら進めるようになるで。"
+    reactNotification: "あんたのノートが誰かにツッコまれたら、すぐ通知するで。"
+    reactDone: "「ー」ボタンでツッコミやめれるで。"
+  _timeline:
+    title: "タイムラインのしくみ"
+    description1: "Misskeyには、いろいろタイムラインがあんで(ただ、サーバーによっては無効化されてるところもあるな)。"
+    home: "あんたがフォローしてるアカウントの投稿が見れんねん。"
+    local: "このサーバーの中におる全員の投稿が見れるで。"
+    social: "ホームタイムラインの投稿もローカルタイムラインのも一緒に見れるで。"
+    global: "繋がってる他の全サーバーからの投稿が見れるで。"
+    description2: "それぞれのタイムラインは、いつでも画面上で切り替えられんねん。覚えとき。"
+    description3: "その他にも、リストタイムラインとかチャンネルタイムラインとかがあんねん。詳しいのは{link}を見とき。"
+  _postNote:
+    title: "ノートの投稿設定"
+    description1: "Misskeyにノートを投稿するとき、いろんなオプションが付けれるで。投稿画面はこんな感じや。"
+    _visibility:
+      description: "ノートを見れる相手を制限できるわ。"
+      public: "みんなに見せるで。"
+      home: "ホームタイムラインにだけ見せるで。フォロワーとか、プロフィールを見に来た人、リノートからも見れるから、実質は全員見れるけどな。あんまし広がりにくいってことや。"
+      followers: "フォロワーにだけ見せるで。自分以外はリノートできへんし、フォロワー以外は絶対に見れへん。"
+      direct: "指定した人にだけ公開されて、ついでに通知も送るで。ダイレクトメールの代わりとして使ってな。"
+      doNotSendConfidencialOnDirect1: "機密情報を送るときは十分注意せえよ。"
+      doNotSendConfidencialOnDirect2: "送信先のサーバーの管理者は投稿内容が見れるから、信用できへんサーバーのひとにダイレクト投稿するときには、めっちゃ用心しとくんやで。"
+      localOnly: "他のサーバーに投稿せえへんくなるで。他の公開範囲とか一切無視して、他のサーバーの人らはこの設定がされたノートは絶対に見れへん。"
+    _cw:
+      title: "内容隠し(CW)"
+      description: "本文のかわりに「注釈」に書いた内容だけ見せるで。「続き見して!」を押すと本文も見れんねん。"
+      _exampleNote:
+        cw: "飯テロ注意"
+        note: "チョコドーナツめっちゃ美味かったわ🍩😋"
+      useCases: "サーバーのガイドラインに決められとるノートに使うたり、ネタバレとかきわどい内容を自分で隠したりするとき用やな。"
+  _howToMakeAttachmentsSensitive:
+    title: "のっけたファイルをセンシティブにするんは?"
+    description: "サーバーのガイドラインに書いてあったり、そのままおっぴろげとくのはあんま良うないファイルには「センシティブ」っちゅう設定をつけるんや。"
+    tryThisFile: "試しに、これにのっけてある画像をセンシティブにしてみいや!"
+    _exampleNote:
+      note: "納豆のフタ開けるときにやらかしてもうた…"
+    method: "のっけたファイルをセンシティブにするときは、そのファイルを押してメニュー開けて、「ちょっとこれはアカン」を押すんよ。"
+    sensitiveSucceeded: "ファイルをのっけるときは、サーバーの言うこと聞いてちゃんと設定するんやで。"
+    doItToContinue: "画像をちゃんと設定したら先に進めるで。"
+  _done:
+    title: "チュートリアル終わり!おつかれさん🎉"
+    description: "ここで紹介したのは全部の中のちょび~っとだけや。もっと使い方知りたいんやったら、{link}を見ときや。"
+_timelineDescription:
+  home: "ホームタイムラインは、あんたがフォローしとるアカウントの投稿だけ見れるで。"
+  local: "ローカルタイムラインは、このサーバーにおる全員の投稿を見れるで。"
+  social: "ソーシャルタイムラインは、ホームタイムラインの投稿もローカルタイムラインのも一緒に見れるで。"
+  global: "グローバルタイムラインは、繋がっとる他のサーバーの投稿、全部ひっくるめて見れんで。"
 _serverRules:
-  description: "新規登録前に見せる、サーバーの簡潔なルールを設定すんで。内容は使うための決め事の要約とすることを推奨するわ。"
+  description: "新規登録前に見せる、サーバーのカンタンなルールを決めるで。内容は使うための決め事の要約がええと思うわ。"
 _serverSettings:
   iconUrl: "アイコン画像のURL"
   appIconDescription: "{host}がアプリとして表示してるんやつをアイコンを指定すんで。"
-  appIconUsageExample: "PWAや、スマートフォンのホーム画面にブックマークとして追加された時など"
-  appIconStyleRecommendation: "円形もしくは角丸にクロップされる場合があるさかいに、塗り潰された余白のある背景があるものが推奨されるで。"
-  appIconResolutionMustBe: "解像度は必ず{resolution}である必要があるで。"
+  appIconUsageExample: "例えば、PWAとか、スマホのホームにブックマークしたときとか"
+  appIconStyleRecommendation: "円か角丸に切り取られることがあるさかい、塗り潰した余白のある背景があるものがおすすめや。"
+  appIconResolutionMustBe: "解像度は絶対{resolution}じゃないとアカン。"
   manifestJsonOverride: "manifest.jsonのオーバーライド"
   shortName: "略称"
-  shortNameDescription: "サーバーの名前が長い時に、代わりに表示することのできるあだ名。"
-  fanoutTimelineDescription: ""
+  shortNameDescription: "サーバーの名前が長ったらしい時に、代わりに出すあだ名。"
+  fanoutTimelineDescription: "入れると、おのおのタイムラインを取得するときにめちゃめちゃ動きが良うなって、データベースが軽くなるわ。でも、Redisのメモリ使う量が増えるから注意な。サーバーのメモリが足りんときとか、動きが変なときは切れるで。"
+  fanoutTimelineDbFallback: "データベースにフォールバックする"
+  fanoutTimelineDbFallbackDescription: "有効にしたら、タイムラインがキャッシュん中に入ってないときにDBにもっかい問い合わせるフォールバック処理ってのをやっとくで。切ったらフォールバック処理をやらんからサーバーはもっと軽くなんねんけど、タイムラインの取得範囲がちょっと減るで。"
 _accountMigration:
   moveFrom: "別のアカウントからこのアカウントに引っ越す"
   moveFromSub: "別のアカウントへエイリアスを作る"
-  moveFromLabel: "引っ越し元のアカウント:"
-  moveFromDescription: "別のアカウントからこのアカウントにフォロワーを引き継いで引っ越したかったら、ここでエイリアスを作っとく必要があるで。必ずお引っ越しを実行する前に作っとかなあかんで!引っ越し元のアカウントをこんな風に入力してくれへんか?:@person@instance.com"
+  moveFromLabel: "引っ越しする前のアカウント #{n}"
+  moveFromDescription: "別のアカウントからこのアカウントにフォロワーを引っ越ししたいんなら、ここでエイリアスを作っとかなアカンで。\n引っ越す前のアカウントをこんな感じに入力してや: @username@server.example.com\n入力欄空っぽやったら消しとくで(おすすめはせえへん)。"
   moveTo: "このアカウントをさらのアカウントに引っ越すで"
-  moveToLabel: "引っ越し先のアカウント:"
-  moveCannotBeUndone: "アカウントを移行すると、取り消すことはできへんくなります。"
+  moveToLabel: "引っ越し先のアカウント:"
+  moveCannotBeUndone: "アカウント引っ越したらもう戻せへん。"
   moveAccountDescription: "おニューのアカウントに移行すんで。\n ・フォロワーがおニューの方を勝手にフォローすんで。\n ・このアカウントからのフォローはまるまる全部解除されんで。\n ・このアカウントでノート作れへんようになるで。\n\nフォロワーの移行は勝手にこっちでやっとくけど、フォローの移行は自分でしてや。移行前にこのアカウントでフォローエクスポートして、移行したあとすぐにおニューのところでインポートしてくれな。\nリストとかミュート、あとブロックもおんなじや。自分で移行してな。\n\n(この説明はこのサーバー、つまりMisskey v13.12.0から後の仕様や。Mastodonとか他のActivityPubソフトやとちょっと挙動が違うこともあんで。)"
-  moveAccountHowTo: "アカウントの引っ越しには、まず引っ越し先のアカウントで自分のアカウントに対しエイリアスを作成しなはれや。\nエイリアス作成した後、引っ越し先のアカウントを次のように入力してくれへんか?:@username@server.example.com"
-  startMigration: "引っ越しする"
+  moveAccountHowTo: "アカウントの引っ越しには、まず引っ越し先のアカウントで自分のアカウントに対しエイリアスを作ってな。\nエイリアス作ったら、引っ越し先のアカウントをこんな感じに入れてや: @username@server.example.com"
+  startMigration: "引っ越す"
   migrationConfirm: "ほんまにこのアカウントを {account} に引っ越すんか?一回引っ越してもうたら取り消されへんし、二度とこのアカウントを元に戻されへんくなるで。\nそれと、引っ越し先のアカウントでエイリアスが作れたかちゃ~んと確認しーや?"
-  movedAndCannotBeUndone: "\nアカウントはもう引っ越されてます。\n引っ越しを取り消すことはできまへん。"
+  movedAndCannotBeUndone: "\nアカウントはもう引っ越し済みや。\nこれはもう戻せへん。"
   postMigrationNote: "このアカウントからのフォロー解除は移行操作から丸一日経ったら実行されんで。\nこのアカウントのフォロー・フォロワー数はどっちも0や。フォローの解除はされへんから、あんたのフォロワーはこのアカウントのフォロワー向けの投稿をこの後も見れるで。"
-  movedTo: "引っ越し先のアカウント:"
+  movedTo: "引っ越し先のアカウント:"
 _achievements:
   earnedAt: "貰った日ぃ"
   _types:
@@ -1251,10 +1318,10 @@ _achievements:
       title: "箕面の滝からノート"
       description: "ノートを5,000回投稿した"
     _notes10000:
-      title: "スーパーノート"
+      title: "えげつないノート"
       description: "ノートを10,000回投稿した"
     _notes20000:
-      title: "ニードモアノート"
+      title: "もっとノートよこせ!"
       description: "ノートを20,000回投稿した"
     _notes30000:
       title: "ノートノートノート"
@@ -1351,7 +1418,7 @@ _achievements:
       title: "はじめてのフォロー"
       description: "初めてフォローした"
     _following10:
-      title: "ついてく、ついてく"
+      title: "すたこらさっさ"
       description: "フォローが10人超えた"
     _following50:
       title: "友達ぎょうさん"
@@ -1404,10 +1471,10 @@ _achievements:
       description: "クライアント付けてから1時間経ってもうたで。"
     _noteDeletedWithin1min:
       title: "*おおっと*"
-      description: "投稿してから1分以内にその投稿を消した"
+      description: "投稿してから1分以内にその投稿をほかした"
     _postedAtLateNight:
       title: "夜行性"
-      description: "深夜にノートを投稿した"
+      description: "真夜中にノートを投稿した"
       flavor: "そろそろ寝よか"
     _postedAt0min0sec:
       title: "時報"
@@ -1424,7 +1491,7 @@ _achievements:
       description: "サーバーのチャートを表示した"
     _outputHelloWorldOnScratchpad:
       title: "Hello, world!"
-      description: "スクラッチパッドで hello worldを出力した"
+      description: "スクラッチパッドで hello world を出力した"
     _open3windows:
       title: "マド開けすぎ"
       description: "ウィンドウを3つ以上開いた状態にした"
@@ -1433,7 +1500,7 @@ _achievements:
       description: "ドライブのフォルダを再帰的な入れ子にしようとした"
     _reactWithoutRead:
       title: "ちゃんと読んだんか?"
-      description: "100文字以上のテキストを含むノートに投稿されてから3秒以内にツッコんだ"
+      description: "100文字以上のノートに投稿3秒以内にツッコんだ"
     _clickedClickHere:
       title: "ここをクリック"
       description: "ここをクリックした"
@@ -1442,7 +1509,7 @@ _achievements:
       description: "10秒ごとに0.005%の確率で獲得"
     _setNameToSyuilo:
       title: "神様コンプレックス"
-      description: "名前を syuilo に設定した"
+      description: "名前を syuilo にした"
     _passedSinceAccountCreated1:
       title: "一周年"
       description: "アカウント作成から1年経過した"
@@ -1468,8 +1535,11 @@ _achievements:
       description: "Brain Diverへのリンクを投稿したった"
       flavor: "Misskey-Misskey La-Tu-Ma"
     _smashTestNotificationButton:
-      title: "テスト過剰"
-      description: "通知テストをごく短時間のうちに連続して行ったねん"
+      title: "心配性"
+      description: "通知のテストしすぎやって"
+    _tutorialCompleted:
+      title: "Misskeyひよっこ講座 修了証"
+      description: "チュートリアル全部やった"
 _role:
   new: "ロールの作成"
   edit: "ロールの編集"
@@ -1480,60 +1550,62 @@ _role:
   assignTarget: "アサイン"
   descriptionOfAssignTarget: "<b>マニュアル</b>は誰がこのロールに含まれてるかを手動で管理するで。\n<b>コンディショナル</b>は条件を設定して、それに合うユーザーが自動で含まれるようになるで。"
   manual: "マニュアル"
+  manualRoles: "マニュアルロール"
   conditional: "コンディショナル"
+  conditionalRoles: "コンディショナルロール"
   condition: "条件"
   isConditionalRole: "これはコンディショナルロールやで"
   isPublic: "ロールを公開"
-  descriptionOfIsPublic: "ロールにアサインされたユーザーを誰でも見ることができるで。そんで、ユーザーのプロフィールでこのロールが表示されるで。"
+  descriptionOfIsPublic: "プロフィールでこのロールが出されるで。"
   options: "オプション"
   policies: "ポリシー"
   baseRole: "ベースロール"
-  useBaseValue: "ベースロールの値を使用"
-  chooseRoleToAssign: "アサインするロールを選択"
+  useBaseValue: "ベースロールの値使う"
+  chooseRoleToAssign: "アサインするロール選ぶ"
   iconUrl: "アイコン画像のURL"
   asBadge: "バッジとして見せる"
   descriptionOfAsBadge: "オンにすると、ユーザー名の横んとこにロールのアイコンが表示されるで。"
-  isExplorable: "ロールタイムラインを公開するで〜"
-  descriptionOfIsExplorable: "オンにしたらロールのタイムラインを公開するで〜。でもロールの公開をオフにしたら公開されへんよ。"
+  isExplorable: "ユーザーを見つけやすくしたる"
+  descriptionOfIsExplorable: "オンにしたらロールの面子一覧が「みつける」で公開されるし、ロールのタイムラインが使えるようになるで。"
   displayOrder: "表示順"
   descriptionOfDisplayOrder: "数がでかいほど、UI上で先に表示されるで。"
-  canEditMembersByModerator: "モデレーターのメンバー編集を許可"
-  descriptionOfCanEditMembersByModerator: "オンにすると、管理者に加えてモデレーターもこのロールへユーザーをアサイン/アサイン解除できるようになるで。オフにすると管理者のみが行えるで。"
+  canEditMembersByModerator: "モデレーターがメンバーいじるのを許す"
+  descriptionOfCanEditMembersByModerator: "オンにすると、管理者だけやなくてモデレーターもこのロールにユーザーを入れたり抜いたりできるで。オフにすると管理者だけしかやれへんくなるで。"
   priority: "優先度"
   _priority:
     low: "低い"
-    middle: "中"
+    middle: "中くらい"
     high: "高い"
   _options:
-    gtlAvailable: "グローバルタイムラインの閲覧"
-    ltlAvailable: "ローカルタイムラインの閲覧"
-    canPublicNote: "パブリック投稿の許可"
-    canInvite: "サーバー招待コードの発行"
-    inviteLimit: "招待コードの作成可能数"
-    inviteLimitCycle: "招待コードの発行間隔"
-    inviteExpirationTime: "招待コードの有効期限"
+    gtlAvailable: "グローバルタイムライン見る"
+    ltlAvailable: "ローカルタイムライン見る"
+    canPublicNote: "パブリック投稿できるか"
+    canInvite: "サーバー招待コード作る"
+    inviteLimit: "招待コード作れる数"
+    inviteLimitCycle: "招待コードの作れる間隔"
+    inviteExpirationTime: "招待コードの期限"
     canManageCustomEmojis: "カスタム絵文字の管理"
     canManageAvatarDecorations: "アバターを飾るモンの管理"
     driveCapacity: "ドライブ容量"
     alwaysMarkNsfw: "勝手にファイルにNSFWをくっつける"
-    pinMax: "ノートのピン留めの最大数"
-    antennaMax: "アンテナの作成可能数"
+    pinMax: "ノートピン留めできる数"
+    antennaMax: "アンテナ作れる数"
     wordMuteMax: "ワードミュートの最大文字数"
-    webhookMax: "Webhockの作成可能数"
-    clipMax: "クリップの作成可能数"
-    noteEachClipsMax: "クリップ内のノートの最大数"
-    userListMax: "ユーザーリストの作成可能数"
+    webhookMax: "Webhook作れる数"
+    clipMax: "クリップ作れる数"
+    noteEachClipsMax: "クリップの中にノート作れる数"
+    userListMax: "ユーザーリスト作れる数"
     userEachUserListsMax: "ユーザーリスト内のユーザーの最大数"
     rateLimitFactor: "レートリミット"
     descriptionOfRateLimitFactor: "ちっちゃいほど制限が緩なって、大きいほど制限されるで。"
-    canHideAds: "広告を表示させへん"
-    canSearchNotes: "ノート検索を使わすかどうか"
-    canUseTranslator: "翻訳機能の利用"
+    canHideAds: "広告映さへん"
+    canSearchNotes: "ノート探せるかどうか"
+    canUseTranslator: "翻訳使えるかどうか"
   _condition:
     isLocal: "ローカルユーザー"
     isRemote: "リモートユーザー"
-    createdLessThan: "アカウント作成から~以内"
-    createdMoreThan: "アカウント作成から~経過"
+    createdLessThan: "アカウント作ってから~以内"
+    createdMoreThan: "アカウント作ってから~経過"
     followersLessThanOrEq: "フォロワー数が~以下"
     followersMoreThanOrEq: "フォロワー数が~以上"
     followingLessThanOrEq: "フォロー数が~以下"
@@ -1542,45 +1614,45 @@ _role:
     notesMoreThanOrEq: "投稿を~以上しとる"
     and: "~かつ~"
     or: "~または~"
-    not: "~ではない"
+    not: "~じゃない"
 _sensitiveMediaDetection:
-  description: "機械学習を使って自動でセンシティブなメディアを検出して、モデレーションに役立てることができるで。サーバーの負荷が少し増えてまうなあ。"
+  description: "機械学習で自動できわどいメディアを検出して、運営しやすくするで。でもサーバーが少し重くなってまうわ。"
   sensitivity: "検出感度やで"
   sensitivityDescription: "感度を低くすると、誤検知(偽陽性)が減るで。感度を高くすると、検知漏れ(偽陰性)が減るで。"
-  setSensitiveFlagAutomatically: "NSFWフラグを設定するで"
-  setSensitiveFlagAutomaticallyDescription: "この設定をオフにしても内部的に判定結果は保持されるで。"
+  setSensitiveFlagAutomatically: "センシティブフラグを設定するで"
+  setSensitiveFlagAutomaticallyDescription: "この設定切っても内部的には判定結果はそのままや。"
   analyzeVideos: "動画の解析をオンにするで"
-  analyzeVideosDescription: "画像に加えて動画も解析するようにするで。鯖の負荷が少し増えるで。"
+  analyzeVideosDescription: "画像だけじゃなくて動画も解析するようにするで。サーバーがちょっと重くなるで。"
 _emailUnavailable:
-  used: "もう使われとるで"
+  used: "もう使われとるわ"
   format: "形式がおかしいで"
-  disposable: "永久に使えるアドレスじゃないみたいやで"
-  mx: "正しいメールサーバーじゃない見たいやで"
-  smtp: "メールサーバーが応答してないみたいや"
+  disposable: "ずーっと使えるアドレスじゃないみたいや"
+  mx: "正しいメールサーバーじゃないっぽいわ"
+  smtp: "メールサーバーがうんともすんとも言わへん"
 _ffVisibility:
   public: "公開"
   followers: "フォロワーだけに公開"
   private: "非公開"
 _signup:
-  almostThere: "ほぼ完了やで"
+  almostThere: "ほぼ終わったようなもんや"
   emailAddressInfo: "あんたが使っとるメアドを入力してなー。入れたメアドが公開されることはないで。"
-  emailSent: "さっき入れたメールアドレス({email})宛に確認のメールが送られたで。メールに書かれたリンクにアクセスすれば、アカウントの作成が完了や!"
+  emailSent: "さっき入れたメアド({email})宛に確認メールを送ったで。メールに書かれたリンク押してアカウント作るの終わらしてな。\nメールの認証リンクの期限は30分や。"
 _accountDelete:
   accountDelete: "アカウントの削除"
-  mayTakeTime: "アカウントの削除は負荷がかかる処理やねんて。やから作ったコンテンツの数や上げたファイルの数が多いと削除が終わるまでに時間がかかることがあるんやって。"
-  sendEmail: "アカウントの削除が終わるときは、登録してたメールアドレス宛に通知を送るで。"
-  requestAccountDelete: "アカウント削除をリクエスト"
+  mayTakeTime: "アカウント消すんはサーバーが重いんやって。やから作ったコンテンツとか上げたファイルの数が多いと消し終わるまでに時間がかかるかもしれへん。"
+  sendEmail: "アカウントの消し終わるときは、登録してたメアドに通知するで。"
+  requestAccountDelete: "アカウント削除頼む"
   started: "削除処理が始まったで。"
-  inProgress: "削除が進んでるで"
+  inProgress: "今消しよるで"
 _ad:
   back: "戻る"
-  reduceFrequencyOfThisAd: "この広告の表示頻度を下げるで"
+  reduceFrequencyOfThisAd: "この広告ちょっとうざったらしいわ"
   hide: "表示せん"
-  timezoneinfo: "曜日はサーバーのタイムゾーンを元に指定されるで。"
+  timezoneinfo: "曜日はサーバーのタイムゾーンを元に決めるで。"
   adsSettings: "広告配信設定"
   notesPerOneAd: "リアタイ更新中に広告を出す間隔(ノートの個数な)"
   setZeroToDisable: "0でリアタイ更新時の広告配信を無効にすんで"
-  adsTooClose: "広告を出す間隔がめっちゃ短いから、ユーザー体験が著しく損なわれる可能性があんで。"
+  adsTooClose: "広告を出す間隔がめっちゃ短いから、ユーザー体験がめちゃめちゃ悪くなるかもしれへん。"
 _forgotPassword:
   enterEmail: "アカウントに登録したメールアドレスをここに入力してや。そのアドレス宛に、パスワードリセット用のリンクが送られるから待っててな~。"
   ifNoEmail: "メールアドレスを登録してへんのやったら、管理者まで教えてな~。"
@@ -1599,7 +1671,7 @@ _plugin:
   install: "プラグインのインストール"
   installWarn: "信頼できへんプラグインはインストールせんとってな"
   manage: "プラグインの管理"
-  viewSource: "ソースを表示"
+  viewSource: "ソース見る"
 _preferencesBackups:
   list: "作ったバックアップ"
   saveNew: "新しく保存"
@@ -1640,24 +1712,25 @@ _displayOfSensitiveMedia:
   force: "常にメディアを隠すで"
 _instanceTicker:
   none: "表示せん"
-  remote: "リモートユーザーに表示"
-  always: "常に表示"
+  remote: "リモートユーザーに見せる"
+  always: "いつでも見せる"
 _serverDisconnectedBehavior:
   reload: "自動でリロード"
   dialog: "ダイアログで警告"
   quiet: "控えめに警告"
 _channel:
-  create: "チャンネルを作る"
-  edit: "チャンネルを編集"
+  create: "チャンネル作る"
+  edit: "チャンネルいじる"
   setBanner: "バナーを設定"
   removeBanner: "バナーを削除"
   featured: "トレンド"
-  owned: "管理中"
+  owned: "管理しとる"
   following: "フォロー中やで"
-  usersCount: "{n}人が参加中やで"
+  usersCount: "{n}人が参加しとる"
   notesCount: "{n}こ投稿があるで"
   nameAndDescription: "名前と説明"
   nameOnly: "名前だけ"
+  allowRenoteToExternal: "チャンネルの外にリノートできるようにする"
 _menuDisplay:
   sideFull: "横"
   sideIcon: "横(アイコン)"
@@ -1683,7 +1756,7 @@ _theme:
   builtinThemes: "標準のテーマ"
   alreadyInstalled: "そのテーマはもうインストールされとるで?"
   invalid: "テーマの形式が間違ってるみたいや"
-  make: "テーマを作る"
+  make: "テーマ作る"
   base: "ベース"
   addConstant: "定数を追加"
   constant: "定数"
@@ -1749,6 +1822,14 @@ _sfx:
   notification: "通知"
   antenna: "アンテナ受信"
   channel: "チャンネル通知"
+  reaction: "ツッコミ選んどるとき"
+_soundSettings:
+  driveFile: "ドライブん中の音使う"
+  driveFileWarn: "ドライブん中のファイル選びや"
+  driveFileTypeWarn: "このファイルは対応しとらへん"
+  driveFileTypeWarnDescription: "音声ファイルを選びや"
+  driveFileDurationWarn: "音が長すぎるわ"
+  driveFileDurationWarnDescription: "長い音使うたらMisskey使うのに良うないかもしれへんで。それでもええか?"
 _ago:
   future: "未来"
   justNow: "ついさっき"
@@ -1760,6 +1841,14 @@ _ago:
   monthsAgo: "{n}ヶ月前"
   yearsAgo: "{n}年前"
   invalid: "あらへん"
+_timeIn:
+  seconds: "{n}秒後"
+  minutes: "{n}分後"
+  hours: "{n}時間後"
+  days: "{n}日後"
+  weeks: "{n}週間後"
+  months: "{n}ヶ月後"
+  years: "{n}年後"
 _time:
   second: "秒"
   minute: "分"
@@ -1770,12 +1859,12 @@ _2fa:
   registerTOTP: "認証アプリの設定はじめる"
   step1: "ほんなら、{a}や{b}とかの認証アプリを使っとるデバイスにインストールしてな。"
   step2: "次に、ここにあるQRコードをアプリでスキャンしてな~。"
-  step2Click: "QRコードをクリックすると、今使とる端末に入っとる認証アプリとかキーリングに登録できるで。"
+  step2Click: "QRコード押したら、今使とる端末に入っとる認証アプリとかキーリングに登録できるで。"
   step2Uri: "デスクトップアプリを使う時は次のURIを入れるで"
   step3Title: "確認コードを入れてーや"
-  step3: "アプリに表示されているトークンを入力して終わりや。"
-  setupCompleted: "設定が完了したで。"
-  step4: "これからログインするときも、同じようにトークンを入力するんやで"
+  step3: "アプリに映っとる確認コード(トークン)を入れて終わりや。"
+  setupCompleted: "設定が終わったで。"
+  step4: "これからログインするときも、同じようにコードを入れるんや。"
   securityKeyNotSupported: "今使とるブラウザはセキュリティキーに対応してへんのやってさ。"
   registerTOTPBeforeKey: "セキュリティキー・パスキーを登録するんやったら、まず認証アプリを設定してーな。"
   securityKeyInfo: "FIDO2をサポートするハードウェアセキュリティキーか端末の指紋認証やPINを使ってログインするように設定できるで。"
@@ -1885,6 +1974,7 @@ _widgets:
   _userList:
     chooseList: "リストを選ぶ"
   clicker: "クリッカー"
+  birthdayFollowings: "今日誕生日のツレ"
 _cw:
   hide: "隠す"
   show: "続き見して!"
@@ -2061,7 +2151,7 @@ _notification:
   youGotMention: "{name}からのメンション"
   youGotReply: "{name}からのリプライ"
   youGotQuote: "{name}による引用"
-  youRenoted: "{name}がRenoteしたみたいやで"
+  youRenoted: "{name}がリノートしたみたいやで"
   youWereFollowed: "フォローされたで"
   youReceivedFollowRequest: "フォロー許可してほしいみたいやな"
   yourFollowRequestAccepted: "フォローさせてもろたで"
@@ -2074,6 +2164,9 @@ _notification:
   checkNotificationBehavior: "通知の表示を確かめるで"
   sendTestNotification: "テスト通知を送信するで"
   notificationWillBeDisplayedLikeThis: "通知はこのように表示されるで"
+  reactedBySomeUsers: "{n}人がツッコんだで"
+  renotedBySomeUsers: "{n}人がリノートしたで"
+  followedBySomeUsers: "{n}人にフォローされたで"
   _types:
     all: "すべて"
     note: "あんたらの新規投稿"
@@ -2143,8 +2236,8 @@ _webhookSettings:
     followed: "フォローもらったとき~!"
     note: "ノートを投稿したとき~!"
     reply: "返信があるとき~!"
-    renote: "Renoteされるとき~!"
-    reaction: "ツッコミがあるとき~!"
+    renote: "リノートされるとき~!"
+    reaction: "ツッコまれたとき~!"
     mention: "メンションがあるとき~!"
 _moderationLogTypes:
   createRole: "ロールを追加すんで"
@@ -2173,13 +2266,15 @@ _moderationLogTypes:
   markSensitiveDriveFile: "ファイルをセンシティブ付与"
   unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
   resolveAbuseReport: "苦情を解決"
-  createInvitation: "招待コードを作成"
+  createInvitation: "招待コード作る"
   createAd: "広告を作んで"
   deleteAd: "広告ほかす"
   updateAd: "広告を更新"
   createAvatarDecoration: "アイコンデコレーションを作成"
   updateAvatarDecoration: "アイコンデコレーションを更新"
   deleteAvatarDecoration: "アイコンデコレーションを削除"
+  unsetUserAvatar: "この子のアイコン元に戻す"
+  unsetUserBanner: "この子のバナー元に戻す"
 _fileViewer:
   title: "ファイルの詳しい情報"
   type: "ファイルの種類"
@@ -2212,6 +2307,11 @@ _externalResourceInstaller:
       description: ""
     _failedToFetch:
       title: ""
+      fetchErrorDescription: "他のサイトに繋がらんかったわ。もっかいやってもダメやったら、サイトの管理してる人に言っといて。"
+      parseErrorDescription: "他のサイトから持ってきたデータ、よう分からんかったわ。サイトの管理してる人に言っといて。"
+    _hashUnmatched:
+      title: "ちゃんとしたデータが持ってこれんかったわ"
+      description: "もらったデータがなんかおかしいっぽいわ。ちょっと危ないからインストールはできへん。サイト管理してる人に言っといてな。"
     _pluginParseFailed:
       title: "AiScriptエラー起こしてもうたねん"
       description: "データは取得できたものの、AiScript解析時にエラーがあったから読み込めへんかってん。すまんが、プラグインを作った人に問い合わせてくれへん?ごめんな。エラーの詳細はJavaScriptコンソール読んでな。"
@@ -2220,7 +2320,20 @@ _externalResourceInstaller:
       description: "プラグインのインストール中に問題発生してもた、もう1度試してな。エラーの詳細はJavaScriptのコンソール見てや。"
     _themeParseFailed:
       title: "テーマ解析エラー"
-      description: "データは取得できたものの、テーマファイル解析時にエラーがあったから読み込めへんかってん。すまんが、テーマ作った人に問い合わせてくれへん?ごめんな。エラーの詳細はJavaScriptコンソール読んでな。"
+      description: "データは取れたんやが、テーマファイル読み込んどる時にエラーがあったから読み込めへんかったわ。すまんけど、テーマ作った人に言うてくれへん?ごめんな。エラーの詳細はJavaScriptコンソール読んでな。"
     _themeInstallFailed:
       title: "テーマインストールに失敗してもた"
-      description: "テーマのインストール中に問題発生してもた、もう1度試してな。エラーの詳細はJavaScriptのコンソール見てや。"
+      description: "なんかテーマインストールできんかったわ。もう一回試してな。細かいのはJavaScriptのコンソール見てや。"
+_dataSaver:
+  _media:
+    title: "メディアの読み込み"
+    description: "絵・動画が自動で読まれるのをふせぐわ。隠れてる絵・動画はタップするとひょっこりはんしてくれんで。"
+  _avatar:
+    title: "アイコンの絵"
+    description: "アイコン画像のアニメが止まるで。普通の画像よりもデータ量がでかいから、もっと通信量を節約できるねん。"
+  _urlPreview:
+    title: "URLプレビューのサムネイル画像"
+    description: "URLプレビューのサムネイル画像が読み込まへんなるで。"
+  _code:
+    title: "コードハイライト"
+    description: "MFMとかでコードハイライト記法が使われてるとき、タップするまで読み込まれへんくなるで。コードハイライトではハイライトする言語ごとにその決めてるファイルを読む必要はあんねんな。けどな、それは自動で読み込まれなくなるから、通信量を少なくできることができるねん。"
diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml
new file mode 100644
index 0000000000..760247c483
--- /dev/null
+++ b/locales/ko-GS.yml
@@ -0,0 +1,528 @@
+---
+_lang_: "한국어(경상)"
+headlineMisskey: "노트로 이언 네트워크"
+introMisskey: "어서 오이소! Misskey넌 오픈소스 분산헹 마이크로 블로그 서비스입니다.\n‘노트’럴 맨걸어서 지검 일나넌 일얼 노누던가 내 이바구럴 남한데 서 보이소.📡\n‘리액션’ 기넝서 남으 노트에 억수로 빠리게 답할 수 잇십니다.👍\n새롭운 세게럴 탐험해 보입시다.🚀"
+poweredByMisskeyDescription: "{name} 서버넌 오픈소스 플랫폼 <b>Misskey</b>으 서버 가운데 하나입니다."
+monthAndDay: "{month}월 {day}일"
+search: "찾기"
+notifications: "알림"
+username: "사용자 이럼"
+password: "비밀번호"
+forgotPassword: "비밀번호럴 잊엇뿟십니꺼?"
+fetchingAsApObject: "연합서 찾아보고 잇어예"
+ok: "예"
+gotIt: "알것어예"
+cancel: "아이예"
+noThankYou: "뎃어예"
+enterUsername: "사용자 이럼 서기"
+renotedBy: "{user}님이 리노트햇십니다"
+noNotes: "노트가 없십니다"
+noNotifications: "알림이 없십니다"
+instance: "서버"
+settings: "설정"
+notificationSettings: "알림 설정"
+basicSettings: "기본 설정"
+otherSettings: "다린 설정"
+openInWindow: "창서 옐기"
+profile: "프로필"
+timeline: "타임라인"
+noAccountDescription: "자기소개가 없십니다"
+login: "로그인"
+loggingIn: "로그인하고 잇어예"
+logout: "로그아웃"
+signup: "가입하기"
+uploading: "올리고 잇어예"
+save: "저장하기"
+users: "사용자"
+addUser: "사용자 옇기"
+favorite: "질겨찾기"
+favorites: "질겨찾기"
+unfavorite: "질겨찾기서 어ᇝ애기"
+favorited: "질겨찾기에 담앗십니다."
+alreadyFavorited: "벌시로 질겨찾기에 담기 잇십니다."
+cantFavorite: "질겨찾기에 몬 담았십니다."
+pin: "프로필에 붙이기"
+unpin: "프로필서 띠기"
+copyContent: "내용 복사하기"
+copyLink: "링크 복사하기"
+copyLinkRenote: "리노트 링크 복사"
+delete: "내삐리기"
+deleteAndEdit: "내삐리고 새로 적기"
+deleteAndEditConfirm: "요 노트럴 뭉캐고 새로 적십니꺼? 요 노트서 리액션하고 리노트, 답하기도 말캉 뭉캐집니다."
+addToList: "리스트에 옇기"
+addToAntenna: "안테나에 옇기"
+sendMessage: "메시지 보내기"
+copyRSS: "알에스에스 복사하기"
+copyUsername: "사용자 이럼 복사하기"
+copyUserId: "사용자 아이디 복사하기"
+copyNoteId: "노트 아이디 복사하기"
+copyFileId: "파일 아이디 복사하기"
+copyFolderId: "폴더 아이디 복사하기"
+copyProfileUrl: "프로필 주소 복사하기"
+searchUser: "사용자 찾기"
+reply: "답하기"
+loadMore: "더 볼래예"
+showMore: "더 볼래예"
+showLess: "꺼기"
+youGotNewFollower: "새 팔로워가 잇십니다"
+receiveFollowRequest: "팔로잉 요청이 잇십니다"
+followRequestAccepted: "팔로잉이 받아딜이젓십니다"
+mention: "멘션"
+mentions: "받언 멘션"
+directNotes: "쪽지 서기"
+importAndExport: "가오기하고 내가기"
+import: "가오기"
+export: "내가기"
+files: "파일"
+download: "내리받기"
+driveFileDeleteConfirm: "‘{name}’ 파일얼 뭉캡니꺼? 요 파일얼 서넌 콘텐츠도 뭉캐집니다."
+unfollowConfirm: "{name}님얼 고만 팔로잉합니꺼?"
+exportRequested: "내가기 요청얼 햇십니다. 시간이 쪼매 걸릴 깁니다. 요청이 껕나모 ‘드라이브’에 옇십니다."
+importRequested: "가오기 요청얼 햇십니다. 시간이 쪼매 걸릴 깁니다."
+lists: "리스트"
+noLists: "리스트가 없십니다"
+note: "노트"
+notes: "노트"
+following: "팔로잉"
+followers: "팔로워"
+followsYou: "내럴 팔로잉합니다"
+createList: "리스트 맨걸기"
+manageLists: "리스트 간리하기"
+error: "우짭니꺼"
+somethingHappened: "먼가 일낫십니다"
+retry: "다시 하기"
+pageLoadError: "하멘 부리오기가 아이뎁니다."
+pageLoadErrorDescription: "네트워크나 브라우저 캐시 때문일 깁니다. 캐시럴 뭉캐던가 쪼매 잇다 새로 해 주이소."
+serverIsDead: "서버가 대답얼 아이합니다. 쪼매 잇다 새로 해 주이소."
+youShouldUpgradeClient: "요 하멘얼 볼라먼 새로 곤치던가 새 버전으 클라이언트럴 받아 서 보이소."
+enterListName: "리스트 이럼 서기"
+privacy: "개인 정보"
+makeFollowManuallyApprove: "팔로잉얼 하나석 받아딜이기"
+defaultNoteVisibility: "기본 공개 범위"
+follow: "팔로우"
+followRequest: "팔로우 요청하기"
+followRequests: "팔로우 요청"
+unfollow: "팔로우 무루기"
+followRequestPending: "팔로우 수락 지둘림"
+enterEmoji: "이모지 서기"
+renote: "리노트"
+unrenote: "리노트 무루기"
+renoted: "리노트럴 햇십니다."
+cantRenote: "요 걸언 리노트럴 몬 합니다."
+cantReRenote: "리노트넌 지럴 리노트 몬 합니다."
+quote: "따오기"
+inChannelRenote: "채널 안 리노트"
+inChannelQuote: "채널 안 따오기"
+pinnedNote: "프로필에 붙인 노트"
+pinned: "프로필에 붙이기"
+you: "나"
+clickToShow: "누질라서 보기"
+sensitive: "수ᇚ힛섭니다"
+add: "옇기"
+reaction: "반엉"
+reactions: "반엉"
+reactionSetting: "모엄함서 포시할 반엉"
+reactionSettingDescription2: "꺼시서 두고, 누질라서 뭉캐고,  ‘+’럴 누질라서 옇십니다."
+rememberNoteVisibility: "공개 범위럴 기억하기"
+attachCancel: "붙임 빼기"
+markAsSensitive: "수ᇚ힘 설정"
+unmarkAsSensitive: "수ᇚ힘 무루기"
+enterFileName: "파일 이럼 서기"
+mute: "수ᇚ후기"
+unmute: "수ᇚ훈 거 무루기"
+renoteMute: "리노트 수ᇚ후기"
+renoteUnmute: "리노트 수ᇚ훈 거 무루기"
+block: "차단하기"
+unblock: "차단 무루기"
+suspend: "얼우기"
+unsuspend: "얼우기 풀기"
+blockConfirm: "차단합니꺼?"
+unblockConfirm: "차단얼 무룹니꺼?"
+suspendConfirm: "얼웁니꺼?"
+unsuspendConfirm: "얼운 거 풉니꺼?"
+selectList: "리스트 개리기"
+editList: "리스트 적기"
+selectChannel: "채널 개리기"
+selectAntenna: "안테나 개리기"
+editAntenna: "안테나 적기"
+selectWidget: "위젯 개리기"
+editWidgets: "위젯 적기"
+editWidgetsExit: "고마 적기"
+customEmojis: "사용자 지정 이모지"
+emoji: "이모지"
+emojis: "이모지"
+emojiName: "이모지 이럼"
+emojiUrl: "이모지 주소"
+addEmoji: "이모지 옇기"
+settingGuide: "개않언 설정"
+cacheRemoteFiles: "웬겍 파일 캐시하기"
+cacheRemoteFilesDescription: "요 설정얼 키모 웬겍 파일얼 요 서버으 스토리지에 캐시합니다. 미디어가 사게 비이지먼 서버으 스토리지럴 마이 섭니다. 웬겍 사용자가 얼매나 캐시럴 둘 긴가넌 고 옉할으 드라이브 크기 제한마중 다립니다. 요 제한얼 넘구모 엣날 파일버터 캐시서 뭉캐지서 링크가 뎁니다. 요 설정얼 꺼모 웬겍 파일언 첨버터 링크가 뎁니다. 이미지으 섬네일얼 맨걸던 사용자으 개인 정보럴 징키던 할라먼 default.yml서 proxyRemoteFiles럴 ture로 하입시다."
+youCanCleanRemoteFilesCache: "파일 간리으 🗑️ 모냥얼 누질리모 캐시럴 말캉 뭉캘 수 잇십니다."
+cacheRemoteSensitiveFiles: "웬겍으 수ᇚ힌 파일얼 캐시하기"
+cacheRemoteSensitiveFilesDescription: "요 설정얼 꺼모 웬겍 수ᇚ힌 파일이 캐시하지 아이하고 바리 링크합니다."
+flagAsBot: "자동 게정입니다"
+flagAsBotDescription: "요 게정얼 프로그램서 설라먼 키야 합니다. 키모 다런 개발자가 반엉얼 끋없이 데풀이하지 몬 하게 도아 줄 수 잇고 Misskey으 시스템서 자동 게정이 뎁니다."
+flagAsCat: "애웅애웅애웅애웅!"
+flagAsCatDescription: "애옹?"
+flagShowTimelineReplies: "타임라인서 노트으 답하기 보기"
+flagShowTimelineRepliesDescription: "키모 타임라인서 다런 사용자덜으 답하기도 봅니다."
+autoAcceptFollowed: "팔로잉하넌 사용자으 팔로잉 요청 바리 받아딜이기"
+addAccount: "게정 옇기"
+reloadAccountsList: "게정 리스트으 정보 새로 바꾸기"
+loginFailed: "로그인이 아이뎁니다."
+showOnRemote: "웬겍서 보기"
+general: "일반"
+wallpaper: "벡지"
+setWallpaper: "벡지 설정"
+removeWallpaper: "벡지 뭉캐기"
+searchWith: "찾기: {q}"
+youHaveNoLists: "리스트가 없십니다"
+followConfirm: "{name}님얼 팔로잉합니꺼?"
+proxyAccount: "프락시 게정"
+proxyAccountDescription: "프락시 게정언 턱벨한 조겐서 웬겍 팔로잉얼 하넌 게정입니다. 사용자가 웬겍 사용자럴 리스트에 옇얼 때 리스트에 옇언 사용자럴 누도 팔로잉 아이하모 할동이 서버로 아이 오니께 요 게정이 아인 프락시 게정얼 팔로잉하게 합니다."
+host: "호스트 이럼"
+selectUser: "사용자 개리기"
+recipient: "받넌 사람"
+annotation: "주석"
+federation: "옌합"
+instances: "서버"
+registeredAt: "첫 발겐"
+latestRequestReceivedAt: "막죽에 받언 요청"
+latestStatus: "막죽 상태"
+storageUsage: "스토리지 사용량"
+charts: "차트"
+perHour: "한 시간마중"
+perDay: "하리마중"
+stopActivityDelivery: "할동 고마 보내기"
+blockThisInstance: "요 서버 차단하기"
+silenceThisInstance: "서버 수ᇚ후기"
+operations: "동작"
+software: "소프트웨어"
+version: "버전"
+metadata: "메타데이터"
+withNFiles: "파일 {n}개"
+monitor: "모니터"
+jobQueue: "작업 대기옐"
+cpuAndMemory: "시피유하고 메모리"
+network: "네트워크"
+disk: "디스크"
+instanceInfo: "서버 정보"
+statistics: "통게"
+clearQueue: "대기옐 비우기"
+clearQueueConfirmTitle: "대기옐얼 비웁니꺼?"
+clearQueueConfirmText: "대기옐에 잇넌 걸얼 아이 보냅니다. 흐이 요 동작언 할 필요가 없십니다."
+clearCachedFiles: "캐시 비우기"
+clearCachedFilesConfirm: "캐시한 웬겍 파일얼 말캉 뭉캡니꺼?"
+blockedInstances: "차단한 서버"
+blockedInstancesDescription: "차단할라넌 서버으 호스트럴 줄 바꿈해서로 비이 줍니다. 차단한 서버넌 요 서버하고 교류 몬 합니다."
+silencedInstances: "수ᇚ훈 서버"
+silencedInstancesDescription: "수ᇚ훌라넌 서버으 호스트럴 줄 바꿈해서로 비이 줍니다. 수ᇚ훈 서버으 게정언 말캉 ‘수ᇚ후기’가 데서 팔로잉 요청만 데고 팔로워가 아인 로컬 게정서 멘션얼 몬 합니다. 차단한 서버넌 상간 없십니다."
+muteAndBlock: "수ᇚ훔하고 차단"
+mutedUsers: "수ᇚ훈 사용자"
+blockedUsers: "차단한 사용자"
+noUsers: "사용자가 없십니다"
+editProfile: "프로필 적기"
+noteDeleteConfirm: "요 노트럴 뭉캡니꺼?"
+pinLimitExceeded: "더 몬 붙입니다"
+intro: "Misskey럴 다 깔앗십니다! 간리자 게정얼 맨걸어 보입시다."
+done: "햇어예"
+processing: "처리하고 잇어예"
+preview: "미리보기"
+default: "기본값"
+defaultValueIs: "기본값: {value}"
+noCustomEmojis: "이모지가 없십니다"
+noJobs: "작업이 없십니다"
+federating: "옌합하고 잇어예"
+blocked: "차단햇어예"
+suspended: "고만 보내예"
+all: "말캉"
+subscribing: "구독하고 잇어예"
+publishing: "보내고 잇어예"
+notResponding: "답이 없어예"
+instanceFollowing: "서버으 팔로잉"
+instanceFollowers: "서버으 팔로워"
+instanceUsers: "서버으 사용자"
+changePassword: "비밀번호 바꾸기"
+security: "보안"
+retypedNotMatch: "선 거가 안 맞십니다."
+currentPassword: "지검 비밀번호"
+newPassword: "새 비밀번호"
+newPasswordRetype: "새 비밀번호 다시 서기"
+attachFile: "파일 붙이기"
+more: "더 볼래예!"
+featured: "인기"
+usernameOrUserId: "사용자 이럼이나 사용자 아이디"
+noSuchUser: "사용자럴 몬 찾앗십니다"
+lookup: "찾아보기"
+announcements: "공지 걸"
+imageUrl: "이미지 주소"
+remove: "내삐리기"
+removed: "뭉캣십니다"
+removeAreYouSure: "‘{x}’(얼)럴 뭉캡니꺼?"
+deleteAreYouSure: "‘{x}’(얼)럴 뭉캡니꺼?"
+resetAreYouSure: "아시로 데돌립니꺼?"
+saved: "저장햇십니다"
+messaging: "대화"
+upload: "올리기"
+keepOriginalUploading: "온본 두기"
+keepOriginalUploadingDescription: "이미지럴 올릴 때 온본얼 고대로 둡니다. 꺼모 올릴 때 브라우저서 웹 공개 이미지럴 맨겁니다."
+fromDrive: "드라이브서"
+fromUrl: "주소서"
+uploadFromUrl: "주소 올리기"
+uploadFromUrlDescription: "올리기할라넌 파일으 주소"
+uploadFromUrlRequested: "올리기럴 요청햇십니다"
+uploadFromUrlMayTakeTime: "올리기가 껕날라먼 시간이 쪼매 걸릴 깁니다."
+explore: "살펴보기"
+messageRead: "이럿어예"
+noMoreHistory: "요카마 엣날 기록이 없십니다"
+startMessaging: "대화하기"
+nUsersRead: "{n}멩이 이럿십니다"
+agreeTo: "{0}에 동이하기"
+agree: "동이합니다"
+agreeBelow: "밑으 내용에 동이합니다"
+basicNotesBeforeCreateAccount: "주이할 내용"
+termsOfService: "이용 약간"
+start: "시작하기"
+home: "덜머리"
+remoteUserCaution: "웬겍 사용자넌 정보가 학실하지 아이할 수 잇십니다."
+activity: "할동"
+images: "이미지"
+image: "이미지"
+birthday: "생일"
+yearsOld: "{age}살"
+registeredDate: "맨건 날"
+location: "장소"
+theme: "테마"
+themeForLightMode: "볽엄 모드서 설 테마"
+themeForDarkMode: "어덥엄 모드서 설 테마"
+light: "볽엄"
+dark: "어덥엄"
+lightThemes: "볽언 테마"
+darkThemes: "어덥언 테마"
+syncDeviceDarkMode: "드라이브으 어덥엄 모드하고 같구로 마추기"
+drive: "드라이브"
+fileName: "파일 이럼"
+selectFile: "파일 개리기"
+selectFiles: "파일 개리기"
+selectFolder: "폴더 개리기"
+selectFolders: "폴더 개리기"
+renameFile: "파일 이럼 바꾸기"
+folderName: "폴더 이럼"
+createFolder: "폴더 맨걸기"
+renameFolder: "폴더 이럼 바꾸기"
+deleteFolder: "폴더 뭉캐기"
+folder: "폴더"
+addFile: "파일 옇기"
+emptyDrive: "드라이브가 비잇십니다"
+emptyFolder: "폴더가 비잇십니다"
+unableToDelete: "몬 뭉캡니다"
+inputNewFileName: "새 파일 이럼얼 서 보이소"
+inputNewDescription: "새 설멩얼 서 보이소"
+inputNewFolderName: "새 폴더 이럼얼 서 보이소"
+circularReferenceFolder: "엚길 폴더으 아래 폴더입니다."
+hasChildFilesOrFolders: "요 폴더넌 아이 비잇어니께 몬 뭉캡니다."
+copyUrl: "주소 복사하기"
+rename: "이럼 바꾸기"
+avatar: "아바타"
+banner: "배너"
+displayOfSensitiveMedia: "수ᇚ힌 옝상물 보기"
+whenServerDisconnected: "서버하고 옌겔이 껂기모"
+disconnectedFromServer: "서버하고 옌겔이 껂깃십니다"
+reload: "새로곤침"
+doNothing: "무시하기"
+reloadConfirm: "새로곤침합니까?"
+watch: "간심 갖기"
+unwatch: "간심 고마 갖기"
+accept: "받기"
+reject: "아이 받기"
+normal: "일반"
+instanceName: "서버 이럼"
+instanceDescription: "서버 소개"
+maintainerName: "간리자 이럼"
+maintainerEmail: "간리자 전자우펜"
+tosUrl: "이용 약간 주소"
+thisYear: "올개"
+thisMonth: "요달"
+today: "오올"
+dayX: "{day}일"
+monthX: "{month}월"
+yearX: "{year}년"
+pages: "바닥"
+integration: "옌겔"
+connectService: "옌겔하기"
+disconnectService: "껂기"
+enableLocalTimeline: "로컬 타임라인 키기"
+enableGlobalTimeline: "글로벌 타임라인 키기"
+disablingTimelinesInfo: "요 타임라인얼 꺼도 간리자하고 중재자넌 고대로 설 수 잇십니다."
+registration: "맨걸기"
+enableRegistration: "누라도 새로 맨걸 수 잇거로 하기"
+invite: "초대하기"
+driveCapacityPerLocalAccount: "로컬 사용자 하나마중 드라이브 커기"
+driveCapacityPerRemoteAccount: "웬겍 사용자 하나마중 드라이브 커기"
+inMb: "메가바이트 단이"
+bannerUrl: "배너 이미지 주소"
+backgroundImageUrl: "배겡 이미지 주소"
+basicInfo: "기본 정보"
+pinnedUsers: "붙인 사용자"
+pinnedUsersDescription: "‘살펴보기’서 붙일라넌 사용자럴 줄 바꿈해서로 적십니다."
+pinnedPages: "붙인 바닥"
+pinnedPagesDescription: "서버으 대문서 붙일라넌 바닥으 겡로럴 줄 바꿈해서로 적십니다."
+pinnedClipId: "붙일 클립으 아이디"
+pinnedNotes: "프로필에 붙인 노트"
+hcaptcha: "에이치캡차"
+enableHcaptcha: "에이치캡차 키기"
+hcaptchaSiteKey: "사이트키"
+hcaptchaSecretKey: "시크릿키"
+recaptcha: "리캡차"
+enableRecaptcha: "리캡차 키기"
+recaptchaSiteKey: "사이트키"
+recaptchaSecretKey: "시크릿키"
+turnstile: "턴스타일"
+enableTurnstile: "턴스타일 키기"
+turnstileSiteKey: "사이트키"
+turnstileSecretKey: "시크릿키"
+avoidMultiCaptchaConfirm: "오만 캡차럴 서모 간섭이 잇얼 깁니다. 다린 캡차를 껍니까? ‘아이예’럴 누질리모 오만 캡차럴 키 둘 수도 잇십니다."
+antennas: "안테나"
+manageAntennas: "안테나 간리"
+name: "이럼"
+antennaSource: "받얼 소스"
+antennaKeywords: "받얼 검색어"
+antennaExcludeKeywords: "수ᇚ훌 검색어"
+antennaKeywordsDescription: "띠어서기럴 하모 ‘거라고’가 데고 줄 바꿈얼 하모 ‘아이먼’이 뎁니다"
+notifyAntenna: "새 노트럴 알리기"
+withFileAntenna: "파일이 붙언 노트마"
+enableServiceworker: "브라우저서 알림 포시럴 키기"
+antennaUsersDescription: "사용자 이럼얼 줄 바꿈해서로 섭니다"
+caseSensitive: "대소문자럴 구벨하기"
+withReplies: "답하기도 옇기"
+connectedTo: "요 게정하고 옌겔데어 잇십니다"
+notesAndReplies: "걸하고 답걸"
+withFiles: "파일에 붙이기"
+silence: "수ᇚ후기"
+silenceConfirm: "수ᇚ훕니꺼?"
+unsilence: "수ᇚ후기 어ᇝ애기"
+unsilenceConfirm: "수ᇚ후기럴 어ᇝ앱니꺼?"
+popularUsers: "소문난 사용자"
+recentlyUpdatedUsers: "얼마 전에 걸 선 사용자"
+recentlyRegisteredUsers: "얼마 전에 맨건 사용자"
+recentlyDiscoveredUsers: "얼마 전에 찾언 사용자"
+exploreUsersCount: "사용자 {count}멩이 잇십니다."
+exploreFediverse: "옌합우주 탐험하기"
+popularTags: "소문난 태그"
+userList: "리스트"
+about: "정보"
+aboutMisskey: "Misskey넌예"
+administrator: "간리자"
+token: "학인 코드"
+2fa: "두 단게 정멩"
+setupOf2fa: "두 단게 정멩 설정"
+totp: "정멩 앱"
+totpDescription: "정멩 앱서 단헤용 비밀번호 서기"
+moderator: "중재자"
+moderation: "중재"
+moderationNote: "중재 노트"
+addModerationNote: "중재 노트 옇기"
+moderationLogs: "중재 일지"
+nUsersMentioned: "{n}멩이 이바구하고 잇어예"
+securityKeyAndPasskey: "보안키·패스키"
+securityKey: "보안키"
+invites: "초대하기"
+invitations: "초대하기"
+language: "언어"
+manage: "간리"
+smtpHost: "호스트 이럼"
+smtpUser: "사용자 이럼"
+smtpPass: "비밀번호"
+clearCache: "캐시 비우기"
+unlikeConfirm: "좋네예럴 무룹니꺼?"
+info: "정보"
+user: "사용자"
+administration: "간리"
+on: "킴"
+off: "껌"
+searchByGoogle: "찾기"
+tenMinutes: "십 분"
+oneHour: "한 시간"
+oneDay: "하리"
+oneWeek: "한 주"
+oneMonth: "한 달"
+file: "파일"
+tools: "도구"
+like: "좋네예!"
+unlike: "좋네예 무루기"
+numberOfLikes: "좋네예 수"
+roles: "옉할"
+role: "옉할"
+noRole: "옉할이 없십니다"
+thisPostMayBeAnnoyingCancel: "아이예"
+likeOnly: "좋네예마"
+icon: "아바타"
+replies: "답하기"
+renotes: "리노트"
+_gallery:
+  liked: "좋네예한 걸"
+  like: "좋네예!"
+  unlike: "좋네예 무루기"
+_email:
+  _follow:
+    title: "새 팔로워가 잇십니다"
+_theme:
+  keys:
+    mention: "멘션"
+_sfx:
+  note: "노트"
+  notification: "알림"
+_2fa:
+  renewTOTPCancel: "뎃어예"
+_widgets:
+  profile: "프로필"
+  instanceInfo: "서버 정보"
+  notifications: "알림"
+  timeline: "타임라인"
+  activity: "할동"
+  federation: "옌합"
+  jobQueue: "작업 대기옐"
+  _userList:
+    chooseList: "리스트 개리기"
+_cw:
+  show: "더 볼래예"
+_visibility:
+  home: "덜머리"
+  followers: "팔로워"
+_profile:
+  name: "이럼"
+  username: "사용자 이럼"
+_exportOrImport:
+  followingList: "팔로잉"
+  muteList: "수ᇚ후기"
+  blockingList: "차단하기"
+  userLists: "리스트"
+_charts:
+  federation: "옌합"
+_timelines:
+  home: "덜머리"
+_pages:
+  like: "좋네예"
+  unlike: "좋네예 무루기"
+  blocks:
+    image: "이미지"
+_notification:
+  youWereFollowed: "새 팔로워가 잇십니다"
+  _types:
+    follow: "팔로잉"
+    mention: "멘션"
+    quote: "따오기"
+    reaction: "반엉"
+  _actions:
+    reply: "답하기"
+_deck:
+  _columns:
+    notifications: "알림"
+    tl: "타임라인"
+    antenna: "안테나"
+    list: "리스트"
+    mentions: "받언 멘션"
+_webhookSettings:
+  name: "이럼"
+_moderationLogTypes:
+  suspend: "얼우기"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index ec346c9ec1..2673e947f3 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -2,14 +2,14 @@
 _lang_: "한국어"
 headlineMisskey: "노트로 연결되는 네트워크"
 introMisskey: "환영합니다! Misskey는 오픈 소스 분산형 마이크로 블로그 서비스입니다.\n'노트'를 작성해서 지금 일어나고 있는 일을 공유하거나, 당신만의 이야기를 모두에게 발신하세요📡\n'리액션' 기능으로 친구의 노트에 총알같이 반응을 추가할 수도 있습니다👍\n새로운 세계를 탐험해 보세요🚀"
-poweredByMisskeyDescription: "{name}은(는) 오픈소스 플랫폼<b>Misskey</b>를 사용한 서비스(Misskey 인스턴스라고 불립니다) 중 하나입니다."
+poweredByMisskeyDescription: "{name} 서버는 오픈소스 플랫폼 <b>Misskey</b>의 서버 가운데 하나입니다."
 monthAndDay: "{month}월 {day}일"
 search: "검색"
 notifications: "알림"
 username: "유저명"
 password: "비밀번호"
 forgotPassword: "비밀번호 재설정"
-fetchingAsApObject: "연합에 조회 중"
+fetchingAsApObject: "연합에서 찾아보는 중"
 ok: "확인"
 gotIt: "알겠어요"
 cancel: "취소"
@@ -45,7 +45,7 @@ pin: "프로필에 고정"
 unpin: "프로필에서 고정 해제"
 copyContent: "내용 복사"
 copyLink: "링크 복사"
-copyLinkRenote: "Renote 링크 복사"
+copyLinkRenote: "리노트 링크 복사"
 delete: "삭제"
 deleteAndEdit: "삭제 후 편집"
 deleteAndEditConfirm: "이 노트를 삭제한 뒤 다시 편집하시겠습니까? 이 노트에 대한 리액션, 리노트, 답글 또한 모두 삭제됩니다."
@@ -53,8 +53,8 @@ addToList: "리스트에 추가"
 addToAntenna: "안테나에 추가"
 sendMessage: "메시지 보내기"
 copyRSS: "RSS 복사"
-copyUsername: "유저명 복사"
-copyUserId: "유저 ID 복사"
+copyUsername: "사용자 이름 복사"
+copyUserId: "사용자 ID 복사"
 copyNoteId: "노트 ID 복사"
 copyFileId: "파일 ID 복사"
 copyFolderId: "폴더 ID 복사"
@@ -75,7 +75,7 @@ import: "가져오기"
 export: "내보내기"
 files: "파일"
 download: "다운로드"
-driveFileDeleteConfirm: "파일 \"{name}\" 을 삭제하시겠습니까? 이 파일이 첨부된 노트도 함께 삭제됩니다."
+driveFileDeleteConfirm: "‘{name}’ 파일을 삭제하시겠습니까? 이 파일을 사용하는 일부 콘텐츠도 삭제됩니다."
 unfollowConfirm: "{name}님을 언팔로우하시겠습니까?"
 exportRequested: "내보내기를 요청하였습니다. 이 작업은 시간이 걸릴 수 있습니다. 내보내기가 완료되면 \"드라이브\"에 추가됩니다."
 importRequested: "가져오기를 요청하였습니다. 이 작업에는 시간이 걸릴 수 있습니다."
@@ -85,7 +85,7 @@ note: "노트"
 notes: "노트"
 following: "팔로잉"
 followers: "팔로워"
-followsYou: "당신을 팔로우합니다"
+followsYou: "나를 팔로우 합니다"
 createList: "리스트 만들기"
 manageLists: "리스트 관리"
 error: "오류"
@@ -115,7 +115,7 @@ inChannelRenote: "채널 내 리노트"
 inChannelQuote: "채널 내 인용"
 pinnedNote: "고정된 노트"
 pinned: "프로필에 고정"
-you: "당신"
+you: "나"
 clickToShow: "클릭하여 보기"
 sensitive: "열람 주의"
 add: "추가"
@@ -130,7 +130,7 @@ unmarkAsSensitive: "열람주의 해제"
 enterFileName: "파일명을 입력"
 mute: "뮤트"
 unmute: "뮤트 해제"
-renoteMute: "리노트를 뮤트"
+renoteMute: "리노트 뮤트하기"
 renoteUnmute: "리노트 뮤트 해제"
 block: "차단"
 unblock: "차단 해제"
@@ -247,13 +247,13 @@ security: "보안"
 retypedNotMatch: "입력이 일치하지 않습니다."
 currentPassword: "현재 비밀번호"
 newPassword: "새 비밀번호"
-newPasswordRetype: "새 비밀번호 (재입력)"
+newPasswordRetype: "새 비밀번호(재입력)"
 attachFile: "파일 첨부"
-more: "더보기"
-featured: "하이라이트"
+more: "더 보기!"
+featured: "유행"
 usernameOrUserId: "유저명이나 ID"
 noSuchUser: "유저를 찾을 수 없습니다"
-lookup: "조회"
+lookup: "찾아보기"
 announcements: "공지사항"
 imageUrl: "이미지 URL"
 remove: "삭제"
@@ -307,10 +307,11 @@ selectFiles: "파일 선택"
 selectFolder: "폴더 선택"
 selectFolders: "폴더 선택"
 renameFile: "파일 이름 변경"
-folderName: "폴더명"
+folderName: "폴더 이름"
 createFolder: "폴더 만들기"
 renameFolder: "폴더 이름 바꾸기"
 deleteFolder: "폴더 삭제"
+folder: "폴더"
 addFile: "파일 추가"
 emptyDrive: "드라이브가 비어 있습니다"
 emptyFolder: "폴더가 비어 있습니다"
@@ -330,10 +331,10 @@ disconnectedFromServer: "서버와의 연결이 끊어졌습니다"
 reload: "새로고침"
 doNothing: "무시하기"
 reloadConfirm: "새로고침 하시겠습니까?"
-watch: "지켜보기"
-unwatch: "지켜보기 해제"
-accept: "허가"
-reject: "거부"
+watch: "관심 갖기"
+unwatch: "관심 해제하기"
+accept: "수락하기"
+reject: "거절하기"
 normal: "일반"
 instanceName: "서버 이름"
 instanceDescription: "서버 소개"
@@ -341,7 +342,7 @@ maintainerName: "관리자 이름"
 maintainerEmail: "관리자 이메일"
 tosUrl: "이용약관 URL"
 thisYear: "올해"
-thisMonth: "이번 달"
+thisMonth: "이달"
 today: "오늘"
 dayX: "{day}일"
 monthX: "{month}월"
@@ -385,8 +386,8 @@ antennas: "안테나"
 manageAntennas: "안테나 관리"
 name: "이름"
 antennaSource: "받을 소스"
-antennaKeywords: "받을 키워드"
-antennaExcludeKeywords: "제외할 키워드"
+antennaKeywords: "받을 검색어"
+antennaExcludeKeywords: "제외할 검색어"
 antennaKeywordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정됩니다"
 notifyAntenna: "새로운 노트를 알림"
 withFileAntenna: "파일이 첨부된 노트만"
@@ -437,7 +438,6 @@ share: "공유"
 notFound: "찾을 수 없습니다"
 notFoundDescription: "지정한 URL에 해당하는 페이지가 존재하지 않습니다."
 uploadFolder: "기본 업로드 위치"
-cacheClear: "캐시 지우기"
 markAsReadAllNotifications: "모든 알림을 읽은 상태로 표시"
 markAsReadAllUnreadNotes: "모든 글을 읽은 상태로 표시"
 markAsReadAllTalkMessages: "모든 대화를 읽은 상태로 표시"
@@ -479,7 +479,7 @@ language: "언어"
 uiLanguage: "UI 표시 언어"
 aboutX: "{x}에 대하여"
 emojiStyle: "이모지 스타일"
-native: "네이티브"
+native: "기본"
 disableDrawer: "드로어 메뉴를 사용하지 않기"
 showNoteActionsOnlyHover: "노트 액션 버튼을 마우스를 올렸을 때에만 표시"
 noHistory: "기록이 없습니다"
@@ -544,6 +544,8 @@ showInPage: "페이지로 보기"
 popout: "새 창으로 열기"
 volume: "음량"
 masterVolume: "마스터 볼륨"
+notUseSound: "음소거 하기"
+useSoundOnlyWhenActive: "Misskey가 활성화 되어져 있을 때만 소리 출력하기"
 details: "자세히"
 chooseEmoji: "이모지 선택"
 unableToProcess: "작업을 완료할 수 없습니다"
@@ -564,10 +566,14 @@ output: "출력"
 script: "스크립트"
 disablePagesScript: "Pages 에서 AiScript 를 사용하지 않음"
 updateRemoteUser: "리모트 유저 정보 갱신"
+unsetUserAvatar: "아바타 제거"
+unsetUserAvatarConfirm: "아바타를 제거할까요?"
+unsetUserBanner: "배너 제거"
+unsetUserBannerConfirm: "배너를 제거할까요?"
 deleteAllFiles: "모든 파일 삭제"
 deleteAllFilesConfirm: "모든 파일을 삭제하시겠습니까?"
 removeAllFollowing: "모든 팔로잉 해제"
-removeAllFollowingDescription: "{host}(으)로부터 모든 팔로잉을 해제합니다. 해당 서버가 더 이상 존재하지 않게 된 경우 등에 실행해 주세요."
+removeAllFollowingDescription: "{host} 서버의 모든 팔로잉을 해제합니다. 해당 서버가 더 이상 존재하지 않는 경우 등에 실행해 주세요."
 userSuspended: "이 계정은 정지된 상태입니다."
 userSilenced: "이 계정은 사일런스된 상태입니다."
 yourAccountSuspendedTitle: "계정이 정지되었습니다"
@@ -587,7 +593,7 @@ addedRelays: "추가된 릴레이"
 serviceworkerInfo: "푸시 알림을 수행하려면 활성화해야 합니다."
 deletedNote: "삭제된 노트"
 invisibleNote: "비공개 노트"
-enableInfiniteScroll: "자동으로 좀 더 보기"
+enableInfiniteScroll: "자동으로 더 보기"
 visibility: "공개 범위"
 poll: "투표"
 useCw: "내용 숨기기"
@@ -628,13 +634,14 @@ emailAddress: "메일 주소"
 smtpConfig: "SMTP 서버 설정"
 smtpHost: "호스트"
 smtpPort: "포트"
-smtpUser: "유저명"
+smtpUser: "사용자 이름"
 smtpPass: "비밀번호"
 emptyToDisableSmtpAuth: "SMTP 인증을 사용하지 않으려면 공란으로 비워둡니다."
 smtpSecure: "SMTP 연결에 Implicit SSL/TTS 사용"
 smtpSecureInfo: "STARTTLS 사용 시에는 해제합니다."
 testEmail: "이메일 전송 테스트"
 wordMute: "단어 뮤트"
+hardWordMute: "하드 단어 뮤트"
 regexpError: "정규 표현식 오류"
 regexpErrorDescription: "{tab}단어 뮤트 {line}행의 정규 표현식에 오류가 발생했습니다:"
 instanceMute: "서버 뮤트"
@@ -662,7 +669,7 @@ behavior: "동작"
 sample: "예시"
 abuseReports: "신고"
 reportAbuse: "신고"
-reportAbuseRenote: "Renote를 신고"
+reportAbuseRenote: "리노트 신고하기"
 reportAbuseOf: "{name}을 신고하기"
 fillAbuseReportDescription: "신고하려는 이유를 자세히 알려주세요. 특정 게시물을 신고할 때에는 게시물의 URL도 포함해 주세요."
 abuseReported: "신고를 보냈습니다. 신고해 주셔서 감사합니다."
@@ -696,9 +703,9 @@ manageAccessTokens: "액세스 토큰 관리"
 accountInfo: "계정 정보"
 notesCount: "노트 수"
 repliesCount: "답글 수"
-renotesCount: "Renote 수"
+renotesCount: "리노트 수"
 repliedCount: "받은 답글 수"
-renotedCount: "받은 Renote 수"
+renotedCount: "받은 리노트 수"
 followingCount: "팔로우 수"
 followersCount: "팔로워 수"
 sentReactionsCount: "보낸 리액션 수"
@@ -804,7 +811,7 @@ switchAccount: "계정 바꾸기"
 enabled: "활성화"
 disabled: "비활성화"
 quickAction: "빠른 동작"
-user: "유저"
+user: "사용자"
 administration: "관리"
 accounts: "계정"
 switch: "전환"
@@ -831,7 +838,7 @@ previewNoteText: "본문 미리보기"
 customCss: "CSS 사용자화"
 customCssWarn: "이 설정은 기능을 알고 있는 경우에만 사용해야 합니다. 잘못된 값을 입력하면 클라이언트가 정상적으로 작동하지 않을 수 있습니다."
 global: "글로벌"
-squareAvatars: "프로필 아이콘을 사각형으로 표시"
+squareAvatars: "프로필 아바타를 사각형으로 표시"
 sent: "전송"
 received: "수신"
 searchResult: "검색 결과"
@@ -866,11 +873,11 @@ manageAccounts: "계정 관리"
 makeReactionsPublic: "리액션 목록을 공개하기"
 makeReactionsPublicDescription: "나의 리액션을 누구나 볼 수 있게 합니다."
 classic: "클래식"
-muteThread: "이 글타래를 뮤트"
+muteThread: "글타래 뮤트"
 unmuteThread: "글타래 뮤트 해제"
 ffVisibility: "내 인맥의 공개 범위"
 ffVisibilityDescription: "나의 팔로우와 팔로워 정보에 대한 공개 범위를 설정할 수 있습니다."
-continueThread: "이 글타래 이어서 보기"
+continueThread: "글타래 더 보기"
 deleteAccountConfirm: "계정이 삭제되고 되돌릴 수 없게 됩니다. 계속하시겠습니까? "
 incorrectPassword: "비밀번호가 올바르지 않습니다."
 voteConfirm: "\"{choice}\"에 투표하시겠습니까?"
@@ -969,7 +976,7 @@ show: "표시"
 neverShow: "다시 보지 않기"
 remindMeLater: "나중에 알림"
 didYouLikeMisskey: "Misskey가 마음에 드시나요?"
-pleaseDonate: "{host}은(는) 무료 소프트웨어 Misskey를 사용합니다. 후원을 통해 저희의 개발이 이어질 수 있게 도와주세요!"
+pleaseDonate: "Misskey는 {host} 서버의 무료 소프트웨어입니다. 앞으로도 개발을 이어 나가려면 후원이 절실히 필요합니다!"
 roles: "역할"
 role: "역할"
 noRole: "역할이 없습니다"
@@ -1014,12 +1021,14 @@ reactionAcceptance: "리액션 수신"
 likeOnly: "좋아요만 받기"
 likeOnlyForRemote: "리모트에서는 좋아요만 받기"
 nonSensitiveOnly: "민감한 이모지를 제외하고 받기"
-nonSensitiveOnlyForLocalLikeOnlyForRemote: "민감한 이모지를 제외하고 받기 (리모트에서는 좋아요만 받기)"
+nonSensitiveOnlyForLocalLikeOnlyForRemote: "민감한 이모지를 제외하고 받기(리모트에서는 좋아요만 받기)"
 rolesAssignedToMe: "나에게 할당된 역할"
 resetPasswordConfirm: "비밀번호를 재설정하시겠습니까?"
 sensitiveWords: "민감한 단어"
 sensitiveWordsDescription: "설정한 단어가 포함된 노트의 공개 범위를 '홈'으로 강제합니다. 개행으로 구분하여 여러 개를 지정할 수 있습니다."
 sensitiveWordsDescription2: "공백으로 구분하면 AND 지정이 되며, 키워드를 슬래시로 둘러싸면 정규 표현식이 됩니다."
+hiddenTags: "숨긴 해시태그"
+hiddenTagsDescription: "설정한 태그를 트렌드에 표시하지 않도록 합니다. 줄 바꿈으로 하나씩 나눠서 설정할 수 있습니다."
 notesSearchNotAvailable: "노트 검색을 이용하실 수 없습니다."
 license: "라이선스"
 unfavoriteConfirm: "즐겨찾기를 해제하시겠습니까?"
@@ -1032,6 +1041,7 @@ enableChartsForRemoteUser: "리모트 유저의 차트를 생성"
 enableChartsForFederatedInstances: "리모트 서버의 차트를 생성"
 showClipButtonInNoteFooter: "노트 동작에 클립을 추가"
 reactionsDisplaySize: "리액션 표시 크기"
+limitWidthOfReaction: "리액션의 최대 폭을 제한하고 작게 표시하기"
 noteIdOrUrl: "노트 ID 및 URL"
 video: "동영상"
 videos: "동영상"
@@ -1044,7 +1054,7 @@ forceShowAds: "광고를 항상 표시"
 addMemo: "메모 추가"
 editMemo: "메모 편집"
 reactionsList: "리액션 목록"
-renotesList: "Renote 목록"
+renotesList: "리노트 목록"
 notificationDisplay: "알림 표시"
 leftTop: "왼쪽 상단"
 rightTop: "오른쪽 상단"
@@ -1109,7 +1119,7 @@ beSureToReadThisAsItIsImportant: "중요하므로 반드시 읽어주십시오."
 iHaveReadXCarefullyAndAgree: "\"{x}\"의 내용을 읽고 동의합니다."
 dialog: "다이얼로그"
 icon: "아바타"
-forYou: "당신에게"
+forYou: "나에게"
 currentAnnouncements: "현재 공지사항"
 pastAnnouncements: "과거 공지사항"
 youHaveUnreadAnnouncements: "읽지 않은 공지사항이 있습니다."
@@ -1144,12 +1154,12 @@ impressumDescription: "독일 등의 일부 나라와 지역에서는 꼭 표시
 privacyPolicy: "개인정보 보호 정책"
 privacyPolicyUrl: "개인정보 보호 정책 URL"
 tosAndPrivacyPolicy: "약관 및 개인정보 보호 정책"
-avatarDecorations: "아이콘 장식"
+avatarDecorations: "아바타 장식"
 attach: "붙이기"
 detach: "떼기"
 angle: "각도"
 flip: "플립"
-showAvatarDecorations: "아이콘 장식을 표시"
+showAvatarDecorations: "아바타 장식 표시"
 releaseToRefresh: "놓아서 새로고침"
 refreshing: "새로고침 중"
 pullDownToRefresh: "아래로 내려서 새로고침"
@@ -1158,6 +1168,8 @@ useGroupedNotifications: "알림을 그룹화하고 표시"
 signupPendingError: "메일 주소 확인중에 문제가 발생했습니다. 링크의 유효기간이 지났을 가능성이 있습니다."
 cwNotationRequired: "'내용을 숨기기'를 체크한 경우 주석을 써야 합니다."
 doReaction: "리액션 추가"
+code: "문자열"
+reloadRequiredToApplySettings: "설정을 적용하려면 새로고침을 해야 합니다."
 _announcement:
   forExistingUsers: "기존 유저에게만 알림"
   forExistingUsersDescription: "활성화하면 이 공지사항을 게시한 시점에서 이미 가입한 유저에게만 표시합니다. 비활성화하면 게시 후에 가입한 유저에게도 표시합니다."
@@ -1198,7 +1210,7 @@ _initialTutorial:
   _note:
     title: "'노트'가 무엇인가요?"
     description: "미스키에서는 게시물을 '노트'라고 합니다. 노트는 타임라인에 시간순으로 정렬되어 있고, 실시간으로 갱신됩니다."
-    reply: "답글을 다는 것이 가능합니다. 답글에 답글을 다는 것도 가능하며 스레드처럼 대화를 계속하는 것도 가능합니다."
+    reply: "답글을 달 수 있습니다. 답글에 답글을 달 수도 있고 글타래처럼 대화를 이어갈 수도 있습니다."
     renote: "그 노트를 자기 타임라인에 가져와서 공유하는 것이 가능합니다. 글을 추가해서 인용하는 것도 가능합니다."
     reaction: "리액션을 다는 것이 가능합니다. 다음 페이지에서 자세한 설명을 볼 수 있습니다."
     menu: "노트의 상세 정보를 표시하거나, 링크를 복사하는 등의 다양한 조작을 할 수 있습니다."
@@ -1211,7 +1223,7 @@ _initialTutorial:
     reactDone: "'-' 버튼을 눌러서 리액션을 취소할 수 있습니다."
   _timeline:
     title: "타임라인에 대하여"
-    description1: "Misskey에는 종류에 따라 여러 가지의 타임라인으로 구성되어 있습니다. (서버에 따라서는 일부 타임라인을 사용할 수 없는 경우가 있습니다)"
+    description1: "Misskey에는 종류에 따라 여러 가지의 타임라인으로 구성되어 있습니다.(서버에 따라서는 일부 타임라인을 사용할 수 없는 경우가 있습니다)"
     home: "내가 팔로우 중인 계정의 노트를 볼 수 있습니다."
     local: "이 서버에 있는 모든 유저의 게시물을 볼 수 있습니다."
     social: "홈 타임라인과 로컬 타임라인의 게시물을 모두 볼 수 있습니다."
@@ -1266,6 +1278,8 @@ _serverSettings:
   shortName: "약칭"
   shortNameDescription: "서버의 정식 명칭이 긴 경우에, 대신에 표시할 수 있는 약칭이나 통칭."
   fanoutTimelineDescription: "활성화하면 각종 타임라인을 가져올 때의 성능을 대폭 향상하며, 데이터베이스의 부하를 줄일 수 있습니다. 단, Redis의 메모리 사용량이 증가합니다. 서버의 메모리 용량이 작거나, 서비스가 불안정해지는 경우 비활성화할 수 있습니다."
+  fanoutTimelineDbFallback: "데이터베이스를 예비로 사용하기"
+  fanoutTimelineDbFallbackDescription: "활성화하면 타임라인의 캐시되어 있지 않은 부분에 대해 DB에 질의하여 정보를 가져옵니다. 비활성화하면 이를 실행하지 않음으로써 서버의 부하를 줄일 수 있지만, 타임라인에서 가져올 수 있는 게시물 범위가 한정됩니다."
 _accountMigration:
   moveFrom: "다른 계정에서 이 계정으로 이사"
   moveFromSub: "다른 계정에 대한 별칭을 생성"
@@ -1285,29 +1299,29 @@ _achievements:
   earnedAt: "달성 일시"
   _types:
     _notes1:
-      title: "미스키 시작했는데요"
+      title: "미스키 계정 만들었어요"
       description: "첫 노트를 작성했습니다"
-      flavor: "Misskey에 오신 것을 환영합니다!"
+      flavor: "Misskey에 어서 오세요!"
     _notes10:
-      title: "노트 조금"
+      title: "몇 가지 노트"
       description: "10개의 노트를 작성했습니다"
     _notes100:
-      title: "노트 많이"
+      title: "많은 노트"
       description: "100개의 노트를 작성했습니다"
     _notes500:
-      title: "노트로 뒤덮여버렸어"
+      title: "노트 범벅"
       description: "500개의 노트를 작성했습니다"
     _notes1000:
-      title: "노트만 산더미"
+      title: "노트가 산더미"
       description: "1,000개의 노트를 작성했습니다"
     _notes5000:
-      title: "노트가 어디서 솟아?"
+      title: "솟아나는 노트"
       description: "5,000개의 노트를 작성했습니다"
     _notes10000:
       title: "슈퍼 노트"
       description: "10,000개의 노트를 작성했습니다"
     _notes20000:
-      title: "노트 더 없어?"
+      title: "노트가 필요해요"
       description: "20,000개의 노트를 작성했습니다"
     _notes30000:
       title: "노트노트노트"
@@ -1333,27 +1347,27 @@ _achievements:
     _notes100000:
       title: "ALL YOUR NOTE ARE BELONG TO US"
       description: "100,000개의 노트를 작성했습니다"
-      flavor: "이만큼 쓸 일도 없겠지만... 다른 할 일이 있진 않으신가요?"
+      flavor: "이렇게나 쓸 게 있어요?"
     _login3:
-      title: "비기너 I"
-      description: "총 3일간 로그인했습니다"
-      flavor: "오늘부터 여러분도 미스키스트에요!"
+      title: "초보자 I"
+      description: "총 로그인한 날이 3일"
+      flavor: "오늘부터 여러분도 미스키스트랍니다"
     _login7:
-      title: "비기너 II"
-      description: "총 7일간 로그인했습니다"
+      title: "초보자 II"
+      description: "총 로그인한 날이 7일"
       flavor: "슬슬 익숙해지셨나요?"
     _login15:
-      title: "비기너 III"
-      description: "총 15일간 로그인했습니다"
+      title: "초보자 III"
+      description: "총 로그인한 날이 15일"
     _login30:
       title: "미스키스트 I"
-      description: "총 30일간 로그인했습니다"
+      description: "총 로그인한 날이 30일"
     _login60:
       title: "미스키스트 II"
-      description: "총 60일간 로그인했습니다"
+      description: "총 로그인한 날이 60일"
     _login100:
       title: "미스키스트 III"
-      description: "총 100일간 로그인했습니다"
+      description: "총 로그인한 날이 100일"
       flavor: "그 유저, 미스키스트이다"
     _login200:
       title: "단골 I"
@@ -1450,7 +1464,7 @@ _achievements:
       title: "보물찾기"
       description: "숨겨진 보물을 발견했습니다"
     _client30min:
-      title: "잠깐 쉬어"
+      title: "잠시 쉬어요"
       description: "클라이언트를 시작하고 30분이 경과하였습니다"
     _client60min:
       title: "No \"Miss\" in Misskey"
@@ -1488,8 +1502,8 @@ _achievements:
       title: "읽고 답하긴 하시는 건가요?"
       description: "100자가 넘는 노트가 작성되고 3초 안에 반응했습니다"
     _clickedClickHere:
-      title: "여길 눌러보세요"
-      description: "여길을 눌러봤습니다"
+      title: "여기를 누르세요"
+      description: "여기를 눌렀습니다"
     _justPlainLucky:
       title: "그냥 운이 좋았어"
       description: "매 10초마다 0.01%의 확률로 달성됩니다"
@@ -1536,7 +1550,9 @@ _role:
   assignTarget: "할당 대상"
   descriptionOfAssignTarget: "<b>수동</b>을 선택하면 누가 이 역할에 포함되는지를 수동으로 관리할 수 있습니다.\n<b>조건부</b>를 선택하면 조건을 설정해 일치하는 사용자를 자동으로 포함되게 할 수 있습니다."
   manual: "수동"
+  manualRoles: "수동 역할"
   conditional: "조건부"
+  conditionalRoles: "조건부 역할"
   condition: "조건"
   isConditionalRole: "조건부 역할입니다."
   isPublic: "역할 공개"
@@ -1681,7 +1697,7 @@ _registry:
   domain: "도메인"
   createKey: "키 생성"
 _aboutMisskey:
-  about: "Misskey는 syuilo에 의해서 2014년부터 개발되어 온 오픈소스 소프트웨어 입니다."
+  about: "Misskey는 syuilo가 2014년부터 개발한 오픈소스 소프트웨어입니다."
   contributors: "주요 기여자"
   allContributors: "모든 기여자"
   source: "소스 코드"
@@ -1796,7 +1812,7 @@ _theme:
     driveFolderBg: "드라이브 폴더 배경"
     wallpaperOverlay: "배경화면 오버레이"
     badge: "배지"
-    messageBg: "채팅 배경"
+    messageBg: "대화 배경"
     accentDarken: "강조 색상 (어두움)"
     accentLighten: "강조 색상 (밝음)"
     fgHighlighted: "강조된 텍스트"
@@ -1806,6 +1822,14 @@ _sfx:
   notification: "알림"
   antenna: "안테나 수신"
   channel: "채널 알림"
+  reaction: "리액션 선택"
+_soundSettings:
+  driveFile: "드라이브에 있는 오디오를 사용"
+  driveFileWarn: "드라이브에 있는 파일을 선택하세요."
+  driveFileTypeWarn: "이 파일은 지원되지 않습니다."
+  driveFileTypeWarnDescription: "오디오 파일을 선택하세요."
+  driveFileDurationWarn: "오디오가 너무 길어요."
+  driveFileDurationWarnDescription: "길은 오디오를 사용하시는 경우 미스키 사용에 지장이 갈 수도 있습니다. 그래도 괜찮습니까?"
 _ago:
   future: "미래"
   justNow: "방금 전"
@@ -1817,6 +1841,14 @@ _ago:
   monthsAgo: "{n}개월 전"
   yearsAgo: "{n}년 전"
   invalid: "없음"
+_timeIn:
+  seconds: "{n}초 후"
+  minutes: "{n}분 후"
+  hours: "{n}시간 후"
+  days: "{n}일 후"
+  weeks: "{n}주 후"
+  months: "{n}개월 후"
+  years: "{n}년 후"
 _time:
   second: "초"
   minute: "분"
@@ -1856,9 +1888,9 @@ _permissions:
   "write:account": "계정의 정보를 변경합니다"
   "read:blocks": "차단 여부를 확인합니다"
   "write:blocks": "차단을 하거나 해제합니다"
-  "read:drive": "드라이브를 조회합니다"
+  "read:drive": "드라이브 보기"
   "write:drive": "드라이브에 파일을 올리거나, 이름을 변경하거나, 삭제합니다"
-  "read:favorites": "즐겨찾기를 조회합니다"
+  "read:favorites": "즐겨찾기 보기"
   "write:favorites": "즐겨찾기에 추가하거나 삭제합니다"
   "read:following": "팔로우 상태를 봅니다"
   "write:following": "팔로우하거나 팔로우를 해제합니다"
@@ -1876,7 +1908,7 @@ _permissions:
   "write:pages": "페이지를 수정합니다"
   "read:page-likes": "페이지의 좋아요를 확인합니다"
   "write:page-likes": "페이지에 좋아요를 추가하거나 취소합니다"
-  "read:user-groups": "유저 그룹을 조회합니다"
+  "read:user-groups": "사용자 그룹 보기"
   "write:user-groups": "유저 그룹을 만들거나, 초대하거나, 이름을 변경하거나, 양도하거나, 삭제합니다"
   "read:channels": "채널을 보기"
   "write:channels": "채널을 추가하거나 삭제합니다"
@@ -1942,6 +1974,7 @@ _widgets:
   _userList:
     chooseList: "리스트 선택"
   clicker: "클리커"
+  birthdayFollowings: "오늘이 생일인 사용자"
 _cw:
   hide: "숨기기"
   show: "더 보기"
@@ -1989,7 +2022,7 @@ _postForm:
     b: "무슨 일이 일어나고 있나요?"
     c: "무엇을 생각하고 있나요?"
     d: "말하고 싶은 게 있나요?"
-    e: "여기에 적어주세요"
+    e: "여기에 적어 주세요"
     f: "작성해주시길 기다리고 있어요..."
 _profile:
   name: "이름"
@@ -2118,7 +2151,7 @@ _notification:
   youGotMention: "{name}님이 멘션함"
   youGotReply: "{name}님이 답글함"
   youGotQuote: "{name}님이 인용함"
-  youRenoted: "{name}님이 Renote"
+  youRenoted: "{name}님이 리노트했습니다"
   youWereFollowed: "새로운 팔로워가 있습니다"
   youReceivedFollowRequest: "새로운 팔로우 요청이 있습니다"
   yourFollowRequestAccepted: "팔로우 요청이 수락되었습니다"
@@ -2203,7 +2236,7 @@ _webhookSettings:
     followed: "누군가 나를 팔로우했을 때"
     note: "노트를 게시할 때"
     reply: "답글을 받았을 때"
-    renote: "누군가 내 글을 Renote했을 때"
+    renote: "누군가 내 글을 리노트했을 때"
     reaction: "누군가 내 노트에 리액션했을 때"
     mention: "누군가 나를 멘션했을 때"
 _moderationLogTypes:
@@ -2240,6 +2273,8 @@ _moderationLogTypes:
   createAvatarDecoration: "아이콘 장식 추가"
   updateAvatarDecoration: "아이콘 장식 수정"
   deleteAvatarDecoration: "아이콘 장식 삭제"
+  unsetUserAvatar: "유저 아바타 제거"
+  unsetUserBanner: "유저 배너 제거"
 _fileViewer:
   title: "파일 상세"
   type: "파일 유형"
@@ -2289,3 +2324,16 @@ _externalResourceInstaller:
     _themeInstallFailed:
       title: "테마를 설치하지 못했습니다"
       description: "테마를 설치하는 도중 문제가 발생하였습니다. 다시 한 번 시도하십시오. 자세한 사항은 브라우저에 내장된 개발자 도구의 Javascript 콘솔에서 확인하실 수 있습니다."
+_dataSaver:
+  _media:
+    title: "미디어 불러오기"
+    description: "사진이나 동영상을 자동으로 불러오지 않습니다. 숨겨 놓은 사진이나 동영상은 누르면 불러옵니다."
+  _avatar:
+    title: "아이콘 이미지"
+    description: "아이콘 이미지의 애니메이션을 멈춥니다. 애니메이션 이미지는 일반 이미지보다 파일 크기가 클 수 있으므로 데이터 사용량을 더 줄일 수 있습니다."
+  _urlPreview:
+    title: "URL 미리보기의 섬네일"
+    description: "URL 미리보기의 섬네일 이미지를 불러오지 않게 됩니다."
+  _code:
+    title: "문자열 강조"
+    description: "MFM 등으로 문자열 강조 기법을 사용할 때 누르기 전에는 불러오지 않습니다. 문자열 강조에서는 강조할 언어마다 그 정의 파일을 불러와야 하지만 이를 자동으로 불러오지 않으므로 데이터 사용량을 줄일 수 있습니다."
diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml
index b22e047cfa..c9e5aea1ed 100644
--- a/locales/lo-LA.yml
+++ b/locales/lo-LA.yml
@@ -320,7 +320,6 @@ administrator: "ຜູ້ບໍລິຫານ"
 token: "ໂທເຄັນ"
 share: "ແບ່ງປັນ"
 notFound: "ບໍ່ພົບ"
-cacheClear: "ລຶບລ້າງແຄສ"
 help: "ຊ່ວຍເຫຼືອ"
 close: "ປິດ"
 invites: "ເຊີນ"
diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml
index 1c207d89c8..c1bdbede2c 100644
--- a/locales/nl-NL.yml
+++ b/locales/nl-NL.yml
@@ -396,7 +396,6 @@ reduceUiAnimation: "Verminder beweging in de UI"
 share: "Delen"
 notFound: "Niet gevonden"
 uploadFolder: "Standaardmap voor uploaden"
-cacheClear: "Cache verwijderen"
 markAsReadAllNotifications: "Markeer alle meldingen als gelezen"
 markAsReadAllUnreadNotes: "Markeer alle berichten als gelezen"
 markAsReadAllTalkMessages: "Markeer alle berichten als gelezen"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index 1a15532c08..3a83f9b7ee 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -407,7 +407,6 @@ share: "Udostępnij"
 notFound: "Nie znaleziono"
 notFoundDescription: "Nie ma strony odpowiadającej określonemu adresowi URL."
 uploadFolder: "Domyślne położenie wysłanych"
-cacheClear: "Wyczyść pamięć podręczną"
 markAsReadAllNotifications: "Oznacz wszystkie powiadomienia jako przeczytane"
 markAsReadAllUnreadNotes: "Oznacz wszystkie wpisy jako przeczytane"
 markAsReadAllTalkMessages: "Oznacz wszystkie wiadomości jako przeczytane"
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index 32740175e3..b7a333f9e7 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -431,7 +431,6 @@ share: "Compartilhar"
 notFound: "Não encontrado"
 notFoundDescription: "Não havia página correspondente ao URL especificado."
 uploadFolder: "Destino de upload padrão"
-cacheClear: "Excluir memória transitória"
 markAsReadAllNotifications: "Marcar todas as notificações como lidas"
 markAsReadAllUnreadNotes: "Marcar todas as postagens como lidas"
 markAsReadAllTalkMessages: "Marcar todas as conversas como lidas"
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index 77bccb7e6b..4a90d1e006 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -2,6 +2,7 @@
 _lang_: "Română"
 headlineMisskey: "O rețea conectată prin note"
 introMisskey: "Bine ai venit! Misskey este un serviciu de microblogging open source și decentralizat.\nCreează \"note\" cu care să îți poți împărți gândurile cu oricine din jurul tău. 📡\nCu \"reacții\" îți poți expirma rapid părerea despre notele oricui. 👍\nHai să explorăm o lume nouă! 🚀"
+poweredByMisskeyDescription: "{name} este unul dintre serviciile care se folosește de platforma open source <b>Misskey</b>."
 monthAndDay: "{day}/{month}"
 search: "Caută"
 notifications: "Notificări"
@@ -12,12 +13,14 @@ fetchingAsApObject: "Se aduce din Fediverse..."
 ok: "OK"
 gotIt: "Am înțeles!"
 cancel: "Anulează"
+noThankYou: "Nu, mulțumesc."
 enterUsername: "Introdu numele de utilizator"
 renotedBy: "Re-notat de {user}"
 noNotes: "Nicio notă"
 noNotifications: "Nicio notificare"
 instance: "Instanță"
 settings: "Setări"
+notificationSettings: "Setări notificări"
 basicSettings: "Setări generale"
 otherSettings: "Alte Setări"
 openInWindow: "Deschide într-o fereastră"
@@ -42,12 +45,20 @@ pin: "Fixează pe profil"
 unpin: "Anulati fixare"
 copyContent: "Copiază conținutul"
 copyLink: "Copiază link-ul"
+copyLinkRenote: "Copiază linkul pentru renote"
 delete: "Şterge"
 deleteAndEdit: "Șterge și editează"
 deleteAndEditConfirm: "Ești sigur că vrei să ștergi această notă și să o editezi? Vei pierde reacțiile, re-notele și răspunsurile acesteia."
 addToList: "Adaugă în listă"
+addToAntenna: "Adaugă la antenă"
 sendMessage: "Trimite un mesaj"
+copyRSS: "Copiază RSS"
 copyUsername: "Copiază numele de utilizator"
+copyUserId: "Copiază numele de utilizator"
+copyNoteId: "Copiază ID-ul notiței"
+copyFileId: "Copiază ID-ul fișierului"
+copyFolderId: "Copiază ID-ul folderului"
+copyProfileUrl: "Copiază URL profil"
 searchUser: "Caută un utilizator"
 reply: "Răspunde"
 loadMore: "Incarcă mai mult"
@@ -100,6 +111,8 @@ renoted: "Re-notat."
 cantRenote: "Această postare nu poate fi re-notată."
 cantReRenote: "O re-notă nu poate fi re-notată."
 quote: "Citează"
+inChannelRenote: "Renotează în canal"
+inChannelQuote: "Citează în canal"
 pinnedNote: "Notă fixată"
 pinned: "Fixat pe profil"
 you: "Tu"
@@ -117,6 +130,8 @@ unmarkAsSensitive: "Demarchează ca NSFW"
 enterFileName: "Introduceţi numele fişierului"
 mute: "Amuțește"
 unmute: "Înlătură amuțirea"
+renoteMute: "Renotări pe modul silențios"
+renoteUnmute: "Scoate renotările de pe modul silențios"
 block: "Blochează"
 unblock: "Deblochează"
 suspend: "Suspendă"
@@ -126,7 +141,10 @@ unblockConfirm: "Ești sigur ca vrei să deblochezi acest cont?"
 suspendConfirm: "Ești sigur ca vrei să suspendezi acest cont?"
 unsuspendConfirm: "Ești sigur ca vrei să nu mai suspendezi acest cont?"
 selectList: "Selectează o listă"
+editList: "Editați lista"
+selectChannel: "Selectaţi canalul"
 selectAntenna: "Selectează o antenă"
+editAntenna: "Editează antena"
 selectWidget: "Selectați un widget"
 editWidgets: "Editează widget-urile"
 editWidgetsExit: "Terminat"
@@ -139,6 +157,7 @@ addEmoji: "Adaugă un emoji"
 settingGuide: "Setări recomandate"
 cacheRemoteFiles: "Ține fișierele externe in cache"
 cacheRemoteFilesDescription: "Când această setare este dezactivată, fișierele externe sunt încărcate direct din instanța externă. Dezactivarea va scădea utilizarea spațiului de stocare, dar va crește traficul, deoarece thumbnail-urile nu vor fi generate."
+youCanCleanRemoteFilesCache: "Poți goli cache-ul prin a apăsa pe butonul de 🗑️ din fereastra de gestionare a fișierelor."
 flagAsBot: "Marchează acest cont ca bot"
 flagAsBotDescription: "Activează această opțiune dacă acest cont este controlat de un program. Daca e activată, aceasta va juca rolul unui indicator pentru dezvoltatori pentru a preveni interacțiunea în lanțuri infinite cu ceilalți boți și ajustează sistemele interne al Misskey pentru a trata acest cont drept un bot."
 flagAsCat: "Marchează acest cont ca pisică"
@@ -393,7 +412,6 @@ share: "Distribuie"
 notFound: "Nu a fost găsit"
 notFoundDescription: "N-a fost găsită nicio pagină cu acest URL."
 uploadFolder: "Folder implicit pentru încărcări"
-cacheClear: "Golește cache-ul"
 markAsReadAllNotifications: "Marchează toate notificările drept citit"
 markAsReadAllUnreadNotes: "Marchează toate notele drept citit"
 markAsReadAllTalkMessages: "Marchează toate mesajele drept citit"
@@ -649,6 +667,8 @@ _sfx:
   notification: "Notificări"
 _ago:
   invalid: "Nu e nimic de văzut aici"
+_2fa:
+  renewTOTPCancel: "Nu, mulțumesc."
 _widgets:
   profile: "Profil"
   instanceInfo: "Informații despre instanță"
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index d8f7fe5193..bea08a19fd 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -61,7 +61,7 @@ copyProfileUrl: "Скопировать URL профиля "
 searchUser: "Поиск людей"
 reply: "Ответ"
 loadMore: "Показать еще"
-showMore: "Показать еще"
+showMore: "Показать ещё"
 showLess: "Закрыть"
 youGotNewFollower: "Новый подписчик"
 receiveFollowRequest: "Получен запрос на подписку"
@@ -429,7 +429,6 @@ share: "Поделиться"
 notFound: "Не найдено"
 notFoundDescription: "Страница по указанной ссылке не найдена"
 uploadFolder: "Место загрузки по умолчанию"
-cacheClear: "Очистка кэша"
 markAsReadAllNotifications: "Отметить все уведомления как прочитанные"
 markAsReadAllUnreadNotes: "Отметить все заметки как прочитанные"
 markAsReadAllTalkMessages: "Отметить все реплики как прочитанные"
@@ -643,7 +642,7 @@ create: "Создать"
 notificationSetting: "Настройки уведомлений"
 notificationSettingDesc: "Выберите тип уведомлений для отображения"
 useGlobalSetting: "Использовать глобальные настройки"
-useGlobalSettingDesc: "Если включено, будут использоваться настройки учётной записи. Если включить, этот виджет можно будет настроить индивидуально."
+useGlobalSettingDesc: "Если включено, будут использоваться настройки учётной записи. Если отключить, этот виджет можно будет настроить индивидуально."
 other: "Другие"
 regenerateLoginToken: "Создать новый токен для входа"
 regenerateLoginTokenDescription: "Создаёт новый токен, используемый внутри программы во время входа. Обычно в этом нет необходимости. При создании все устройства будут отключены."
@@ -681,7 +680,7 @@ createNewClip: "Новая подборка"
 unclip: "Убрать из подборки"
 confirmToUnclipAlreadyClippedNote: "Эта заметка уже есть в подборке «{name}». Удалить из этой подборки?"
 public: "Общедоступно"
-private: "Показываются только вам"
+private: "Личное"
 i18nInfo: "Misskey переводят на разные языки добровольцы со всего света. Ваша помощь тоже пригодится здесь: {link}."
 manageAccessTokens: "Управление токенами доступа"
 accountInfo: "Сведения об учётной записи"
@@ -955,7 +954,7 @@ numberOfProfileView: "Количество профилей для просмо
 like: "Нравится!"
 unlike: "Отменить «нравится»"
 numberOfLikes: "Количество лайков"
-show: "Отображение"
+show: "Показать"
 neverShow: "Больше не показывать"
 remindMeLater: "Напомнить позже"
 didYouLikeMisskey: "Вам нравится Misskey?"
@@ -1071,6 +1070,7 @@ doYouAgree: "Согласны?"
 icon: "Аватар"
 replies: "Ответы"
 renotes: "Репост"
+loadReplies: "Показать ответы"
 flip: "Переворот"
 _initialAccountSetting:
   accountCreated: "Аккаунт успешно создан!"
@@ -1082,6 +1082,11 @@ _initialAccountSetting:
 _initialTutorial:
   _note:
     description: "Посты в Misskey называются 'Заметками.' Заметки отсортированы в хронологическом порядке в ленте и обновляются в режиме реального времени."
+_timelineDescription:
+  home: "В персональной ленте располагаются заметки тех, на которых вы подписаны."
+  local: "Местная лента показывает заметки всех пользователей этого сайта."
+  social: "В социальной ленте собирается всё, что есть в персональной и местной лентах."
+  global: "В глобальную ленту попадает вообще всё со связанных инстансов."
 _serverSettings:
   iconUrl: "Адрес на иконку роли"
 _achievements:
@@ -1589,6 +1594,14 @@ _ago:
   monthsAgo: "{n} мес. назад"
   yearsAgo: "{n} г. назад"
   invalid: "Ничего нет"
+_timeIn:
+  seconds: "Через {n} с"
+  minutes: "Через {n} мин"
+  hours: "Через {n} ч"
+  days: "Через {n} сут"
+  weeks: "Через {n} нед."
+  months: "Через {n} мес."
+  years: "Через {n} г."
 _time:
   second: "с"
   minute: "мин"
@@ -1704,7 +1717,7 @@ _widgets:
   clicker: "Счётчик щелчков"
 _cw:
   hide: "Спрятать"
-  show: "Показать еще"
+  show: "Показать"
   chars: "знаков: {count}"
   files: "файлов: {count}"
 _poll:
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index 903891fdb9..19b06b475a 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -411,7 +411,6 @@ share: "Zdieľať"
 notFound: "Nenájdené"
 notFoundDescription: "Nenašla sa žiadna stránka na zadanej URL."
 uploadFolder: "Predvolený priečinok pre nahrávanie"
-cacheClear: "Vyčistiť cache"
 markAsReadAllNotifications: "Označiť všetky oznámenia ako prečítané"
 markAsReadAllUnreadNotes: "Označiť všetky poznámky ako prečítané"
 markAsReadAllTalkMessages: "Označiť všetky správy ako prečítané"
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index 8df36a6829..d27e90b855 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -195,6 +195,7 @@ perHour: "ทุกชั่วโมง"
 perDay: "ต่อวัน"
 stopActivityDelivery: "หยุดส่งกิจกรรม"
 blockThisInstance: "บล็อกอินสแตนซ์นี้"
+silenceThisInstance: "ปกปิดอินสแตนซ์นี้"
 operations: "ดำเนินการ"
 software: "ซอฟต์แวร์"
 version: "เวอร์ชั่น"
@@ -214,6 +215,7 @@ clearCachedFiles: "ล้างแคช"
 clearCachedFilesConfirm: "นายแน่ใจแล้วหรอว่าต้องการที่จะลบไฟล์ระยะไกลที่แคชไว้ทั้งหมด?"
 blockedInstances: "อินสแตนซ์ที่ ถูกบล็อก"
 blockedInstancesDescription: "ระบุชื่อโฮสต์ของอินสแตนซ์ที่คุณต้องการบล็อก อินสแตนซ์ที่อยู่ในรายการนั้นจะไม่สามารถพูดคุยกับอินสแตนซ์นี้ได้อีกต่อไป"
+silencedInstances: "ปกปิดอินสแตนซ์นี้แล้ว"
 muteAndBlock: "ปิดเสียงและบล็อก"
 mutedUsers: "ผู้ใช้ที่ถูกปิดเสียง"
 blockedUsers: "ผู้ใช้ที่ถูกบล็อก"
@@ -434,7 +436,6 @@ share: "แชร์"
 notFound: "ไม่พบหน้าที่ต้องการ"
 notFoundDescription: "ไม่พบหน้าที่สอดคล้องตรงกันกับ URL นี้นะ"
 uploadFolder: "โฟลเดอร์เริ่มต้นสำหรับอัพโหลด"
-cacheClear: "ล้างแคช"
 markAsReadAllNotifications: "ทำเครื่องหมายการแจ้งเตือนทั้งหมดว่าอ่านแล้ว"
 markAsReadAllUnreadNotes: "ทำเครื่องหมายโน้ตทั้งหมดว่าอ่านแล้ว"
 markAsReadAllTalkMessages: "ทำเครื่องหมายข้อความทั้งหมดว่าอ่านแล้ว"
@@ -560,6 +561,10 @@ output: "เอาท์พุต"
 script: "สคริปต์"
 disablePagesScript: "ปิดการใช้งาน AiScript บนเพจ"
 updateRemoteUser: "อัปเดตข้อมูลผู้ใช้งานระยะไกล"
+unsetUserAvatar: "เลิกตั้งอวตาร"
+unsetUserAvatarConfirm: "คุณแน่ใจหรือไม่ว่าต้องการเลิกตั้งอวตาร?"
+unsetUserBanner: "เลิกตั้งแบนเนอร์"
+unsetUserBannerConfirm: "คุณแน่ใจหรือไม่ว่าต้องการเลิกตั้งแบนเนอร์เลยมั้ย?"
 deleteAllFiles: "ลบไฟล์ทั้งหมด"
 deleteAllFilesConfirm: "นายแน่ใจแล้วหรอว่าต้องการที่จะลบไฟล์ทั้งหมด?"
 removeAllFollowing: "เลิกติดตามผู้ใช้ที่ติดตามทั้งหมด"
@@ -631,6 +636,7 @@ smtpSecure: "ใช้โดยนัย SSL/TLS สำหรับการเ
 smtpSecureInfo: "ปิดสิ่งนี้เมื่อใช้ STARTTLS"
 testEmail: "ทดสอบการส่งอีเมล"
 wordMute: "ปิดเสียงคำ"
+hardWordMute: "ปิดเสียงคำยาก"
 regexpError: "ข้อผิดพลาดของนิพจน์ทั่วไป"
 regexpErrorDescription: "เกิดข้อผิดพลาดในนิพจน์ทั่วไปในบรรทัดที่ {line} ของการปิดเสียงคำ {tab} ของคุณ:"
 instanceMute: "ปิดเสียง อินสแตนซ์"
@@ -975,6 +981,7 @@ assign: "กำหนด"
 unassign: "ยังไม่มอบหมาย"
 color: "สี"
 manageCustomEmojis: "จัดการอีโมจิแบบกำหนดเอง"
+manageAvatarDecorations: "จัดการตกแต่งอวตาร"
 youCannotCreateAnymore: "คุณถึงขีดจํากัดการสร้างแล้วนะ"
 cannotPerformTemporary: "ไม่สามารถใช้การได้ชั่วคราว"
 cannotPerformTemporaryDescription: "ไม่สามารถดําเนินการได้ชั่วคราว เนื่องจากเกินขีดจํากัดการดําเนินการ กรุณารอสักครู่แล้วลองใหม่อีกครั้ง"
@@ -1132,7 +1139,19 @@ impressumUrl: "URL อิมเพรสชั่น"
 privacyPolicy: "นโยบายความเป็นส่วนตัว"
 privacyPolicyUrl: "URL นโยบายความเป็นส่วนตัว"
 tosAndPrivacyPolicy: "เงื่อนไขในการให้บริการและนโยบายความเป็นส่วนตัว"
+avatarDecorations: "การตกแต่งอวตาร"
+attach: "แนบ"
+detach: "นำออก"
+angle: "แองเกิล"
 flip: "ย้อนกลับ"
+showAvatarDecorations: "แสดงตกแต่งอวตาร"
+releaseToRefresh: "ปล่อยเพื่อรีเฟรช"
+refreshing: "กำลังรีเฟรช..."
+pullDownToRefresh: "ดึงลงเพื่อรีเฟรช"
+disableStreamingTimeline: "ปิดใช้งานอัปเดตไทม์ไลน์แบบเรียลไทม์"
+useGroupedNotifications: "แสดงผลการแจ้งเตือนแบบกลุ่มแล้ว"
+signupPendingError: "มีปัญหาในการตรวจสอบที่อยู่อีเมลลิงก์อาจหมดอายุแล้ว"
+doReaction: "เพิ่มรีแอคชั่น"
 _announcement:
   forExistingUsers: "ผู้ใช้งานที่มีอยู่เท่านั้น"
   forExistingUsersDescription: "การประกาศนี้จะแสดงต่อผู้ใช้ที่มีอยู่ ณ จุดที่เผยแพร่นั้นๆถ้าหากเปิดใช้งาน ถ้าหากปิดใช้งานผู้ที่กำลังสมัครใหม่หลังจากโพสต์แล้วนั้นก็จะเห็นเช่นกัน"
@@ -1142,6 +1161,7 @@ _announcement:
   tooManyActiveAnnouncementDescription: "การมีประกาศที่ใช้งานมากเกินไปนั้นอาจจะทำให้ประสบการณ์ของผู้ใช้งานนั้นดูแย่ลง โปรดกรุณาพิจารณาการเก็บประกาศที่ล้าสมัยด้วยนะค่ะ"
   readConfirmTitle: "ทำเครื่องหมายบอกว่าอ่านแล้วเลยมั้ย?"
   readConfirmText: "การดำเนินการนี้จะทำเครื่องหมายเนื้อหาของ \"{title}\" บอกว่าอ่านแล้วนะ"
+  silence: "ไม่มีการแจ้งเตือน"
 _initialAccountSetting:
   accountCreated: "คุณได้สร้างบัญชีของคุณสำเร็จเรียบร้อยแล้ว!"
   letsStartAccountSetup: "สำหรับผู้เริ่มต้นมาตั้งค่าโปรไฟล์ของคุณกันเถอะ"
@@ -1154,8 +1174,31 @@ _initialAccountSetting:
   pushNotificationDescription: "กำลังเปิดใช้งานการแจ้งเตือนแบบพุชจะช่วยให้คุณได้รับการแจ้งเตือนจาก {name} โดยตรงบนอุปกรณ์ของคุณนะ"
   initialAccountSettingCompleted: "ตั้งค่าโปรไฟล์เสร็จสมบูรณ์แล้ว!"
   haveFun: "ขอให้สนุก {name}!"
+  startTutorial: "เริ่มการฝึกสอน"
   skipAreYouSure: "ต้องการข้ามการตั้งค่าโปรไฟล์จริงๆแบบนั้นหรอ?"
   laterAreYouSure: "ต้องการตั้งค่าโปรไฟล์ในภายหลังจริงๆอย่างงั้นหรอ?"
+_initialTutorial:
+  launchTutorial: "เริ่มบทช่วยสอน"
+  title: "บทช่วยสอน"
+  wellDone: "ทำได้ดีมาก!"
+  skipAreYouSure: "ต้องการออกจากบทช่วยสอนใช่ไหม?"
+  _landing:
+    title: "ยินดีต้อนรับสู่บทช่วยสอน"
+  _note:
+    title: "โน้ตคืออะไร?"
+  _reaction:
+    title: "รีแอคชั่นคืออะไร?"
+  _timeline:
+    title: "แนวคิดเรื่องของไทม์ไลน์"
+  _postNote:
+    title: "ตั้งค่ากำลังโพสต์โน้ต"
+    _visibility:
+      description: "คุณสามารถจำกัดผู้ที่สามารถดูโน้ตของคุณได้นะ"
+      public: "โน้ตของคุณนั้นจะปรากฏแก่ผู้ใช้งานทุกคน"
+    _cw:
+      title: "คำเตือนเกี่ยวกับเนื้อหา"
+      _exampleNote:
+        cw: "นี่อาจจะทำให้คุณหิวอย่างแน่นอน!"
 _serverRules:
   description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ"
 _serverSettings:
@@ -1464,6 +1507,7 @@ _role:
     inviteLimitCycle: "จำกัดการเชิญไว้คูลดาวน์"
     inviteExpirationTime: "วันหมดอายุของรหัสการเชิญ"
     canManageCustomEmojis: "จัดการอีโมจิแบบกำหนดเอง"
+    canManageAvatarDecorations: "จัดการตกแต่งอวตาร"
     driveCapacity: "ความจุของไดรฟ์"
     alwaysMarkNsfw: "ทำเครื่องหมายไฟล์ว่าเป็น NSFW เสมอ"
     pinMax: "จํานวนสูงสุดของโน้ตที่ปักหมุดไว้"
@@ -2130,3 +2174,15 @@ _fileViewer:
   uploadedAt: "วันที่เข้าร่วม"
   attachedNotes: "โน้ตที่แนบมาด้วย"
   thisPageCanBeSeenFromTheAuthor: "หน้าเพจนี้จะสามารถปรากฏได้โดยผู้ใช้ที่อัปโหลดไฟล์นี้เท่านั้น"
+_externalResourceInstaller:
+  _plugin:
+    metaTitle: "ข้อมูลส่วนเสริม"
+  _theme:
+    metaTitle: "ข้อมูลธีม"
+  _vendorInfo:
+    title: "ข้อมูลผู้จัดจำหน่าย"
+  _errors:
+    _pluginParseFailed:
+      title: "ข้อผิดพลาด AiScript"
+    _themeParseFailed:
+      title: "การแยกวิเคราะห์ธีมล้มเหลว"
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index 016f41a8d6..f10f257fa0 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -408,7 +408,6 @@ share: "Поділитись"
 notFound: "Не знайдено"
 notFoundDescription: "Сторінка за вказаною адресою не знайдена."
 uploadFolder: "Місце для завантаження за замовчуванням"
-cacheClear: "Очистити кеш"
 markAsReadAllNotifications: "Позначити всі сповіщення як прочитані"
 markAsReadAllUnreadNotes: "Позначити всі нотатки як прочитані"
 markAsReadAllTalkMessages: "Позначити всі повідомлення як прочитані"
diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml
index e9b3915f07..8d3e8043f3 100644
--- a/locales/uz-UZ.yml
+++ b/locales/uz-UZ.yml
@@ -428,7 +428,6 @@ share: "Yuborish"
 notFound: "Topilmadi"
 notFoundDescription: "Ushbu sahifa topilmadi"
 uploadFolder: "Jildni yuklash"
-cacheClear: "Keshni tozalash"
 markAsReadAllNotifications: "Bildirishnomalarni o'qilgan deb belgilash"
 markAsReadAllUnreadNotes: "Barch xabarlarni oq'ilgan deb belgilash"
 markAsReadAllTalkMessages: "Barcha suhbatlarni o'qilgan deb belgilang"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index c816fc314b..0f60578963 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -433,7 +433,6 @@ share: "Chia sẻ"
 notFound: "Không tìm thấy"
 notFoundDescription: "Không tìm thấy trang nào tương ứng với URL này."
 uploadFolder: "Thư mục tải lên mặc định"
-cacheClear: "Xóa bộ nhớ đệm"
 markAsReadAllNotifications: "Đánh dấu tất cả các thông báo là đã đọc"
 markAsReadAllUnreadNotes: "Đánh dấu tất cả các tút là đã đọc"
 markAsReadAllTalkMessages: "Đánh dấu tất cả các tin nhắn là đã đọc"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 76bc5c3271..1b440284ab 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -311,6 +311,7 @@ folderName: "文件夹名称"
 createFolder: "创建文件夹"
 renameFolder: "重命名文件夹"
 deleteFolder: "删除文件夹"
+folder: "文件夹"
 addFile: "添加文件"
 emptyDrive: "网盘中无文件"
 emptyFolder: "此文件夹中无文件"
@@ -437,7 +438,6 @@ share: "分享"
 notFound: "未找到"
 notFoundDescription: "没有与指定 URL 对应的页面。"
 uploadFolder: "默认上传文件夹"
-cacheClear: "清空缓存"
 markAsReadAllNotifications: "将所有通知标为已读"
 markAsReadAllUnreadNotes: "将所有帖子标记为已读"
 markAsReadAllTalkMessages: "将所有聊天标记为已读"
@@ -1131,6 +1131,7 @@ mutualFollow: "互相关注"
 fileAttachedOnly: "仅限媒体"
 showRepliesToOthersInTimeline: "在时间线上显示给其他人的回复"
 hideRepliesToOthersInTimeline: "在时间线上隐藏给其他人的回复"
+avatarDecorations: "头像挂件"
 flip: "翻转"
 _announcement:
   forExistingUsers: "仅限现有用户"
@@ -1706,6 +1707,9 @@ _ago:
   monthsAgo: "{n} 月前"
   yearsAgo: "{n} 年前"
   invalid: "没有"
+_timeIn:
+  seconds: "{n}秒后"
+  days: "{n}天后"
 _time:
   second: "秒"
   minute: "分"
@@ -2123,3 +2127,7 @@ _moderationLogTypes:
 _fileViewer:
   url: "URL"
   uploadedAt: "添加日期"
+_externalResourceInstaller:
+  _errors:
+    _pluginParseFailed:
+      title: "AiScript 错误"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index ad0741693f..7f3399ed90 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -261,6 +261,7 @@ removed: "已刪除"
 removeAreYouSure: "確定要刪掉「{x}」嗎?"
 deleteAreYouSure: "確定要刪掉「{x}」嗎?"
 resetAreYouSure: "確定要重設嗎?"
+areYouSure: "您確定要移除所有裝飾嗎?"
 saved: "已儲存"
 messaging: "聊天"
 upload: "上傳"
@@ -292,13 +293,13 @@ birthday: "生日"
 yearsOld: "{age} 歲"
 registeredDate: "註冊日期"
 location: "位置"
-theme: "外觀主題"
-themeForLightMode: "在淺色模式下使用的主題"
-themeForDarkMode: "在深色模式下使用的主題"
+theme: "佈景主題"
+themeForLightMode: "在淺色模式下使用的佈景主題"
+themeForDarkMode: "在深色模式下使用的佈景主題"
 light: "淺色"
 dark: "深色"
-lightThemes: "淺色主題"
-darkThemes: "深色主題"
+lightThemes: "淺色佈景主題"
+darkThemes: "深色佈景主題"
 syncDeviceDarkMode: "與設備的深色模式同步"
 drive: "雲端硬碟"
 fileName: "檔案名稱"
@@ -311,6 +312,7 @@ folderName: "資料夾名稱"
 createFolder: "新增資料夾"
 renameFolder: "重新命名資料夾"
 deleteFolder: "刪除資料夾"
+folder: "資料夾"
 addFile: "加入附件"
 emptyDrive: "雲端硬碟為空"
 emptyFolder: "資料夾為空"
@@ -437,7 +439,6 @@ share: "分享"
 notFound: "查無項目"
 notFoundDescription: "查無此頁"
 uploadFolder: "預設上傳資料夾"
-cacheClear: "清除快取"
 markAsReadAllNotifications: "標記所有通知為已讀"
 markAsReadAllUnreadNotes: "標記所有貼文為已讀"
 markAsReadAllTalkMessages: "標記所有訊息為已讀"
@@ -544,6 +545,8 @@ showInPage: "在頁面中顯示"
 popout: "彈出式視窗"
 volume: "音量"
 masterVolume: "主音量"
+notUseSound: "關閉音效"
+useSoundOnlyWhenActive: "瀏覽器在前景運作時,Misskey 才會發出音效"
 details: "詳細資訊"
 chooseEmoji: "選擇您的表情符號"
 unableToProcess: "操作無法完成"
@@ -564,6 +567,10 @@ output: "輸出"
 script: "腳本"
 disablePagesScript: "停用頁面的 AiScript 腳本"
 updateRemoteUser: "更新遠端使用者資訊"
+unsetUserAvatar: "移除使用者的大頭貼"
+unsetUserAvatarConfirm: "確定要移除使用者的大頭貼嗎?"
+unsetUserBanner: "移除使用者的橫幅圖像"
+unsetUserBannerConfirm: "確定要移除使用者的橫幅圖像嗎?"
 deleteAllFiles: "刪除所有檔案"
 deleteAllFilesConfirm: "要刪除所有檔案嗎?"
 removeAllFollowing: "解除所有追隨"
@@ -589,12 +596,12 @@ deletedNote: "已刪除的貼文"
 invisibleNote: "私密的貼文"
 enableInfiniteScroll: "啟用自動滾動頁面模式"
 visibility: "可見性"
-poll: "投票"
+poll: "票選活動"
 useCw: "隱藏內容"
 enablePlayer: "開啟播放器"
 disablePlayer: "關閉播放器"
 expandTweet: "展開推文"
-themeEditor: "主題編輯器"
+themeEditor: "佈景主題編輯器"
 description: "描述"
 describeFile: "新增標題"
 enterFileDescription: "輸入標題"
@@ -635,6 +642,7 @@ smtpSecure: "在 SMTP 連接中使用隱式 SSL/TLS"
 smtpSecureInfo: "使用 STARTTLS 時關閉。"
 testEmail: "測試郵件發送"
 wordMute: "被靜音的文字"
+hardWordMute: "硬文字靜音"
 regexpError: "正規表達式錯誤"
 regexpErrorDescription: "{tab} 靜音文字的第 {line} 行的正規表達式有錯誤:"
 instanceMute: "被靜音的實例"
@@ -831,7 +839,7 @@ previewNoteText: "預覽文本"
 customCss: "自定義 CSS"
 customCssWarn: "這個設定必須由具備相關知識的人員操作,不當的設定可能導致客戶端無法正常使用。"
 global: "全域"
-squareAvatars: "頭像以方形顯示"
+squareAvatars: "大頭貼以方形顯示"
 sent: "發送"
 received: "收取"
 searchResult: "搜尋結果"
@@ -882,13 +890,13 @@ overridedDeviceKind: "裝置類型"
 smartphone: "智慧型手機"
 tablet: "平板"
 auto: "自動"
-themeColor: "主題顏色"
+themeColor: "佈景主題顏色"
 size: "大小"
 numberOfColumn: "列數"
 searchByGoogle: "搜尋"
-instanceDefaultLightTheme: "實例預設的淺色主題"
-instanceDefaultDarkTheme: "實例預設的深色主題"
-instanceDefaultThemeDescription: "輸入物件形式的主題代碼"
+instanceDefaultLightTheme: "實例預設的淺色佈景主題"
+instanceDefaultDarkTheme: "實例預設的深色佈景主題"
+instanceDefaultThemeDescription: "輸入物件形式的佈景主題代碼"
 mutePeriod: "靜音的期限"
 period: "期限"
 indefinitely: "無期限"
@@ -1020,6 +1028,8 @@ resetPasswordConfirm: "重設密碼?"
 sensitiveWords: "敏感詞"
 sensitiveWordsDescription: "將含有設定詞彙的貼文可見性設為發送至首頁。可以用換行來進行複數的設定。"
 sensitiveWordsDescription2: "空格代表「以及」(AND),斜線包圍關鍵字代表使用正規表達式。"
+hiddenTags: "隱藏標籤"
+hiddenTagsDescription: "設定的標籤不會在趨勢中顯示,換行可以設定多個標籤。"
 notesSearchNotAvailable: "無法使用搜尋貼文功能。"
 license: "授權"
 unfavoriteConfirm: "要取消收錄我的最愛嗎?"
@@ -1032,6 +1042,7 @@ enableChartsForRemoteUser: "生成遠端使用者的圖表"
 enableChartsForFederatedInstances: "生成遠端伺服器的圖表"
 showClipButtonInNoteFooter: "新增摘錄按鈕至貼文"
 reactionsDisplaySize: "反應的顯示尺寸"
+limitWidthOfReaction: "限制反應的最大寬度,並縮小顯示尺寸。"
 noteIdOrUrl: "貼文ID或URL"
 video: "影片"
 videos: "影片"
@@ -1147,6 +1158,7 @@ tosAndPrivacyPolicy: "服務條款和隱私政策"
 avatarDecorations: "頭像裝飾"
 attach: "裝上"
 detach: "取下"
+detachAll: "移除所有裝飾"
 angle: "角度"
 flip: "翻轉"
 showAvatarDecorations: "顯示頭像裝飾"
@@ -1158,6 +1170,9 @@ useGroupedNotifications: "分組顯示通知訊息"
 signupPendingError: "驗證您的電子郵件地址時出現問題。連結可能已過期。"
 cwNotationRequired: "如果開啟「隱藏內容」,則需要註解說明。"
 doReaction: "做出反應"
+code: "程式碼"
+reloadRequiredToApplySettings: "需要重新載入頁面設定才能生效。"
+remainingN: "剩餘:{n}"
 _announcement:
   forExistingUsers: "僅限既有的使用者"
   forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。"
@@ -1538,7 +1553,9 @@ _role:
   assignTarget: "指派目標"
   descriptionOfAssignTarget: "<b>手動</b>是以手動管理這個角色包含的人員。\n<b>符合條件</b>是設定條件以自動包含符合條件的使用者。"
   manual: "手動"
+  manualRoles: "手動角色"
   conditional: "符合條件"
+  conditionalRoles: "有條件的角色"
   condition: "條件"
   isConditionalRole: "這是條件角色。"
   isPublic: "角色為公開"
@@ -1587,6 +1604,7 @@ _role:
     canHideAds: "不顯示廣告"
     canSearchNotes: "可否搜尋貼文"
     canUseTranslator: "使用翻譯功能"
+    avatarDecorationLimit: "頭像裝飾的最大設置量"
   _condition:
     isLocal: "本地使用者"
     isRemote: "遠端使用者"
@@ -1732,17 +1750,17 @@ _instanceMute:
   title: "將隱藏被設定的實例貼文。"
   heading: "將實例靜音"
 _theme:
-  explore: "取得佈景主題"
+  explore: "探索佈景主題"
   install: "安裝佈景主題"
-  manage: "佈景主題管理員"
-  code: "主題代碼"
+  manage: "管理佈景主題"
+  code: "佈景主題代碼"
   description: "描述"
   installed: "{name}已安裝"
-  installedThemes: "已經安裝的主題"
-  builtinThemes: "標準主題"
-  alreadyInstalled: "此主題已經安裝"
-  invalid: "主題格式錯誤"
-  make: "製作主題"
+  installedThemes: "已經安裝的佈景主題"
+  builtinThemes: "標準佈景主題"
+  alreadyInstalled: "已安裝此佈景主題"
+  invalid: "佈景主題格式錯誤"
+  make: "製作佈景主題"
   base: "基於"
   addConstant: "添加常數"
   constant: "常數"
@@ -1759,7 +1777,7 @@ _theme:
   darken: "暗度"
   lighten: "亮度"
   inputConstantName: "請輸入常數名稱"
-  importInfo: "您可以在此貼上主題代碼,將其匯入編輯器中"
+  importInfo: "您可以在此貼上佈景主題代碼,將其匯入編輯器中"
   deleteConstantConfirm: "確定要刪除常數{const}嗎?"
   keys:
     accent: "重點色彩"
@@ -1808,6 +1826,14 @@ _sfx:
   notification: "通知"
   antenna: "天線接收"
   channel: "頻道通知"
+  reaction: "選擇反應時"
+_soundSettings:
+  driveFile: "使用雲端硬碟的音效檔案"
+  driveFileWarn: "請選擇雲端硬碟中的檔案"
+  driveFileTypeWarn: "不支援此檔案"
+  driveFileTypeWarnDescription: "請選擇音效檔案"
+  driveFileDurationWarn: "音效太長了"
+  driveFileDurationWarnDescription: "使用長音效檔可能會影響 Misskey 的使用體驗。仍要使用此檔案嗎?"
 _ago:
   future: "未來"
   justNow: "剛剛"
@@ -1952,6 +1978,7 @@ _widgets:
   _userList:
     chooseList: "選擇清單"
   clicker: "點擊器"
+  birthdayFollowings: "今天生日的使用者"
 _cw:
   hide: "隱藏"
   show: "顯示內容"
@@ -1961,7 +1988,7 @@ _poll:
   noOnlyOneChoice: "需要至少兩個選項。"
   choiceN: "選項 {n}"
   noMore: "沒辦法再添加選項了"
-  canMultipleVote: "可以多次投票"
+  canMultipleVote: "允許複選"
   expiration: "期限"
   infinite: "無期限"
   at: "結束時間"
@@ -1970,7 +1997,7 @@ _poll:
   deadlineTime: "小時"
   duration: "時長"
   votesCount: "{n} 票"
-  totalVotes: "合共 {n} 票"
+  totalVotes: "一共{n}票"
   vote: "投票"
   showResult: "顯示結果"
   voted: "已投票"
@@ -2014,6 +2041,7 @@ _profile:
   changeAvatar: "更換大頭貼"
   changeBanner: "變更橫幅圖像"
   verifiedLinkDescription: "如果輸入包含您個人資料的網站 URL,欄位旁邊將出現驗證圖示。"
+  avatarDecorationMax: "最多可以設置{max}個裝飾。"
 _exportOrImport:
   allNotes: "所有貼文"
   favoritedNotes: "「我的最愛」貼文"
@@ -2028,7 +2056,7 @@ _charts:
   federation: "聯邦宇宙"
   apRequest: "請求"
   usersIncDec: "使用者增減"
-  usersTotal: "使用者合共"
+  usersTotal: "使用者總數"
   activeUsers: "活躍使用者"
   notesIncDec: "貼文増減"
   localNotesIncDec: "本地貼文増減"
@@ -2250,6 +2278,8 @@ _moderationLogTypes:
   createAvatarDecoration: "建立頭像裝飾"
   updateAvatarDecoration: "更新頭像裝飾"
   deleteAvatarDecoration: "刪除頭像裝飾"
+  unsetUserAvatar: "移除使用者的大頭貼"
+  unsetUserBanner: "移除使用者的橫幅圖像"
 _fileViewer:
   title: "檔案詳細資訊"
   type: "檔案類型 "
@@ -2265,8 +2295,8 @@ _externalResourceInstaller:
     title: "要安裝此外掛嘛?"
     metaTitle: "外掛資訊"
   _theme:
-    title: "要安裝此外觀主題嘛?"
-    metaTitle: "外觀主題資訊"
+    title: "要安裝此佈景主題嗎?"
+    metaTitle: "佈景主題資訊"
   _meta:
     base: "基本配色方案"
   _vendorInfo:
@@ -2294,8 +2324,21 @@ _externalResourceInstaller:
       title: "外掛安裝失敗"
       description: "安裝插件時出現問題。請再試一次。請參閱 Javascript 控制台以取得錯誤詳細資訊。"
     _themeParseFailed:
-      title: "外觀主題解析錯誤"
-      description: "已取得資料但解析外觀主題時發生錯誤,導致無法載入。請聯絡主題作者。請檢查 Javascript 控制台以取得錯誤詳細資訊。"
+      title: "佈景主題解析錯誤"
+      description: "已取得資料但解析佈景主題時發生錯誤,導致無法載入。請聯絡佈景主題的作者。請檢查 Javascript 控制台以取得錯誤詳細資訊。"
     _themeInstallFailed:
-      title: "無法安裝外觀主題"
-      description: "安裝外觀主題時出現問題。請再試一次。請參閱 Javascript 控制台以取得錯誤詳細資訊。"
+      title: "無法安裝佈景主題"
+      description: "安裝佈景主題時出現問題。請再試一次。請參閱 Javascript 控制台以取得錯誤詳細資訊。"
+_dataSaver:
+  _media:
+    title: "載入媒體檔案"
+    description: "防止自動載入圖片和影片。點擊隱藏的圖片/影片即可載入。"
+  _avatar:
+    title: "大頭貼"
+    description: "停止顯示大頭貼的動畫。由於動畫圖片的檔案大小可能比普通圖片大,這可以進一步減少資料流量。"
+  _urlPreview:
+    title: "網址預覽縮圖"
+    description: "將不再自動載入網址預覽縮圖。"
+  _code:
+    title: "程式碼突出顯示"
+    description: "如果使用了 MFM 的程式碼突顯標記,則在點擊之前不會載入。程式碼突顯要求加載每種程式語言的突顯定義檔案,但由於這些檔案不再自動載入,因此有望減少資料流量。"

From 0c0b7d77b35720abb9252194dbe0de1516389922 Mon Sep 17 00:00:00 2001
From: 1STEP621 <86859447+1STEP621@users.noreply.github.com>
Date: Thu, 14 Dec 2023 16:16:21 +0900
Subject: [PATCH 223/435] fix: contextmenu does not appear when plugin enabled
 / devMode enabled (#12656)

---
 packages/frontend/src/scripts/get-note-menu.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 1f6cfffce1..50d76167fe 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -389,7 +389,7 @@ export function getNoteMenu(props: {
 	}
 
 	if (noteActions.length > 0) {
-		menu = menu.concat([null, ...noteActions.map(action => ({
+		menu = menu.concat([{ type: "divider" }, ...noteActions.map(action => ({
 			icon: 'ti ti-plug',
 			text: action.title,
 			action: () => {
@@ -399,7 +399,7 @@ export function getNoteMenu(props: {
 	}
 
 	if (defaultStore.state.devMode) {
-		menu = menu.concat([null, {
+		menu = menu.concat([{ type: "divider" }, {
 			icon: 'ti ti-id',
 			text: i18n.ts.copyNoteId,
 			action: () => {

From b9318d09ee39722f94eb6a2afccc385965cf4f54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Thu, 14 Dec 2023 16:17:01 +0900
Subject: [PATCH 224/435] =?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=E3=81=AE=E8=A1=A8=E7=A4=BA?=
 =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E3=82=92=E5=BE=AE=E8=AA=BF=E6=95=B4=20(#1265?=
 =?UTF-8?q?1)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend) リアクションの表示位置を微調整

* fix inline-flex
---
 CHANGELOG.md                                                | 1 +
 .../frontend/src/components/MkReactionsViewer.reaction.vue  | 6 ++++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7fbc1e06de..d2447bae12 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -64,6 +64,7 @@
 - Fix: 長い名前のチャンネルにおける投稿フォームの表示が崩れる問題を修正
 - Fix: セキュリティ向上のためAiScriptの`Mk:apiExternal`を無効化
 - Fix: ノート中の絵文字をタップして「リアクションする」からリアクションした際にリアクションサウンドが鳴らない不具合を修正
+- Fix: ノート中のリアクションの表示を微調整 #12650
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 65a5c2374e..8de226802d 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -139,12 +139,14 @@ if (!mock) {
 
 <style lang="scss" module>
 .root {
-	display: inline-block;
+	display: inline-flex;
 	height: 42px;
 	margin: 2px;
 	padding: 0 6px;
 	font-size: 1.5em;
 	border-radius: 6px;
+	align-items: center;
+	justify-content: center;
 
 	&.canToggle {
 		background: var(--buttonBg);
@@ -183,7 +185,7 @@ if (!mock) {
 	&.reacted, &.reacted:hover {
 		background: var(--accentedBg);
 		color: var(--accent);
-		box-shadow: 0 0 0px 1px var(--accent) inset;
+		box-shadow: 0 0 0 1px var(--accent) inset;
 
 		> .count {
 			color: var(--accent);

From f7eef546a6e3862778fb393f4e28abc6f1367eef Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 14 Dec 2023 17:05:29 +0900
Subject: [PATCH 225/435] fix(frontend): fix missing localization

---
 locales/index.d.ts                                    | 1 +
 locales/ja-JP.yml                                     | 1 +
 packages/frontend/src/pages/settings/emoji-picker.vue | 4 ++--
 3 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 40837b05a2..48c3daf05a 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1181,6 +1181,7 @@ export interface Locale {
     "code": string;
     "reloadRequiredToApplySettings": string;
     "remainingN": string;
+    "overwriteContentConfirm": string;
     "_announcement": {
         "forExistingUsers": string;
         "forExistingUsersDescription": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 3ad27910ef..daa6a32032 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1178,6 +1178,7 @@ doReaction: "リアクションする"
 code: "コード"
 reloadRequiredToApplySettings: "設定の反映にはリロードが必要です。"
 remainingN: "残り: {n}"
+overwriteContentConfirm: "現在の内容に上書きされますがよろしいですか?"
 
 _announcement:
   forExistingUsers: "既存ユーザーのみ"
diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue
index f3f974a96f..7e7bf9bd1f 100644
--- a/packages/frontend/src/pages/settings/emoji-picker.vue
+++ b/packages/frontend/src/pages/settings/emoji-picker.vue
@@ -167,7 +167,7 @@ function previewEmoji(ev: MouseEvent) {
 async function copyFromPinnedEmojis() {
 	const { canceled } = await os.confirm({
 		type: 'warning',
-		text: 'a',
+		text: i18n.ts.overwriteContentConfirm,
 	});
 
 	if (canceled) {
@@ -180,7 +180,7 @@ async function copyFromPinnedEmojis() {
 async function copyFromPinnedEmojisForReaction() {
 	const { canceled } = await os.confirm({
 		type: 'warning',
-		text: 'a',
+		text: i18n.ts.overwriteContentConfirm,
 	});
 
 	if (canceled) {

From d7766162d63eb24693b200db41d222fab017d748 Mon Sep 17 00:00:00 2001
From: YAVIIGI <118232419+YAVIIGI@users.noreply.github.com>
Date: Thu, 14 Dec 2023 17:57:57 +0900
Subject: [PATCH 226/435] =?UTF-8?q?feat(frontend):=20MFM=20=E3=81=AE?=
 =?UTF-8?q?=E3=82=A2=E3=83=8B=E3=83=A1=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3?=
 =?UTF-8?q?=E8=A6=81=E7=B4=A0=E3=81=AB=20delay=20=E3=82=AA=E3=83=97?=
 =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0=20(#1265?=
 =?UTF-8?q?9)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Add delay option

* Update CHANGELOG.md
---
 CHANGELOG.md                                  |  1 +
 .../global/MkMisskeyFlavoredMarkdown.ts       | 24 ++++++++++++-------
 2 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d2447bae12..0581466d7c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -36,6 +36,7 @@
 ### Client
 - Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加
 - Feat: データセーバーでコードハイライトの読み込みを削減できるように
+- Feat: MFMのアニメーション要素(`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)に `delay` オプションを追加
 - Enhance: 投稿フォームの絵文字ピッカーをリアクション時に使用するものと同じのを使用するように #12336 #12560
 - Enhance: リアクション用ピン留め絵文字と投稿時の絵文字入力用ピン留め絵文字を分けて設定できるように #12560
 - Enhance: 絵文字のオートコンプリート機能強化 #12364
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index 28293b287c..a46c7f0cec 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -107,22 +107,26 @@ export default function(props: MfmProps) {
 				switch (token.props.name) {
 					case 'tada': {
 						const speed = validTime(token.props.args.speed) ?? '1s';
-						style = 'font-size: 150%;' + (useAnim ? `animation: tada ${speed} linear infinite both;` : '');
+						const delay = validTime(token.props.args.delay) ?? '0s';
+						style = 'font-size: 150%;' + (useAnim ? `animation: tada ${speed} linear infinite both; animation-delay: ${delay};` : '');
 						break;
 					}
 					case 'jelly': {
 						const speed = validTime(token.props.args.speed) ?? '1s';
-						style = (useAnim ? `animation: mfm-rubberBand ${speed} linear infinite both;` : '');
+						const delay = validTime(token.props.args.delay) ?? '0s';
+						style = (useAnim ? `animation: mfm-rubberBand ${speed} linear infinite both; animation-delay: ${delay};` : '');
 						break;
 					}
 					case 'twitch': {
 						const speed = validTime(token.props.args.speed) ?? '0.5s';
-						style = useAnim ? `animation: mfm-twitch ${speed} ease infinite;` : '';
+						const delay = validTime(token.props.args.delay) ?? '0s';
+						style = useAnim ? `animation: mfm-twitch ${speed} ease infinite; animation-delay: ${delay};` : '';
 						break;
 					}
 					case 'shake': {
 						const speed = validTime(token.props.args.speed) ?? '0.5s';
-						style = useAnim ? `animation: mfm-shake ${speed} ease infinite;` : '';
+						const delay = validTime(token.props.args.delay) ?? '0s';
+						style = useAnim ? `animation: mfm-shake ${speed} ease infinite; animation-delay: ${delay};` : '';
 						break;
 					}
 					case 'spin': {
@@ -135,17 +139,20 @@ export default function(props: MfmProps) {
 							token.props.args.y ? 'mfm-spinY' :
 							'mfm-spin';
 						const speed = validTime(token.props.args.speed) ?? '1.5s';
-						style = useAnim ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : '';
+						const delay = validTime(token.props.args.delay) ?? '0s';
+						style = useAnim ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction}; animation-delay: ${delay};` : '';
 						break;
 					}
 					case 'jump': {
 						const speed = validTime(token.props.args.speed) ?? '0.75s';
-						style = useAnim ? `animation: mfm-jump ${speed} linear infinite;` : '';
+						const delay = validTime(token.props.args.delay) ?? '0s';
+						style = useAnim ? `animation: mfm-jump ${speed} linear infinite; animation-delay: ${delay};` : '';
 						break;
 					}
 					case 'bounce': {
 						const speed = validTime(token.props.args.speed) ?? '0.75s';
-						style = useAnim ? `animation: mfm-bounce ${speed} linear infinite; transform-origin: center bottom;` : '';
+						const delay = validTime(token.props.args.delay) ?? '0s';
+						style = useAnim ? `animation: mfm-bounce ${speed} linear infinite; transform-origin: center bottom; animation-delay: ${delay};` : '';
 						break;
 					}
 					case 'flip': {
@@ -195,7 +202,8 @@ export default function(props: MfmProps) {
 							}, genEl(token.children, scale));
 						}
 						const speed = validTime(token.props.args.speed) ?? '1s';
-						style = `animation: mfm-rainbow ${speed} linear infinite;`;
+						const delay = validTime(token.props.args.delay) ?? '0s';
+						style = `animation: mfm-rainbow ${speed} linear infinite; animation-delay: ${delay};`;
 						break;
 					}
 					case 'sparkle': {

From 76d1ed39a11cb2377714d9df0dbd2e84a2d8a30f Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Thu, 14 Dec 2023 18:25:39 +0900
Subject: [PATCH 227/435] Cleanup unused dependencies (#12660)

---
 packages/frontend/package.json |  5 ----
 pnpm-lock.yaml                 | 52 ----------------------------------
 2 files changed, 57 deletions(-)

diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index e566f86d1c..4cf4779563 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -19,7 +19,6 @@
 	"dependencies": {
 		"@discordapp/twemoji": "14.1.2",
 		"@github/webauthn-json": "2.1.1",
-		"@rollup/plugin-alias": "5.1.0",
 		"@rollup/plugin-json": "6.1.0",
 		"@rollup/plugin-replace": "5.0.5",
 		"@rollup/pluginutils": "5.1.0",
@@ -28,7 +27,6 @@
 		"@vitejs/plugin-vue": "4.5.2",
 		"@vue/compiler-sfc": "3.3.11",
 		"astring": "1.8.6",
-		"autosize": "6.0.1",
 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
 		"broadcast-channel": "6.0.0",
 		"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
@@ -56,7 +54,6 @@
 		"misskey-js": "workspace:*",
 		"photoswipe": "5.4.3",
 		"punycode": "2.3.1",
-		"querystring": "0.2.1",
 		"rollup": "4.9.0",
 		"sanitize-html": "2.11.0",
 		"shiki": "0.14.6",
@@ -72,7 +69,6 @@
 		"typescript": "5.3.3",
 		"uuid": "9.0.1",
 		"v-code-diff": "1.7.2",
-		"vanilla-tilt": "1.8.1",
 		"vite": "5.0.8",
 		"vue": "3.3.11",
 		"vuedraggable": "next"
@@ -107,7 +103,6 @@
 		"@types/throttle-debounce": "5.0.2",
 		"@types/tinycolor2": "1.4.6",
 		"@types/uuid": "9.0.7",
-		"@types/websocket": "1.0.10",
 		"@types/ws": "8.5.10",
 		"@typescript-eslint/eslint-plugin": "6.14.0",
 		"@typescript-eslint/parser": "6.14.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 80b892652f..4ba9db7747 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -655,9 +655,6 @@ importers:
       '@github/webauthn-json':
         specifier: 2.1.1
         version: 2.1.1
-      '@rollup/plugin-alias':
-        specifier: 5.1.0
-        version: 5.1.0(rollup@4.9.0)
       '@rollup/plugin-json':
         specifier: 6.1.0
         version: 6.1.0(rollup@4.9.0)
@@ -685,9 +682,6 @@ importers:
       astring:
         specifier: 1.8.6
         version: 1.8.6
-      autosize:
-        specifier: 6.0.1
-        version: 6.0.1
       broadcast-channel:
         specifier: 6.0.0
         version: 6.0.0
@@ -766,9 +760,6 @@ importers:
       punycode:
         specifier: 2.3.1
         version: 2.3.1
-      querystring:
-        specifier: 0.2.1
-        version: 0.2.1
       rollup:
         specifier: 4.9.0
         version: 4.9.0
@@ -814,9 +805,6 @@ importers:
       v-code-diff:
         specifier: 1.7.2
         version: 1.7.2(vue@3.3.11)
-      vanilla-tilt:
-        specifier: 1.8.1
-        version: 1.8.1
       vite:
         specifier: 5.0.8
         version: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
@@ -914,9 +902,6 @@ importers:
       '@types/uuid':
         specifier: 9.0.7
         version: 9.0.7
-      '@types/websocket':
-        specifier: 1.0.10
-        version: 1.0.10
       '@types/ws':
         specifier: 8.5.10
         version: 8.5.10
@@ -5556,19 +5541,6 @@ packages:
       '@babel/runtime': 7.23.2
     dev: true
 
-  /@rollup/plugin-alias@5.1.0(rollup@4.9.0):
-    resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==}
-    engines: {node: '>=14.0.0'}
-    peerDependencies:
-      rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
-    peerDependenciesMeta:
-      rollup:
-        optional: true
-    dependencies:
-      rollup: 4.9.0
-      slash: 4.0.0
-    dev: false
-
   /@rollup/plugin-json@6.1.0(rollup@4.9.0):
     resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
     engines: {node: '>=14.0.0'}
@@ -8178,12 +8150,6 @@ packages:
     requiresBuild: true
     dev: false
 
-  /@types/websocket@1.0.10:
-    resolution: {integrity: sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww==}
-    dependencies:
-      '@types/node': 20.10.4
-    dev: true
-
   /@types/ws@8.5.10:
     resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
     dependencies:
@@ -9220,10 +9186,6 @@ packages:
     engines: {node: '>=8.0.0'}
     dev: false
 
-  /autosize@6.0.1:
-    resolution: {integrity: sha512-f86EjiUKE6Xvczc4ioP1JBlWG7FKrE13qe/DxBCpe8GCipCq2nFw73aO8QEBKHfSbYGDN5eB9jXWKen7tspDqQ==}
-    dev: false
-
   /available-typed-arrays@1.0.5:
     resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
     engines: {node: '>= 0.4'}
@@ -16802,11 +16764,6 @@ packages:
     engines: {node: '>=0.6'}
     dev: false
 
-  /querystring@0.2.1:
-    resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==}
-    engines: {node: '>=0.4.x'}
-    dev: false
-
   /querystringify@2.2.0:
     resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
 
@@ -17921,11 +17878,6 @@ packages:
     resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
     engines: {node: '>=8'}
 
-  /slash@4.0.0:
-    resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==}
-    engines: {node: '>=12'}
-    dev: false
-
   /slice-ansi@3.0.0:
     resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
     engines: {node: '>=8'}
@@ -19331,10 +19283,6 @@ packages:
     engines: {node: '>= 0.10'}
     dev: true
 
-  /vanilla-tilt@1.8.1:
-    resolution: {integrity: sha512-hPB1XUsnh+SIeVSW2beb5RnuFxz4ZNgxjGD78o52F49gS4xaoLeEMh9qrQnJrnEn/vjjBI7IlxrrXmz4tGV0Kw==}
-    dev: false
-
   /vary@1.1.2:
     resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
     engines: {node: '>= 0.8'}

From a2a6a94614ab69c9cee66151ee97b9029078b086 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 14 Dec 2023 18:28:04 +0900
Subject: [PATCH 228/435] update deps

---
 packages/backend/package.json |  4 ++--
 pnpm-lock.yaml                | 40 ++++++++++++++---------------------
 2 files changed, 18 insertions(+), 26 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index c68966de44..17113a5e0d 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -100,7 +100,7 @@
 		"content-disposition": "0.5.4",
 		"date-fns": "2.30.0",
 		"deep-email-validator": "0.1.21",
-		"fastify": "4.25.0",
+		"fastify": "4.24.3",
 		"fastify-raw-body": "4.3.0",
 		"feed": "4.2.2",
 		"file-type": "18.7.0",
@@ -148,7 +148,7 @@
 		"ratelimiter": "3.4.1",
 		"re2": "1.20.9",
 		"redis-lock": "0.1.4",
-		"reflect-metadata": "0.2.0",
+		"reflect-metadata": "0.1.14",
 		"rename": "1.0.4",
 		"rss-parser": "3.13.0",
 		"rxjs": "7.8.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4ba9db7747..5afc583343 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -103,10 +103,10 @@ importers:
         version: 8.2.0
       '@nestjs/common':
         specifier: 10.2.10
-        version: 10.2.10(reflect-metadata@0.2.0)(rxjs@7.8.1)
+        version: 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
       '@nestjs/core':
         specifier: 10.2.10
-        version: 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.2.0)(rxjs@7.8.1)
+        version: 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1)
       '@nestjs/testing':
         specifier: 10.2.10
         version: 10.2.10(@nestjs/common@10.2.10)(@nestjs/core@10.2.10)
@@ -183,8 +183,8 @@ importers:
         specifier: 0.1.21
         version: 0.1.21
       fastify:
-        specifier: 4.25.0
-        version: 4.25.0
+        specifier: 4.24.3
+        version: 4.24.3
       fastify-raw-body:
         specifier: 4.3.0
         version: 4.3.0
@@ -327,8 +327,8 @@ importers:
         specifier: 0.1.4
         version: 0.1.4
       reflect-metadata:
-        specifier: 0.2.0
-        version: 0.2.0
+        specifier: 0.1.14
+        version: 0.1.14
       rename:
         specifier: 1.0.4
         version: 1.0.4
@@ -4818,7 +4818,7 @@ packages:
       tar-fs: 2.1.1
     dev: true
 
-  /@nestjs/common@10.2.10(reflect-metadata@0.2.0)(rxjs@7.8.1):
+  /@nestjs/common@10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1):
     resolution: {integrity: sha512-fwAk931rjW8CNH2Mgwawq/7HWHH1dxkOLdcgs7U52ddLk8CtHXjejm1cbNahewlSbNhvlOl7y1STLHutE6sUqw==}
     peerDependencies:
       class-transformer: '*'
@@ -4832,13 +4832,13 @@ packages:
         optional: true
     dependencies:
       iterare: 1.2.1
-      reflect-metadata: 0.2.0
+      reflect-metadata: 0.1.14
       rxjs: 7.8.1
       tslib: 2.6.2
       uid: 2.0.2
     dev: false
 
-  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.2.0)(rxjs@7.8.1):
+  /@nestjs/core@10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1):
     resolution: {integrity: sha512-+ckOI6BPi2ZMHikT9MCG4ctHDc4OnjhoIytrn7f2AYMMXI4bnutJhqyQKc30VDka5x3Wq6QAD57pgSP7y+JjJg==}
     requiresBuild: true
     peerDependencies:
@@ -4856,12 +4856,12 @@ packages:
       '@nestjs/websockets':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.10(reflect-metadata@0.2.0)(rxjs@7.8.1)
+      '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
       '@nuxtjs/opencollective': 0.3.2
       fast-safe-stringify: 2.1.1
       iterare: 1.2.1
       path-to-regexp: 3.2.0
-      reflect-metadata: 0.2.0
+      reflect-metadata: 0.1.14
       rxjs: 7.8.1
       tslib: 2.6.2
       uid: 2.0.2
@@ -4882,8 +4882,8 @@ packages:
       '@nestjs/platform-express':
         optional: true
     dependencies:
-      '@nestjs/common': 10.2.10(reflect-metadata@0.2.0)(rxjs@7.8.1)
-      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.2.0)(rxjs@7.8.1)
+      '@nestjs/common': 10.2.10(reflect-metadata@0.1.14)(rxjs@7.8.1)
+      '@nestjs/core': 10.2.10(@nestjs/common@10.2.10)(reflect-metadata@0.1.14)(rxjs@7.8.1)
       tslib: 2.6.2
     dev: false
 
@@ -11848,8 +11848,8 @@ packages:
       secure-json-parse: 2.7.0
     dev: false
 
-  /fastify@4.25.0:
-    resolution: {integrity: sha512-2XANZZDadl/PccnVbmrIC8BmUtb16MO5Hyfnqv1cZIslvf7GYTVwlPuVxLKL23NNxWRc5BikY8HyhWj+NJvokA==}
+  /fastify@4.24.3:
+    resolution: {integrity: sha512-6HHJ+R2x2LS3y1PqxnwEIjOTZxFl+8h4kSC/TuDPXtA+v2JnV9yEtOsNSKK1RMD7sIR2y1ZsA4BEFaid/cK5pg==}
     dependencies:
       '@fastify/ajv-compiler': 3.5.0
       '@fastify/error': 3.4.0
@@ -11861,7 +11861,7 @@ packages:
       find-my-way: 7.7.0
       light-my-request: 5.11.0
       pino: 8.17.0
-      process-warning: 3.0.0
+      process-warning: 2.2.0
       proxy-addr: 2.0.7
       rfdc: 1.3.0
       secure-json-parse: 2.7.0
@@ -16493,10 +16493,6 @@ packages:
     resolution: {integrity: sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==}
     dev: false
 
-  /process-warning@3.0.0:
-    resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==}
-    dev: false
-
   /process@0.11.10:
     resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
     engines: {node: '>= 0.6.0'}
@@ -17133,10 +17129,6 @@ packages:
     resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
     dev: false
 
-  /reflect-metadata@0.2.0:
-    resolution: {integrity: sha512-vUN0wuk3MuhSVMfU/ImnPQAK8QZcXJ339DtVsP3jDscxCe6dT+PsOe3J1BYS9Ec2Fd4oC6ry6bCBebzTya0IYw==}
-    dev: false
-
   /regenerate-unicode-properties@10.1.0:
     resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
     engines: {node: '>=4'}

From 386fcedf3525eb18ed2297bd93d45d1d78a36f84 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Thu, 14 Dec 2023 20:16:02 +0900
Subject: [PATCH 229/435] =?UTF-8?q?(dev-mode)=20dev=E3=83=A2=E3=83=BC?=
 =?UTF-8?q?=E3=83=89=E3=81=AE=E6=94=B9=E5=96=84=20(#12639)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix dev-mode

* fix dev-mode

* fix dev-mode

* fix dev-mode

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 package.json                               |  2 +-
 packages/backend/package.json              |  4 +-
 packages/frontend/@types/global.d.ts       |  3 +
 packages/frontend/src/_dev_boot_.ts        | 84 ++++++++++++++++++++++
 packages/frontend/vite.config.local-dev.ts |  6 ++
 packages/misskey-js/package.json           |  6 +-
 packages/sw/package.json                   |  3 +-
 pnpm-lock.yaml                             |  9 +++
 scripts/build-assets.mjs                   |  1 +
 scripts/build-pre.js                       | 23 +++++-
 scripts/dev.mjs                            | 42 ++++-------
 11 files changed, 146 insertions(+), 37 deletions(-)

diff --git a/package.json b/package.json
index bb44951e8a..3057c5d804 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,7 @@
 		"check:connect": "cd packages/backend && pnpm check:connect",
 		"migrateandstart": "pnpm migrate && pnpm start",
 		"watch": "pnpm dev",
-		"dev": "pnpm -r dev",
+		"dev": "node scripts/dev.mjs",
 		"lint": "pnpm -r lint",
 		"cy:open": "pnpm cypress open --browser --e2e --config-file=cypress.config.ts",
 		"cy:run": "pnpm cypress run",
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 17113a5e0d..504cc882ff 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -16,7 +16,8 @@
 		"watch:swc": "swc src -d built -D -w",
 		"build:tsc": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
 		"watch": "node watch.mjs",
-		"dev": "node ./built/boot/entry.js",
+		"restart": "pnpm build && pnpm start",
+		"dev": "nodemon -w src -e ts,js,mjs,cjs,json --exec \"cross-env NODE_ENV=development pnpm run restart\"",
 		"typecheck": "tsc --noEmit",
 		"eslint": "eslint --quiet \"src/**/*.ts\"",
 		"lint": "pnpm typecheck && pnpm eslint",
@@ -226,6 +227,7 @@
 		"execa": "8.0.1",
 		"jest": "29.7.0",
 		"jest-mock": "29.7.0",
+		"nodemon": "3.0.2",
 		"simple-oauth2": "5.0.0"
 	}
 }
diff --git a/packages/frontend/@types/global.d.ts b/packages/frontend/@types/global.d.ts
index 390f63990b..7d9335cc52 100644
--- a/packages/frontend/@types/global.d.ts
+++ b/packages/frontend/@types/global.d.ts
@@ -13,3 +13,6 @@ declare const _PERF_PREFIX_: string;
 declare const _DATA_TRANSFER_DRIVE_FILE_: string;
 declare const _DATA_TRANSFER_DRIVE_FOLDER_: string;
 declare const _DATA_TRANSFER_DECK_COLUMN_: string;
+
+// for dev-mode
+declare const _LANGS_FULL_: string[][];
diff --git a/packages/frontend/src/_dev_boot_.ts b/packages/frontend/src/_dev_boot_.ts
index 2e95a03576..d01a957048 100644
--- a/packages/frontend/src/_dev_boot_.ts
+++ b/packages/frontend/src/_dev_boot_.ts
@@ -8,4 +8,88 @@
 // (pnpm start時はpugファイルの中で静的リソースとして読み込むようになっており、この問題は起こっていない)
 import '@tabler/icons-webfont/tabler-icons.scss';
 
+await main();
+
 import('@/_boot_.js');
+
+/**
+ * backend/src/server/web/boot.jsで差し込まれている起動処理のうち、最低限必要なものを模倣するための処理
+ */
+async function main() {
+	const forceError = localStorage.getItem('forceError');
+	if (forceError != null) {
+		renderError('FORCED_ERROR', 'This error is forced by having forceError in local storage.');
+	}
+
+	//#region Detect language & fetch translations
+
+	// dev-modeの場合は常に取り直す
+	const supportedLangs = _LANGS_.map(it => it[0]);
+	let lang: string | null | undefined = localStorage.getItem('lang');
+	if (lang == null || !supportedLangs.includes(lang)) {
+		if (supportedLangs.includes(navigator.language)) {
+			lang = navigator.language;
+		} else {
+			lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
+
+			// Fallback
+			if (lang == null) lang = 'en-US';
+		}
+	}
+
+	// TODO:今のままだと言語ファイル変更後はpnpm devをリスタートする必要があるので、chokidarを使ったり等で対応できるようにする
+	const locale = _LANGS_FULL_.find(it => it[0] === lang);
+	localStorage.setItem('lang', lang);
+	localStorage.setItem('locale', JSON.stringify(locale[1]));
+	localStorage.setItem('localeVersion', _VERSION_);
+	//#endregion
+
+	//#region Theme
+	const theme = localStorage.getItem('theme');
+	if (theme) {
+		for (const [k, v] of Object.entries(JSON.parse(theme))) {
+			document.documentElement.style.setProperty(`--${k}`, v.toString());
+
+			// HTMLの theme-color 適用
+			if (k === 'htmlThemeColor') {
+				for (const tag of document.head.children) {
+					if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
+						tag.setAttribute('content', v);
+						break;
+					}
+				}
+			}
+		}
+	}
+	const colorScheme = localStorage.getItem('colorScheme');
+	if (colorScheme) {
+		document.documentElement.style.setProperty('color-scheme', colorScheme);
+	}
+	//#endregion
+
+	const fontSize = localStorage.getItem('fontSize');
+	if (fontSize) {
+		document.documentElement.classList.add('f-' + fontSize);
+	}
+
+	const useSystemFont = localStorage.getItem('useSystemFont');
+	if (useSystemFont) {
+		document.documentElement.classList.add('useSystemFont');
+	}
+
+	const wallpaper = localStorage.getItem('wallpaper');
+	if (wallpaper) {
+		document.documentElement.style.backgroundImage = `url(${wallpaper})`;
+	}
+
+	const customCss = localStorage.getItem('customCss');
+	if (customCss && customCss.length > 0) {
+		const style = document.createElement('style');
+		style.innerHTML = customCss;
+		document.head.appendChild(style);
+	}
+}
+
+function renderError(code: string, details?: string) {
+	console.log(code, details);
+}
diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts
index 6b6394a722..aea46f4231 100644
--- a/packages/frontend/vite.config.local-dev.ts
+++ b/packages/frontend/vite.config.local-dev.ts
@@ -1,5 +1,6 @@
 import dns from 'dns';
 import { defineConfig } from 'vite';
+import locales from '../../locales';
 import { getConfig } from './vite.config.js';
 
 dns.setDefaultResultOrder('ipv4first');
@@ -49,6 +50,11 @@ const devConfig = {
 			input: 'index.html',
 		},
 	},
+
+	define: {
+		...defaultConfig.define,
+		_LANGS_FULL_: JSON.stringify(Object.entries(locales)),
+	},
 };
 
 export default defineConfig(({ command, mode }) => devConfig);
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index 6a7712af90..0b54c6a59c 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -6,6 +6,7 @@
 	"types": "./built/index.d.ts",
 	"scripts": {
 		"build": "tsc",
+		"watch": "nodemon -w src -e ts,js,cjs,mjs,json --exec \"pnpm run build\"",
 		"tsd": "tsd",
 		"api": "pnpm api-extractor run --local --verbose",
 		"api-prod": "pnpm api-extractor run --verbose",
@@ -32,9 +33,10 @@
 		"jest-fetch-mock": "3.0.3",
 		"jest-websocket-mock": "2.5.0",
 		"mock-socket": "9.3.1",
+		"ncp": "2.0.0",
+		"nodemon": "3.0.2",
 		"tsd": "0.29.0",
-		"typescript": "5.3.3",
-		"ncp": "2.0.0"
+		"typescript": "5.3.3"
 	},
 	"files": [
 		"built"
diff --git a/packages/sw/package.json b/packages/sw/package.json
index 039a6887aa..990e991966 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -2,7 +2,7 @@
 	"name": "sw",
 	"private": true,
 	"scripts": {
-		"watch": "node build.js watch",
+		"watch": "nodemon -w ../../package.json -e json --exec \"node build.js watch\"",
 		"build": "node build.js",
 		"typecheck": "tsc --noEmit",
 		"eslint": "eslint --quiet src/**/*.ts",
@@ -18,6 +18,7 @@
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
 		"eslint": "8.55.0",
 		"eslint-plugin-import": "2.29.0",
+		"nodemon": "3.0.2",
 		"typescript": "5.3.3"
 	},
 	"type": "module"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5afc583343..7c0bc470b0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -643,6 +643,9 @@ importers:
       jest-mock:
         specifier: 29.7.0
         version: 29.7.0
+      nodemon:
+        specifier: 3.0.2
+        version: 3.0.2
       simple-oauth2:
         specifier: 5.0.0
         version: 5.0.0
@@ -1041,6 +1044,9 @@ importers:
       ncp:
         specifier: 2.0.0
         version: 2.0.0
+      nodemon:
+        specifier: 3.0.2
+        version: 3.0.2
       tsd:
         specifier: 0.29.0
         version: 0.29.0
@@ -1105,6 +1111,9 @@ importers:
       eslint-plugin-import:
         specifier: 2.29.0
         version: 2.29.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)
+      nodemon:
+        specifier: 3.0.2
+        version: 3.0.2
       typescript:
         specifier: 5.3.3
         version: 5.3.3
diff --git a/scripts/build-assets.mjs b/scripts/build-assets.mjs
index f8f09ec2fb..2e1268130c 100644
--- a/scripts/build-assets.mjs
+++ b/scripts/build-assets.mjs
@@ -95,6 +95,7 @@ if (process.argv.includes("--watch")) {
 	for await (const event of watcher) {
 		const filename = event.filename?.replaceAll('\\', '/');
 		if (/^[a-z]+-[A-Z]+\.yml/.test(filename)) {
+			console.log(`update ${filename} ...`)
 			locales = buildLocales();
 			await copyFrontendLocales()
 		}
diff --git a/scripts/build-pre.js b/scripts/build-pre.js
index bf3e355b5b..ed75aa6553 100644
--- a/scripts/build-pre.js
+++ b/scripts/build-pre.js
@@ -4,7 +4,24 @@
  */
 
 const fs = require('fs');
-const meta = require('../package.json');
+const packageJsonPath = __dirname + '/../package.json'
 
-fs.mkdirSync(__dirname + '/../built', { recursive: true });
-fs.writeFileSync(__dirname + '/../built/meta.json', JSON.stringify({ version: meta.version }), 'utf-8');
+function build() {
+	try {
+		const json = fs.readFileSync(packageJsonPath, 'utf-8')
+		const meta = JSON.parse(json);
+		fs.mkdirSync(__dirname + '/../built', { recursive: true });
+		fs.writeFileSync(__dirname + '/../built/meta.json', JSON.stringify({ version: meta.version }), 'utf-8');
+	} catch (e) {
+		console.error(e)
+	}
+}
+
+build();
+
+if (process.argv.includes("--watch")) {
+	fs.watch(packageJsonPath, (event, filename) => {
+		console.log(`update ${filename} ...`)
+		build()
+	})
+}
diff --git a/scripts/dev.mjs b/scripts/dev.mjs
index 1d06aa541f..30dbebcf0f 100644
--- a/scripts/dev.mjs
+++ b/scripts/dev.mjs
@@ -3,7 +3,6 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import fs from 'node:fs';
 import { dirname } from 'node:path';
 import { fileURLToPath } from 'node:url';
 import { execa } from 'execa';
@@ -11,8 +10,6 @@ import { execa } from 'execa';
 const _filename = fileURLToPath(import.meta.url);
 const _dirname = dirname(_filename);
 
-const vitePort = process.env.VITE_PORT ? ["--strictPort", "--port", process.env.VITE_PORT] : ["--strictPort"];
-
 await execa('pnpm', ['clean'], {
 	cwd: _dirname + '/../',
 	stdout: process.stdout,
@@ -31,19 +28,25 @@ await execa('pnpm', ['build-assets'], {
 	stderr: process.stderr,
 });
 
+execa('pnpm', ['build-pre', '--watch'], {
+	cwd: _dirname + '/../',
+	stdout: process.stdout,
+	stderr: process.stderr,
+});
+
 execa('pnpm', ['build-assets', '--watch'], {
 	cwd: _dirname + '/../',
 	stdout: process.stdout,
 	stderr: process.stderr,
 });
 
-execa('pnpm', ['--filter', 'backend', 'watch'], {
+execa('pnpm', ['--filter', 'backend', 'dev'], {
 	cwd: _dirname + '/../',
 	stdout: process.stdout,
 	stderr: process.stderr,
 });
 
-execa('pnpm', ['--filter', 'frontend', 'watch', ...vitePort], {
+execa('pnpm', ['--filter', 'frontend', 'dev'], {
 	cwd: _dirname + '/../',
 	stdout: process.stdout,
 	stderr: process.stderr,
@@ -55,27 +58,8 @@ execa('pnpm', ['--filter', 'sw', 'watch'], {
 	stderr: process.stderr,
 });
 
-const start = async () => {
-	try {
-		const stat = fs.statSync(_dirname + '/../packages/backend/built/boot/entry.js');
-		if (!stat) throw new Error('not exist yet');
-		if (stat.size === 0) throw new Error('not built yet');
-
-		const subprocess = await execa('pnpm', ['start'], {
-			cwd: _dirname + '/../',
-			stdout: process.stdout,
-			stderr: process.stderr,
-		});
-
-		// なぜかworkerだけが終了してmasterが残るのでその対策
-		process.on('SIGINT', () => {
-			subprocess.kill('SIGINT');
-			process.exit(0);
-		});
-	} catch (e) {
-		await new Promise(resolve => setTimeout(resolve, 3000));
-		start();
-	}
-};
-
-start();
+execa('pnpm', ['--filter', 'misskey-js', 'watch'], {
+	cwd: _dirname + '/../',
+	stdout: process.stdout,
+	stderr: process.stderr,
+});

From 239507d7d6d0a8574bb641adee66d9da96537bb9 Mon Sep 17 00:00:00 2001
From: yukineko <27853966+hideki0403@users.noreply.github.com>
Date: Thu, 14 Dec 2023 20:21:57 +0900
Subject: [PATCH 230/435] =?UTF-8?q?fix:=20dev=E3=83=A2=E3=83=BC=E3=83=89?=
 =?UTF-8?q?=E3=81=AE=E7=89=B9=E5=AE=9A=E6=9D=A1=E4=BB=B6=E4=B8=8B=E3=81=A7?=
 =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95?=
 =?UTF-8?q?=E3=82=8C=E3=81=AA=E3=81=8F=E3=81=AA=E3=82=8B=E5=95=8F=E9=A1=8C?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12653)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: devモードでキャッシュクリアするとページが表示されなくなる問題を修正

* fix: localeがnullの場合も最新のlocaleを取得するように
---
 packages/frontend/src/boot/common.ts         | 4 ++--
 packages/frontend/src/scripts/clear-cache.ts | 1 +
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index 60f2781fdf..728f39962b 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -8,7 +8,7 @@ import { compareVersions } from 'compare-versions';
 import widgets from '@/widgets/index.js';
 import directives from '@/directives/index.js';
 import components from '@/components/index.js';
-import { version, ui, lang, updateLocale } from '@/config.js';
+import { version, ui, lang, updateLocale, locale } from '@/config.js';
 import { applyTheme } from '@/scripts/theme.js';
 import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
 import { i18n, updateI18n } from '@/i18n.js';
@@ -88,7 +88,7 @@ export async function common(createVue: () => App<Element>) {
 
 	//#region Detect language & fetch translations
 	const localeVersion = miLocalStorage.getItem('localeVersion');
-	const localeOutdated = (localeVersion == null || localeVersion !== version);
+	const localeOutdated = (localeVersion == null || localeVersion !== version || locale == null);
 	if (localeOutdated) {
 		const res = await window.fetch(`/assets/locales/${lang}.${version}.json`);
 		if (res.status === 200) {
diff --git a/packages/frontend/src/scripts/clear-cache.ts b/packages/frontend/src/scripts/clear-cache.ts
index 5f27254b8a..f2db87c4fb 100644
--- a/packages/frontend/src/scripts/clear-cache.ts
+++ b/packages/frontend/src/scripts/clear-cache.ts
@@ -6,6 +6,7 @@ import { fetchCustomEmojis } from '@/custom-emojis.js';
 export async function clearCache() {
 	os.waiting();
 	miLocalStorage.removeItem('locale');
+	miLocalStorage.removeItem('localeVersion');
 	miLocalStorage.removeItem('theme');
 	miLocalStorage.removeItem('emojis');
 	miLocalStorage.removeItem('lastEmojisFetchedAt');

From 417852779f871197f6aec2fec0fd56a2380836e4 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 14 Dec 2023 20:58:08 +0900
Subject: [PATCH 231/435] =?UTF-8?q?enhance:=20=E3=82=A2=E3=82=A4=E3=82=B3?=
 =?UTF-8?q?=E3=83=B3=E3=83=87=E3=82=B3=E3=83=AC=E3=83=BC=E3=82=B7=E3=83=A7?=
 =?UTF-8?q?=E3=83=B3=E3=81=AE=E4=BD=8D=E7=BD=AE=E3=82=92=E5=BE=AE=E8=AA=BF?=
 =?UTF-8?q?=E6=95=B4=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                   |  1 +
 .../src/core/entities/UserEntityService.ts     |  2 ++
 packages/backend/src/models/User.ts            |  6 ++++--
 .../src/server/api/endpoints/i/update.ts       |  4 ++++
 .../src/components/global/MkAvatar.vue         |  7 +++++++
 .../profile.avatar-decoration.decoration.vue   |  4 +++-
 .../profile.avatar-decoration.dialog.vue       | 18 ++++++++++++++++++
 .../settings/profile.avatar-decoration.vue     |  6 ++++++
 8 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0581466d7c..599bd463fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,7 @@
 - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
 - Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加
 - Enhance: アイコンデコレーションを複数設定できるように
+- Enhance: アイコンデコレーションの位置を微調整できるように
 - Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
 
 ### Client
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index 917f4e06d0..fb7aa0c244 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -362,6 +362,8 @@ export class UserEntityService implements OnModuleInit {
 				id: ud.id,
 				angle: ud.angle || undefined,
 				flipH: ud.flipH || undefined,
+				offsetX: ud.offsetX || undefined,
+				offsetY: ud.offsetY || undefined,
 				url: decorations.find(d => d.id === ud.id)!.url,
 			}))) : [],
 			isBot: user.isBot,
diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts
index c3762fcd3e..219497a125 100644
--- a/packages/backend/src/models/User.ts
+++ b/packages/backend/src/models/User.ts
@@ -143,8 +143,10 @@ export class MiUser {
 	})
 	public avatarDecorations: {
 		id: string;
-		angle: number;
-		flipH: boolean;
+		angle?: number;
+		flipH?: boolean;
+		offsetX?: number;
+		offsetY?: number;
 	}[];
 
 	@Index()
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index 399e6b88cb..a56f50115b 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -143,6 +143,8 @@ export const paramDef = {
 				id: { type: 'string', format: 'misskey:id' },
 				angle: { type: 'number', nullable: true, maximum: 0.5, minimum: -0.5 },
 				flipH: { type: 'boolean', nullable: true },
+				offsetX: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 },
+				offsetY: { type: 'number', nullable: true, maximum: 0.25, minimum: -0.25 },
 			},
 			required: ['id'],
 		} },
@@ -341,6 +343,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 					id: d.id,
 					angle: d.angle ?? 0,
 					flipH: d.flipH ?? false,
+					offsetX: d.offsetX ?? 0,
+					offsetY: d.offsetY ?? 0,
 				}));
 			}
 
diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index 9d13c03290..af5b6e44f5 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -31,6 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:style="{
 				rotate: getDecorationAngle(decoration),
 				scale: getDecorationScale(decoration),
+				translate: getDecorationOffset(decoration),
 			}"
 			alt=""
 		>
@@ -99,6 +100,12 @@ function getDecorationScale(decoration: Omit<Misskey.entities.UserDetailed['avat
 	return scaleX === 1 ? undefined : `${scaleX} 1`;
 }
 
+function getDecorationOffset(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) {
+	const offsetX = decoration.offsetX ?? 0;
+	const offsetY = decoration.offsetY ?? 0;
+	return offsetX === 0 && offsetY === 0 ? undefined : `${offsetX * 100}% ${offsetY * 100}%`;
+}
+
 const color = ref<string | undefined>();
 
 watch(() => props.user.avatarBlurhash, () => {
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue
index c113868238..9c95b5547e 100644
--- a/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	@click="emit('click')"
 >
 	<div :class="$style.name"><MkCondensedLine :minScale="0.5">{{ decoration.name }}</MkCondensedLine></div>
-	<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: decoration.url, angle, flipH }]" forceShowDecoration/>
+	<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: decoration.url, angle, flipH, offsetX, offsetY }]" forceShowDecoration/>
 	<i v-if="decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.lock" class="ti ti-lock"></i>
 </div>
 </template>
@@ -28,6 +28,8 @@ const props = defineProps<{
 	};
 	angle?: number;
 	flipH?: boolean;
+	offsetX?: number;
+	offsetY?: number;
 }>();
 
 const emit = defineEmits<{
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue
index 26cacf3c37..77e6b28fad 100644
--- a/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue
@@ -23,6 +23,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkRange v-model="angle" continuousUpdate :min="-0.5" :max="0.5" :step="0.025" :textConverter="(v) => `${Math.floor(v * 360)}°`">
 					<template #label>{{ i18n.ts.angle }}</template>
 				</MkRange>
+				<MkRange v-model="offsetX" continuousUpdate :min="-0.25" :max="0.25" :step="0.025" :textConverter="(v) => `${Math.floor(v * 100)}%`">
+					<template #label>X {{ i18n.ts.position }}</template>
+				</MkRange>
+				<MkRange v-model="offsetY" continuousUpdate :min="-0.25" :max="0.25" :step="0.025" :textConverter="(v) => `${Math.floor(v * 100)}%`">
+					<template #label>Y {{ i18n.ts.position }}</template>
+				</MkRange>
 				<MkSwitch v-model="flipH">
 					<template #label>{{ i18n.ts.flip }}</template>
 				</MkSwitch>
@@ -64,10 +70,14 @@ const emit = defineEmits<{
 	(ev: 'attach', payload: {
 		angle: number;
 		flipH: boolean;
+		offsetX: number;
+		offsetY: number;
 	}): void;
 	(ev: 'update', payload: {
 		angle: number;
 		flipH: boolean;
+		offsetX: number;
+		offsetY: number;
 	}): void;
 	(ev: 'detach'): void;
 }>();
@@ -76,6 +86,8 @@ const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
 const exceeded = computed(() => ($i.policies.avatarDecorationLimit - $i.avatarDecorations.length) <= 0);
 const angle = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].angle : null) ?? 0);
 const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false);
+const offsetX = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetX : null) ?? 0);
+const offsetY = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetY : null) ?? 0);
 
 const decorationsForPreview = computed(() => {
 	const decoration = {
@@ -83,6 +95,8 @@ const decorationsForPreview = computed(() => {
 		url: props.decoration.url,
 		angle: angle.value,
 		flipH: flipH.value,
+		offsetX: offsetX.value,
+		offsetY: offsetY.value,
 	};
 	const decorations = [...$i.avatarDecorations];
 	if (props.usingIndex != null) {
@@ -101,6 +115,8 @@ async function update() {
 	emit('update', {
 		angle: angle.value,
 		flipH: flipH.value,
+		offsetX: offsetX.value,
+		offsetY: offsetY.value,
 	});
 	dialog.value.close();
 }
@@ -109,6 +125,8 @@ async function attach() {
 	emit('attach', {
 		angle: angle.value,
 		flipH: flipH.value,
+		offsetX: offsetX.value,
+		offsetY: offsetY.value,
 	});
 	dialog.value.close();
 }
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue
index bfef6e0325..8579acfed8 100644
--- a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue
+++ b/packages/frontend/src/pages/settings/profile.avatar-decoration.vue
@@ -16,6 +16,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 				:decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)"
 				:angle="avatarDecoration.angle"
 				:flipH="avatarDecoration.flipH"
+				:offsetX="avatarDecoration.offsetX"
+				:offsetY="avatarDecoration.offsetY"
 				:active="true"
 				@click="openDecoration(avatarDecoration, i)"
 			/>
@@ -66,6 +68,8 @@ function openDecoration(avatarDecoration, index?: number) {
 				id: avatarDecoration.id,
 				angle: payload.angle,
 				flipH: payload.flipH,
+				offsetX: payload.offsetX,
+				offsetY: payload.offsetY,
 			};
 			const update = [...$i.avatarDecorations, decoration];
 			await os.apiWithDialog('i/update', {
@@ -78,6 +82,8 @@ function openDecoration(avatarDecoration, index?: number) {
 				id: avatarDecoration.id,
 				angle: payload.angle,
 				flipH: payload.flipH,
+				offsetX: payload.offsetX,
+				offsetY: payload.offsetY,
 			};
 			const update = [...$i.avatarDecorations];
 			update[index] = decoration;

From 7f2a66f262e5b378909c15f5a2c51befeffbf82a Mon Sep 17 00:00:00 2001
From: Gianni Ceccarelli <gceccarelli@veritone.com>
Date: Thu, 14 Dec 2023 13:58:07 +0000
Subject: [PATCH 232/435] allow a theme to specify a font - #225

---
 packages/backend/src/server/web/boot.js | 23 +++++++++++++++++-
 packages/frontend/src/scripts/theme.ts  | 31 +++++++++++++++++++++++++
 packages/frontend/src/style.scss        |  2 +-
 3 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index e1864ae124..e1aaee1d49 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -106,8 +106,29 @@
 
 	//#region Theme
 	const theme = localStorage.getItem('theme');
+	const themeFontFaceName = 'sharkey-theme-font-face';
 	if (theme) {
-		for (const [k, v] of Object.entries(JSON.parse(theme))) {
+		let existingFontFace;
+		document.fonts.forEach((v,k,s)=>{if (v.family === themeFontFaceName) existingFontFace=v;});
+		if (existingFontFace) document.fonts.delete(existingFontFace);
+
+		const themeProps = JSON.parse(theme);
+		const fontFaceSrc = themeProps.fontFaceSrc;
+		const fontFaceOpts = themeProps.fontFaceOpts || {};
+		if (fontFaceSrc) {
+			const fontFace = new FontFace(
+				themeFontFaceName,
+				fontFaceSrc, fontFaceOpts || {},
+			);
+			document.fonts.add(fontFace);
+			fontFace.load().catch(
+				(failure) => {
+					console.log(failure)
+				}
+			);
+		}
+		for (const [k, v] of Object.entries(themeProps)) {
+			if (k.startsWith('font')) continue;
 			document.documentElement.style.setProperty(`--${k}`, v.toString());
 
 			// HTMLの theme-color 適用
diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index d2474bf10f..8bfb03f0dd 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -54,6 +54,8 @@ export const getBuiltinThemesRef = () => {
 	return builtinThemes;
 };
 
+const themeFontFaceName = 'sharkey-theme-font-face';
+
 let timeout = null;
 
 export function applyTheme(theme: Theme, persist = true) {
@@ -84,7 +86,32 @@ export function applyTheme(theme: Theme, persist = true) {
 		}
 	}
 
+	let existingFontFace;
+	document.fonts.forEach(
+		(fontFace) => {
+			if (fontFace.family === themeFontFaceName) existingFontFace = fontFace;
+		},
+	);
+	if (existingFontFace) document.fonts.delete(existingFontFace);
+
+	const fontFaceSrc = props.fontFaceSrc;
+	const fontFaceOpts = props.fontFaceOpts || {};
+
+	if (fontFaceSrc) {
+		const fontFace = new FontFace(
+			themeFontFaceName,
+			fontFaceSrc, fontFaceOpts,
+		);
+		document.fonts.add(fontFace);
+		fontFace.load().catch(
+			(failure) => {
+				console.log(failure);
+			},
+		);
+	}
+
 	for (const [k, v] of Object.entries(props)) {
+		if (k.startsWith('font')) continue;
 		document.documentElement.style.setProperty(`--${k}`, v.toString());
 	}
 
@@ -128,6 +155,10 @@ function compile(theme: Theme): Record<string, string> {
 
 	for (const [k, v] of Object.entries(theme.props)) {
 		if (k.startsWith('$')) continue; // ignore const
+		if (k.startsWith('font')) { // font specs are different
+			props[k] = v;
+			continue;
+		}
 
 		props[k] = v.startsWith('"') ? v.replace(/^"\s*/, '') : genValue(getColor(v));
 	}
diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss
index 8c531fc2cc..b863f61cb4 100644
--- a/packages/frontend/src/style.scss
+++ b/packages/frontend/src/style.scss
@@ -62,7 +62,7 @@ html {
 	accent-color: var(--accent);
 	overflow: auto;
 	overflow-wrap: break-word;
-	font-family: 'Lexend', 'Hiragino Maru Gothic Pro', "BIZ UDGothic", Roboto, HelveticaNeue, Arial, sans-serif;
+	font-family: 'sharkey-theme-font-face', 'Lexend', 'Hiragino Maru Gothic Pro', "BIZ UDGothic", Roboto, HelveticaNeue, Arial, sans-serif;
 	font-size: 14px;
 	line-height: 1.35;
 	text-size-adjust: 100%;

From 5cc3d3c8733fca22fb3052b06f3fefcb998468ad Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Fri, 15 Dec 2023 11:22:49 +0900
Subject: [PATCH 233/435] Remove an unnecessary type assertion (#12666)

---
 packages/backend/src/server/ActivityPubServerService.ts | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 78d2daa403..380cdfc34c 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -493,8 +493,7 @@ export class ActivityPubServerService {
 
 	@bindThis
 	public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
-		// addConstraintStrategy の型定義がおかしいため
-		(fastify.addConstraintStrategy as any)({
+		fastify.addConstraintStrategy({
 			name: 'apOrHtml',
 			storage() {
 				const store = {} as any;

From bd4d8694dda63dfe8093e605bcb9184231818cad Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Fri, 15 Dec 2023 11:24:13 +0900
Subject: [PATCH 234/435] perf: early return users/notes and
 users/featured-notes if me is blocked by requesting user (#12663)

---
 .../src/server/api/endpoints/users/featured-notes.ts  | 11 ++++++++---
 .../backend/src/server/api/endpoints/users/notes.ts   |  8 ++++++++
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/users/featured-notes.ts b/packages/backend/src/server/api/endpoints/users/featured-notes.ts
index f84148d727..7243aa3b3e 100644
--- a/packages/backend/src/server/api/endpoints/users/featured-notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/featured-notes.ts
@@ -51,6 +51,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private cacheService: CacheService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const userIdsWhoBlockingMe = me ? await this.cacheService.userBlockedCache.fetch(me.id) : new Set<string>();
+
+			// early return if me is blocked by requesting user
+			if (userIdsWhoBlockingMe.has(ps.userId)) {
+				return [];
+			}
+
 			let noteIds = await this.featuredService.getPerUserNotesRanking(ps.userId, 50);
 
 			noteIds.sort((a, b) => a > b ? -1 : 1);
@@ -65,11 +72,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const [
 				userIdsWhoMeMuting,
-				userIdsWhoBlockingMe,
 			] = me ? await Promise.all([
 				this.cacheService.userMutingsCache.fetch(me.id),
-				this.cacheService.userBlockedCache.fetch(me.id),
-			]) : [new Set<string>(), new Set<string>()];
+			]) : [new Set<string>()];
 
 			const query = this.notesRepository.createQueryBuilder('note')
 				.where('note.id IN (:...noteIds)', { noteIds: noteIds })
diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts
index b32128a8aa..b485126ed8 100644
--- a/packages/backend/src/server/api/endpoints/users/notes.ts
+++ b/packages/backend/src/server/api/endpoints/users/notes.ts
@@ -86,6 +86,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			if (ps.withReplies && ps.withFiles) throw new ApiError(meta.errors.bothWithRepliesAndWithFiles);
 
+			// early return if me is blocked by requesting user
+			if (me != null) {
+				const userIdsWhoBlockingMe = await this.cacheService.userBlockedCache.fetch(me.id);
+				if (userIdsWhoBlockingMe.has(ps.userId)) {
+					return [];
+				}
+			}
+
 			if (!serverSettings.enableFanoutTimeline) {
 				const timeline = await this.getFromDb({
 					untilId,

From eacc2040a1a3ef1c28af5293a163289e7ec1958a Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 15 Dec 2023 15:37:19 +0900
Subject: [PATCH 235/435] perf(frontend): introduce MkLazy for lazy loading

---
 .../frontend/src/components/global/MkLazy.vue | 53 +++++++++++++++++++
 packages/frontend/src/components/index.ts     |  3 ++
 packages/frontend/src/pages/user/home.vue     | 12 +++--
 3 files changed, 65 insertions(+), 3 deletions(-)
 create mode 100644 packages/frontend/src/components/global/MkLazy.vue

diff --git a/packages/frontend/src/components/global/MkLazy.vue b/packages/frontend/src/components/global/MkLazy.vue
new file mode 100644
index 0000000000..6d7ff4ca49
--- /dev/null
+++ b/packages/frontend/src/components/global/MkLazy.vue
@@ -0,0 +1,53 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div ref="rootEl" :class="$style.root">
+	<div v-if="!showing" :class="$style.placeholder"></div>
+	<slot v-else></slot>
+</div>
+</template>
+
+<script lang="ts" setup>
+import { nextTick, onMounted, onActivated, onBeforeUnmount, ref, shallowRef } from 'vue';
+
+const rootEl = shallowRef<HTMLDivElement>();
+const showing = ref(false);
+
+const observer = new IntersectionObserver(
+	(entries) => {
+		if (entries.some((entry) => entry.isIntersecting)) {
+			showing.value = true;
+		}
+	},
+);
+
+onMounted(() => {
+	nextTick(() => {
+		observer.observe(rootEl.value!);
+	});
+});
+
+onActivated(() => {
+	nextTick(() => {
+		observer.observe(rootEl.value!);
+	});
+});
+
+onBeforeUnmount(() => {
+	observer.disconnect();
+});
+</script>
+
+<style lang="scss" module>
+.root {
+	display: block;
+}
+
+.placeholder {
+	display: block;
+	min-height: 150px;
+}
+</style>
diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts
index c740d181f9..a3e13c3a50 100644
--- a/packages/frontend/src/components/index.ts
+++ b/packages/frontend/src/components/index.ts
@@ -25,6 +25,7 @@ import MkPageHeader from './global/MkPageHeader.vue';
 import MkSpacer from './global/MkSpacer.vue';
 import MkFooterSpacer from './global/MkFooterSpacer.vue';
 import MkStickyContainer from './global/MkStickyContainer.vue';
+import MkLazy from './global/MkLazy.vue';
 
 export default function(app: App) {
 	for (const [key, value] of Object.entries(components)) {
@@ -53,6 +54,7 @@ export const components = {
 	MkSpacer: MkSpacer,
 	MkFooterSpacer: MkFooterSpacer,
 	MkStickyContainer: MkStickyContainer,
+	MkLazy: MkLazy,
 };
 
 declare module '@vue/runtime-core' {
@@ -77,5 +79,6 @@ declare module '@vue/runtime-core' {
 		MkSpacer: typeof MkSpacer;
 		MkFooterSpacer: typeof MkFooterSpacer;
 		MkStickyContainer: typeof MkStickyContainer;
+		MkLazy: typeof MkLazy;
 	}
 }
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index ecc3bb36c1..a0163bfc68 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -128,12 +128,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</div>
 				<MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo>
 				<template v-if="narrow">
-					<XFiles :key="user.id" :user="user"/>
-					<XActivity :key="user.id" :user="user"/>
+					<MkLazy>
+						<XFiles :key="user.id" :user="user"/>
+					</MkLazy>
+					<MkLazy>
+						<XActivity :key="user.id" :user="user"/>
+					</MkLazy>
 				</template>
 				<div v-if="!disableNotes">
 					<div style="margin-bottom: 8px;">{{ i18n.ts.featured }}</div>
-					<MkNotes :class="$style.tl" :noGap="true" :pagination="pagination"/>
+					<MkLazy>
+						<MkNotes :class="$style.tl" :noGap="true" :pagination="pagination"/>
+					</MkLazy>
 				</div>
 			</div>
 		</div>

From c41924399b1c0b06b451159644789fe00a29d7da Mon Sep 17 00:00:00 2001
From: 1STEP621 <86859447+1STEP621@users.noreply.github.com>
Date: Fri, 15 Dec 2023 17:18:31 +0900
Subject: [PATCH 236/435] =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E5=85=A5?=
 =?UTF-8?q?=E5=8A=9B=E3=83=9C=E3=83=83=E3=82=AF=E3=82=B9=E3=81=A7Tab?=
 =?UTF-8?q?=E3=82=92=E5=85=A5=E5=8A=9B=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=20(#12671)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkTextarea.vue        | 10 ++++++++++
 packages/frontend/src/pages/flash/flash-edit.vue       |  2 +-
 packages/frontend/src/pages/registry.value.vue         |  2 +-
 packages/frontend/src/pages/settings/custom-css.vue    |  2 +-
 .../frontend/src/pages/settings/plugin.install.vue     |  2 +-
 packages/frontend/src/pages/settings/theme.install.vue |  2 +-
 packages/frontend/src/pages/theme-editor.vue           |  2 +-
 7 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue
index 23fdd5bfe1..696787122e 100644
--- a/packages/frontend/src/components/MkTextarea.vue
+++ b/packages/frontend/src/components/MkTextarea.vue
@@ -91,6 +91,16 @@ const onKeydown = (ev: KeyboardEvent) => {
 	if (ev.code === 'Enter') {
 		emit('enter');
 	}
+
+	if (props.code && ev.key === 'Tab') {
+		const pos = inputEl.value?.selectionStart ?? 0;
+		const posEnd = inputEl.value?.selectionEnd ?? v.value.length;
+		v.value = v.value.slice(0, pos) + '\t' + v.value.slice(posEnd);
+		nextTick(() => {
+			inputEl.value?.setSelectionRange(pos + 1, pos + 1);
+		});
+		ev.preventDefault();
+	}
 };
 
 const updated = () => {
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index cfda4d6556..365b054f7a 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<template #label>{{ i18n.ts._play.summary }}</template>
 			</MkTextarea>
 			<MkButton primary @click="selectPreset">{{ i18n.ts.selectFromPresets }}<i class="ti ti-chevron-down"></i></MkButton>
-			<MkTextarea v-model="script" class="_monospace" tall spellcheck="false">
+			<MkTextarea v-model="script" code tall spellcheck="false">
 				<template #label>{{ i18n.ts._play.script }}</template>
 			</MkTextarea>
 			<div class="_buttons">
diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue
index 29406ec83c..1b2cf9f237 100644
--- a/packages/frontend/src/pages/registry.value.vue
+++ b/packages/frontend/src/pages/registry.value.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					</MkKeyValue>
 				</FormSplit>
 
-				<MkTextarea v-model="valueForEditor" tall class="_monospace">
+				<MkTextarea v-model="valueForEditor" tall code>
 					<template #label>{{ i18n.ts.value }} (JSON)</template>
 				</MkTextarea>
 
diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue
index e33e778246..8e52686c12 100644
--- a/packages/frontend/src/pages/settings/custom-css.vue
+++ b/packages/frontend/src/pages/settings/custom-css.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <div class="_gaps_m">
 	<FormInfo warn>{{ i18n.ts.customCssWarn }}</FormInfo>
 
-	<MkTextarea v-model="localCustomCss" manualSave tall class="_monospace" style="tab-size: 2;">
+	<MkTextarea v-model="localCustomCss" manualSave tall code style="tab-size: 2;">
 		<template #label>CSS</template>
 	</MkTextarea>
 </div>
diff --git a/packages/frontend/src/pages/settings/plugin.install.vue b/packages/frontend/src/pages/settings/plugin.install.vue
index f304d777a5..44472d47d9 100644
--- a/packages/frontend/src/pages/settings/plugin.install.vue
+++ b/packages/frontend/src/pages/settings/plugin.install.vue
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <div class="_gaps_m">
 	<FormInfo warn>{{ i18n.ts._plugin.installWarn }}</FormInfo>
 
-	<MkTextarea v-model="code" tall>
+	<MkTextarea v-model="code" tall code>
 		<template #label>{{ i18n.ts.code }}</template>
 	</MkTextarea>
 
diff --git a/packages/frontend/src/pages/settings/theme.install.vue b/packages/frontend/src/pages/settings/theme.install.vue
index c2ca53c743..0f3fbad0b7 100644
--- a/packages/frontend/src/pages/settings/theme.install.vue
+++ b/packages/frontend/src/pages/settings/theme.install.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div class="_gaps_m">
-	<MkTextarea v-model="installThemeCode">
+	<MkTextarea v-model="installThemeCode" code>
 		<template #label>{{ i18n.ts._theme.code }}</template>
 	</MkTextarea>
 
diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue
index 0d137137fc..c4138e23de 100644
--- a/packages/frontend/src/pages/theme-editor.vue
+++ b/packages/frontend/src/pages/theme-editor.vue
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<template #label>{{ i18n.ts.editCode }}</template>
 
 				<div class="_gaps_m">
-					<MkTextarea v-model="themeCode" tall>
+					<MkTextarea v-model="themeCode" tall code>
 						<template #label>{{ i18n.ts._theme.code }}</template>
 					</MkTextarea>
 					<MkButton primary @click="applyThemeCode">{{ i18n.ts.apply }}</MkButton>

From 272dc208b4c71ceb0f3d4308460577f3c4d41758 Mon Sep 17 00:00:00 2001
From: 1STEP621 <86859447+1STEP621@users.noreply.github.com>
Date: Fri, 15 Dec 2023 17:57:31 +0900
Subject: [PATCH 237/435] =?UTF-8?q?Fix(frontend):=20=E3=81=BB=E3=81=A8?=
 =?UTF-8?q?=E3=82=93=E3=81=A9=E3=81=AEMkTextarea=E3=81=A7MFM=E3=83=97?=
 =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC=E3=81=8C=E8=A1=A8=E7=A4=BA?=
 =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=A6=E3=81=97=E3=81=BE=E3=81=A3=E3=81=A6?=
 =?UTF-8?q?=E3=81=84=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#12672)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* ほとんどのMkTextareaでMFMのプレビューが表示されてしまっている不具合を修正

* refactor

* そもそも #12130 でプロフィールはnyaizeされない仕様にもどっていたらしいので修正
---
 packages/frontend/src/components/MkTextarea.vue  | 6 +++---
 packages/frontend/src/pages/settings/profile.vue | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue
index 696787122e..3ee374300a 100644
--- a/packages/frontend/src/components/MkTextarea.vue
+++ b/packages/frontend/src/components/MkTextarea.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:readonly="readonly"
 			:placeholder="placeholder"
 			:pattern="pattern"
-			:autocomplete="autocomplete"
+			:autocomplete="props.autocomplete"
 			:spellcheck="spellcheck"
 			@focus="focused = true"
 			@blur="focused = false"
@@ -26,8 +26,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 		></textarea>
 	</div>
 	<div :class="$style.caption"><slot name="caption"></slot></div>
-	<button style="font-size: 0.85em;" class="_textButton" type="button" @click="preview = !preview">{{ i18n.ts.preview }}</button>
-	<div v-show="preview" v-panel :class="$style.mfmPreview">
+	<button v-if="mfmPreview" style="font-size: 0.85em;" class="_textButton" type="button" @click="preview = !preview">{{ i18n.ts.preview }}</button>
+	<div v-if="mfmPreview" v-show="preview" v-panel :class="$style.mfmPreview">
 		<Mfm :text="v"/>
 	</div>
 
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index 1381042c39..83cd698e66 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<template #label>{{ i18n.ts._profile.name }}</template>
 	</MkInput>
 
-	<MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true" :nyaize="$i?.isCat ? 'respect' : undefined" :author="($i as Misskey.entities.UserLite)">
+	<MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true">
 		<template #label>{{ i18n.ts._profile.description }}</template>
 		<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
 	</MkTextarea>

From b5c319b2c716fd383b99f203b5d05106e21936a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 16 Dec 2023 08:56:26 +0900
Subject: [PATCH 238/435] =?UTF-8?q?fix(frontend):=20test=E3=81=8C=E8=90=BD?=
 =?UTF-8?q?=E3=81=A1=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1267?=
 =?UTF-8?q?9)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/package.json      | 5 +++--
 packages/frontend/test/home.test.ts | 5 +++--
 pnpm-lock.yaml                      | 7 +++++++
 3 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 4cf4779563..4d0139f252 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -26,8 +26,8 @@
 		"@tabler/icons-webfont": "2.44.0",
 		"@vitejs/plugin-vue": "4.5.2",
 		"@vue/compiler-sfc": "3.3.11",
-		"astring": "1.8.6",
 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
+		"astring": "1.8.6",
 		"broadcast-channel": "6.0.0",
 		"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
 		"buraha": "0.0.1",
@@ -56,8 +56,8 @@
 		"punycode": "2.3.1",
 		"rollup": "4.9.0",
 		"sanitize-html": "2.11.0",
-		"shiki": "0.14.6",
 		"sass": "1.69.5",
+		"shiki": "0.14.6",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
 		"three": "0.159.0",
@@ -116,6 +116,7 @@
 		"eslint-plugin-vue": "9.19.2",
 		"fast-glob": "3.3.2",
 		"happy-dom": "10.0.3",
+		"intersection-observer": "0.12.2",
 		"micromatch": "4.0.5",
 		"msw": "1.3.2",
 		"msw-storybook-addon": "1.10.0",
diff --git a/packages/frontend/test/home.test.ts b/packages/frontend/test/home.test.ts
index 6d38b7e526..094ea071b9 100644
--- a/packages/frontend/test/home.test.ts
+++ b/packages/frontend/test/home.test.ts
@@ -3,13 +3,14 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { describe, test, assert, afterEach } from 'vitest';
-import { render, cleanup, type RenderResult } from '@testing-library/vue';
+import { afterEach, assert, describe, test } from 'vitest';
+import { cleanup, render, type RenderResult } from '@testing-library/vue';
 import './init';
 import type * as Misskey from 'misskey-js';
 import { directives } from '@/directives/index.js';
 import { components } from '@/components/index.js';
 import XHome from '@/pages/user/home.vue';
+import 'intersection-observer';
 
 describe('XHome', () => {
 	const renderHome = (user: Partial<Misskey.entities.UserDetailed>): RenderResult => {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7c0bc470b0..5319acf932 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -944,6 +944,9 @@ importers:
       happy-dom:
         specifier: 10.0.3
         version: 10.0.3
+      intersection-observer:
+        specifier: 0.12.2
+        version: 0.12.2
       micromatch:
         specifier: 4.0.5
         version: 4.0.5
@@ -12996,6 +12999,10 @@ packages:
       side-channel: 1.0.4
     dev: true
 
+  /intersection-observer@0.12.2:
+    resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
+    dev: true
+
   /invariant@2.2.4:
     resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
     dependencies:

From 617ff00a45ccf54c7d61d2623d977f542b688451 Mon Sep 17 00:00:00 2001
From: FineArchs <133759614+FineArchs@users.noreply.github.com>
Date: Sat, 16 Dec 2023 08:57:45 +0900
Subject: [PATCH 239/435] =?UTF-8?q?Fix:=20AiScript=E3=81=AE`readline`?=
 =?UTF-8?q?=E3=81=8C=E4=B8=8D=E6=AD=A3=E3=81=AA=E5=80=A4=E3=82=92=E8=BF=94?=
 =?UTF-8?q?=E3=81=99=E3=81=93=E3=81=A8=E3=81=8C=E3=81=82=E3=82=8B=E5=95=8F?=
 =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12675)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix input bug

* Update CHANGELOG.md
---
 CHANGELOG.md                                | 1 +
 packages/frontend/src/pages/flash/flash.vue | 8 ++------
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 599bd463fd..bba587cbd3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -67,6 +67,7 @@
 - Fix: セキュリティ向上のためAiScriptの`Mk:apiExternal`を無効化
 - Fix: ノート中の絵文字をタップして「リアクションする」からリアクションした際にリアクションサウンドが鳴らない不具合を修正
 - Fix: ノート中のリアクションの表示を微調整 #12650
+- Fix: AiScriptの`readline`が不正な値を返すことがある問題を修正
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 0ac95ca282..8c5188a1e9 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -165,12 +165,8 @@ async function run() {
 			return new Promise(ok => {
 				os.inputText({
 					title: q,
-				}).then(({ canceled, result: a }) => {
-					if (canceled) {
-						ok('');
-					} else {
-						ok(a);
-					}
+				}).then(({ result: a }) => {
+					ok(a ?? '');
 				});
 			});
 		},

From 1260e8b74b2e83eb70a650bfe07ce45e8ee95b62 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Sat, 16 Dec 2023 08:58:03 +0900
Subject: [PATCH 240/435] chore(deps): bump actions/upload-artifact from 2 to 4
 (#12670)

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v2...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/get-api-diff.yml    | 4 ++--
 .github/workflows/report-api-diff.yml | 2 +-
 .github/workflows/test-frontend.yml   | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index 75a458424e..9dc812061b 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -54,7 +54,7 @@ jobs:
     - name: Copy API.json
       run: cp packages/backend/built/api.json ${{ matrix.api-json-name }}
     - name: Upload Artifact
-      uses: actions/upload-artifact@v3
+      uses: actions/upload-artifact@v4
       with:
         name: api-artifact
         path: ${{ matrix.api-json-name }}
@@ -67,7 +67,7 @@ jobs:
           PR_NUMBER: ${{ github.event.number }}
         run: |
           echo "$PR_NUMBER" > ./pr_number
-      - uses: actions/upload-artifact@v3
+      - uses: actions/upload-artifact@v4
         with:
           name: api-artifact
           path: pr_number
diff --git a/.github/workflows/report-api-diff.yml b/.github/workflows/report-api-diff.yml
index 2868d6cc09..309516772f 100644
--- a/.github/workflows/report-api-diff.yml
+++ b/.github/workflows/report-api-diff.yml
@@ -56,7 +56,7 @@ jobs:
       - name: Echo full diff
         run: cat ./api-full.json.diff
       - name: Upload full diff to Artifact
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: api-artifact
           path: |
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index 50c225189d..e5c461e6d1 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -108,12 +108,12 @@ jobs:
         wait-on: 'http://localhost:61812'
         headed: true
         browser: ${{ matrix.browser }}
-    - uses: actions/upload-artifact@v2
+    - uses: actions/upload-artifact@v4
       if: failure()
       with:
         name: ${{ matrix.browser }}-cypress-screenshots
         path: cypress/screenshots
-    - uses: actions/upload-artifact@v2
+    - uses: actions/upload-artifact@v4
       if: always()
       with:
         name: ${{ matrix.browser }}-cypress-videos

From 3e256eee2cc8a0a7b93084b82bad61e0af9d6689 Mon Sep 17 00:00:00 2001
From: shiosyakeyakini <blueskis382@gmail.com>
Date: Sat, 16 Dec 2023 09:00:32 +0900
Subject: [PATCH 241/435] =?UTF-8?q?Fix(backend):=20JSONSchema=E3=81=AB?=
 =?UTF-8?q?=E4=B8=8D=E8=B6=B3=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E3=83=91?=
 =?UTF-8?q?=E3=83=A9=E3=83=A1=E3=83=BC=E3=82=BF=E3=82=92=E8=BF=BD=E5=8A=A0?=
 =?UTF-8?q?=20(#12680)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Fix(backend): JSONSchemaに不足しているパラメータを追加

* nullable:falseに修正

---------

Co-authored-by: sorairo <sorairo@shiosyakeyakini.info>
---
 packages/backend/src/models/json-schema/user.ts | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index c6b96b85f0..7a3ca58269 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -74,6 +74,14 @@ export const packedUserLiteSchema = {
 						format: 'url',
 						nullable: false, optional: false,
 					},
+					offsetX: {
+						type: 'number',
+						nullable: false, optional: true,
+					},
+					offsetY: {
+						type: 'number',
+						nullable: false, optional: true,
+					},
 				},
 			},
 		},

From 9f49b9f4d2081effeccde27ed34a65bfc0f11c28 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 16 Dec 2023 10:58:44 +0900
Subject: [PATCH 242/435] =?UTF-8?q?fix(backend):=20HTTP=20Digest=E3=83=98?=
 =?UTF-8?q?=E3=83=83=E3=83=80=E3=81=AE=E3=82=A2=E3=83=AB=E3=82=B4=E3=83=AA?=
 =?UTF-8?q?=E3=82=BA=E3=83=A0=E9=83=A8=E5=88=86=E3=81=AB=E5=A4=A7=E6=96=87?=
 =?UTF-8?q?=E5=AD=97=E3=81=AE"SHA-256"=E3=81=97=E3=81=8B=E4=BD=BF=E3=81=88?=
 =?UTF-8?q?=E3=81=AA=E3=81=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix #12678
---
 CHANGELOG.md                                            | 1 +
 packages/backend/src/server/ActivityPubServerService.ts | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bba587cbd3..ee8cbb69bc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -86,6 +86,7 @@
 - Fix: 「みつける」が年越し時に壊れる問題を修正
 - Fix: アカウントをブロックした際に、自身のユーザーのページでノートが相手に表示される問題を修正
 - Fix: モデレーションログがモデレーターは閲覧できないように修正
+- Fix: HTTP Digestヘッダのアルゴリズム部分に大文字の"SHA-256"しか使えない
 
 ## 2023.11.1
 
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 380cdfc34c..2bc7e1136a 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -138,7 +138,7 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			const algo = match[1];
+			const algo = match[1].toUpperCase();
 			const digestValue = match[2];
 
 			if (algo !== 'SHA-256') {

From 390602837bf85a01e0c2f91deb3bab6770a87f0d Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 16 Dec 2023 11:56:58 +0900
Subject: [PATCH 243/435] enhance(frontend): tweak user home page

---
 packages/frontend/src/pages/user/home.vue     | 12 +----
 .../src/pages/user/index.timeline.vue         | 48 ++++++++++---------
 packages/frontend/src/pages/user/index.vue    |  4 +-
 3 files changed, 31 insertions(+), 33 deletions(-)

diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index a0163bfc68..a87e03e761 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -136,9 +136,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 					</MkLazy>
 				</template>
 				<div v-if="!disableNotes">
-					<div style="margin-bottom: 8px;">{{ i18n.ts.featured }}</div>
 					<MkLazy>
-						<MkNotes :class="$style.tl" :noGap="true" :pagination="pagination"/>
+						<XTimeline :user="user"/>
 					</MkLazy>
 				</div>
 			</div>
@@ -193,6 +192,7 @@ function calcAge(birthdate: string): number {
 
 const XFiles = defineAsyncComponent(() => import('./index.files.vue'));
 const XActivity = defineAsyncComponent(() => import('./index.activity.vue'));
+const XTimeline = defineAsyncComponent(() => import('./index.timeline.vue'));
 
 const props = withDefaults(defineProps<{
 	user: Misskey.entities.UserDetailed;
@@ -219,14 +219,6 @@ watch(moderationNote, async () => {
 	await os.api('admin/update-user-note', { userId: props.user.id, text: moderationNote.value });
 });
 
-const pagination = {
-	endpoint: 'users/featured-notes' as const,
-	limit: 10,
-	params: computed(() => ({
-		userId: props.user.id,
-	})),
-};
-
 const style = computed(() => {
 	if (props.user.bannerUrl == null) return {};
 	return {
diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue
index 6cf5bcf91f..e5a0f49e3d 100644
--- a/packages/frontend/src/pages/user/index.timeline.vue
+++ b/packages/frontend/src/pages/user/index.timeline.vue
@@ -4,18 +4,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkSpacer :contentMax="800" style="padding-top: 0">
-	<MkStickyContainer>
-		<template #header>
-			<MkTab v-model="include" :class="$style.tab">
-				<option :value="null">{{ i18n.ts.notes }}</option>
-				<option value="all">{{ i18n.ts.all }}</option>
-				<option value="files">{{ i18n.ts.withFiles }}</option>
-			</MkTab>
-		</template>
-		<MkNotes :noGap="true" :pagination="pagination" :class="$style.tl"/>
-	</MkStickyContainer>
-</MkSpacer>
+<MkStickyContainer>
+	<template #header>
+		<MkTab v-model="tab" :class="$style.tab">
+			<option value="featured">{{ i18n.ts.featured }}</option>
+			<option :value="null">{{ i18n.ts.notes }}</option>
+			<option value="all">{{ i18n.ts.all }}</option>
+			<option value="files">{{ i18n.ts.withFiles }}</option>
+		</MkTab>
+	</template>
+	<MkNotes :noGap="true" :pagination="pagination" :class="$style.tl"/>
+</MkStickyContainer>
 </template>
 
 <script lang="ts" setup>
@@ -29,24 +28,29 @@ const props = defineProps<{
 	user: Misskey.entities.UserDetailed;
 }>();
 
-const include = ref<string | null>('all');
+const tab = ref<string | null>('all');
 
-const pagination = {
+const pagination = computed(() => tab.value === 'featured' ? {
+	endpoint: 'users/featured-notes' as const,
+	limit: 10,
+	params: {
+		userId: props.user.id,
+	},
+} : {
 	endpoint: 'users/notes' as const,
 	limit: 10,
-	params: computed(() => ({
+	params: {
 		userId: props.user.id,
-		withRenotes: include.value === 'all',
-		withReplies: include.value === 'all',
-		withChannelNotes: include.value === 'all',
-		withFiles: include.value === 'files',
-	})),
-};
+		withRenotes: tab.value === 'all',
+		withReplies: tab.value === 'all',
+		withChannelNotes: tab.value === 'all',
+		withFiles: tab.value === 'files',
+	},
+});
 </script>
 
 <style lang="scss" module>
 .tab {
-	margin: calc(var(--margin) / 2) 0;
 	padding: calc(var(--margin) / 2) 0;
 	background: var(--bg);
 }
diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue
index 4725aedeee..4efa834d14 100644
--- a/packages/frontend/src/pages/user/index.vue
+++ b/packages/frontend/src/pages/user/index.vue
@@ -9,7 +9,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<div>
 		<div v-if="user">
 			<XHome v-if="tab === 'home'" :user="user"/>
-			<XTimeline v-else-if="tab === 'notes'" :user="user"/>
+			<MkSpacer v-else-if="tab === 'notes'" :contentMax="800" style="padding-top: 0">
+				<XTimeline :user="user"/>
+			</MkSpacer>
 			<XActivity v-else-if="tab === 'activity'" :user="user"/>
 			<XAchievements v-else-if="tab === 'achievements'" :user="user"/>
 			<XReactions v-else-if="tab === 'reactions'" :user="user"/>

From b1a7dcb05b9fe2c4eed2c6ea68aff5ef4282b332 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, 16 Dec 2023 12:34:35 +0900
Subject: [PATCH 244/435] =?UTF-8?q?enhance(frontend):=20=E3=82=B3=E3=83=BC?=
 =?UTF-8?q?=E3=83=89=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF=E3=81=AE=E3=83=8F?=
 =?UTF-8?q?=E3=82=A4=E3=83=A9=E3=82=A4=E3=83=88=E6=A9=9F=E8=83=BD=E3=82=92?=
 =?UTF-8?q?=E5=88=A9=E7=94=A8=E3=81=99=E3=82=8B=E3=81=AB=E3=81=AF=E8=A8=80?=
 =?UTF-8?q?=E8=AA=9E=E3=82=92=E6=98=8E=E7=A4=BA=E7=9A=84=E3=81=AB=E6=8C=87?=
 =?UTF-8?q?=E5=AE=9A=E3=81=95=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=20(#12681)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (enhance) コードブロックのハイライトを使用するには言語指定を求める

* Update changelog

* fix

* typo
---
 CHANGELOG.md                                | 10 +++++++---
 packages/frontend/src/components/MkCode.vue | 18 +++++++++++++++++-
 packages/frontend/src/index.html            |  2 +-
 3 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee8cbb69bc..14acf9c58e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -38,6 +38,8 @@
 - Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加
 - Feat: データセーバーでコードハイライトの読み込みを削減できるように
 - Feat: MFMのアニメーション要素(`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)に `delay` オプションを追加
+- Feat: センシティブと判断されたウェブサイトのサムネイルをぼかすように
+  - ウェブサイトをセンシティブと判断する仕組みが動いていないため、summalyProxyを使用しないと機能しません。
 - Enhance: 投稿フォームの絵文字ピッカーをリアクション時に使用するものと同じのを使用するように #12336 #12560
 - Enhance: リアクション用ピン留め絵文字と投稿時の絵文字入力用ピン留め絵文字を分けて設定できるように #12560
 - Enhance: 絵文字のオートコンプリート機能強化 #12364
@@ -53,11 +55,13 @@
 - Enhance: タイムライン上のタブからリスト、アンテナ、チャンネルの管理ページにジャンプできるように
 - Enhance: ユーザー名、プロフィール、お知らせ、ページの編集画面でMFMや絵文字のオートコンプリートが使用できるように
 - Enhance: プロフィール、お知らせの編集画面でMFMのプレビューを表示できるように
-- Feat: センシティブと判断されたウェブサイトのサムネイルをぼかすように
-  - ウェブサイトをセンシティブと判断する仕組みが動いていないため、summalyProxyを使用しないと機能しません。
+- Enhance: 絵文字の詳細ページに記載される情報を追加
+- Enhance: コードブロックのハイライト機能を利用するには言語を明示的に指定させるように
+	- MFMでコードブロックを利用する際に意図しないハイライトが起こらないようになりました
+	- 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります  
+	(例: ` ```js ` → Javascript, ` ```ais ` → AiScript)
 - fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
-- Enhance: 絵文字の詳細ページに記載される情報を追加
 - Fix: コードエディタが正しく表示されない問題を修正
 - Fix: プロフィールの「ファイル」にセンシティブな画像がある際のデザインを修正
 - Fix: 一度に大量の通知が入った際に通知音が音割れする問題を修正
diff --git a/packages/frontend/src/components/MkCode.vue b/packages/frontend/src/components/MkCode.vue
index cb0eef0549..2c016e4d7c 100644
--- a/packages/frontend/src/components/MkCode.vue
+++ b/packages/frontend/src/components/MkCode.vue
@@ -9,7 +9,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<MkLoading v-if="!inline ?? true"/>
 	</template>
 	<code v-if="inline" :class="$style.codeInlineRoot">{{ code }}</code>
-	<XCode v-else-if="show" :code="code" :lang="lang"/>
+	<XCode v-else-if="show && lang" :code="code" :lang="lang"/>
+	<pre v-else-if="show" :class="$style.codeBlockFallbackRoot"><code :class="$style.codeBlockFallbackCode">{{ code }}</code></pre>
 	<button v-else :class="$style.codePlaceholderRoot" @click="show = true">
 		<div :class="$style.codePlaceholderContainer">
 			<div><i class="ti ti-code"></i> {{ i18n.ts.code }}</div>
@@ -47,6 +48,21 @@ const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue'))
 	border-radius: .3em;
 }
 
+.codeBlockFallbackRoot {
+	display: block;
+	overflow-wrap: anywhere;
+	color: #D4D4D4;
+	background: #1E1E1E;
+	padding: 1em;
+	margin: .5em 0;
+	overflow: auto;
+	border-radius: 8px;
+}
+
+.codeBlockFallbackCode {
+	font-family: Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace;
+}
+
 .codePlaceholderRoot {
 	display: block;
 	width: 100%;
diff --git a/packages/frontend/src/index.html b/packages/frontend/src/index.html
index 49189914a0..558856690d 100644
--- a/packages/frontend/src/index.html
+++ b/packages/frontend/src/index.html
@@ -18,7 +18,7 @@
 		http-equiv="Content-Security-Policy"
 		content="default-src 'self';
 			worker-src 'self';
-			script-src 'self';
+			script-src 'self' 'unsafe-eval';
 			style-src 'self' 'unsafe-inline';
 			img-src 'self' data: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
 			media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;"

From 17065418cf9fd67b8172f0085bc0c2ae56d547dc 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, 16 Dec 2023 13:18:12 +0900
Subject: [PATCH 245/435] =?UTF-8?q?(enhance)=20=E3=82=B3=E3=83=BC=E3=83=89?=
 =?UTF-8?q?=E5=85=A5=E5=8A=9B=E3=82=92MkCodeEditor=E3=81=AB=E5=A4=89?=
 =?UTF-8?q?=E6=9B=B4=20(#12682)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../frontend/src/components/MkCodeEditor.vue  | 88 ++++++++++++++-----
 .../frontend/src/pages/flash/flash-edit.vue   |  5 +-
 .../frontend/src/pages/registry.value.vue     |  6 +-
 .../src/pages/settings/custom-css.vue         |  6 +-
 .../src/pages/settings/plugin.install.vue     |  6 +-
 .../src/pages/settings/theme.install.vue      |  6 +-
 packages/frontend/src/pages/theme-editor.vue  |  5 +-
 7 files changed, 85 insertions(+), 37 deletions(-)

diff --git a/packages/frontend/src/components/MkCodeEditor.vue b/packages/frontend/src/components/MkCodeEditor.vue
index 60f16f285f..6341c454ae 100644
--- a/packages/frontend/src/components/MkCodeEditor.vue
+++ b/packages/frontend/src/components/MkCodeEditor.vue
@@ -4,30 +4,38 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div :class="[$style.codeEditorRoot, { [$style.focused]: focused }]">
-	<div :class="$style.codeEditorScroller">
-		<textarea
-			ref="inputEl"
-			v-model="vModel"
-			:class="[$style.textarea]"
-			:disabled="disabled"
-			:required="required"
-			:readonly="readonly"
-			autocomplete="off"
-			wrap="off"
-			spellcheck="false"
-			@focus="focused = true"
-			@blur="focused = false"
-			@keydown="onKeydown($event)"
-			@input="onInput"
-		></textarea>
-		<XCode :class="$style.codeEditorHighlighter" :codeEditor="true" :code="v" :lang="lang"/>
+<div>
+	<div :class="$style.label" @click="focus"><slot name="label"></slot></div>
+	<div :class="[$style.codeEditorRoot, { [$style.focused]: focused }]">
+		<div :class="$style.codeEditorScroller">
+			<textarea
+				ref="inputEl"
+				v-model="vModel"
+				:class="[$style.textarea]"
+				:disabled="disabled"
+				:required="required"
+				:readonly="readonly"
+				autocomplete="off"
+				wrap="off"
+				spellcheck="false"
+				@focus="focused = true"
+				@blur="focused = false"
+				@keydown="onKeydown($event)"
+				@input="onInput"
+			></textarea>
+			<XCode :class="$style.codeEditorHighlighter" :codeEditor="true" :code="v" :lang="lang"/>
+		</div>
 	</div>
+	<div :class="$style.caption"><slot name="caption"></slot></div>
+	<MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
 </div>
 </template>
 
 <script lang="ts" setup>
 import { ref, watch, toRefs, shallowRef, nextTick } from 'vue';
+import { debounce } from 'throttle-debounce';
+import MkButton from '@/components/MkButton.vue';
+import { i18n } from '@/i18n.js';
 import XCode from '@/components/MkCode.core.vue';
 
 const props = withDefaults(defineProps<{
@@ -36,6 +44,8 @@ const props = withDefaults(defineProps<{
 	required?: boolean;
 	readonly?: boolean;
 	disabled?: boolean;
+	debounce?: boolean;
+	manualSave?: boolean;
 }>(), {
 	lang: 'js',
 });
@@ -54,6 +64,8 @@ const focused = ref(false);
 const changed = ref(false);
 const inputEl = shallowRef<HTMLTextAreaElement>();
 
+const focus = () => inputEl.value?.focus();
+
 const onInput = (ev) => {
 	v.value = ev.target?.value ?? v.value;
 	changed.value = true;
@@ -100,16 +112,48 @@ const updated = () => {
 	emit('update:modelValue', v.value);
 };
 
+const debouncedUpdated = debounce(1000, updated);
+
 watch(modelValue, newValue => {
 	v.value = newValue ?? '';
 });
 
-watch(v, () => {
-	updated();
+watch(v, newValue => {
+	if (!props.manualSave) {
+		if (props.debounce) {
+			debouncedUpdated();
+		} else {
+			updated();
+		}
+	}
 });
 </script>
 
 <style lang="scss" module>
+.label {
+	font-size: 0.85em;
+	padding: 0 0 8px 0;
+	user-select: none;
+
+	&:empty {
+		display: none;
+	}
+}
+
+.caption {
+	font-size: 0.85em;
+	padding: 8px 0 0 0;
+	color: var(--fgTransparentWeak);
+
+	&:empty {
+		display: none;
+	}
+}
+
+.save {
+	margin: 8px 0 0 0;
+}
+
 .codeEditorRoot {
 	min-width: 100%;
 	max-width: 100%;
@@ -117,6 +161,7 @@ watch(v, () => {
 	overflow-y: hidden;
 	box-sizing: border-box;
 	margin: 0;
+	border-radius: 6px;
 	padding: 0;
 	color: var(--fg);
 	border: solid 1px var(--panel);
@@ -157,9 +202,10 @@ watch(v, () => {
 	caret-color: rgb(225, 228, 232);
 	background-color: transparent;
 	border: 0;
+	border-radius: 6px;
 	outline: 0;
 	min-width: calc(100% - 24px);
-	height: calc(100% - 24px);
+	height: 100%;
 	padding: 12px;
 	line-height: 1.5em;
 	font-size: 1em;
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index 365b054f7a..67a655b677 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -15,9 +15,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<template #label>{{ i18n.ts._play.summary }}</template>
 			</MkTextarea>
 			<MkButton primary @click="selectPreset">{{ i18n.ts.selectFromPresets }}<i class="ti ti-chevron-down"></i></MkButton>
-			<MkTextarea v-model="script" code tall spellcheck="false">
+			<MkCodeEditor v-model="script" lang="is">
 				<template #label>{{ i18n.ts._play.script }}</template>
-			</MkTextarea>
+			</MkCodeEditor>
 			<div class="_buttons">
 				<MkButton primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
 				<MkButton @click="show"><i class="ti ti-eye"></i> {{ i18n.ts.show }}</MkButton>
@@ -40,6 +40,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkTextarea from '@/components/MkTextarea.vue';
+import MkCodeEditor from '@/components/MkCodeEditor.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import { useRouter } from '@/router.js';
diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue
index 1b2cf9f237..8efc0e0504 100644
--- a/packages/frontend/src/pages/registry.value.vue
+++ b/packages/frontend/src/pages/registry.value.vue
@@ -26,9 +26,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 					</MkKeyValue>
 				</FormSplit>
 
-				<MkTextarea v-model="valueForEditor" tall code>
+				<MkCodeEditor v-model="valueForEditor" lang="json5">
 					<template #label>{{ i18n.ts.value }} (JSON)</template>
-				</MkTextarea>
+				</MkCodeEditor>
 
 				<MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
 
@@ -52,7 +52,7 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
-import MkTextarea from '@/components/MkTextarea.vue';
+import MkCodeEditor from '@/components/MkCodeEditor.vue';
 import FormSplit from '@/components/form/split.vue';
 import FormInfo from '@/components/MkInfo.vue';
 
diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue
index 8e52686c12..2564562089 100644
--- a/packages/frontend/src/pages/settings/custom-css.vue
+++ b/packages/frontend/src/pages/settings/custom-css.vue
@@ -7,15 +7,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 <div class="_gaps_m">
 	<FormInfo warn>{{ i18n.ts.customCssWarn }}</FormInfo>
 
-	<MkTextarea v-model="localCustomCss" manualSave tall code style="tab-size: 2;">
+	<MkCodeEditor v-model="localCustomCss" manualSave lang="css">
 		<template #label>CSS</template>
-	</MkTextarea>
+	</MkCodeEditor>
 </div>
 </template>
 
 <script lang="ts" setup>
 import { ref, watch, computed } from 'vue';
-import MkTextarea from '@/components/MkTextarea.vue';
+import MkCodeEditor from '@/components/MkCodeEditor.vue';
 import FormInfo from '@/components/MkInfo.vue';
 import * as os from '@/os.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
diff --git a/packages/frontend/src/pages/settings/plugin.install.vue b/packages/frontend/src/pages/settings/plugin.install.vue
index 44472d47d9..c174ba176f 100644
--- a/packages/frontend/src/pages/settings/plugin.install.vue
+++ b/packages/frontend/src/pages/settings/plugin.install.vue
@@ -7,9 +7,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <div class="_gaps_m">
 	<FormInfo warn>{{ i18n.ts._plugin.installWarn }}</FormInfo>
 
-	<MkTextarea v-model="code" tall code>
+	<MkCodeEditor v-model="code" lang="is">
 		<template #label>{{ i18n.ts.code }}</template>
-	</MkTextarea>
+	</MkCodeEditor>
 
 	<div>
 		<MkButton :disabled="code == null" primary inline @click="install"><i class="ti ti-check"></i> {{ i18n.ts.install }}</MkButton>
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { nextTick, ref, computed } from 'vue';
-import MkTextarea from '@/components/MkTextarea.vue';
+import MkCodeEditor from '@/components/MkCodeEditor.vue';
 import MkButton from '@/components/MkButton.vue';
 import FormInfo from '@/components/MkInfo.vue';
 import * as os from '@/os.js';
diff --git a/packages/frontend/src/pages/settings/theme.install.vue b/packages/frontend/src/pages/settings/theme.install.vue
index 0f3fbad0b7..f9be5720e0 100644
--- a/packages/frontend/src/pages/settings/theme.install.vue
+++ b/packages/frontend/src/pages/settings/theme.install.vue
@@ -5,9 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div class="_gaps_m">
-	<MkTextarea v-model="installThemeCode" code>
+	<MkCodeEditor v-model="installThemeCode" lang="json5">
 		<template #label>{{ i18n.ts._theme.code }}</template>
-	</MkTextarea>
+	</MkCodeEditor>
 
 	<div class="_buttons">
 		<MkButton :disabled="installThemeCode == null" inline @click="() => previewTheme(installThemeCode)"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton>
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, computed } from 'vue';
-import MkTextarea from '@/components/MkTextarea.vue';
+import MkCodeEditor from '@/components/MkCodeEditor.vue';
 import MkButton from '@/components/MkButton.vue';
 import { parseThemeCode, previewTheme, installTheme } from '@/scripts/install-theme.js';
 import * as os from '@/os.js';
diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue
index c4138e23de..eee3e49db1 100644
--- a/packages/frontend/src/pages/theme-editor.vue
+++ b/packages/frontend/src/pages/theme-editor.vue
@@ -51,9 +51,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<template #label>{{ i18n.ts.editCode }}</template>
 
 				<div class="_gaps_m">
-					<MkTextarea v-model="themeCode" tall code>
+					<MkCodeEditor v-model="themeCode" lang="json5">
 						<template #label>{{ i18n.ts._theme.code }}</template>
-					</MkTextarea>
+					</MkCodeEditor>
 					<MkButton primary @click="applyThemeCode">{{ i18n.ts.apply }}</MkButton>
 				</div>
 			</MkFolder>
@@ -80,6 +80,7 @@ import { v4 as uuid } from 'uuid';
 import JSON5 from 'json5';
 
 import MkButton from '@/components/MkButton.vue';
+import MkCodeEditor from '@/components/MkCodeEditor.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkFolder from '@/components/MkFolder.vue';
 

From fda5147d063b7c9ec59d4c347442f8dd2b4b48c5 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, 16 Dec 2023 13:23:25 +0900
Subject: [PATCH 246/435] =?UTF-8?q?fix(dev):=20=E3=83=95=E3=82=A1=E3=82=A4?=
 =?UTF-8?q?=E3=83=AB=E3=81=AE=E8=AA=AD=E3=81=BF=E8=BE=BC=E3=81=BF=E3=81=AB?=
 =?UTF-8?q?=E5=A4=B1=E6=95=97=E3=81=99=E3=82=8B=E3=81=93=E3=81=A8=E3=81=8C?=
 =?UTF-8?q?=E3=81=82=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1268?=
 =?UTF-8?q?4)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* (enhance) コード入力をMkCodeEditorに変更

* (fix) devでファイルの読み込みに失敗することがある

* Revert "(enhance) コード入力をMkCodeEditorに変更"

This reverts commit 726d56c3e962680efc5b5a166e2210d09730341f.
---
 packages/backend/src/server/FileServerService.ts | 3 +++
 packages/frontend/src/index.html                 | 5 +++--
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts
index 11721263d3..0c7fc8cefe 100644
--- a/packages/backend/src/server/FileServerService.ts
+++ b/packages/backend/src/server/FileServerService.ts
@@ -61,6 +61,9 @@ export class FileServerService {
 	public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
 		fastify.addHook('onRequest', (request, reply, done) => {
 			reply.header('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\'');
+			if (process.env.NODE_ENV === 'development') {
+				reply.header('Access-Control-Allow-Origin', '*');
+			}
 			done();
 		});
 
diff --git a/packages/frontend/src/index.html b/packages/frontend/src/index.html
index 558856690d..8de01e4802 100644
--- a/packages/frontend/src/index.html
+++ b/packages/frontend/src/index.html
@@ -12,7 +12,7 @@
 <html>
 <head>
 	<meta charset="UTF-8" />
-	<title>misskey</title>
+	<title>[DEV] Loading...</title>
 	<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
 	<meta
 		http-equiv="Content-Security-Policy"
@@ -21,7 +21,8 @@
 			script-src 'self' 'unsafe-eval';
 			style-src 'self' 'unsafe-inline';
 			img-src 'self' data: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
-			media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;"
+			media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
+			connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;"
 	/>
 	<meta property="og:site_name" content="[DEV BUILD] Misskey" />
 	<meta name="viewport" content="width=device-width, initial-scale=1">

From d7d9304a4992c356e24f0be2310cf39c47194191 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 16 Dec 2023 13:23:54 +0900
Subject: [PATCH 247/435] =?UTF-8?q?fix(frontend):=20=E7=B5=B5=E6=96=87?=
 =?UTF-8?q?=E5=AD=97=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC=E8=A8=AD=E5=AE=9A?=
 =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=AE=E3=83=9C=E3=82=BF=E3=83=B3?=
 =?UTF-8?q?=E6=96=87=E8=A8=80=E4=BF=AE=E6=AD=A3=20(#12676)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(frontend): 絵文字ピッカー設定ページのボタン文言修正

* fix
---
 locales/index.d.ts                                    | 4 ++--
 locales/ja-JP.yml                                     | 4 ++--
 packages/frontend/src/pages/settings/emoji-picker.vue | 8 ++++----
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index 48c3daf05a..ae89f883fb 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -128,8 +128,8 @@ export interface Locale {
     "pinnedEmojisForReactionSettingDescription": string;
     "pinnedEmojisSettingDescription": string;
     "emojiPickerDisplay": string;
-    "copyFromPinnedEmojisForReaction": string;
-    "copyFromPinnedEmojis": string;
+    "overwriteFromPinnedEmojisForReaction": string;
+    "overwriteFromPinnedEmojis": string;
     "reactionSettingDescription2": string;
     "rememberNoteVisibility": string;
     "attachCancel": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index daa6a32032..922ce4fe31 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -125,8 +125,8 @@ emojiPicker: "絵文字ピッカー"
 pinnedEmojisForReactionSettingDescription: "リアクション時にピン留め表示する絵文字を設定できます"
 pinnedEmojisSettingDescription: "絵文字入力時にピン留め表示する絵文字を設定できます"
 emojiPickerDisplay: "ピッカーの表示"
-copyFromPinnedEmojisForReaction: "リアクション設定からコピーする"
-copyFromPinnedEmojis: "絵文字設定からコピーする"
+overwriteFromPinnedEmojisForReaction: "リアクション設定から上書きする"
+overwriteFromPinnedEmojis: "全般設定から上書きする"
 reactionSettingDescription2: "ドラッグして並び替え、クリックして削除、+を押して追加します。"
 rememberNoteVisibility: "公開範囲を記憶する"
 attachCancel: "添付取り消し"
diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue
index 7e7bf9bd1f..61f3332122 100644
--- a/packages/frontend/src/pages/settings/emoji-picker.vue
+++ b/packages/frontend/src/pages/settings/emoji-picker.vue
@@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div class="_buttons">
 				<MkButton inline @click="previewReaction"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton>
 				<MkButton inline danger @click="setDefaultReaction"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton>
-				<MkButton inline danger @click="copyFromPinnedEmojis"><i class="ti ti-copy"></i> {{ i18n.ts.copyFromPinnedEmojis }}</MkButton>
+				<MkButton inline danger @click="overwriteFromPinnedEmojis"><i class="ti ti-copy"></i> {{ i18n.ts.overwriteFromPinnedEmojis }}</MkButton>
 			</div>
 		</div>
 	</MkFolder>
@@ -80,7 +80,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div class="_buttons">
 				<MkButton inline @click="previewEmoji"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton>
 				<MkButton inline danger @click="setDefaultEmoji"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton>
-				<MkButton inline danger @click="copyFromPinnedEmojisForReaction"><i class="ti ti-copy"></i> {{ i18n.ts.copyFromPinnedEmojisForReaction }}</MkButton>
+				<MkButton inline danger @click="overwriteFromPinnedEmojisForReaction"><i class="ti ti-copy"></i> {{ i18n.ts.overwriteFromPinnedEmojisForReaction }}</MkButton>
 			</div>
 		</div>
 	</MkFolder>
@@ -164,7 +164,7 @@ function previewEmoji(ev: MouseEvent) {
 	emojiPicker.show(getHTMLElement(ev));
 }
 
-async function copyFromPinnedEmojis() {
+async function overwriteFromPinnedEmojis() {
 	const { canceled } = await os.confirm({
 		type: 'warning',
 		text: i18n.ts.overwriteContentConfirm,
@@ -177,7 +177,7 @@ async function copyFromPinnedEmojis() {
 	pinnedEmojisForReaction.value = [...pinnedEmojis.value];
 }
 
-async function copyFromPinnedEmojisForReaction() {
+async function overwriteFromPinnedEmojisForReaction() {
 	const { canceled } = await os.confirm({
 		type: 'warning',
 		text: i18n.ts.overwriteContentConfirm,

From 8c218397bcd33536f02069654a96980152b2682c Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 16 Dec 2023 13:24:13 +0900
Subject: [PATCH 248/435] clean up

---
 packages/frontend/src/boot/common.ts | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index 728f39962b..b0825ef11c 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -12,19 +12,16 @@ import { version, ui, lang, updateLocale, locale } from '@/config.js';
 import { applyTheme } from '@/scripts/theme.js';
 import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
 import { i18n, updateI18n } from '@/i18n.js';
-import { confirm, alert, post, popup, toast } from '@/os.js';
 import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js';
 import { defaultStore, ColdDeviceStorage } from '@/store.js';
 import { fetchInstance, instance } from '@/instance.js';
 import { deviceKind } from '@/scripts/device-kind.js';
 import { reloadChannel } from '@/scripts/unison-reload.js';
-import { reactionPicker } from '@/scripts/reaction-picker.js';
 import { getUrlWithoutLoginId } from '@/scripts/login-id.js';
 import { getAccountFromId } from '@/scripts/get-account-from-id.js';
 import { deckStore } from '@/ui/deck/deck-store.js';
 import { miLocalStorage } from '@/local-storage.js';
 import { fetchCustomEmojis } from '@/custom-emojis.js';
-import { mainRouter } from '@/router.js';
 
 export async function common(createVue: () => App<Element>) {
 	console.info(`Misskey v${version}`);

From 742da2f1e9ee49b5b50bf6332d84bb878bc0f4b6 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 16 Dec 2023 13:52:52 +0900
Subject: [PATCH 249/435] =?UTF-8?q?feat(frontend):=20=E7=94=BB=E9=9D=A2?=
 =?UTF-8?q?=E3=81=AB=E9=9B=AA=E3=82=92=E9=99=8D=E3=82=89=E3=81=9B=E3=82=8B?=
 =?UTF-8?q?=E6=A9=9F=E8=83=BD=E3=82=92=E5=BE=A9=E6=B4=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                  |   9 +-
 locales/index.d.ts                            |   1 +
 locales/ja-JP.yml                             |   1 +
 packages/frontend/src/boot/main-boot.ts       |   8 +
 .../frontend/src/pages/settings/general.vue   |   3 +
 .../frontend/src/scripts/snowfall-effect.ts   | 479 ++++++++++++++++++
 packages/frontend/src/store.ts                |   4 +
 7 files changed, 501 insertions(+), 4 deletions(-)
 create mode 100644 packages/frontend/src/scripts/snowfall-effect.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 14acf9c58e..f214ef5ab3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -36,9 +36,9 @@
 
 ### Client
 - Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加
-- Feat: データセーバーでコードハイライトの読み込みを削減できるように
-- Feat: MFMのアニメーション要素(`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)に `delay` オプションを追加
-- Feat: センシティブと判断されたウェブサイトのサムネイルをぼかすように
+- Feat: 画面に雪を降らせられるように
+- Enhance: MFMのアニメーション要素(`tada`, `jelly`, `twitch`, `shake`, `spin`, `jump`, `bounce`, `rainbow`)に `delay` オプションを追加
+- Enhance: センシティブと判断されたウェブサイトのサムネイルを非表示に
   - ウェブサイトをセンシティブと判断する仕組みが動いていないため、summalyProxyを使用しないと機能しません。
 - Enhance: 投稿フォームの絵文字ピッカーをリアクション時に使用するものと同じのを使用するように #12336 #12560
 - Enhance: リアクション用ピン留め絵文字と投稿時の絵文字入力用ピン留め絵文字を分けて設定できるように #12560
@@ -50,6 +50,7 @@
 - Enhance: Shareページで投稿を完了すると、親ウィンドウ(親フレーム)にpostMessageするように
 - Enhance: チャンネル、クリップ、ページ、Play、ギャラリーにURLのコピーボタンを設置 #11305
 - Enhance: ノートプレビューに「内容を隠す」が反映されるように
+- Enhance: データセーバーでコードハイライトの読み込みを削減できるように
 - Enhance: データセーバーの適用範囲を個別で設定できるように
 	- 従来のデータセーバーの設定はリセットされます
 - Enhance: タイムライン上のタブからリスト、アンテナ、チャンネルの管理ページにジャンプできるように
@@ -60,7 +61,7 @@
 	- MFMでコードブロックを利用する際に意図しないハイライトが起こらないようになりました
 	- 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります  
 	(例: ` ```js ` → Javascript, ` ```ais ` → AiScript)
-- fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
+- Fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
 - Fix: プロフィールの「ファイル」にセンシティブな画像がある際のデザインを修正
diff --git a/locales/index.d.ts b/locales/index.d.ts
index ae89f883fb..c8a7b4c70d 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1182,6 +1182,7 @@ export interface Locale {
     "reloadRequiredToApplySettings": string;
     "remainingN": string;
     "overwriteContentConfirm": string;
+    "seasonalScreenEffect": string;
     "_announcement": {
         "forExistingUsers": string;
         "forExistingUsersDescription": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 922ce4fe31..25a734ef4f 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1179,6 +1179,7 @@ code: "コード"
 reloadRequiredToApplySettings: "設定の反映にはリロードが必要です。"
 remainingN: "残り: {n}"
 overwriteContentConfirm: "現在の内容に上書きされますがよろしいですか?"
+seasonalScreenEffect: "季節に応じた画面の演出"
 
 _announcement:
   forExistingUsers: "既存ユーザーのみ"
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index 88e2f83895..e3fd6d5fca 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -20,6 +20,7 @@ import { mainRouter } from '@/router.js';
 import { initializeSw } from '@/scripts/initialize-sw.js';
 import { deckStore } from '@/ui/deck/deck-store.js';
 import { emojiPicker } from '@/scripts/emoji-picker.js';
+import { SnowfallEffect } from '@/scripts/snowfall-effect.js';
 
 export async function mainBoot() {
 	const { isClientUpdated } = await common(() => createApp(
@@ -75,6 +76,13 @@ export async function mainBoot() {
 		},
 	};
 
+	if (defaultStore.state.enableSeasonalScreenEffect) {
+		const month = new Date().getMonth() + 1;
+		if (month === 12 || month === 1) {
+			new SnowfallEffect().render();
+		}
+	}
+
 	if ($i) {
 		// only add post shortcuts if logged in
 		hotkeys['p|n'] = post;
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 0c83abf7f6..826ede17e5 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -122,6 +122,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch>
 				<MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch>
 				<MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch>
+				<MkSwitch v-model="enableSeasonalScreenEffect">{{ i18n.ts.seasonalScreenEffect }}</MkSwitch>
 			</div>
 			<div>
 				<MkRadios v-model="emojiStyle">
@@ -289,6 +290,7 @@ const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificati
 const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn'));
 const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline'));
 const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications'));
+const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enableSeasonalScreenEffect'));
 
 watch(lang, () => {
 	miLocalStorage.setItem('lang', lang.value as string);
@@ -328,6 +330,7 @@ watch([
 	highlightSensitiveMedia,
 	keepScreenOn,
 	disableStreamingTimeline,
+	enableSeasonalScreenEffect,
 ], async () => {
 	await reloadAsk();
 });
diff --git a/packages/frontend/src/scripts/snowfall-effect.ts b/packages/frontend/src/scripts/snowfall-effect.ts
new file mode 100644
index 0000000000..54fb48c5e4
--- /dev/null
+++ b/packages/frontend/src/scripts/snowfall-effect.ts
@@ -0,0 +1,479 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class SnowfallEffect {
+	private VERTEX_SOURCE = `
+		precision highp float;
+
+		attribute vec4 a_position;
+		attribute vec4 a_color;
+		attribute vec3 a_rotation;
+		attribute vec3 a_speed;
+		attribute float a_size;
+
+		uniform float u_time;
+		uniform mat4 u_projection;
+		uniform vec3 u_worldSize;
+		uniform float u_gravity;
+		uniform float u_wind;
+
+		varying vec4 v_color;
+		varying float v_rotation;
+
+		void main() {
+			v_color = a_color;
+			v_rotation = a_rotation.x + u_time * a_rotation.y;
+
+			vec3 pos = a_position.xyz;
+
+			float turbulence = 1.0;
+
+			pos.x = mod(pos.x + u_time + u_wind * a_speed.x, u_worldSize.x * 2.0) - u_worldSize.x;
+			pos.y = mod(pos.y - u_time * a_speed.y * u_gravity, u_worldSize.y * 2.0) - u_worldSize.y;
+
+			pos.x += sin(u_time * a_speed.z * turbulence) * a_rotation.z;
+			pos.z += cos(u_time * a_speed.z * turbulence) * a_rotation.z;
+
+			gl_Position = u_projection * vec4(pos.xyz, a_position.w);
+			gl_PointSize = (a_size / gl_Position.w) * 100.0;
+		}
+	`;
+
+	private FRAGMENT_SOURCE = `
+		precision highp float;
+
+		uniform sampler2D u_texture;
+
+		varying vec4 v_color;
+		varying float v_rotation;
+
+		void main() {
+			vec2 rotated = vec2(
+				cos(v_rotation) * (gl_PointCoord.x - 0.5) + sin(v_rotation) * (gl_PointCoord.y - 0.5) + 0.5,
+				cos(v_rotation) * (gl_PointCoord.y - 0.5) - sin(v_rotation) * (gl_PointCoord.x - 0.5) + 0.5
+			);
+
+			vec4 snowflake = texture2D(u_texture, rotated);
+
+			gl_FragColor = vec4(snowflake.rgb * v_color.xyz, snowflake.a * v_color.a);
+		}
+	`;
+
+	private gl: WebGLRenderingContext;
+	private program: WebGLProgram;
+	private canvas: HTMLCanvasElement;
+	private buffers: Record<string, {
+		size: number;
+		value: number[] | Float32Array;
+		location: number;
+		ref: WebGLBuffer;
+	}>;
+	private uniforms: Record<string, {
+		type: string;
+		value: number[] | Float32Array;
+		location: WebGLUniformLocation;
+	}>;
+	private texture: WebGLTexture;
+	private camera: {
+		fov: number;
+		near: number;
+		far: number;
+		aspect: number;
+		z: number;
+	};
+	private wind: {
+		current: number;
+		force: number;
+		target: number;
+		min: number;
+		max: number;
+		easing: number;
+	};
+	private time: {
+		start: number;
+		previous: number;
+	} = {
+			start: 0,
+			previous: 0,
+		};
+	private raf = 0;
+
+	private density: number = 1 / 90;
+	private depth = 100;
+	private count = 1000;
+	private gravity = 100;
+	private speed: number = 1 / 15000;
+	private color: number[] = [1, 1, 1];
+	private opacity = 0.75;
+	private size = 4;
+	private snowflake = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KTMInWQAAErRJREFUeAHdmgnYlmPax5MShaxRKRElPmXJXpaSsRxDU0bTZ+kt65RloiRDltEMQsxYKmS+zzYjxCCamCzV2LchResMIxFRQ1G93+93Pdf5dL9v7zuf4/hm0fc/jt9znddy3/e1nNd53c/7vHXq/AtVWVnZA/bzkaQjoWG298DeMdvrmP6/EIOqC4fBsbAx7Arz4TaYBPXgWVDnO2jSBrB2T0IMIA9mCmmoE8aonPkR6WPZHlp9xSlfeyeBzq9bHBD5feEdUGfDXBgBqnde+a2wvw/dYdNctvZNAp1PnTaFttA6JgP7eVgBM0CNzgO9HNvy0AcYDda6SaDTdXOnz8X+IkZDugAGQmOYA+ob6Ah/MIOMDRPhJjgJ6uV7pXtWt81/50SnY/Wvwn4ZDHAvwJ9ATYcxyaqsnEnqZCyCPaE80BgYZXG/5A3VyyP/b08LHa11z9KmFUwA5eqruRBHYX1s8WSI1Xcbme8Mt8PWUCU+kF8XbFN+dtH+p06OD4IU8EjD/VOZ5bnezq0XHcHuC2oV7BDlkVIWq56uIX8UjAO31GRIMYW0Vo/xXtSXJyTuXVO6xk1qalRTmQ9AfqzEvog2XYpllnsd6Qr4unCPT7NtByu0uU7vuAaOoy1JuvfXpJdTvSX0gI1gCXwGZdFmEFxoQb7Wid8s7lNu+I8wuHGsTqz2zpQ9DAa5R6HC55A2gvCMXthvwi25bjx26H0M9/9f4Rnok9s0zulFlC2HzzP9cnld8nH/p7DVrbmuIfYs6JLz9U3/z+KGadDeCDsmwre7GyEifn/su8HVSsL2HeBn8CK8AW+B7u9R5yrPgyOjvSn5DWAaXAG2UU7CE9Ayt4k4sR1lX4LaLdd9gn2ftsL+Vtuh1Dp/elH1C8lvCdUj8kDK3gbP8XdhCnSC86rcsNSR9pQvhc/gVlB9bUfqoFNAy/mLrUROrpMwCtpBxBbTtLqkF4K6IF9rf57I9pnYekx5AS0P1VhopXso9pR5buC7+kewU86nFcB+BT4EXdIvNO73sRBubGTXLZtTtgp+DEb++bACdqBuJOlAaMMzLVM3whegNznQDtCb+pW5b8YY76euB5+7pxm0IbzCfS8m3Zf2q4T8/+4JNArXGoptpxz8LqDmQJq0Qnostt/sfIn5GygD4/Zeq7B7wljQO2yjB/QGj0Pjxz4wGdqXrkjXtCT/ISyDa6EPpHrSraFjvnecFpMoMx40Br3xSlD262rYObevddHTs2kYwWUG9uP5It/f1eU5Xw9btwoXPALbwYXcg+unG/KB3Rq8n9ddAOpn4Kr8BAaBcltcDo9D7Ouavig1o34x7F94xqPk74eLQH0MH8HvwS3SLPe9iheEG6f70KiuLpZv6sxG/Va5bFJOabaO7ucAvGEbeAH+AN1hV7iDOidQFz4A2oJb6D1YDhXZHkTqpL8EbqHDYRtwW20AsdIb8syl5N2e6dTAPB2mWYa+hE4Qk7I59iMwFZ70GlJlfyuTVfygs7Hyw7HbwI0w3Tak14BqEtdg7wVdIx8pZbtBUbrjZeA3vUPBANkU+sEehev8O4Db6QpwYm+D8II0KPKHwUFeQ3oLDIMN4WgID1yOPQ+MAXMhNAtju3ztmtuAypiAw7EXwo/Am+0NfUG5mknYc6GfGVIjsoFNuyuoh8COuDcd2LmwA9jWE8bB3Q7N4XrwWAz5XOXR+Tx4n6FgdHeB6sF/w2QwhlSXdXvl/jixx4NH8GW5LDzb7GrR4ES4F5QddB99CieAwStOAPegdUZ2B71F3AXbQSn3vJ1bYaYWrayh3NUPTcbYFExVW3CfXwlvgfoavMbnDAY9dxGo6dCt0LeaB54H4UydDEPA2R4PDlrFLB9XuNmTlO+Xr7X9ZNBr9J4+EN8AMcv6ButpMND9FM6EnTOHkLrSnvtzwbbq3vwMB2ow/qWFSC8ZC++ZQaldbquH2afQWbl8TdcvVtC6LtipifAuOKt6gA9Tzqgzb5R2gP1hX3DVtZVHVvdklY5DA5beIkVPuZn8LOgAnWEfeAaUkxCan/voBNkfF+U5cFu5z5XlxZU20OmZtgm1K45VO4naNCukrcBZVk/CD+E/YBjoYjXJY8Zg9DxsDrbbBHTRotxOrug4eBs+hHgWZtKzfHrdXHBi9gDvqzxFHNA5KVfyBCf0ExgB7nkXStLLEKkniNf0AzUs5+ublkVFKiC9FBZAvGxshT0NnN3zoSUYSJQPcjAvm0HmjcIPemNS96F6E36drFLwugx7EEzNZV/l9IjoEPkW4B7eFtYH9QKcBcfA/aCWgpPQOT+zMbb9fS3nDbYR2MdgV0S5aVlUhLs0w45IHi7sqnnGJ2E7CXqHWgZXgJ1y8KqpDUmfSLmSV5yB/XrpDqVP8ofmehNdOv7I0ShfP4yyJdl2a4SchI1gCXgkHgljYfvc1i3cs/SU1A9jQRpfri/b0Sal1RrtSj4ULyHprY5C6+6E1+EBULq0E+DK7A96iwqX0z4td8B3dCdob5gD3UB3j9fUcNuDKFOvgc+bZAZFf4Zgu/q/AGPMgfm+5ShPWay+k6I31BwAvVDRYL2cuqfUVTkfnTqvVFx5ai7/MXn3tp1UrtRkDWRsaAMjzaD08uJ1irz7+8ps/6ZYj90V3FKrQBkvmubULbN7vs7tZRyJV9w0ePLbQ4PcJspqXnkbhbgoGk/AVptZRxpB0hU7Mpc1x34cdgKPm1dzeTts9XPwlFAO5Au4BDbO7ZycO7J9A/Zh2b4A2+ucALefWpTrflDKVq4kHQBOoi9PO1qvsDeGd6AxXAJbQ5VxlFrW8EnDcJlTsOPcjElxL7WNy7AduC4f2+A/rSN/Hyg7YMBTxgqPUT3F2HAqtIb58GvQW86GqyG+ff4UWz0FBuH4UhaTal1vmAGfg98dfP4d4HPGwmwYAg+D2/J7uU0ap/YaolHZVbBj5d1DaSK8ADsmqiH2JIhgNRhbPZrbhSdZ5heVJGw7477VfYuaagMK2sM8iMloga1HXAt/AeWELgQnR/0Z7k3W6pe3xTn/JamTFPGnPMZSj6p90rA8YOziwHcnH/EgTovJlJ0LPSHkyrTKmZNJ+8KrYKBsCQeB0pWdBFNleieMgzjL44jejTK1CPSY0CiMdyOT09g6ni5O3Ceg51U4VNLaPSA3SDNEwwiKFdgHgANNrpjb7UVejYTYCuZ92DR42HYh8gfDJfAMqBi4dqxk+RrKGkD0YXNsA6AT5qCUXhBe5CR0gPCC4dhqKFwI1m1qX0hr94CotDE4aAd3PCyBX4Jyn+sNL5tBDsRAp3S7b5KVYwa2A0nHaO5AXBeDtnlMxizsW+HomLh8zX9R5sTeBSEn/cqc2Tvak9eDXCyP2PgbYWzn2gefHxT7+0Qu/h18DO7XmPWYcYqSXuHz2myb6G7RNs7meLgeMxXugbiPA3clQx0xtgNPGN819L7+oCzvm6zSx+EkI+Du3Pe0LbOd/jqc7dhG9Wib+mJ5jaJBuL8e4B5aAMpAomKlb8d+KZWUVnw+dgzKSdDtvKaLDyJ1ReZB7O0J2EV5Xwd8OsTJExNpu7Q1SJ8zgy7K93UCX4P4mr4udoyhPGDKygOP+tomIFarMw2d+cfgF2DnDVAGoBvzw33YTHgPDoXQ7Fx/Wy6YkdMrcrmrehO4Pz3WvP90cIVPgonwITg4973yu0XTZK0+ZQaQd+K816twVAwKO71ZRj9zeg7lcVzXHghpVN4n2G3BAHQ1NILx4MBjoppgLwL3Ww8IHZsf6vGk3O8fwx9heK7rhD0o2zdg75JtT6GzQQ8KzcZwElSr3M5J85ktYCzEG+Gx2NNzm/Cm5pSp+K2gfLrZbg3RcB2IQcZN1qPM3+l06SjbAltX/TiXe1wtg7+AdR+AcgIs7xUPw94XxuTrnOD4E1bEoe9Rptw+DWGOGeQi7JOs1SfKKfk+epcakPNxbI8uFVdem8vT6aJdq7jASYjOFPdQDP4Q6t+Em8HVutmbkbYH9Tv4LcQW+H6ujy9Wrtxc6A7vQnznb5TbHUPZ0mw7CeoaOBAegmfBIKw8WZzs34M/oNiPGPzB2KHdrVMUlD29VFLLpw2jMWmnaIbdDNxXur+dWgVumTMglI4zMgbUEV5LmjqW7XnRkDS9qhbu/xZlZ8LWuc3UfM22Of80aVcYDJ/lstdIWxXu0TGXm/TO19vveHWuOglUxOo6iMfyBe7JOEp01ech9puuuBCMA8pVcUUNUB5lqgMYwJyE1oXOGTh9v1gO6kmogKEwHtREMHYofz5zAl3lJ2AWqJfgfohJiKB8HWWfg54YA9Zr1fn5Xmm80SdvHhNwVmq2umF8vWxA+WRwwE9BPNhOulrq0nxz97j6Go6DF8HYcBfYyer6MwWuoINeDG6roq4iE97QCtsJuxWc2JrkCeKEbgX7waOgnLiavxdQEWfohtgRwCrygIoxoQv1K0FNgR7gAKPTB+dr5lAWMliqmbAb7AzbgCs42vYK21NmOiwHJ9atpdxqDlhdA75QdYJT4XUYDfbBiVRe5ySoZTAbBpeekp6T4lo5uFnBz0fpJ6P8E9SJufEdXHipdRA/mw2hzmvfhrfgfjCKPwJnwn2g3igldb4hNaD5a6/fz7eHVuAb2wPwPs+4DB7E/hTagd64BbgoC6Ab9IAfgn+OX0p/ppAaGxZjnw6+Ep8DK8Cj0IDrmHw3GaeN9EZ/AlxFfk1RuVGUYu8K00D9Fa6EvrAUVKzO29gXg9vC1VW3g540w0xBcU2hKJnz+FxYvTCXWaduK/StuTZlLcD6JjnfEvsb6A56m32z78q4FMGw1gA4lEa60WmwMeiSnsljIBSDmEOBE3RdfvggbMuMIbNhItgJtbyUpE9ddjA0Bid1sderXDaQ1OdPAO9zH6hDcpuG2Ml7SQfArHRx6Xpf3JTluySrsrIP6Seg9/iMqsEvF6YZoXIDeAZCRmpneAHEnnLQnaEuXATX53schR3n/e7YyuvOT1bpnyV107Io3xZ6QWs4EirAyXkEqqvK3xa9CQ0c5C5xQ+zN8kWjcr2xZxTsBHfmsipbP671ZmW3wHYA58DdEPobhtwVF2HfBE9H3pT8xjkdja3iiDK4PQBO8Dx4B9wiH8JKeANcKTUW9IITwKNMeYrcArfDhVDsb1pVyty26le5D97/zWzrzVUGXyVjI0WjHUgq4CjoAuGiRuuJkN7mSJX7cn+uaZNyfBBgDHZqXvqsU2cZ6aPwChgE/ap8M9wLbSH+0DKOaw18z8N12GPAyf4BfADbwBmwCbxAHY9NvxQXx2GgVLZXPvurZDE0rqk5+NmAm8U2aIbdH9yDalgpSS80ltlB29fPqW9c8XLUHnsIuGquqt8gN7edwtazrOsAn4MysLryX8BD4Ap3y+0dZROIwPsl9h/hHjgit4lXdrdvHN8dc91wyk7JdvIS7VpF46Jb2ZGz4WJIRyBpBKQW3oR8lZuSvwQMhKtAfQUpYuf27cgbNx6EEeDAzgMHPwYMYi2gEcSfxC7B9qicDMoo/1vQI8p9IG88WAY/yeVpYrJdHpf5vytu4Ky7X46xIamrvjDb52OrG3K+HrZt4xq9wYEZPGPVfp7bhsdE2os2ylV6J1n5mbYPUX4S7AkGX+OAk2t6mm1Iw3PtQ+O4LuooK26RYvW3s7nBLZDiAGlbUHYiRV/S5AWk28DTEFqB4eo+B+n1M55Ivhu4kspj92uYCm6Px0Gv61lor0fcDQNBrQQnOr71lVeYsm894L/bkBuFe/u93eBngJtJMlwTDIDKyfDt6n3se8Dt8jHoNU0o70waq34obZ8lPx4coG+LbifrP6Pt0aQvwn65LFzcAHY8ZUtgAnwExp2WoMpeQLvaA12p7bf/pLPFmS3a/ajr750cfE43wX4YYmU9wi7IddHBCsrc69vm8uuwQydYVhQVvmsUn7s+ebfD0GhXrI+yf2jqA4oPKdo+iHxMwHbYRmgjta4cUTqCWXkg0UHatIR4SxxWKK9PeXhgKiZfxWOthzXuGff4p6b54bH3Y3W3pNxJcK8ebgdI44iys0G0N/8qKGOAGg9Ni50n3yjy2GkxSKtMRtT/21I7Fg/H9lRIX6qK5YX6zSjvDL4BGiBfBnUNmFdzwfKX4Ct40OtJv1sDj0Hlzrk6xbM3tob7uCf4amyk96VHvQg7gltGzQG9wpcwX6BCesfJ3/kJiMmgs+Gm4errUeZqF+Up4IoOzoWLcmqETyLve/2BsKkFpGUvK7VYCz6j06RbQx+ogHhN3Qdb3QF+a/wVKF94OhSHR77sWcXytcKm82usHGW9QE2B3skq/QB7APaqnJ9NuvaufnF1GIhxYH3LSAeA+hM0hMfgNzATdHvjgDHDv+qkP8gW77XW2gwmYsJe2F3zZDgxI7NteTo+/1WD/B9Au3Zjh2RyrgAAAABJRU5ErkJggg==';
+
+	private INITIAL_BUFFERS = () => ({
+		position: { size: 3, value: [] },
+		color: { size: 4, value: [] },
+		size: { size: 1, value: [] },
+		rotation: { size: 3, value: [] },
+		speed: { size: 3, value: [] },
+	});
+
+	private INITIAL_UNIFORMS = () => ({
+		time: { type: 'float', value: 0 },
+		worldSize: { type: 'vec3', value: [0, 0, 0] },
+		gravity: { type: 'float', value: this.gravity },
+		wind: { type: 'float', value: 0 },
+		projection: {
+			type: 'mat4',
+			value: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
+		},
+	});
+
+	private UNIFORM_SETTERS = {
+		int: 'uniform1i',
+		float: 'uniform1f',
+		vec2: 'uniform2fv',
+		vec3: 'uniform3fv',
+		vec4: 'uniform4fv',
+		mat2: 'uniformMatrix2fv',
+		mat3: 'uniformMatrix3fv',
+		mat4: 'uniformMatrix4fv',
+	};
+
+	private CAMERA = {
+		fov: 60,
+		near: 5,
+		far: 10000,
+		aspect: 1,
+		z: 100,
+	};
+
+	private WIND = {
+		current: 0,
+		force: 0.01,
+		target: 0.01,
+		min: 0,
+		max: 0.125,
+		easing: 0.0005,
+	};
+
+	constructor() {
+		const canvas = this.initCanvas();
+		const gl = canvas.getContext('webgl', { antialias: true });
+		if (gl == null) throw new Error('Failed to get WebGL context');
+
+		document.body.append(canvas);
+
+		this.canvas = canvas;
+		this.gl = gl;
+		this.program = this.initProgram();
+		this.buffers = this.initBuffers();
+		this.uniforms = this.initUniforms();
+		this.texture = this.initTexture();
+		this.camera = this.initCamera();
+		this.wind = this.initWind();
+
+		this.resize = this.resize.bind(this);
+		this.update = this.update.bind(this);
+
+		window.addEventListener('resize', () => this.resize());
+	}
+
+	private initCanvas(): HTMLCanvasElement {
+		const canvas = document.createElement('canvas');
+
+		Object.assign(canvas.style, {
+			position: 'fixed',
+			top: 0,
+			left: 0,
+			width: '100vw',
+			height: '100vh',
+			background: 'transparent',
+			'pointer-events': 'none',
+		});
+
+		return canvas;
+	}
+
+	private initCamera() {
+		return { ...this.CAMERA };
+	}
+
+	private initWind() {
+		return { ...this.WIND };
+	}
+
+	private initShader(type, source): WebGLShader {
+		const { gl } = this;
+		const shader = gl.createShader(type);
+		if (shader == null) throw new Error('Failed to create shader');
+
+		gl.shaderSource(shader, source);
+		gl.compileShader(shader);
+
+		return shader;
+	}
+
+	private initProgram(): WebGLProgram {
+		const { gl } = this;
+		const vertex = this.initShader(gl.VERTEX_SHADER, this.VERTEX_SOURCE);
+		const fragment = this.initShader(gl.FRAGMENT_SHADER, this.FRAGMENT_SOURCE);
+		const program = gl.createProgram();
+		if (program == null) throw new Error('Failed to create program');
+
+		gl.attachShader(program, vertex);
+		gl.attachShader(program, fragment);
+		gl.linkProgram(program);
+		gl.useProgram(program);
+
+		return program;
+	}
+
+	private initBuffers(): SnowfallEffect['buffers'] {
+		const { gl, program } = this;
+		const buffers = this.INITIAL_BUFFERS() as unknown as SnowfallEffect['buffers'];
+
+		for (const [name, buffer] of Object.entries(buffers)) {
+			buffer.location = gl.getAttribLocation(program, `a_${name}`);
+			buffer.ref = gl.createBuffer()!;
+
+			gl.bindBuffer(gl.ARRAY_BUFFER, buffer.ref);
+			gl.enableVertexAttribArray(buffer.location);
+			gl.vertexAttribPointer(
+				buffer.location,
+				buffer.size,
+				gl.FLOAT,
+				false,
+				0,
+				0,
+			);
+		}
+
+		return buffers;
+	}
+
+	private updateBuffers() {
+		const { buffers } = this;
+
+		for (const name of Object.keys(buffers)) {
+			this.setBuffer(name);
+		}
+	}
+
+	private setBuffer(name: string, value?) {
+		const { gl, buffers } = this;
+		const buffer = buffers[name];
+
+		buffer.value = new Float32Array(value ?? buffer.value);
+
+		gl.bindBuffer(gl.ARRAY_BUFFER, buffer.ref);
+		gl.bufferData(gl.ARRAY_BUFFER, buffer.value, gl.STATIC_DRAW);
+	}
+
+	private initUniforms(): SnowfallEffect['uniforms'] {
+		const { gl, program } = this;
+		const uniforms = this.INITIAL_UNIFORMS() as unknown as SnowfallEffect['uniforms'];
+
+		for (const [name, uniform] of Object.entries(uniforms)) {
+			uniform.location = gl.getUniformLocation(program, `u_${name}`)!;
+		}
+
+		return uniforms;
+	}
+
+	private updateUniforms() {
+		const { uniforms } = this;
+
+		for (const name of Object.keys(uniforms)) {
+			this.setUniform(name);
+		}
+	}
+
+	private setUniform(name: string, value?) {
+		const { gl, uniforms } = this;
+		const uniform = uniforms[name];
+		const setter = this.UNIFORM_SETTERS[uniform.type];
+		const isMatrix = /^mat[2-4]$/i.test(uniform.type);
+
+		uniform.value = value ?? uniform.value;
+
+		if (isMatrix) {
+			gl[setter](uniform.location, false, uniform.value);
+		} else {
+			gl[setter](uniform.location, uniform.value);
+		}
+	}
+
+	private initTexture() {
+		const { gl } = this;
+		const texture = gl.createTexture();
+		if (texture == null) throw new Error('Failed to create texture');
+		const image = new Image();
+
+		gl.bindTexture(gl.TEXTURE_2D, texture);
+		gl.texImage2D(
+			gl.TEXTURE_2D,
+			0,
+			gl.RGBA,
+			1,
+			1,
+			0,
+			gl.RGBA,
+			gl.UNSIGNED_BYTE,
+			new Uint8Array([0, 0, 0, 0]),
+		);
+
+		image.onload = () => {
+			gl.bindTexture(gl.TEXTURE_2D, texture);
+			gl.texImage2D(
+				gl.TEXTURE_2D,
+				0,
+				gl.RGBA,
+				gl.RGBA,
+				gl.UNSIGNED_BYTE,
+				image,
+			);
+			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+			gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+		};
+
+		image.src = this.snowflake;
+
+		return texture;
+	}
+
+	private initSnowflakes(vw: number, vh: number, dpi: number) {
+		const position: number[] = [];
+		const color: number[] = [];
+		const size: number[] = [];
+		const rotation: number[] = [];
+		const speed: number[] = [];
+
+		const height = 1 / this.density;
+		const width = (vw / vh) * height;
+		const depth = this.depth;
+		const count = this.count;
+		const length = (vw / vh) * count;
+
+		for (let i = 0; i < length; ++i) {
+			position.push(
+				-width + Math.random() * width * 2,
+				-height + Math.random() * height * 2,
+				Math.random() * depth * 2,
+			);
+
+			speed.push(1 + Math.random(), 1 + Math.random(), Math.random() * 10);
+
+			rotation.push(
+				Math.random() * 2 * Math.PI,
+				Math.random() * 20,
+				Math.random() * 10,
+			);
+
+			color.push(...this.color, 0.1 + Math.random() * this.opacity);
+			//size.push((this.size * Math.random() * this.size * vh * dpi) / 1000);
+			size.push((this.size * vh * dpi) / 1000);
+		}
+
+		this.setUniform('worldSize', [width, height, depth]);
+
+		this.setBuffer('position', position);
+		this.setBuffer('color', color);
+		this.setBuffer('rotation', rotation);
+		this.setBuffer('size', size);
+		this.setBuffer('speed', speed);
+	}
+
+	private setProjection(aspect: number) {
+		const { camera } = this;
+
+		camera.aspect = aspect;
+
+		const fovRad = (camera.fov * Math.PI) / 180;
+		const f = Math.tan(Math.PI * 0.5 - 0.5 * fovRad);
+		const rangeInv = 1.0 / (camera.near - camera.far);
+
+		const m0 = f / camera.aspect;
+		const m5 = f;
+		const m10 = (camera.near + camera.far) * rangeInv;
+		const m11 = -1;
+		const m14 = camera.near * camera.far * rangeInv * 2 + camera.z;
+		const m15 = camera.z;
+
+		return [m0, 0, 0, 0, 0, m5, 0, 0, 0, 0, m10, m11, 0, 0, m14, m15];
+	}
+
+	public render() {
+		const { gl } = this;
+
+		gl.enable(gl.BLEND);
+		gl.enable(gl.CULL_FACE);
+		gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
+		gl.disable(gl.DEPTH_TEST);
+
+		this.updateBuffers();
+		this.updateUniforms();
+		this.resize(true);
+
+		this.time = {
+			start: window.performance.now(),
+			previous: window.performance.now(),
+		};
+
+		if (this.raf) window.cancelAnimationFrame(this.raf);
+		this.raf = window.requestAnimationFrame(this.update);
+
+		return this;
+	}
+
+	private resize(updateSnowflakes = false) {
+		const { canvas, gl } = this;
+		const vw = canvas.offsetWidth;
+		const vh = canvas.offsetHeight;
+		const aspect = vw / vh;
+		const dpi = window.devicePixelRatio;
+
+		canvas.width = vw * dpi;
+		canvas.height = vh * dpi;
+
+		gl.viewport(0, 0, vw * dpi, vh * dpi);
+		gl.clearColor(0, 0, 0, 0);
+
+		if (updateSnowflakes === true) {
+			this.initSnowflakes(vw, vh, dpi);
+		}
+
+		this.setUniform('projection', this.setProjection(aspect));
+	}
+
+	private update(timestamp: number) {
+		const { gl, buffers, wind } = this;
+		const elapsed = (timestamp - this.time.start) * this.speed;
+		const delta = timestamp - this.time.previous;
+
+		gl.clear(gl.COLOR_BUFFER_BIT);
+		gl.drawArrays(
+			gl.POINTS,
+			0,
+			buffers.position.value.length / buffers.position.size,
+		);
+
+		if (Math.random() > 0.995) {
+			wind.target =
+				(wind.min + Math.random() * (wind.max - wind.min)) *
+				(Math.random() > 0.5 ? -1 : 1);
+		}
+
+		wind.force += (wind.target - wind.force) * wind.easing;
+		wind.current += wind.force * (delta * 0.2);
+
+		this.setUniform('wind', wind.current);
+		this.setUniform('time', elapsed);
+
+		this.time.previous = timestamp;
+
+		this.raf = window.requestAnimationFrame(this.update);
+	}
+}
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index c7e501aa84..3f8a5f5a6f 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -412,6 +412,10 @@ export const defaultStore = markRaw(new Storage('base', {
 			code: false,
 		} as Record<string, boolean>,
 	},
+	enableSeasonalScreenEffect: {
+		where: 'device',
+		default: false,
+	},
 
 	sound_masterVolume: {
 		where: 'device',

From 2ecc0299b4dd649da0cc2156a050862124c307aa Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 16 Dec 2023 14:00:27 +0900
Subject: [PATCH 250/435] update deps

---
 CHANGELOG.md                     |    1 +
 package.json                     |    8 +-
 packages/backend/package.json    |   10 +-
 packages/frontend/package.json   |   44 +-
 packages/misskey-js/package.json |    2 +-
 packages/sw/package.json         |    4 +-
 pnpm-lock.yaml                   | 1212 ++++++++++++++++--------------
 7 files changed, 700 insertions(+), 581 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f214ef5ab3..67898c604e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@
 ## 2023.x.x (unreleased)
 
 ### Note
+- Node.js 20.10.0が最小要件になりました
 - 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。  
 
 	**影響:**  
diff --git a/package.json b/package.json
index 3057c5d804..7475e77f66 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
 		"type": "git",
 		"url": "https://github.com/misskey-dev/misskey.git"
 	},
-	"packageManager": "pnpm@8.10.5",
+	"packageManager": "pnpm@8.12.1",
 	"workspaces": [
 		"packages/frontend",
 		"packages/backend",
@@ -46,10 +46,10 @@
 	},
 	"dependencies": {
 		"execa": "8.0.1",
-		"cssnano": "6.0.1",
+		"cssnano": "6.0.2",
 		"js-yaml": "4.1.0",
 		"postcss": "8.4.32",
-		"terser": "5.24.0",
+		"terser": "5.26.0",
 		"typescript": "5.3.3"
 	},
 	"devDependencies": {
@@ -57,7 +57,7 @@
 		"@typescript-eslint/parser": "6.14.0",
 		"cross-env": "7.0.3",
 		"cypress": "13.6.1",
-		"eslint": "8.55.0",
+		"eslint": "8.56.0",
 		"start-server-and-test": "2.0.3",
 		"ncp": "2.0.0"
 	},
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 504cc882ff..8a9871b78c 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -4,7 +4,7 @@
 	"private": true,
 	"type": "module",
 	"engines": {
-		"node": ">=18.16.0"
+		"node": ">=20.10.0"
 	},
 	"scripts": {
 		"start": "node ./built/boot/entry.js",
@@ -90,7 +90,7 @@
 		"bcryptjs": "2.4.3",
 		"blurhash": "2.0.5",
 		"body-parser": "1.20.2",
-		"bullmq": "4.15.3",
+		"bullmq": "4.15.4",
 		"cacheable-lookup": "7.0.0",
 		"cbor": "9.0.1",
 		"chalk": "5.3.0",
@@ -107,7 +107,7 @@
 		"file-type": "18.7.0",
 		"fluent-ffmpeg": "2.1.2",
 		"form-data": "4.0.0",
-		"got": "13.0.0",
+		"got": "14.0.0",
 		"happy-dom": "10.0.3",
 		"hpagent": "1.2.0",
 		"http-link-header": "1.1.1",
@@ -222,8 +222,8 @@
 		"@typescript-eslint/parser": "6.14.0",
 		"aws-sdk-client-mock": "3.0.0",
 		"cross-env": "7.0.3",
-		"eslint": "8.55.0",
-		"eslint-plugin-import": "2.29.0",
+		"eslint": "8.56.0",
+		"eslint-plugin-import": "2.29.1",
 		"execa": "8.0.1",
 		"jest": "29.7.0",
 		"jest-mock": "29.7.0",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 4d0139f252..b2ab1eb9ce 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -44,7 +44,7 @@
 		"escape-regexp": "0.0.1",
 		"estree-walker": "3.0.3",
 		"eventemitter3": "5.0.1",
-		"gsap": "3.12.3",
+		"gsap": "3.12.4",
 		"idb-keyval": "6.2.1",
 		"insert-text-at-cursor": "0.3.0",
 		"is-file-animated": "1.0.2",
@@ -57,7 +57,7 @@
 		"rollup": "4.9.0",
 		"sanitize-html": "2.11.0",
 		"sass": "1.69.5",
-		"shiki": "0.14.6",
+		"shiki": "0.14.7",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
 		"three": "0.159.0",
@@ -69,29 +69,29 @@
 		"typescript": "5.3.3",
 		"uuid": "9.0.1",
 		"v-code-diff": "1.7.2",
-		"vite": "5.0.8",
+		"vite": "5.0.10",
 		"vue": "3.3.11",
 		"vuedraggable": "next"
 	},
 	"devDependencies": {
-		"@storybook/addon-actions": "7.6.4",
-		"@storybook/addon-essentials": "7.6.4",
-		"@storybook/addon-interactions": "7.6.4",
-		"@storybook/addon-links": "7.6.4",
-		"@storybook/addon-storysource": "7.6.4",
-		"@storybook/addons": "7.6.4",
-		"@storybook/blocks": "7.6.4",
-		"@storybook/core-events": "7.6.4",
+		"@storybook/addon-actions": "7.6.5",
+		"@storybook/addon-essentials": "7.6.5",
+		"@storybook/addon-interactions": "7.6.5",
+		"@storybook/addon-links": "7.6.5",
+		"@storybook/addon-storysource": "7.6.5",
+		"@storybook/addons": "7.6.5",
+		"@storybook/blocks": "7.6.5",
+		"@storybook/core-events": "7.6.5",
 		"@storybook/jest": "0.2.3",
-		"@storybook/manager-api": "7.6.4",
-		"@storybook/preview-api": "7.6.4",
-		"@storybook/react": "7.6.4",
-		"@storybook/react-vite": "7.6.4",
+		"@storybook/manager-api": "7.6.5",
+		"@storybook/preview-api": "7.6.5",
+		"@storybook/react": "7.6.5",
+		"@storybook/react-vite": "7.6.5",
 		"@storybook/testing-library": "0.2.2",
-		"@storybook/theming": "7.6.4",
-		"@storybook/types": "7.6.4",
-		"@storybook/vue3": "7.6.4",
-		"@storybook/vue3-vite": "7.6.4",
+		"@storybook/theming": "7.6.5",
+		"@storybook/types": "7.6.5",
+		"@storybook/vue3": "7.6.5",
+		"@storybook/vue3-vite": "7.6.5",
 		"@testing-library/vue": "8.0.1",
 		"@types/escape-regexp": "0.0.3",
 		"@types/estree": "1.0.5",
@@ -111,8 +111,8 @@
 		"acorn": "8.11.2",
 		"cross-env": "7.0.3",
 		"cypress": "13.6.1",
-		"eslint": "8.55.0",
-		"eslint-plugin-import": "2.29.0",
+		"eslint": "8.56.0",
+		"eslint-plugin-import": "2.29.1",
 		"eslint-plugin-vue": "9.19.2",
 		"fast-glob": "3.3.2",
 		"happy-dom": "10.0.3",
@@ -125,7 +125,7 @@
 		"react": "18.2.0",
 		"react-dom": "18.2.0",
 		"start-server-and-test": "2.0.3",
-		"storybook": "7.6.4",
+		"storybook": "7.6.5",
 		"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 0b54c6a59c..fed440f6db 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -28,7 +28,7 @@
 		"@types/node": "20.10.4",
 		"@typescript-eslint/eslint-plugin": "6.14.0",
 		"@typescript-eslint/parser": "6.14.0",
-		"eslint": "8.55.0",
+		"eslint": "8.56.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 990e991966..e43e1f9a8a 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -16,8 +16,8 @@
 	"devDependencies": {
 		"@typescript-eslint/parser": "6.14.0",
 		"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
-		"eslint": "8.55.0",
-		"eslint-plugin-import": "2.29.0",
+		"eslint": "8.56.0",
+		"eslint-plugin-import": "2.29.1",
 		"nodemon": "3.0.2",
 		"typescript": "5.3.3"
 	},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5319acf932..564e5b7929 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -13,8 +13,8 @@ importers:
   .:
     dependencies:
       cssnano:
-        specifier: 6.0.1
-        version: 6.0.1(postcss@8.4.32)
+        specifier: 6.0.2
+        version: 6.0.2(postcss@8.4.32)
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -25,8 +25,8 @@ importers:
         specifier: 8.4.32
         version: 8.4.32
       terser:
-        specifier: 5.24.0
-        version: 5.24.0
+        specifier: 5.26.0
+        version: 5.26.0
       typescript:
         specifier: 5.3.3
         version: 5.3.3
@@ -37,10 +37,10 @@ importers:
     devDependencies:
       '@typescript-eslint/eslint-plugin':
         specifier: 6.14.0
-        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3)
+        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 6.14.0
-        version: 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+        version: 6.14.0(eslint@8.56.0)(typescript@5.3.3)
       cross-env:
         specifier: 7.0.3
         version: 7.0.3
@@ -48,8 +48,8 @@ importers:
         specifier: 13.6.1
         version: 13.6.1
       eslint:
-        specifier: 8.55.0
-        version: 8.55.0
+        specifier: 8.56.0
+        version: 8.56.0
       ncp:
         specifier: 2.0.0
         version: 2.0.0
@@ -150,8 +150,8 @@ importers:
         specifier: 1.20.2
         version: 1.20.2
       bullmq:
-        specifier: 4.15.3
-        version: 4.15.3
+        specifier: 4.15.4
+        version: 4.15.4
       cacheable-lookup:
         specifier: 7.0.0
         version: 7.0.0
@@ -201,8 +201,8 @@ importers:
         specifier: 4.0.0
         version: 4.0.0
       got:
-        specifier: 13.0.0
-        version: 13.0.0
+        specifier: 14.0.0
+        version: 14.0.0
       happy-dom:
         specifier: 10.0.3
         version: 10.0.3
@@ -618,10 +618,10 @@ importers:
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
         specifier: 6.14.0
-        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3)
+        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 6.14.0
-        version: 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+        version: 6.14.0(eslint@8.56.0)(typescript@5.3.3)
       aws-sdk-client-mock:
         specifier: 3.0.0
         version: 3.0.0
@@ -629,11 +629,11 @@ importers:
         specifier: 7.0.3
         version: 7.0.3
       eslint:
-        specifier: 8.55.0
-        version: 8.55.0
+        specifier: 8.56.0
+        version: 8.56.0
       eslint-plugin-import:
-        specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)
+        specifier: 2.29.1
+        version: 2.29.1(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)
       execa:
         specifier: 8.0.1
         version: 8.0.1
@@ -675,7 +675,7 @@ importers:
         version: 2.44.0
       '@vitejs/plugin-vue':
         specifier: 4.5.2
-        version: 4.5.2(vite@5.0.8)(vue@3.3.11)
+        version: 4.5.2(vite@5.0.10)(vue@3.3.11)
       '@vue/compiler-sfc':
         specifier: 3.3.11
         version: 3.3.11
@@ -734,8 +734,8 @@ importers:
         specifier: 5.0.1
         version: 5.0.1
       gsap:
-        specifier: 3.12.3
-        version: 3.12.3
+        specifier: 3.12.4
+        version: 3.12.4
       idb-keyval:
         specifier: 6.2.1
         version: 6.2.1
@@ -773,8 +773,8 @@ importers:
         specifier: 1.69.5
         version: 1.69.5
       shiki:
-        specifier: 0.14.6
-        version: 0.14.6
+        specifier: 0.14.7
+        version: 0.14.7
       strict-event-emitter-types:
         specifier: 2.0.0
         version: 2.0.0
@@ -809,8 +809,8 @@ importers:
         specifier: 1.7.2
         version: 1.7.2(vue@3.3.11)
       vite:
-        specifier: 5.0.8
-        version: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+        specifier: 5.0.10
+        version: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
       vue:
         specifier: 3.3.11
         version: 3.3.11(typescript@5.3.3)
@@ -819,59 +819,59 @@ importers:
         version: 4.1.0(vue@3.3.11)
     devDependencies:
       '@storybook/addon-actions':
-        specifier: 7.6.4
-        version: 7.6.4
+        specifier: 7.6.5
+        version: 7.6.5
       '@storybook/addon-essentials':
-        specifier: 7.6.4
-        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.5
+        version: 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/addon-interactions':
-        specifier: 7.6.4
-        version: 7.6.4
+        specifier: 7.6.5
+        version: 7.6.5
       '@storybook/addon-links':
-        specifier: 7.6.4
-        version: 7.6.4(react@18.2.0)
+        specifier: 7.6.5
+        version: 7.6.5(react@18.2.0)
       '@storybook/addon-storysource':
-        specifier: 7.6.4
-        version: 7.6.4
+        specifier: 7.6.5
+        version: 7.6.5
       '@storybook/addons':
-        specifier: 7.6.4
-        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.5
+        version: 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/blocks':
-        specifier: 7.6.4
-        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.5
+        version: 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/core-events':
-        specifier: 7.6.4
-        version: 7.6.4
+        specifier: 7.6.5
+        version: 7.6.5
       '@storybook/jest':
         specifier: 0.2.3
         version: 0.2.3(vitest@0.34.6)
       '@storybook/manager-api':
-        specifier: 7.6.4
-        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.5
+        version: 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/preview-api':
-        specifier: 7.6.4
-        version: 7.6.4
+        specifier: 7.6.5
+        version: 7.6.5
       '@storybook/react':
-        specifier: 7.6.4
-        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
+        specifier: 7.6.5
+        version: 7.6.5(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
       '@storybook/react-vite':
-        specifier: 7.6.4
-        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.0)(typescript@5.3.3)(vite@5.0.8)
+        specifier: 7.6.5
+        version: 7.6.5(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.0)(typescript@5.3.3)(vite@5.0.10)
       '@storybook/testing-library':
         specifier: 0.2.2
         version: 0.2.2
       '@storybook/theming':
-        specifier: 7.6.4
-        version: 7.6.4(react-dom@18.2.0)(react@18.2.0)
+        specifier: 7.6.5
+        version: 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/types':
-        specifier: 7.6.4
-        version: 7.6.4
+        specifier: 7.6.5
+        version: 7.6.5
       '@storybook/vue3':
-        specifier: 7.6.4
-        version: 7.6.4(@vue/compiler-core@3.3.11)(vue@3.3.11)
+        specifier: 7.6.5
+        version: 7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.11)
       '@storybook/vue3-vite':
-        specifier: 7.6.4
-        version: 7.6.4(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.8)(vue@3.3.11)
+        specifier: 7.6.5
+        version: 7.6.5(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.11)
       '@testing-library/vue':
         specifier: 8.0.1
         version: 8.0.1(@vue/compiler-sfc@3.3.11)(vue@3.3.11)
@@ -910,10 +910,10 @@ importers:
         version: 8.5.10
       '@typescript-eslint/eslint-plugin':
         specifier: 6.14.0
-        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3)
+        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 6.14.0
-        version: 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+        version: 6.14.0(eslint@8.56.0)(typescript@5.3.3)
       '@vitest/coverage-v8':
         specifier: 0.34.6
         version: 0.34.6(vitest@0.34.6)
@@ -930,14 +930,14 @@ importers:
         specifier: 13.6.1
         version: 13.6.1
       eslint:
-        specifier: 8.55.0
-        version: 8.55.0
+        specifier: 8.56.0
+        version: 8.56.0
       eslint-plugin-import:
-        specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)
+        specifier: 2.29.1
+        version: 2.29.1(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)
       eslint-plugin-vue:
         specifier: 9.19.2
-        version: 9.19.2(eslint@8.55.0)
+        version: 9.19.2(eslint@8.56.0)
       fast-glob:
         specifier: 3.3.2
         version: 3.3.2
@@ -972,11 +972,11 @@ importers:
         specifier: 2.0.3
         version: 2.0.3
       storybook:
-        specifier: 7.6.4
-        version: 7.6.4
+        specifier: 7.6.5
+        version: 7.6.5
       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.6.4)(@storybook/components@7.6.4)(@storybook/core-events@7.6.4)(@storybook/manager-api@7.6.4)(@storybook/preview-api@7.6.4)(@storybook/theming@7.6.4)(@storybook/types@7.6.4)(react-dom@18.2.0)(react@18.2.0)
+        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.6.4)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0)
       summaly:
         specifier: github:misskey-dev/summaly
         version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8
@@ -985,13 +985,13 @@ importers:
         version: 1.0.3
       vitest:
         specifier: 0.34.6
-        version: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.24.0)
+        version: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.26.0)
       vitest-fetch-mock:
         specifier: 0.2.2
         version: 0.2.2(vitest@0.34.6)
       vue-eslint-parser:
         specifier: 9.3.2
-        version: 9.3.2(eslint@8.55.0)
+        version: 9.3.2(eslint@8.56.0)
       vue-tsc:
         specifier: 1.8.25
         version: 1.8.25(typescript@5.3.3)
@@ -1025,13 +1025,13 @@ importers:
         version: 20.10.4
       '@typescript-eslint/eslint-plugin':
         specifier: 6.14.0
-        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3)
+        version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)(typescript@5.3.3)
       '@typescript-eslint/parser':
         specifier: 6.14.0
-        version: 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+        version: 6.14.0(eslint@8.56.0)(typescript@5.3.3)
       eslint:
-        specifier: 8.55.0
-        version: 8.55.0
+        specifier: 8.56.0
+        version: 8.56.0
       jest:
         specifier: 29.7.0
         version: 29.7.0(@types/node@20.10.4)
@@ -1104,16 +1104,16 @@ importers:
     devDependencies:
       '@typescript-eslint/parser':
         specifier: 6.14.0
-        version: 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+        version: 6.14.0(eslint@8.56.0)(typescript@5.3.3)
       '@typescript/lib-webworker':
         specifier: npm:@types/serviceworker@0.0.67
         version: /@types/serviceworker@0.0.67
       eslint:
-        specifier: 8.55.0
-        version: 8.55.0
+        specifier: 8.56.0
+        version: 8.56.0
       eslint-plugin-import:
-        specifier: 2.29.0
-        version: 2.29.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)
+        specifier: 2.29.1
+        version: 2.29.1(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)
       nodemon:
         specifier: 3.0.2
         version: 3.0.2
@@ -1808,7 +1808,7 @@ packages:
       '@babel/traverse': 7.22.11
       '@babel/types': 7.22.17
       convert-source-map: 1.9.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -1831,7 +1831,7 @@ packages:
       '@babel/traverse': 7.23.5
       '@babel/types': 7.23.5
       convert-source-map: 2.0.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -1933,7 +1933,7 @@ packages:
       '@babel/core': 7.23.5
       '@babel/helper-compilation-targets': 7.22.15
       '@babel/helper-plugin-utils': 7.22.5
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       lodash.debounce: 4.0.8
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -3324,7 +3324,7 @@ packages:
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/parser': 7.23.3
       '@babel/types': 7.22.17
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -3342,7 +3342,7 @@ packages:
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/parser': 7.23.5
       '@babel/types': 7.23.5
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -4003,13 +4003,13 @@ packages:
       eslint-visitor-keys: 3.4.3
     dev: true
 
-  /@eslint-community/eslint-utils@4.4.0(eslint@8.55.0):
+  /@eslint-community/eslint-utils@4.4.0(eslint@8.56.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.55.0
+      eslint: 8.56.0
       eslint-visitor-keys: 3.4.3
     dev: true
 
@@ -4023,7 +4023,7 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dependencies:
       ajv: 6.12.6
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       espree: 9.6.1
       globals: 13.19.0
       ignore: 5.2.4
@@ -4040,7 +4040,7 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dependencies:
       ajv: 6.12.6
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       espree: 9.6.1
       globals: 13.19.0
       ignore: 5.2.4
@@ -4057,8 +4057,8 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
-  /@eslint/js@8.55.0:
-    resolution: {integrity: sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==}
+  /@eslint/js@8.56.0:
+    resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
@@ -4305,7 +4305,7 @@ packages:
     engines: {node: '>=10.10.0'}
     dependencies:
       '@humanwhocodes/object-schema': 2.0.1
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
@@ -4590,7 +4590,7 @@ packages:
       chalk: 4.1.2
     dev: true
 
-  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.0.8):
+  /@joshwooding/vite-plugin-react-docgen-typescript@0.3.0(typescript@5.3.3)(vite@5.0.10):
     resolution: {integrity: sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==}
     peerDependencies:
       typescript: '>= 4.3.x'
@@ -4604,7 +4604,7 @@ packages:
       magic-string: 0.27.0
       react-docgen-typescript: 2.2.2(typescript@5.3.3)
       typescript: 5.3.3
-      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
     dev: true
 
   /@jridgewell/gen-mapping@0.3.2:
@@ -4813,7 +4813,7 @@ packages:
       '@open-draft/until': 1.0.3
       '@types/debug': 4.1.7
       '@xmldom/xmldom': 0.8.6
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       headers-polyfill: 3.2.5
       outvariant: 1.4.0
       strict-event-emitter: 0.2.8
@@ -5770,6 +5770,11 @@ packages:
     resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==}
     engines: {node: '>=14.16'}
 
+  /@sindresorhus/is@6.1.0:
+    resolution: {integrity: sha512-BuvU07zq3tQ/2SIgBsEuxKYDyDjC0n7Zir52bpHy2xnBbW81+po43aLFPLbeV3HRAheFbGud1qgcqSYfhtHMAg==}
+    engines: {node: '>=16'}
+    dev: false
+
   /@sinonjs/commons@1.8.6:
     resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==}
     dependencies:
@@ -6280,10 +6285,10 @@ packages:
     resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==}
     dev: false
 
-  /@storybook/addon-actions@7.6.4:
-    resolution: {integrity: sha512-91UD5KPDik74VKVioPMcbwwvDXN/non8p1wArYAHCHCmd/Pts5MJRiFueSdfomSpNjUtjtn6eSXtwpIL3XVOfQ==}
+  /@storybook/addon-actions@7.6.5:
+    resolution: {integrity: sha512-lW/m9YcaNfBZk+TZLxyzHdd563mBWpsUIveOKYjcPdl/q0FblWWZrRsFHqwLK1ldZ4AZXs8J/47G8CBr6Ew2uQ==}
     dependencies:
-      '@storybook/core-events': 7.6.4
+      '@storybook/core-events': 7.6.5
       '@storybook/global': 5.0.0
       '@types/uuid': 9.0.7
       dequal: 2.0.3
@@ -6291,18 +6296,18 @@ packages:
       uuid: 9.0.1
     dev: true
 
-  /@storybook/addon-backgrounds@7.6.4:
-    resolution: {integrity: sha512-gNy3kIkHSr+Lg/jVDHwbZjIe1po5SDGZNVe39vrJwnqGz8T1clWes9WHCL6zk/uaCDA3yUna2Nt/KlOFAWDSoQ==}
+  /@storybook/addon-backgrounds@7.6.5:
+    resolution: {integrity: sha512-wZZOL19vg4TTRtOTl71XKqPe5hQx3XUh9Fle0wOi91FiFrBdqusrppnyS89wPS8RQG5lXEOFEUvYcMmdCcdZfw==}
     dependencies:
       '@storybook/global': 5.0.0
       memoizerific: 1.11.3
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-controls@7.6.4(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-k4AtZfazmD/nL3JAtLGAB7raPhkhUo0jWnaZWrahd9h1Fm13mBU/RW+JzTRhCw3Mp2HPERD7NI5Qcd2fUP6WDA==}
+  /@storybook/addon-controls@7.6.5(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-EdSZ2pYf74mOXZGGJ22lrDvdvL0YKc95iWv9FFEhUFOloMy/0OZPB2ybYmd2KVCy3SeIE4Zfeiw8pDXdCUniOQ==}
     dependencies:
-      '@storybook/blocks': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/blocks': 7.6.5(react-dom@18.2.0)(react@18.2.0)
       lodash: 4.17.21
       ts-dedent: 2.2.0
     transitivePeerDependencies:
@@ -6314,27 +6319,27 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-docs@7.6.4(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-PbFMbvC9sK3sGdMhwmagXs9TqopTp9FySji+L8O7W9SHRC6wSmdwoWWPWybkOYxr/z/wXi7EM0azSAX7yQxLbw==}
+  /@storybook/addon-docs@7.6.5(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-D9tZyD41IujCHiPYdfS2bKtZRJPNwO4EydzyqODXppomluhFbY3uTEaf0H1UFnJLQxWNXZ7rr3aS0V3O6yu8pA==}
     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.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/client-logger': 7.6.4
-      '@storybook/components': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/csf-plugin': 7.6.4
-      '@storybook/csf-tools': 7.6.4
+      '@storybook/blocks': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/client-logger': 7.6.5
+      '@storybook/components': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/csf-plugin': 7.6.5
+      '@storybook/csf-tools': 7.6.5
       '@storybook/global': 5.0.0
       '@storybook/mdx2-csf': 1.0.0
-      '@storybook/node-logger': 7.6.4
-      '@storybook/postinstall': 7.6.4
-      '@storybook/preview-api': 7.6.4
-      '@storybook/react-dom-shim': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/theming': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.4
+      '@storybook/node-logger': 7.6.5
+      '@storybook/postinstall': 7.6.5
+      '@storybook/preview-api': 7.6.5
+      '@storybook/react-dom-shim': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/theming': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.5
       fs-extra: 11.1.1
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
@@ -6348,25 +6353,25 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-essentials@7.6.4(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-J+zPmP4pbuuFxQ3pjLRYQRnxEtp7jF3xRXGFO8brVnEqtqoxwJ6j3euUrRLe0rpGAU3AD7dYfaaFjd3xkENgTw==}
+  /@storybook/addon-essentials@7.6.5(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-VCLj1JAEpGoqF5iFJOo1CZFFck/tg4m/98DLdQuNuXvxT6jqaF0NI9UUQuJLIGteDCR7NKRbTFc1hV3/Ev+Ziw==}
     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.6.4
-      '@storybook/addon-backgrounds': 7.6.4
-      '@storybook/addon-controls': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-docs': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/addon-highlight': 7.6.4
-      '@storybook/addon-measure': 7.6.4
-      '@storybook/addon-outline': 7.6.4
-      '@storybook/addon-toolbars': 7.6.4
-      '@storybook/addon-viewport': 7.6.4
-      '@storybook/core-common': 7.6.4
-      '@storybook/manager-api': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/node-logger': 7.6.4
-      '@storybook/preview-api': 7.6.4
+      '@storybook/addon-actions': 7.6.5
+      '@storybook/addon-backgrounds': 7.6.5
+      '@storybook/addon-controls': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-docs': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/addon-highlight': 7.6.5
+      '@storybook/addon-measure': 7.6.5
+      '@storybook/addon-outline': 7.6.5
+      '@storybook/addon-toolbars': 7.6.5
+      '@storybook/addon-viewport': 7.6.5
+      '@storybook/core-common': 7.6.5
+      '@storybook/manager-api': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/node-logger': 7.6.5
+      '@storybook/preview-api': 7.6.5
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
       ts-dedent: 2.2.0
@@ -6377,24 +6382,24 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/addon-highlight@7.6.4:
-    resolution: {integrity: sha512-0kvjDzquoPwWWU61QYmEtcSGWXufnV7Z/bfBTYh132uxvV/X9YzDFcXXrxGL7sBJkK32gNUUBDuiTOxs5NxyOQ==}
+  /@storybook/addon-highlight@7.6.5:
+    resolution: {integrity: sha512-CxzmIb30F9nLPQwT0lCPYhOAwGlGF4IkgkO8hYA7VfGCGUkJZEyyN/YkP/ZCUSdCIRChDBouR3KiFFd4mDFKzg==}
     dependencies:
       '@storybook/global': 5.0.0
     dev: true
 
-  /@storybook/addon-interactions@7.6.4:
-    resolution: {integrity: sha512-LjK9uhkgnbGyDwwa7pQhLptDEHeTIFmy+KurfJs9T08DpvRFfuuzyW4mj/hA63R1W5yjFSAhRiZj26+D7kBIyw==}
+  /@storybook/addon-interactions@7.6.5:
+    resolution: {integrity: sha512-8Hzt9u1DQzFvtGER/hCGIvGpCoVwzVoqpM98f2KAIVx/NMFmRW7UyKihXzw1j2t4q2ZaF2jZDYWCBqlP+iwILA==}
     dependencies:
       '@storybook/global': 5.0.0
-      '@storybook/types': 7.6.4
+      '@storybook/types': 7.6.5
       jest-mock: 27.5.1
       polished: 4.2.2
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-links@7.6.4(react@18.2.0):
-    resolution: {integrity: sha512-TEhxYdMhJO28gD84ej1FCwLv9oLuCPt77bRXip9ndaNPRTdHYdWv6IP94dhbuDi8eHux7Z4A/mllciFuDFrnCw==}
+  /@storybook/addon-links@7.6.5(react@18.2.0):
+    resolution: {integrity: sha512-Lx4Ng+iXt0YpIrKGr+nOZlpN9ypOoEDoP/7bZ6m7GXuVAkDm3JrRCBp7e2ZKSKcTxPdjPuO9HVKkIjtqjINlpw==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
     peerDependenciesMeta:
@@ -6407,66 +6412,66 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-measure@7.6.4:
-    resolution: {integrity: sha512-73wsJ8PALsgWniR3MA/cmxcFuU6cRruWdIyYzOMgM8ife2Jm3xSkV7cTTXAqXt2H9Uuki4PGnuMHWWFLpPeyVA==}
+  /@storybook/addon-measure@7.6.5:
+    resolution: {integrity: sha512-tlUudVQSrA+bwI4dhO8J7nYHtYdylcBZ86ybnqMmdTthsnyc7jnaFVQwbb6bbQJpPxvEvoNds5bVGUFocuvymQ==}
     dependencies:
       '@storybook/global': 5.0.0
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/addon-outline@7.6.4:
-    resolution: {integrity: sha512-CFxGASRse/qeFocetDKFNeWZ3Aa2wapVtRciDNa4Zx7k1wCnTjEsPIm54waOuCaNVcrvO+nJUAZG5WyiorQvcg==}
+  /@storybook/addon-outline@7.6.5:
+    resolution: {integrity: sha512-P7X4+Z9L/l/RZW9UvvM+iuK2SUHD22KPc+dbYOifRXDovUqhfmcKVh1CUqTDMyZrg2ZAbropehMz1eI9BlQfxg==}
     dependencies:
       '@storybook/global': 5.0.0
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/addon-storysource@7.6.4:
-    resolution: {integrity: sha512-D63IB8bkqn5ZDq4yjvkcLVfGz3OcAQUohlxSFR1e7COo8jMSTiQWjN7xaVPNOnVJRCj6GrlRlto/hqGl+F+WiQ==}
+  /@storybook/addon-storysource@7.6.5:
+    resolution: {integrity: sha512-mlGReftuGxfyfLXsnw4GF03G79w3rKKRclNasOVPuAR2vlSTRyltoglZ8TcXfxNQ+RzywtEZkjD7SeJZsuvBbQ==}
     dependencies:
-      '@storybook/source-loader': 7.6.4
+      '@storybook/source-loader': 7.6.5
       estraverse: 5.3.0
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/addon-toolbars@7.6.4:
-    resolution: {integrity: sha512-ENMQJgU4sRCLLDVXYfa+P3cQVV9PC0ZxwVAKeM3NPYPNH/ODoryGNtq+Q68LwHlM4ObCE2oc9MzaQqPxloFcCw==}
+  /@storybook/addon-toolbars@7.6.5:
+    resolution: {integrity: sha512-/zqWbVNE/SHc8I5Prnd2Q8U57RGEIYvHfeXjfkuLcE2Quc4Iss4x/9eU7SKu4jm+IOO2s0wlN6HcqI3XEf2XxA==}
     dev: true
 
-  /@storybook/addon-viewport@7.6.4:
-    resolution: {integrity: sha512-SoTcHIoqybhYD28v7QExF1EZnl7FfxuP74VDhtze5LyMd2CbqmVnUfwewLCz/3IvCNce0GqdNyg1m6QJ7Eq1uw==}
+  /@storybook/addon-viewport@7.6.5:
+    resolution: {integrity: sha512-9ghKTaduIUvQ6oShmWLuwMeTjtMR4RgKeKHrTJ7THMqvE/ydDPCYeL7ugF65ocXZSEz/QmxdK7uL686ZMKsqNA==}
     dependencies:
       memoizerific: 1.11.3
     dev: true
 
-  /@storybook/addons@7.6.4(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-YnmLyR/ciALtzoi9HEu+Y+NJWeOVEBo9PRgQaG7zGiNDvOrLY69uU3Ej0+TZlrTqBqce42bRCrDINJfnk0Mfsg==}
+  /@storybook/addons@7.6.5(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-v+d8io1MsgTd7rruYInfKXY0c1uXn+ADLxAppUI0PUwPFYwg9tLn3cvwgt5SVum9E5IkVQwXoW6JNkDC5fC8XQ==}
     dependencies:
-      '@storybook/manager-api': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.6.4
-      '@storybook/types': 7.6.4
+      '@storybook/manager-api': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.6.5
+      '@storybook/types': 7.6.5
     transitivePeerDependencies:
       - react
       - react-dom
     dev: true
 
-  /@storybook/blocks@7.6.4(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-iXinXXhTUBtReREP1Jifpu35DnGg7FidehjvCM8sM4E4aymfb8czdg9DdvG46T2UFUPUct36nnjIdMLWOya8Bw==}
+  /@storybook/blocks@7.6.5(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-/NjuYkPks5w9lKn47KLgVC5cBkwfc+ERAp0CY0Xe//BQJkP+bcI8lE8d9Qc9IXFbOTvYEULeQrFgCkesk5BmLg==}
     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.6.4
-      '@storybook/client-logger': 7.6.4
-      '@storybook/components': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.6.4
+      '@storybook/channels': 7.6.5
+      '@storybook/client-logger': 7.6.5
+      '@storybook/components': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/core-events': 7.6.5
       '@storybook/csf': 0.1.2
-      '@storybook/docs-tools': 7.6.4
+      '@storybook/docs-tools': 7.6.5
       '@storybook/global': 5.0.0
-      '@storybook/manager-api': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.6.4
-      '@storybook/theming': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.4
+      '@storybook/manager-api': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.6.5
+      '@storybook/theming': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.5
       '@types/lodash': 4.14.191
       color-convert: 2.0.1
       dequal: 2.0.3
@@ -6488,13 +6493,13 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-manager@7.6.4:
-    resolution: {integrity: sha512-k5+D3fXw7LdMOWd5tF7cIq8L3irrdW6/vmcEHLaJj1EXZ+DvsNCH9xSsLS+6zfrUcxug4oSfRqvF87w6Oz3DtA==}
+  /@storybook/builder-manager@7.6.5:
+    resolution: {integrity: sha512-FQyI+tfzMam2XKXq7k921YVafIJs9Vqvos5qx8vyRnRffo55UU8tgunwjGn0PswtbMm6sThVqE0C0ZzVr7RG8A==}
     dependencies:
       '@fal-works/esbuild-plugin-global-externals': 2.1.2
-      '@storybook/core-common': 7.6.4
-      '@storybook/manager': 7.6.4
-      '@storybook/node-logger': 7.6.4
+      '@storybook/core-common': 7.6.5
+      '@storybook/manager': 7.6.5
+      '@storybook/node-logger': 7.6.5
       '@types/ejs': 3.1.2
       '@types/find-cache-dir': 3.2.1
       '@yarnpkg/esbuild-plugin-pnp': 3.0.0-rc.15(esbuild@0.18.20)
@@ -6512,8 +6517,8 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/builder-vite@7.6.4(typescript@5.3.3)(vite@5.0.8):
-    resolution: {integrity: sha512-eqb3mLUfuXd4a7+46cWevQ9qH81FvHy1lrAbZGwp4bQ/Tj0YF8Ej7lKBbg7zoIwiu2zDci+BbMiaDOY1kPtILw==}
+  /@storybook/builder-vite@7.6.5(typescript@5.3.3)(vite@5.0.10):
+    resolution: {integrity: sha512-VbAYTGr92lgCWTwO2Z7NgSW3f5/K4Vr0Qxa2IlTgMCymWdDbWdIQiREcmCP0vjAGM2ftq1+vxngohVgx/r7pUw==}
     peerDependencies:
       '@preact/preset-vite': '*'
       typescript: '>= 4.3.x'
@@ -6527,14 +6532,14 @@ packages:
       vite-plugin-glimmerx:
         optional: true
     dependencies:
-      '@storybook/channels': 7.6.4
-      '@storybook/client-logger': 7.6.4
-      '@storybook/core-common': 7.6.4
-      '@storybook/csf-plugin': 7.6.4
-      '@storybook/node-logger': 7.6.4
-      '@storybook/preview': 7.6.4
-      '@storybook/preview-api': 7.6.4
-      '@storybook/types': 7.6.4
+      '@storybook/channels': 7.6.5
+      '@storybook/client-logger': 7.6.5
+      '@storybook/core-common': 7.6.5
+      '@storybook/csf-plugin': 7.6.5
+      '@storybook/node-logger': 7.6.5
+      '@storybook/preview': 7.6.5
+      '@storybook/preview-api': 7.6.5
+      '@storybook/types': 7.6.5
       '@types/find-cache-dir': 3.2.1
       browser-assert: 1.2.1
       es-module-lexer: 0.9.3
@@ -6544,7 +6549,7 @@ packages:
       magic-string: 0.30.5
       rollup: 3.29.4
       typescript: 5.3.3
-      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -6561,22 +6566,33 @@ packages:
       tiny-invariant: 1.3.1
     dev: true
 
-  /@storybook/cli@7.6.4:
-    resolution: {integrity: sha512-GqvaFdkkBMJOdnrVe82XY0V3b+qFMhRNyVoTv2nqB87iMUXZHqh4Pu4LqwaJBsBpuNregvCvVOPe9LGgoOzy4A==}
+  /@storybook/channels@7.6.5:
+    resolution: {integrity: sha512-FIlNkyfQy9uHoJfAFL2/wO3ASGJELFvBzURBE2rcEF/TS7GcUiqWnBfiDxAbwSEjSOm2F0eEq3UXhaZEjpJHDw==}
+    dependencies:
+      '@storybook/client-logger': 7.6.5
+      '@storybook/core-events': 7.6.5
+      '@storybook/global': 5.0.0
+      qs: 6.11.1
+      telejson: 7.2.0
+      tiny-invariant: 1.3.1
+    dev: true
+
+  /@storybook/cli@7.6.5:
+    resolution: {integrity: sha512-w+Y8dx5oCLQVESOVmpsQuFksr/ewARKrnSKl9kwnVMN4sMgjOgoZ3zmV66J7SKexvwyuwlOjf840pmEglGdPPg==}
     hasBin: true
     dependencies:
       '@babel/core': 7.23.5
       '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
       '@babel/types': 7.23.5
       '@ndelangen/get-tarball': 3.0.7
-      '@storybook/codemod': 7.6.4
-      '@storybook/core-common': 7.6.4
-      '@storybook/core-events': 7.6.4
-      '@storybook/core-server': 7.6.4
-      '@storybook/csf-tools': 7.6.4
-      '@storybook/node-logger': 7.6.4
-      '@storybook/telemetry': 7.6.4
-      '@storybook/types': 7.6.4
+      '@storybook/codemod': 7.6.5
+      '@storybook/core-common': 7.6.5
+      '@storybook/core-events': 7.6.5
+      '@storybook/core-server': 7.6.5
+      '@storybook/csf-tools': 7.6.5
+      '@storybook/node-logger': 7.6.5
+      '@storybook/telemetry': 7.6.5
+      '@storybook/types': 7.6.5
       '@types/semver': 7.5.6
       '@yarnpkg/fslib': 2.10.3
       '@yarnpkg/libzip': 2.3.0
@@ -6619,16 +6635,22 @@ packages:
       '@storybook/global': 5.0.0
     dev: true
 
-  /@storybook/codemod@7.6.4:
-    resolution: {integrity: sha512-q4rZVOfozxzbDRH/LzuFDoIGBdXs+orAm18fi6iAx8PeMHe8J/MOXKccNV1zdkm/h7mTQowuRo45KwJHw8vX+g==}
+  /@storybook/client-logger@7.6.5:
+    resolution: {integrity: sha512-S5aROWgssqg7tcs9lgW5wmCAz4SxMAtioiyVj5oFecmPCbQtFVIAREYzeoxE4GfJL+plrfRkum4BzziANn8EhQ==}
+    dependencies:
+      '@storybook/global': 5.0.0
+    dev: true
+
+  /@storybook/codemod@7.6.5:
+    resolution: {integrity: sha512-K5C9ltBClZ0aSyujGt3RJFtRicrUZy8nzhHrcADUj27rrQD26jH/p+Y05jWKj9JcI8SyMg978GN5X/1aw2Y31A==}
     dependencies:
       '@babel/core': 7.23.5
       '@babel/preset-env': 7.23.5(@babel/core@7.23.5)
       '@babel/types': 7.23.5
       '@storybook/csf': 0.1.2
-      '@storybook/csf-tools': 7.6.4
-      '@storybook/node-logger': 7.6.4
-      '@storybook/types': 7.6.4
+      '@storybook/csf-tools': 7.6.5
+      '@storybook/node-logger': 7.6.5
+      '@storybook/types': 7.6.5
       '@types/cross-spawn': 6.0.2
       cross-spawn: 7.0.3
       globby: 11.1.0
@@ -6663,19 +6685,42 @@ packages:
       - '@types/react-dom'
     dev: true
 
-  /@storybook/core-client@7.6.4:
-    resolution: {integrity: sha512-0msqdGd+VYD1dRgAJ2StTu4d543Wveb7LVVujX3PwD/QCxmCaVUHuAoZrekM/H7jZLw546ZIbLZo0xWrADAUMw==}
+  /@storybook/components@7.6.5(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-w4ZucbBBZ+NKMWlJKVj2I/bMBBq7gzDp9lzc4+8QaQ3vUPXKqc1ilIPYo/7UR5oxwDVMZocmMSgl9L8lvf7+Mw==}
+    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.6.4
-      '@storybook/preview-api': 7.6.4
+      '@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.6.5
+      '@storybook/csf': 0.1.2
+      '@storybook/global': 5.0.0
+      '@storybook/theming': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.5
+      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.6.4:
-    resolution: {integrity: sha512-qes4+mXqINu0kCgSMFjk++GZokmYjb71esId0zyJsk0pcIPkAiEjnhbSEQkMhbUfcvO1lztoaQTBW2P7Rd1tag==}
+  /@storybook/core-client@7.6.5:
+    resolution: {integrity: sha512-6FtyJcz8MSl+JYwNJZ53FM6rkT27pFHWcJPdtw/9229Ec8as9RpkNeZ/NBZjRTeDkn9Ki0VOiVAefNie9tZ/8Q==}
     dependencies:
-      '@storybook/core-events': 7.6.4
-      '@storybook/node-logger': 7.6.4
-      '@storybook/types': 7.6.4
+      '@storybook/client-logger': 7.6.5
+      '@storybook/preview-api': 7.6.5
+    dev: true
+
+  /@storybook/core-common@7.6.5:
+    resolution: {integrity: sha512-z4EgzZSIVbID6Ib0jhh3jimKeaDWU8OOhoZYfn3galFmgQWowWOv1oMgipWiXfRLWw9DaLFQiCHIdLANH+VO2g==}
+    dependencies:
+      '@storybook/core-events': 7.6.5
+      '@storybook/node-logger': 7.6.5
+      '@storybook/types': 7.6.5
       '@types/find-cache-dir': 3.2.1
       '@types/node': 18.17.15
       '@types/node-fetch': 2.6.4
@@ -6707,24 +6752,30 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/core-server@7.6.4:
-    resolution: {integrity: sha512-mXxZMpCwOhjEPPRjqrTHdiCpFdkc47f46vlgTj02SX+9xKHxslmZ2D3JG/8O4Ab9tG+bBl6lBm3RIrIzaiCu9Q==}
+  /@storybook/core-events@7.6.5:
+    resolution: {integrity: sha512-zk2q/qicYXAzHA4oV3GDbIql+Kd4TOHUgDE8e4jPCOPp856z2ScqEKUAbiJizs6eEJOH4nW9Db1kuzgrBVEykQ==}
+    dependencies:
+      ts-dedent: 2.2.0
+    dev: true
+
+  /@storybook/core-server@7.6.5:
+    resolution: {integrity: sha512-BfKzK/ObTjUcPvE5/r1pogCifM/4nLRhOUYJl7XekwHkOQwn19e6H3/ku1W3jDoYXBu642Dc9X7l/ERjKTqxFg==}
     dependencies:
       '@aw-web-design/x-default-browser': 1.4.126
       '@discoveryjs/json-ext': 0.5.7
-      '@storybook/builder-manager': 7.6.4
-      '@storybook/channels': 7.6.4
-      '@storybook/core-common': 7.6.4
-      '@storybook/core-events': 7.6.4
+      '@storybook/builder-manager': 7.6.5
+      '@storybook/channels': 7.6.5
+      '@storybook/core-common': 7.6.5
+      '@storybook/core-events': 7.6.5
       '@storybook/csf': 0.1.2
-      '@storybook/csf-tools': 7.6.4
+      '@storybook/csf-tools': 7.6.5
       '@storybook/docs-mdx': 0.1.0
       '@storybook/global': 5.0.0
-      '@storybook/manager': 7.6.4
-      '@storybook/node-logger': 7.6.4
-      '@storybook/preview-api': 7.6.4
-      '@storybook/telemetry': 7.6.4
-      '@storybook/types': 7.6.4
+      '@storybook/manager': 7.6.5
+      '@storybook/node-logger': 7.6.5
+      '@storybook/preview-api': 7.6.5
+      '@storybook/telemetry': 7.6.5
+      '@storybook/types': 7.6.5
       '@types/detect-port': 1.3.2
       '@types/node': 18.17.15
       '@types/pretty-hrtime': 1.0.1
@@ -6758,24 +6809,24 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/csf-plugin@7.6.4:
-    resolution: {integrity: sha512-7g9p8s2ITX+Z9iThK5CehPhJOcusVN7JcUEEW+gVF5PlYT+uk/x+66gmQno+scQuNkV9+8UJD6RLFjP+zg2uCA==}
+  /@storybook/csf-plugin@7.6.5:
+    resolution: {integrity: sha512-iQ8Y/Qq1IUhHRddjDVicWJA2sM7OZA1FR97OvWUT2240WjCuQSCfy32JD8TQlYjqXgEolJeLPv3zW4qH5om4LQ==}
     dependencies:
-      '@storybook/csf-tools': 7.6.4
+      '@storybook/csf-tools': 7.6.5
       unplugin: 1.4.0
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@storybook/csf-tools@7.6.4:
-    resolution: {integrity: sha512-6sLayuhgReIK3/QauNj5BW4o4ZfEMJmKf+EWANPEM/xEOXXqrog6Un8sjtBuJS9N1DwyhHY6xfkEiPAwdttwqw==}
+  /@storybook/csf-tools@7.6.5:
+    resolution: {integrity: sha512-1iaCh7nt+WE7Q5UwRhLLc5flMNoAV/vBr0tvDSCKiHaO+D3dZzlZOe/U+S6wegdyN2QNcvT2xs179CcrX6Qp6w==}
     dependencies:
       '@babel/generator': 7.23.5
       '@babel/parser': 7.23.5
       '@babel/traverse': 7.23.5
       '@babel/types': 7.23.5
       '@storybook/csf': 0.1.2
-      '@storybook/types': 7.6.4
+      '@storybook/types': 7.6.5
       fs-extra: 11.1.1
       recast: 0.23.4
       ts-dedent: 2.2.0
@@ -6793,12 +6844,12 @@ packages:
     resolution: {integrity: sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==}
     dev: true
 
-  /@storybook/docs-tools@7.6.4:
-    resolution: {integrity: sha512-2eGam43aD7O3cocA72Z63kRi7t/ziMSpst0qB218QwBWAeZjT4EYDh8V6j/Xhv6zVQL3msW7AglrQP5kCKPvPA==}
+  /@storybook/docs-tools@7.6.5:
+    resolution: {integrity: sha512-UyHkHu5Af6jMpYsR4lZ69D32GQGeA0pLAn7jaBbQndgAjBdK1ykZcifiUC7Wz1hG7+YpuYspEGuDEddOh+X8FQ==}
     dependencies:
-      '@storybook/core-common': 7.6.4
-      '@storybook/preview-api': 7.6.4
-      '@storybook/types': 7.6.4
+      '@storybook/core-common': 7.6.5
+      '@storybook/preview-api': 7.6.5
+      '@storybook/types': 7.6.5
       '@types/doctrine': 0.0.3
       assert: 2.1.0
       doctrine: 3.0.0
@@ -6831,17 +6882,17 @@ packages:
       - vitest
     dev: true
 
-  /@storybook/manager-api@7.6.4(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-RFb/iaBJfXygSgXkINPRq8dXu7AxBicTGX7MxqKXbz5FU7ANwV7abH6ONBYURkSDOH9//TQhRlVkF5u8zWg3bw==}
+  /@storybook/manager-api@7.6.5(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-tE3OShOcs6A3XtI3NJd6hYQOZLaP++Fn0dCtowBwYh/vS1EN/AyroVmL97tsxn1DZTyoRt0GidwbB6dvLMBOwA==}
     dependencies:
-      '@storybook/channels': 7.6.4
-      '@storybook/client-logger': 7.6.4
-      '@storybook/core-events': 7.6.4
+      '@storybook/channels': 7.6.5
+      '@storybook/client-logger': 7.6.5
+      '@storybook/core-events': 7.6.5
       '@storybook/csf': 0.1.2
       '@storybook/global': 5.0.0
-      '@storybook/router': 7.6.4
-      '@storybook/theming': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.4
+      '@storybook/router': 7.6.5
+      '@storybook/theming': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.5
       dequal: 2.0.3
       lodash: 4.17.21
       memoizerific: 1.11.3
@@ -6854,31 +6905,31 @@ packages:
       - react-dom
     dev: true
 
-  /@storybook/manager@7.6.4:
-    resolution: {integrity: sha512-Ug2ejfKgKre8h/RJbkumukwAA44TbvTPEjDcJmyFdAI+kHYhOYdKPEC2UNmVYz8/4HjwMTJQ3M7t/esK8HHY4A==}
+  /@storybook/manager@7.6.5:
+    resolution: {integrity: sha512-y1KLH0O1PGPyMxGMvOhppzFSO7r4ibjTve5iqsI0JZwxUjNuBKRLYbrhXdAyC2iacvxYNrHgevae1k9XdD+FQw==}
     dev: true
 
   /@storybook/mdx2-csf@1.0.0:
     resolution: {integrity: sha512-dBAnEL4HfxxJmv7LdEYUoZlQbWj9APZNIbOaq0tgF8XkxiIbzqvgB0jhL/9UOrysSDbQWBiCRTu2wOVxedGfmw==}
     dev: true
 
-  /@storybook/node-logger@7.6.4:
-    resolution: {integrity: sha512-GDkEnnDj4Op+PExs8ZY/P6ox3wg453CdEIaR8PR9TxF/H/T2fBL6puzma3hN2CMam6yzfAL8U+VeIIDLQ5BZdQ==}
+  /@storybook/node-logger@7.6.5:
+    resolution: {integrity: sha512-xKw6IH1wLkIssekdBv3bd13xYKUF1t8EwqDR8BYcN8AVjZlqJMTifssqG4bYV+G/B7J3tz4ugJ5nmtWg6RQ0Qw==}
     dev: true
 
-  /@storybook/postinstall@7.6.4:
-    resolution: {integrity: sha512-7uoB82hSzlFSdDMS3hKQD+AaeSvPit/fAMvXCBxn0/D0UGJUZcq4M9JcKBwEHkZJcbuDROgOTJ6TUeXi/FWO0w==}
+  /@storybook/postinstall@7.6.5:
+    resolution: {integrity: sha512-12WxfpqGKsk7GQ3KWiZSbamsYK8vtRmhOTkavZ9IQkcJ/zuVfmqK80/Mds+njJMudUPzuREuSFGWACczo17EDA==}
     dev: true
 
-  /@storybook/preview-api@7.6.4:
-    resolution: {integrity: sha512-KhisNdQX5NdfAln+spLU4B82d804GJQp/CnI5M1mm/taTnjvMgs/wTH9AmR89OPoq+tFZVW0vhy2zgPS3ar71A==}
+  /@storybook/preview-api@7.6.5:
+    resolution: {integrity: sha512-9XzuDXXgNuA6dDZ3DXsUwEG6ElxeTbzLuYuzcjtS1FusSICZ2iYmxfS0GfSud9MjPPYOJYoSOvMdIHjorjgByA==}
     dependencies:
-      '@storybook/channels': 7.6.4
-      '@storybook/client-logger': 7.6.4
-      '@storybook/core-events': 7.6.4
+      '@storybook/channels': 7.6.5
+      '@storybook/client-logger': 7.6.5
+      '@storybook/core-events': 7.6.5
       '@storybook/csf': 0.1.2
       '@storybook/global': 5.0.0
-      '@storybook/types': 7.6.4
+      '@storybook/types': 7.6.5
       '@types/qs': 6.9.7
       dequal: 2.0.3
       lodash: 4.17.21
@@ -6889,12 +6940,12 @@ packages:
       util-deprecate: 1.0.2
     dev: true
 
-  /@storybook/preview@7.6.4:
-    resolution: {integrity: sha512-p9xIvNkgXgTpSRphOMV9KpIiNdkymH61jBg3B0XyoF6IfM1S2/mQGvC89lCVz1dMGk2SrH4g87/WcOapkU5ArA==}
+  /@storybook/preview@7.6.5:
+    resolution: {integrity: sha512-zmLa7C7yFGTYhgGZXoecdww9rx0Z5HpNi/GDBRWoNSK+FEdE8Jj2jF5NJ2ncldtYIyegz9ku29JFMKbhMj9K5Q==}
     dev: true
 
-  /@storybook/react-dom-shim@7.6.4(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-wGJfomlDEBnowNmhmumWDu/AcUInxSoPqUUJPgk2f5oL0EW17fR9fDP/juG3XOEdieMDM0jDX48GML7lyvL2fg==}
+  /@storybook/react-dom-shim@7.6.5(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-Qp3N3zENdvx20ikHmz5yI03z+mAWF8bUAwUofqXarVtZUkBNtvfTfUwgAezOAF0eClClH+ktIziIKd976tLSPw==}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6903,24 +6954,24 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/react-vite@7.6.4(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.0)(typescript@5.3.3)(vite@5.0.8):
-    resolution: {integrity: sha512-1NYzCJRO6k/ZyoMzpu1FQiaUaiLNjAvTAB1x3HE7oY/tEIT8kGpzXGYH++LJVWvyP/5dSWlUnRSy2rJvySraiw==}
+  /@storybook/react-vite@7.6.5(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.0)(typescript@5.3.3)(vite@5.0.10):
+    resolution: {integrity: sha512-fIoSBbou3rQdOo6qX/nD5givb3qIOSwXeZWjAqRB6560cqmeSQFlRGtKUJ0nzQYADwJ0/iNHz3nOvJOOSnPepA==}
     engines: {node: '>=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 || ^5.0.0
     dependencies:
-      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.0.8)
+      '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.0.10)
       '@rollup/pluginutils': 5.1.0(rollup@4.9.0)
-      '@storybook/builder-vite': 7.6.4(typescript@5.3.3)(vite@5.0.8)
-      '@storybook/react': 7.6.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
-      '@vitejs/plugin-react': 3.1.0(vite@5.0.8)
+      '@storybook/builder-vite': 7.6.5(typescript@5.3.3)(vite@5.0.10)
+      '@storybook/react': 7.6.5(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
+      '@vitejs/plugin-react': 3.1.0(vite@5.0.10)
       magic-string: 0.30.5
       react: 18.2.0
       react-docgen: 7.0.1
       react-dom: 18.2.0(react@18.2.0)
-      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - encoding
@@ -6930,8 +6981,8 @@ packages:
       - vite-plugin-glimmerx
     dev: true
 
-  /@storybook/react@7.6.4(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3):
-    resolution: {integrity: sha512-XYRP+eylH3JqkCuziwtQGY5vOCeDreOibRYJmj5na6k4QbURjGVB44WCIW04gWVlmBXM9SqLAmserUi3HP890Q==}
+  /@storybook/react@7.6.5(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3):
+    resolution: {integrity: sha512-z0l5T+gL//VekMXnHi+lW5qr7OQ8X7WoeIRMk38e62ppSpGUZRfoxRmmhU/9YcIFAlCgMaoLSYmhOceKGRZuVw==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
       react: ^16.8.0 || ^17.0.0 || ^18.0.0
@@ -6941,13 +6992,13 @@ packages:
       typescript:
         optional: true
     dependencies:
-      '@storybook/client-logger': 7.6.4
-      '@storybook/core-client': 7.6.4
-      '@storybook/docs-tools': 7.6.4
+      '@storybook/client-logger': 7.6.5
+      '@storybook/core-client': 7.6.5
+      '@storybook/docs-tools': 7.6.5
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.6.4
-      '@storybook/react-dom-shim': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.4
+      '@storybook/preview-api': 7.6.5
+      '@storybook/react-dom-shim': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.5
       '@types/escodegen': 0.0.6
       '@types/estree': 0.0.51
       '@types/node': 18.17.15
@@ -6970,30 +7021,30 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/router@7.6.4:
-    resolution: {integrity: sha512-5MQ7Z4D7XNPN2yhFgjey7hXOYd6s8CggUqeAwhzGTex90SMCkKHSz1hfkcXn1ZqBPaall2b53uK553OvPLp9KQ==}
+  /@storybook/router@7.6.5:
+    resolution: {integrity: sha512-QiTC86gRuoepzzmS6HNJZTwfz/n27NcqtaVEIxJi1Yvsx2/kLa9NkRhylNkfTuZ1gEry9stAlKWanMsB2aKyjQ==}
     dependencies:
-      '@storybook/client-logger': 7.6.4
+      '@storybook/client-logger': 7.6.5
       memoizerific: 1.11.3
       qs: 6.11.1
     dev: true
 
-  /@storybook/source-loader@7.6.4:
-    resolution: {integrity: sha512-1wb/3bVpJZ/3r3qUrLK8jb0kLuvwjNi5T1kci5huREdc1TrIxZXoPw9EiyjcMCZzCURkoj7euNLrLHGyzdBTLg==}
+  /@storybook/source-loader@7.6.5:
+    resolution: {integrity: sha512-3GpXJY9GUOOl3Uq/xcsJ12XWLBNZJwUWzwkBm4Eev1xl5eg/ygeyJflwM5egsA1NfkV77hNxtjQcbfw4cBtqdg==}
     dependencies:
       '@storybook/csf': 0.1.2
-      '@storybook/types': 7.6.4
+      '@storybook/types': 7.6.5
       estraverse: 5.3.0
       lodash: 4.17.21
       prettier: 2.8.8
     dev: true
 
-  /@storybook/telemetry@7.6.4:
-    resolution: {integrity: sha512-Q4QpvcgloHUEqC9PGo7tgqkUH91/PjX+74/0Hi9orLo8QmLMgdYS5fweFwgSKoTwDGNg2PaHp/jqvhhw7UmnJA==}
+  /@storybook/telemetry@7.6.5:
+    resolution: {integrity: sha512-FiLRh9k9LoGphqgBqPYySWdGqplihiZyDwqdo+Qs19RcQ/eiKg0W7fdA09nStcdcsHmDl/1cMfRhz9KUiMtwOw==}
     dependencies:
-      '@storybook/client-logger': 7.6.4
-      '@storybook/core-common': 7.6.4
-      '@storybook/csf-tools': 7.6.4
+      '@storybook/client-logger': 7.6.5
+      '@storybook/core-common': 7.6.5
+      '@storybook/csf-tools': 7.6.5
       chalk: 4.1.2
       detect-package-manager: 2.0.1
       fetch-retry: 5.0.4
@@ -7026,6 +7077,20 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
+  /@storybook/theming@7.6.5(react-dom@18.2.0)(react@18.2.0):
+    resolution: {integrity: sha512-RpcWT0YEgiobO41McVPDfQQHHFnjyr1sJnNTPJIvOUgSfURdgSj17mQVxtD5xcXcPWUdle5UhIOrCixHbL/NNw==}
+    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.6.5
+      '@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.6.4:
     resolution: {integrity: sha512-qyiiXPCvol5uVgfubcIMzJBA0awAyFPU+TyUP1mkPYyiTHnsHYel/mKlSdPjc8a97N3SlJXHOCx41Hde4IyJgg==}
     dependencies:
@@ -7035,18 +7100,27 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.6.4(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.8)(vue@3.3.11):
-    resolution: {integrity: sha512-M1cT6lZsRqwws+H+pv2K/jJGiXUfGHAz7nc0DwK/YYT/w0OOVp5hinh/IvNwIYW5zUmwIyQVEoGrrxQEi0S79w==}
+  /@storybook/types@7.6.5:
+    resolution: {integrity: sha512-Q757v+fYZZSaEpks/zDL5YgXRozxkgKakXFc+BoQHK5q5sVhJ+0jvpLJiAQAniIIaMIkqY/G24Kd6Uo6UdKBCg==}
+    dependencies:
+      '@storybook/channels': 7.6.5
+      '@types/babel__core': 7.20.0
+      '@types/express': 4.17.17
+      file-system-cache: 2.3.0
+    dev: true
+
+  /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.11):
+    resolution: {integrity: sha512-7wUCq2Lrjlekftd5ha3hG0GSGbbzuc370cKkBqSmwFuOfI38z5+VeYt7nDtAlncxcpVSH7DejTGRuKTlC7NyYg==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
       vite: ^3.0.0 || ^4.0.0 || ^5.0.0
     dependencies:
-      '@storybook/builder-vite': 7.6.4(typescript@5.3.3)(vite@5.0.8)
-      '@storybook/core-server': 7.6.4
-      '@storybook/vue3': 7.6.4(@vue/compiler-core@3.3.11)(vue@3.3.11)
-      '@vitejs/plugin-vue': 4.5.2(vite@5.0.8)(vue@3.3.11)
+      '@storybook/builder-vite': 7.6.5(typescript@5.3.3)(vite@5.0.10)
+      '@storybook/core-server': 7.6.5
+      '@storybook/vue3': 7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.11)
+      '@vitejs/plugin-vue': 4.5.2(vite@5.0.10)(vue@3.3.11)
       magic-string: 0.30.5
-      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
       vue-docgen-api: 4.64.1(vue@3.3.11)
     transitivePeerDependencies:
       - '@preact/preset-vite'
@@ -7060,18 +7134,18 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.6.4(@vue/compiler-core@3.3.11)(vue@3.3.11):
-    resolution: {integrity: sha512-9BYxqj9C30/7UBspP4sE0UE/4fgE17jymJHWkxFbEE95MfbLkDFRnJ3Y09VPK0OnmbsBW5xBciMyjGV9Lq2pmg==}
+  /@storybook/vue3@7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.11):
+    resolution: {integrity: sha512-tv/9rVc3XXDOJu5hfZtKhrhM8x4GTLKon62Rmaxlq06weqkGlfBi/V/g1EZ7OE71Pi+woKS/TX7p9qbRrvgahg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
       '@vue/compiler-core': ^3.0.0
       vue: ^3.0.0
     dependencies:
-      '@storybook/core-client': 7.6.4
-      '@storybook/docs-tools': 7.6.4
+      '@storybook/core-client': 7.6.5
+      '@storybook/docs-tools': 7.6.5
       '@storybook/global': 5.0.0
-      '@storybook/preview-api': 7.6.4
-      '@storybook/types': 7.6.4
+      '@storybook/preview-api': 7.6.5
+      '@storybook/types': 7.6.5
       '@vue/compiler-core': 3.3.11
       lodash: 4.17.21
       ts-dedent: 2.2.0
@@ -7539,7 +7613,7 @@ packages:
       dom-accessibility-api: 0.5.16
       lodash: 4.17.21
       redent: 3.0.0
-      vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.24.0)
+      vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.26.0)
     dev: true
 
   /@testing-library/user-event@14.4.3(@testing-library/dom@9.2.0):
@@ -7795,6 +7869,10 @@ packages:
   /@types/http-cache-semantics@4.0.1:
     resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==}
 
+  /@types/http-cache-semantics@4.0.4:
+    resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
+    dev: false
+
   /@types/http-link-header@1.0.5:
     resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==}
     dependencies:
@@ -8209,7 +8287,7 @@ packages:
       '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.53.0
       graphemer: 1.4.0
       ignore: 5.2.4
@@ -8221,7 +8299,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/eslint-plugin@6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0)(typescript@5.3.3):
+  /@typescript-eslint/eslint-plugin@6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)(typescript@5.3.3):
     resolution: {integrity: sha512-1ZJBykBCXaSHG94vMMKmiHoL0MhNHKSVlcHVYZNw+BKxufhqQVTOawNpwwI1P5nIFZ/4jLVop0mcY6mJJDFNaw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8233,13 +8311,13 @@ packages:
         optional: true
     dependencies:
       '@eslint-community/regexpp': 4.6.2
-      '@typescript-eslint/parser': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 6.14.0(eslint@8.56.0)(typescript@5.3.3)
       '@typescript-eslint/scope-manager': 6.14.0
-      '@typescript-eslint/type-utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/type-utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3)
+      '@typescript-eslint/utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.14.0
-      debug: 4.3.4(supports-color@5.5.0)
-      eslint: 8.55.0
+      debug: 4.3.4(supports-color@8.1.1)
+      eslint: 8.56.0
       graphemer: 1.4.0
       ignore: 5.2.4
       natural-compare: 1.4.0
@@ -8264,14 +8342,14 @@ packages:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.53.0
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@typescript-eslint/parser@6.14.0(eslint@8.55.0)(typescript@5.3.3):
+  /@typescript-eslint/parser@6.14.0(eslint@8.56.0)(typescript@5.3.3):
     resolution: {integrity: sha512-QjToC14CKacd4Pa7JK4GeB/vHmWFJckec49FR4hmIRf97+KXole0T97xxu9IFiPxVQ1DBWrQ5wreLwAGwWAVQA==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8285,8 +8363,8 @@ packages:
       '@typescript-eslint/types': 6.14.0
       '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.14.0
-      debug: 4.3.4(supports-color@5.5.0)
-      eslint: 8.55.0
+      debug: 4.3.4(supports-color@8.1.1)
+      eslint: 8.56.0
       typescript: 5.3.3
     transitivePeerDependencies:
       - supports-color
@@ -8320,7 +8398,7 @@ packages:
     dependencies:
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       eslint: 8.53.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
       typescript: 5.3.3
@@ -8328,7 +8406,7 @@ packages:
       - supports-color
     dev: true
 
-  /@typescript-eslint/type-utils@6.14.0(eslint@8.55.0)(typescript@5.3.3):
+  /@typescript-eslint/type-utils@6.14.0(eslint@8.56.0)(typescript@5.3.3):
     resolution: {integrity: sha512-x6OC9Q7HfYKqjnuNu5a7kffIYs3No30isapRBJl1iCHLitD8O0lFbRcVGiOcuyN837fqXzPZ1NS10maQzZMKqw==}
     engines: {node: ^16.0.0 || >=18.0.0}
     peerDependencies:
@@ -8339,9 +8417,9 @@ packages:
         optional: true
     dependencies:
       '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3)
-      '@typescript-eslint/utils': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
-      debug: 4.3.4(supports-color@5.5.0)
-      eslint: 8.55.0
+      '@typescript-eslint/utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3)
+      debug: 4.3.4(supports-color@8.1.1)
+      eslint: 8.56.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
       typescript: 5.3.3
     transitivePeerDependencies:
@@ -8369,7 +8447,7 @@ packages:
     dependencies:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
@@ -8390,7 +8468,7 @@ packages:
     dependencies:
       '@typescript-eslint/types': 6.14.0
       '@typescript-eslint/visitor-keys': 6.14.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
@@ -8419,19 +8497,19 @@ packages:
       - typescript
     dev: true
 
-  /@typescript-eslint/utils@6.14.0(eslint@8.55.0)(typescript@5.3.3):
+  /@typescript-eslint/utils@6.14.0(eslint@8.56.0)(typescript@5.3.3):
     resolution: {integrity: sha512-XwRTnbvRr7Ey9a1NT6jqdKX8y/atWG+8fAIu3z73HSP8h06i3r/ClMhmaF/RGWGW1tHJEwij1uEg2GbEmPYvYg==}
     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.55.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0)
       '@types/json-schema': 7.0.12
       '@types/semver': 7.5.6
       '@typescript-eslint/scope-manager': 6.14.0
       '@typescript-eslint/types': 6.14.0
       '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3)
-      eslint: 8.55.0
+      eslint: 8.56.0
       semver: 7.5.4
     transitivePeerDependencies:
       - supports-color
@@ -8458,7 +8536,7 @@ packages:
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
     dev: true
 
-  /@vitejs/plugin-react@3.1.0(vite@5.0.8):
+  /@vitejs/plugin-react@3.1.0(vite@5.0.10):
     resolution: {integrity: sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
@@ -8469,19 +8547,19 @@ packages:
       '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.23.5)
       magic-string: 0.27.0
       react-refresh: 0.14.0
-      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@vitejs/plugin-vue@4.5.2(vite@5.0.8)(vue@3.3.11):
+  /@vitejs/plugin-vue@4.5.2(vite@5.0.10)(vue@3.3.11):
     resolution: {integrity: sha512-UGR3DlzLi/SaVBPX0cnSyE37vqxU3O6chn8l0HJNzQzDia6/Au2A4xKv+iIJW8w2daf80G7TYHhi1pAUjdZ0bQ==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
       vite: ^4.0.0 || ^5.0.0
       vue: ^3.2.25
     dependencies:
-      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
       vue: 3.3.11(typescript@5.3.3)
 
   /@vitest/coverage-v8@0.34.6(vitest@0.34.6):
@@ -8500,7 +8578,7 @@ packages:
       std-env: 3.3.3
       test-exclude: 6.0.0
       v8-to-istanbul: 9.1.0
-      vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.24.0)
+      vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.26.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -8824,7 +8902,7 @@ packages:
     engines: {node: '>= 6.0.0'}
     requiresBuild: true
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -8832,7 +8910,7 @@ packages:
     resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
     engines: {node: '>= 14'}
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -9207,7 +9285,7 @@ packages:
     resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==}
     dependencies:
       archy: 1.0.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       fastq: 1.15.0
     transitivePeerDependencies:
       - supports-color
@@ -9547,6 +9625,7 @@ packages:
       electron-to-chromium: 1.4.463
       node-releases: 2.0.13
       update-browserslist-db: 1.0.11(browserslist@4.21.9)
+    dev: true
 
   /browserslist@4.22.2:
     resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==}
@@ -9557,7 +9636,6 @@ packages:
       electron-to-chromium: 1.4.601
       node-releases: 2.0.14
       update-browserslist-db: 1.0.13(browserslist@4.22.2)
-    dev: true
 
   /bser@2.1.1:
     resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
@@ -9607,8 +9685,8 @@ packages:
     dependencies:
       node-gyp-build: 4.6.0
 
-  /bullmq@4.15.3:
-    resolution: {integrity: sha512-UvvM61cGGoT6Ish+gjmPYzCoqOnkmdC9OMufcO41ijya8Evk6zqXm3PNYK8CLCkvb3jrWieDo1SFag9Tg3RiPQ==}
+  /bullmq@4.15.4:
+    resolution: {integrity: sha512-lrEtiERjYPPfb0OEXva5G8cF6kbtJFy/BmIL0yjmO+kj2eSVjCpAVVeYikxoqKtaYyEnNFal7ERVLanEvp+1Lw==}
     dependencies:
       cron-parser: 4.8.1
       glob: 8.1.0
@@ -9675,6 +9753,19 @@ packages:
     resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==}
     engines: {node: '>=14.16'}
 
+  /cacheable-request@10.2.14:
+    resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==}
+    engines: {node: '>=14.16'}
+    dependencies:
+      '@types/http-cache-semantics': 4.0.4
+      get-stream: 6.0.1
+      http-cache-semantics: 4.1.1
+      keyv: 4.5.4
+      mimic-response: 4.0.0
+      normalize-url: 8.0.0
+      responselike: 3.0.0
+    dev: false
+
   /cacheable-request@10.2.8:
     resolution: {integrity: sha512-IDVO5MJ4LItE6HKFQTqT2ocAQsisOoCTUDu1ddCmnhyiwFQjXNPp4081Xj23N4tO+AFEFNzGuNEf/c8Gwwt15A==}
     engines: {node: '>=14.16'}
@@ -9741,18 +9832,18 @@ packages:
   /caniuse-api@3.0.0:
     resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
     dependencies:
-      browserslist: 4.21.9
-      caniuse-lite: 1.0.30001516
+      browserslist: 4.22.2
+      caniuse-lite: 1.0.30001566
       lodash.memoize: 4.1.2
       lodash.uniq: 4.5.0
     dev: false
 
   /caniuse-lite@1.0.30001516:
     resolution: {integrity: sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==}
+    dev: true
 
   /caniuse-lite@1.0.30001566:
     resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==}
-    dev: true
 
   /canonicalize@1.0.8:
     resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==}
@@ -10390,9 +10481,9 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
-  /css-declaration-sorter@6.4.1(postcss@8.4.32):
-    resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==}
-    engines: {node: ^10 || ^12 || >=14}
+  /css-declaration-sorter@7.1.1(postcss@8.4.32):
+    resolution: {integrity: sha512-dZ3bVTEEc1vxr3Bek9vGwfB5Z6ESPULhcRvO472mfjVnj8jRcTnKO8/JTczlvxM10Myb+wBM++1MtdO76eWcaQ==}
+    engines: {node: ^14 || ^16 || >=18}
     peerDependencies:
       postcss: ^8.0.9
     dependencies:
@@ -10436,61 +10527,61 @@ packages:
     engines: {node: '>=4'}
     hasBin: true
 
-  /cssnano-preset-default@6.0.1(postcss@8.4.32):
-    resolution: {integrity: sha512-7VzyFZ5zEB1+l1nToKyrRkuaJIx0zi/1npjvZfbBwbtNTzhLtlvYraK/7/uqmX2Wb2aQtd983uuGw79jAjLSuQ==}
+  /cssnano-preset-default@6.0.2(postcss@8.4.32):
+    resolution: {integrity: sha512-VnZybFeZ63AiVqIUNlxqMxpj9VU8B5j0oKgP7WyVt/7mkyf97KsYkNzsPTV/RVmy54Pg7cBhOK4WATbdCB44gw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
-      css-declaration-sorter: 6.4.1(postcss@8.4.32)
-      cssnano-utils: 4.0.0(postcss@8.4.32)
+      css-declaration-sorter: 7.1.1(postcss@8.4.32)
+      cssnano-utils: 4.0.1(postcss@8.4.32)
       postcss: 8.4.32
       postcss-calc: 9.0.1(postcss@8.4.32)
-      postcss-colormin: 6.0.0(postcss@8.4.32)
-      postcss-convert-values: 6.0.0(postcss@8.4.32)
-      postcss-discard-comments: 6.0.0(postcss@8.4.32)
-      postcss-discard-duplicates: 6.0.0(postcss@8.4.32)
-      postcss-discard-empty: 6.0.0(postcss@8.4.32)
-      postcss-discard-overridden: 6.0.0(postcss@8.4.32)
-      postcss-merge-longhand: 6.0.0(postcss@8.4.32)
-      postcss-merge-rules: 6.0.1(postcss@8.4.32)
-      postcss-minify-font-values: 6.0.0(postcss@8.4.32)
-      postcss-minify-gradients: 6.0.0(postcss@8.4.32)
-      postcss-minify-params: 6.0.0(postcss@8.4.32)
-      postcss-minify-selectors: 6.0.0(postcss@8.4.32)
-      postcss-normalize-charset: 6.0.0(postcss@8.4.32)
-      postcss-normalize-display-values: 6.0.0(postcss@8.4.32)
-      postcss-normalize-positions: 6.0.0(postcss@8.4.32)
-      postcss-normalize-repeat-style: 6.0.0(postcss@8.4.32)
-      postcss-normalize-string: 6.0.0(postcss@8.4.32)
-      postcss-normalize-timing-functions: 6.0.0(postcss@8.4.32)
-      postcss-normalize-unicode: 6.0.0(postcss@8.4.32)
-      postcss-normalize-url: 6.0.0(postcss@8.4.32)
-      postcss-normalize-whitespace: 6.0.0(postcss@8.4.32)
-      postcss-ordered-values: 6.0.0(postcss@8.4.32)
-      postcss-reduce-initial: 6.0.0(postcss@8.4.32)
-      postcss-reduce-transforms: 6.0.0(postcss@8.4.32)
-      postcss-svgo: 6.0.0(postcss@8.4.32)
-      postcss-unique-selectors: 6.0.0(postcss@8.4.32)
+      postcss-colormin: 6.0.1(postcss@8.4.32)
+      postcss-convert-values: 6.0.1(postcss@8.4.32)
+      postcss-discard-comments: 6.0.1(postcss@8.4.32)
+      postcss-discard-duplicates: 6.0.1(postcss@8.4.32)
+      postcss-discard-empty: 6.0.1(postcss@8.4.32)
+      postcss-discard-overridden: 6.0.1(postcss@8.4.32)
+      postcss-merge-longhand: 6.0.1(postcss@8.4.32)
+      postcss-merge-rules: 6.0.2(postcss@8.4.32)
+      postcss-minify-font-values: 6.0.1(postcss@8.4.32)
+      postcss-minify-gradients: 6.0.1(postcss@8.4.32)
+      postcss-minify-params: 6.0.1(postcss@8.4.32)
+      postcss-minify-selectors: 6.0.1(postcss@8.4.32)
+      postcss-normalize-charset: 6.0.1(postcss@8.4.32)
+      postcss-normalize-display-values: 6.0.1(postcss@8.4.32)
+      postcss-normalize-positions: 6.0.1(postcss@8.4.32)
+      postcss-normalize-repeat-style: 6.0.1(postcss@8.4.32)
+      postcss-normalize-string: 6.0.1(postcss@8.4.32)
+      postcss-normalize-timing-functions: 6.0.1(postcss@8.4.32)
+      postcss-normalize-unicode: 6.0.1(postcss@8.4.32)
+      postcss-normalize-url: 6.0.1(postcss@8.4.32)
+      postcss-normalize-whitespace: 6.0.1(postcss@8.4.32)
+      postcss-ordered-values: 6.0.1(postcss@8.4.32)
+      postcss-reduce-initial: 6.0.1(postcss@8.4.32)
+      postcss-reduce-transforms: 6.0.1(postcss@8.4.32)
+      postcss-svgo: 6.0.1(postcss@8.4.32)
+      postcss-unique-selectors: 6.0.1(postcss@8.4.32)
     dev: false
 
-  /cssnano-utils@4.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==}
+  /cssnano-utils@4.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-6qQuYDqsGoiXssZ3zct6dcMxiqfT6epy7x4R0TQJadd4LWO3sPR6JH6ZByOvVLoZ6EdwPGgd7+DR1EmX3tiXQQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
     dev: false
 
-  /cssnano@6.0.1(postcss@8.4.32):
-    resolution: {integrity: sha512-fVO1JdJ0LSdIGJq68eIxOqFpIJrZqXUsBt8fkrBcztCQqAjQD51OhZp7tc0ImcbwXD4k7ny84QTV90nZhmqbkg==}
+  /cssnano@6.0.2(postcss@8.4.32):
+    resolution: {integrity: sha512-Tu9wv8UdN6CoiQnIVkCNvi+0rw/BwFWOJBlg2bVfEyKaadSuE3Gq/DD8tniVvggTJGwK88UjqZp7zL5sv6t1aA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
-      cssnano-preset-default: 6.0.1(postcss@8.4.32)
-      lilconfig: 2.1.0
+      cssnano-preset-default: 6.0.2(postcss@8.4.32)
+      lilconfig: 3.0.0
       postcss: 8.4.32
     dev: false
 
@@ -10637,6 +10728,7 @@ packages:
     dependencies:
       ms: 2.1.2
       supports-color: 5.5.0
+    dev: true
 
   /debug@4.3.4(supports-color@8.1.1):
     resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
@@ -10649,7 +10741,6 @@ packages:
     dependencies:
       ms: 2.1.2
       supports-color: 8.1.1
-    dev: true
 
   /decamelize-keys@1.1.1:
     resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
@@ -10866,7 +10957,7 @@ packages:
     hasBin: true
     dependencies:
       address: 1.2.2
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -11007,10 +11098,10 @@ packages:
 
   /electron-to-chromium@1.4.463:
     resolution: {integrity: sha512-fT3hvdUWLjDbaTGzyOjng/CQhQJSQP8ThO3XZAoaxHvHo2kUXiRQVMj9M235l8uDFiNPsPa6KHT1p3RaR6ugRw==}
+    dev: true
 
   /electron-to-chromium@1.4.601:
     resolution: {integrity: sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==}
-    dev: true
 
   /emittery@0.13.1:
     resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
@@ -11190,7 +11281,7 @@ packages:
     peerDependencies:
       esbuild: '>=0.12 <1'
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       esbuild: 0.18.20
     transitivePeerDependencies:
       - supports-color
@@ -11320,7 +11411,7 @@ packages:
       - supports-color
     dev: true
 
-  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.14.0)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0):
+  /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.14.0)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0):
     resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
     engines: {node: '>=4'}
     peerDependencies:
@@ -11341,16 +11432,16 @@ packages:
       eslint-import-resolver-webpack:
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 6.14.0(eslint@8.56.0)(typescript@5.3.3)
       debug: 3.2.7(supports-color@8.1.1)
-      eslint: 8.55.0
+      eslint: 8.56.0
       eslint-import-resolver-node: 0.3.9
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.14.0)(eslint@8.55.0):
-    resolution: {integrity: sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==}
+  /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.14.0)(eslint@8.56.0):
+    resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
     engines: {node: '>=4'}
     peerDependencies:
       '@typescript-eslint/parser': '*'
@@ -11359,16 +11450,16 @@ packages:
       '@typescript-eslint/parser':
         optional: true
     dependencies:
-      '@typescript-eslint/parser': 6.14.0(eslint@8.55.0)(typescript@5.3.3)
+      '@typescript-eslint/parser': 6.14.0(eslint@8.56.0)(typescript@5.3.3)
       array-includes: 3.1.7
       array.prototype.findlastindex: 1.2.3
       array.prototype.flat: 1.3.2
       array.prototype.flatmap: 1.3.2
       debug: 3.2.7(supports-color@8.1.1)
       doctrine: 2.1.0
-      eslint: 8.55.0
+      eslint: 8.56.0
       eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.14.0)(eslint-import-resolver-node@0.3.9)(eslint@8.55.0)
+      eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.14.0)(eslint-import-resolver-node@0.3.9)(eslint@8.56.0)
       hasown: 2.0.0
       is-core-module: 2.13.1
       is-glob: 4.0.3
@@ -11377,26 +11468,26 @@ packages:
       object.groupby: 1.0.1
       object.values: 1.1.7
       semver: 6.3.1
-      tsconfig-paths: 3.14.2
+      tsconfig-paths: 3.15.0
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
       - eslint-import-resolver-webpack
       - supports-color
     dev: true
 
-  /eslint-plugin-vue@9.19.2(eslint@8.55.0):
+  /eslint-plugin-vue@9.19.2(eslint@8.56.0):
     resolution: {integrity: sha512-CPDqTOG2K4Ni2o4J5wixkLVNwgctKXFu6oBpVJlpNq7f38lh9I80pRTouZSJ2MAebPJlINU/KTFSXyQfBUlymA==}
     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.55.0)
-      eslint: 8.55.0
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0)
+      eslint: 8.56.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.2(eslint@8.55.0)
+      vue-eslint-parser: 9.3.2(eslint@8.56.0)
       xml-name-validator: 4.0.0
     transitivePeerDependencies:
       - supports-color
@@ -11435,7 +11526,7 @@ packages:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
@@ -11466,15 +11557,15 @@ packages:
       - supports-color
     dev: true
 
-  /eslint@8.55.0:
-    resolution: {integrity: sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==}
+  /eslint@8.56.0:
+    resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     hasBin: true
     dependencies:
-      '@eslint-community/eslint-utils': 4.4.0(eslint@8.55.0)
+      '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0)
       '@eslint-community/regexpp': 4.6.2
       '@eslint/eslintrc': 2.1.4
-      '@eslint/js': 8.55.0
+      '@eslint/js': 8.56.0
       '@humanwhocodes/config-array': 0.11.13
       '@humanwhocodes/module-importer': 1.0.1
       '@nodelib/fs.walk': 1.2.8
@@ -11482,7 +11573,7 @@ packages:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
@@ -12086,7 +12177,7 @@ packages:
       debug:
         optional: true
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
 
   /for-each@0.3.3:
     resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
@@ -12108,6 +12199,11 @@ packages:
     resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==}
     engines: {node: '>= 14.17'}
 
+  /form-data-encoder@4.0.2:
+    resolution: {integrity: sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==}
+    engines: {node: '>= 18'}
+    dev: false
+
   /form-data@2.3.3:
     resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
     engines: {node: '>= 0.12'}
@@ -12539,20 +12635,20 @@ packages:
       p-cancelable: 3.0.0
       responselike: 3.0.0
 
-  /got@13.0.0:
-    resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==}
-    engines: {node: '>=16'}
+  /got@14.0.0:
+    resolution: {integrity: sha512-X01vTgaX9SwaMq5DfImvS+3GMQFFs5HtrrlS9CuzUSzkxAf/tWGEyynuI+Qy7BjciMczZGjyVSmawYbP4eYhYA==}
+    engines: {node: '>=20'}
     dependencies:
-      '@sindresorhus/is': 5.3.0
+      '@sindresorhus/is': 6.1.0
       '@szmarczak/http-timer': 5.0.1
       cacheable-lookup: 7.0.0
-      cacheable-request: 10.2.8
+      cacheable-request: 10.2.14
       decompress-response: 6.0.0
-      form-data-encoder: 2.1.4
-      get-stream: 6.0.1
-      http2-wrapper: 2.2.0
+      form-data-encoder: 4.0.2
+      get-stream: 8.0.1
+      http2-wrapper: 2.2.1
       lowercase-keys: 3.0.0
-      p-cancelable: 3.0.0
+      p-cancelable: 4.0.1
       responselike: 3.0.0
     dev: false
 
@@ -12568,8 +12664,8 @@ packages:
     engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
     dev: true
 
-  /gsap@3.12.3:
-    resolution: {integrity: sha512-TySXTE+ABiAVa61W+h5wv2p5GkJT1Uj//4nWpK8EjmhcDqwH++35IvtbQlVVFj+rdcJdFCdCt0SKgb+SwdPq/A==}
+  /gsap@3.12.4:
+    resolution: {integrity: sha512-1ByAq8dD0W4aBZ/JArgaQvc0gyUfkGkP8mgAQa0qZGdpOKlSOhOf+WNXjoLimKaKG3Z4Iu6DKZtnyszqQeyqWQ==}
     dev: false
 
   /gunzip-maybe@1.4.2:
@@ -12637,6 +12733,7 @@ packages:
   /has-flag@3.0.0:
     resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
     engines: {node: '>=4'}
+    dev: true
 
   /has-flag@4.0.0:
     resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
@@ -12774,7 +12871,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -12812,6 +12909,14 @@ packages:
       quick-lru: 5.1.1
       resolve-alpn: 1.2.1
 
+  /http2-wrapper@2.2.1:
+    resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==}
+    engines: {node: '>=10.19.0'}
+    dependencies:
+      quick-lru: 5.1.1
+      resolve-alpn: 1.2.1
+    dev: false
+
   /http_ece@1.1.0:
     resolution: {integrity: sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==}
     engines: {node: '>=4'}
@@ -12836,7 +12941,7 @@ packages:
     engines: {node: '>= 6.0.0'}
     dependencies:
       agent-base: 5.1.1
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -12846,7 +12951,7 @@ packages:
     engines: {node: '>= 6'}
     dependencies:
       agent-base: 6.0.2
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
 
@@ -12855,7 +12960,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -12865,7 +12970,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -13015,7 +13120,7 @@ packages:
     dependencies:
       '@ioredis/commands': 1.2.0
       cluster-key-slot: 1.1.2
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       denque: 2.1.0
       lodash.defaults: 4.2.0
       lodash.isarguments: 3.1.0
@@ -13462,7 +13567,7 @@ packages:
     resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
     engines: {node: '>=10'}
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       istanbul-lib-coverage: 3.2.0
       source-map: 0.6.1
     transitivePeerDependencies:
@@ -14140,7 +14245,7 @@ packages:
     resolution: {integrity: sha512-pJ4XLQP4Q9HTxl6RVDLJ8Cyh1uitSs0CzDBAz1uoJ4sRD/Bk7cFSXL1FUXDW3zJ7YnfliJx6eu8Jn283bpZ4Yg==}
     engines: {node: '>=10'}
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       rfdc: 1.3.0
       uri-js: 4.4.1
     transitivePeerDependencies:
@@ -14268,6 +14373,12 @@ packages:
     dependencies:
       json-buffer: 3.0.1
 
+  /keyv@4.5.4:
+    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+    dependencies:
+      json-buffer: 3.0.1
+    dev: false
+
   /kind-of@6.0.3:
     resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
     engines: {node: '>=0.10.0'}
@@ -14340,9 +14451,9 @@ packages:
       set-cookie-parser: 2.6.0
     dev: false
 
-  /lilconfig@2.1.0:
-    resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
-    engines: {node: '>=10'}
+  /lilconfig@3.0.0:
+    resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==}
+    engines: {node: '>=14'}
     dev: false
 
   /lines-and-columns@1.2.4:
@@ -15254,10 +15365,10 @@ packages:
 
   /node-releases@2.0.13:
     resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==}
+    dev: true
 
   /node-releases@2.0.14:
     resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
-    dev: true
 
   /nodemailer@6.9.7:
     resolution: {integrity: sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==}
@@ -15626,6 +15737,11 @@ packages:
     resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==}
     engines: {node: '>=12.20'}
 
+  /p-cancelable@4.0.1:
+    resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==}
+    engines: {node: '>=14.16'}
+    dev: false
+
   /p-finally@1.0.0:
     resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
     engines: {node: '>=4'}
@@ -16068,251 +16184,251 @@ packages:
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-colormin@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==}
+  /postcss-colormin@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-Tb9aR2wCJCzKuNjIeMzVNd0nXjQy25HDgFmmaRsHnP0eP/k8uQWE4S8voX5S2coO5CeKrp+USFs1Ayv9Tpxx6w==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
-      browserslist: 4.21.9
+      browserslist: 4.22.2
       caniuse-api: 3.0.0
       colord: 2.9.3
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-convert-values@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==}
+  /postcss-convert-values@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-zTd4Vh0HxGkhg5aHtfCogcRHzGkvblfdWlQ53lIh1cJhYcGyIxh2hgtKoVh40AMktRERet+JKdB04nNG19kjmA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
-      browserslist: 4.21.9
+      browserslist: 4.22.2
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-discard-comments@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==}
+  /postcss-discard-comments@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-f1KYNPtqYLUeZGCHQPKzzFtsHaRuECe6jLakf/RjSRqvF5XHLZnM2+fXLhb8Qh/HBFHs3M4cSLb1k3B899RYIg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
     dev: false
 
-  /postcss-discard-duplicates@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==}
+  /postcss-discard-duplicates@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-1hvUs76HLYR8zkScbwyJ8oJEugfPV+WchpnA+26fpJ7Smzs51CzGBHC32RS03psuX/2l0l0UKh2StzNxOrKCYg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
     dev: false
 
-  /postcss-discard-empty@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==}
+  /postcss-discard-empty@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-yitcmKwmVWtNsrrRqGJ7/C0YRy53i0mjexBDQ9zYxDwTWVBgbU4+C9jIZLmQlTDT9zhml+u0OMFJh8+31krmOg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
     dev: false
 
-  /postcss-discard-overridden@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==}
+  /postcss-discard-overridden@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-qs0ehZMMZpSESbRkw1+inkf51kak6OOzNRaoLd/U7Fatp0aN2HQ1rxGOrJvYcRAN9VpX8kUF13R2ofn8OlvFVA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
     dev: false
 
-  /postcss-merge-longhand@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==}
+  /postcss-merge-longhand@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-vmr/HZQzaPXc45FRvSctqFTF05UaDnTn5ABX+UtQPJznDWT/QaFbVc/pJ5C2YPxx2J2XcfmWowlKwtCDwiQ5hA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
-      stylehacks: 6.0.0(postcss@8.4.32)
+      stylehacks: 6.0.1(postcss@8.4.32)
     dev: false
 
-  /postcss-merge-rules@6.0.1(postcss@8.4.32):
-    resolution: {integrity: sha512-a4tlmJIQo9SCjcfiCcCMg/ZCEe0XTkl/xK0XHBs955GWg9xDX3NwP9pwZ78QUOWB8/0XCjZeJn98Dae0zg6AAw==}
+  /postcss-merge-rules@6.0.2(postcss@8.4.32):
+    resolution: {integrity: sha512-6lm8bl0UfriSfxI+F/cezrebqqP8w702UC6SjZlUlBYwuRVNbmgcJuQU7yePIvD4MNT53r/acQCUAyulrpgmeQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
-      browserslist: 4.21.9
+      browserslist: 4.22.2
       caniuse-api: 3.0.0
-      cssnano-utils: 4.0.0(postcss@8.4.32)
+      cssnano-utils: 4.0.1(postcss@8.4.32)
       postcss: 8.4.32
       postcss-selector-parser: 6.0.13
     dev: false
 
-  /postcss-minify-font-values@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==}
+  /postcss-minify-font-values@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-tIwmF1zUPoN6xOtA/2FgVk1ZKrLcCvE0dpZLtzyyte0j9zUeB8RTbCqrHZGjJlxOvNWKMYtunLrrl7HPOiR46w==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-minify-gradients@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==}
+  /postcss-minify-gradients@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-M1RJWVjd6IOLPl1hYiOd5HQHgpp6cvJVLrieQYS9y07Yo8itAr6jaekzJphaJFR0tcg4kRewCk3kna9uHBxn/w==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       colord: 2.9.3
-      cssnano-utils: 4.0.0(postcss@8.4.32)
+      cssnano-utils: 4.0.1(postcss@8.4.32)
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-minify-params@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==}
+  /postcss-minify-params@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-eFvGWArqh4khPIgPDu6SZNcaLctx97nO7c59OXnRtGntAp5/VS4gjMhhW9qUFsK6mQ27pEZGt2kR+mPizI+Z9g==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
-      browserslist: 4.21.9
-      cssnano-utils: 4.0.0(postcss@8.4.32)
+      browserslist: 4.22.2
+      cssnano-utils: 4.0.1(postcss@8.4.32)
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-minify-selectors@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==}
+  /postcss-minify-selectors@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-mfReq5wrS6vkunxvJp6GDuOk+Ak6JV7134gp8L+ANRnV9VwqzTvBtX6lpohooVU750AR0D3pVx2Zn6uCCwOAfQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-selector-parser: 6.0.13
     dev: false
 
-  /postcss-normalize-charset@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==}
+  /postcss-normalize-charset@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-aW5LbMNRZ+oDV57PF9K+WI1Z8MPnF+A8qbajg/T8PP126YrGX1f9IQx21GI2OlGz7XFJi/fNi0GTbY948XJtXg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
     dev: false
 
-  /postcss-normalize-display-values@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==}
+  /postcss-normalize-display-values@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-mc3vxp2bEuCb4LgCcmG1y6lKJu1Co8T+rKHrcbShJwUmKJiEl761qb/QQCfFwlrvSeET3jksolCR/RZuMURudw==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-positions@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==}
+  /postcss-normalize-positions@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-HRsq8u/0unKNvm0cvwxcOUEcakFXqZ41fv3FOdPn916XFUrympjr+03oaLkuZENz3HE9RrQE9yU0Xv43ThWjQg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-repeat-style@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==}
+  /postcss-normalize-repeat-style@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-Gbb2nmCy6tTiA7Sh2MBs3fj9W8swonk6lw+dFFeQT68B0Pzwp1kvisJQkdV6rbbMSd9brMlS8I8ts52tAGWmGQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-string@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==}
+  /postcss-normalize-string@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-5Fhx/+xzALJD9EI26Aq23hXwmv97Zfy2VFrt5PLT8lAhnBIZvmaT5pQk+NuJ/GWj/QWaKSKbnoKDGLbV6qnhXg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-timing-functions@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==}
+  /postcss-normalize-timing-functions@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-4zcczzHqmCU7L5dqTB9rzeqPWRMc0K2HoR+Bfl+FSMbqGBUcP5LRfgcH4BdRtLuzVQK1/FHdFoGT3F7rkEnY+g==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-unicode@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==}
+  /postcss-normalize-unicode@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-ok9DsI94nEF79MkvmLfHfn8ddnKXA7w+8YuUoz5m7b6TOdoaRCpvu/QMHXQs9+DwUbvp+ytzz04J55CPy77PuQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
-      browserslist: 4.21.9
+      browserslist: 4.22.2
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-url@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==}
+  /postcss-normalize-url@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-jEXL15tXSvbjm0yzUV7FBiEXwhIa9H88JOXDGQzmcWoB4mSjZIsmtto066s2iW9FYuIrIF4k04HA2BKAOpbsaQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-normalize-whitespace@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==}
+  /postcss-normalize-whitespace@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-76i3NpWf6bB8UHlVuLRxG4zW2YykF9CTEcq/9LGAiz2qBuX5cBStadkk0jSkg9a9TCIXbMQz7yzrygKoCW9JuA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-ordered-values@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==}
+  /postcss-ordered-values@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-XXbb1O/MW9HdEhnBxitZpPFbIvDgbo9NK4c/5bOfiKpnIGZDoL2xd7/e6jW5DYLsWxBbs+1nZEnVgnjnlFViaA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
-      cssnano-utils: 4.0.0(postcss@8.4.32)
+      cssnano-utils: 4.0.1(postcss@8.4.32)
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
     dev: false
 
-  /postcss-reduce-initial@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==}
+  /postcss-reduce-initial@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-cgzsI2ThG1PMSdSyM9A+bVxiiVgPIVz9f5c6H+TqEv0CA89iCOO81mwLWRWLgOKFtQkKob9nNpnkxG/1RlgFcA==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
-      browserslist: 4.21.9
+      browserslist: 4.22.2
       caniuse-api: 3.0.0
       postcss: 8.4.32
     dev: false
 
-  /postcss-reduce-transforms@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==}
+  /postcss-reduce-transforms@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-fUbV81OkUe75JM+VYO1gr/IoA2b/dRiH6HvMwhrIBSUrxq3jNZQZitSnugcTLDi1KkQh1eR/zi+iyxviUNBkcQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
@@ -16325,22 +16441,22 @@ packages:
       cssesc: 3.0.0
       util-deprecate: 1.0.2
 
-  /postcss-svgo@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==}
+  /postcss-svgo@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-eWV4Rrqa06LzTgqirOv5Ln6WTGyU7Pbeqj9WEyKo9tpnWixNATVJMeaEcOHOW1ZYyjcG8wSJwX/28DvU3oy3HA==}
     engines: {node: ^14 || ^16 || >= 18}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-value-parser: 4.2.0
-      svgo: 3.0.2
+      svgo: 3.1.0
     dev: false
 
-  /postcss-unique-selectors@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==}
+  /postcss-unique-selectors@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-/KCCEpNNR7oXVJ38/Id7GC9Nt0zxO1T3zVbhVaq6F6LSG+3gU3B7+QuTHfD0v8NPEHlzewAout29S0InmB78EQ==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
       postcss: 8.4.32
       postcss-selector-parser: 6.0.13
@@ -16701,7 +16817,7 @@ packages:
     engines: {node: '>=8.16.0'}
     dependencies:
       '@types/mime-types': 2.1.4
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       extract-zip: 1.7.0
       https-proxy-agent: 4.0.0
       mime: 2.6.0
@@ -17672,8 +17788,8 @@ packages:
     resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
     engines: {node: '>=8'}
 
-  /shiki@0.14.6:
-    resolution: {integrity: sha512-R4koBBlQP33cC8cpzX0hAoOURBHJILp4Aaduh2eYi+Vj8ZBqtK/5SWNEHBS3qwUMu8dqOtI/ftno3ESfNeVW9g==}
+  /shiki@0.14.7:
+    resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==}
     dependencies:
       ansi-sequence-parser: 1.1.1
       jsonc-parser: 3.2.0
@@ -17714,7 +17830,7 @@ packages:
     dependencies:
       '@hapi/hoek': 10.0.1
       '@hapi/wreck': 18.0.1
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       joi: 17.7.0
     transitivePeerDependencies:
       - supports-color
@@ -17914,7 +18030,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       socks: 2.7.1
     transitivePeerDependencies:
       - supports-color
@@ -18067,7 +18183,7 @@ packages:
       arg: 5.0.2
       bluebird: 3.7.2
       check-more-types: 2.24.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       execa: 5.1.1
       lazy-ass: 1.6.0
       ps-tree: 1.2.0
@@ -18095,11 +18211,11 @@ packages:
     resolution: {integrity: sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==}
     dev: true
 
-  /storybook@7.6.4:
-    resolution: {integrity: sha512-nQhs9XkrroxjqMoBnnToyc6M8ndbmpkOb1qmULO4chtfMy4k0p9Un3K4TJvDaP8c3wPUFGd4ZaJ1hZNVmIl56Q==}
+  /storybook@7.6.5:
+    resolution: {integrity: sha512-uHPrL+g/0v6iIVtDA8J0uWd3jDZcdr51lCR/vPXTkrCY1uVaFjswzl8EMy5PR05I7jMpKUzkJWZtFbgbh9e1Bw==}
     hasBin: true
     dependencies:
-      '@storybook/cli': 7.6.4
+      '@storybook/cli': 7.6.5
     transitivePeerDependencies:
       - bufferutil
       - encoding
@@ -18316,13 +18432,13 @@ packages:
       peek-readable: 5.0.0
     dev: false
 
-  /stylehacks@6.0.0(postcss@8.4.32):
-    resolution: {integrity: sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==}
+  /stylehacks@6.0.1(postcss@8.4.32):
+    resolution: {integrity: sha512-jTqG2aIoX2fYg0YsGvqE4ooE/e75WmaEjnNiP6Ag7irLtHxML8NJRxRxS0HyDpde8DRGuEXTFVHVfR5Tmbxqzg==}
     engines: {node: ^14 || ^16 || >=18.0}
     peerDependencies:
-      postcss: ^8.2.15
+      postcss: ^8.4.31
     dependencies:
-      browserslist: 4.21.9
+      browserslist: 4.22.2
       postcss: 8.4.32
       postcss-selector-parser: 6.0.13
     dev: false
@@ -18332,6 +18448,7 @@ packages:
     engines: {node: '>=4'}
     dependencies:
       has-flag: 3.0.0
+    dev: true
 
   /supports-color@7.2.0:
     resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
@@ -18362,8 +18479,8 @@ packages:
     resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
     engines: {node: '>= 0.4'}
 
-  /svgo@3.0.2:
-    resolution: {integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==}
+  /svgo@3.1.0:
+    resolution: {integrity: sha512-R5SnNA89w1dYgNv570591F66v34b3eQShpIBcQtZtM5trJwm1VvxbIoMpRYY3ybTAutcKTLEmTsdnaknOHbiQA==}
     engines: {node: '>=14.0.0'}
     hasBin: true
     dependencies:
@@ -18371,6 +18488,7 @@ packages:
       commander: 7.2.0
       css-select: 5.1.0
       css-tree: 2.3.1
+      css-what: 6.1.0
       csso: 5.0.5
       picocolors: 1.0.0
     dev: false
@@ -18477,8 +18595,8 @@ packages:
       unique-string: 2.0.0
     dev: true
 
-  /terser@5.24.0:
-    resolution: {integrity: sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==}
+  /terser@5.26.0:
+    resolution: {integrity: sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==}
     engines: {node: '>=10'}
     hasBin: true
     dependencies:
@@ -18721,8 +18839,8 @@ packages:
       plimit-lit: 1.5.0
     dev: false
 
-  /tsconfig-paths@3.14.2:
-    resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==}
+  /tsconfig-paths@3.15.0:
+    resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
     dependencies:
       '@types/json5': 0.0.29
       json5: 1.0.2
@@ -18951,7 +19069,7 @@ packages:
       chalk: 4.1.2
       cli-highlight: 2.1.11
       date-fns: 2.30.0
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       dotenv: 16.0.3
       glob: 8.1.0
       ioredis: 5.3.2
@@ -19144,6 +19262,7 @@ packages:
       browserslist: 4.21.9
       escalade: 3.1.1
       picocolors: 1.0.0
+    dev: true
 
   /update-browserslist-db@1.0.13(browserslist@4.22.2):
     resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
@@ -19154,7 +19273,6 @@ packages:
       browserslist: 4.22.2
       escalade: 3.1.1
       picocolors: 1.0.0
-    dev: true
 
   /uri-js@4.4.1:
     resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
@@ -19303,17 +19421,17 @@ packages:
       core-util-is: 1.0.2
       extsprintf: 1.3.0
 
-  /vite-node@0.34.6(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0):
+  /vite-node@0.34.6(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0):
     resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
     dependencies:
       cac: 6.7.14
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       mlly: 1.4.0
       pathe: 1.1.1
       picocolors: 1.0.0
-      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -19329,8 +19447,8 @@ packages:
     resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
     dev: true
 
-  /vite@5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0):
-    resolution: {integrity: sha512-jYMALd8aeqR3yS9xlHd0OzQJndS9fH5ylVgWdB+pxTwxLKdO1pgC5Dlb398BUxpfaBxa4M9oT7j1g503Gaj5IQ==}
+  /vite@5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0):
+    resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
     peerDependencies:
@@ -19362,7 +19480,7 @@ packages:
       postcss: 8.4.32
       rollup: 4.9.0
       sass: 1.69.5
-      terser: 5.24.0
+      terser: 5.26.0
     optionalDependencies:
       fsevents: 2.3.3
 
@@ -19373,12 +19491,12 @@ packages:
       vitest: '>=0.16.0'
     dependencies:
       cross-fetch: 3.1.5
-      vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.24.0)
+      vitest: 0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.26.0)
     transitivePeerDependencies:
       - encoding
     dev: true
 
-  /vitest@0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.24.0):
+  /vitest@0.34.6(happy-dom@10.0.3)(sass@1.69.5)(terser@5.26.0):
     resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
@@ -19421,7 +19539,7 @@ packages:
       acorn-walk: 8.2.0
       cac: 6.7.14
       chai: 4.3.10
-      debug: 4.3.4(supports-color@5.5.0)
+      debug: 4.3.4(supports-color@8.1.1)
       happy-dom: 10.0.3
       local-pkg: 0.4.3
       magic-string: 0.30.3
@@ -19431,8 +19549,8 @@ packages:
       strip-literal: 1.0.1
       tinybench: 2.5.0
       tinypool: 0.7.0
-      vite: 5.0.8(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
-      vite-node: 0.34.6(@types/node@20.10.4)(sass@1.69.5)(terser@5.24.0)
+      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
+      vite-node: 0.34.6(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
       why-is-node-running: 2.2.2
     transitivePeerDependencies:
       - less
@@ -19484,7 +19602,7 @@ packages:
     dependencies:
       '@babel/parser': 7.23.5
       '@babel/types': 7.23.5
-      '@vue/compiler-dom': 3.3.9
+      '@vue/compiler-dom': 3.3.11
       '@vue/compiler-sfc': 3.3.11
       ast-types: 0.14.2
       hash-sum: 2.0.0
@@ -19497,14 +19615,14 @@ packages:
       - vue
     dev: true
 
-  /vue-eslint-parser@9.3.2(eslint@8.55.0):
+  /vue-eslint-parser@9.3.2(eslint@8.56.0):
     resolution: {integrity: sha512-q7tWyCVaV9f8iQyIA5Mkj/S6AoJ9KBN8IeUSf3XEmBrOtxOZnfTg5s4KClbZBCK3GtnT/+RyCLZyDHuZwTuBjg==}
     engines: {node: ^14.17.0 || >=16.0.0}
     peerDependencies:
       eslint: '>=6.0.0'
     dependencies:
-      debug: 4.3.4(supports-color@5.5.0)
-      eslint: 8.55.0
+      debug: 4.3.4(supports-color@8.1.1)
+      eslint: 8.56.0
       eslint-scope: 7.2.2
       eslint-visitor-keys: 3.4.3
       espree: 9.6.1
@@ -20038,7 +20156,7 @@ packages:
       sharp: 0.31.3
     dev: false
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.4)(@storybook/components@7.6.4)(@storybook/core-events@7.6.4)(@storybook/manager-api@7.6.4)(@storybook/preview-api@7.6.4)(@storybook/theming@7.6.4)(@storybook/types@7.6.4)(react-dom@18.2.0)(react@18.2.0):
+  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.6.4)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(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
@@ -20059,13 +20177,13 @@ packages:
       react-dom:
         optional: true
     dependencies:
-      '@storybook/blocks': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/blocks': 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/components': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/core-events': 7.6.4
-      '@storybook/manager-api': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/preview-api': 7.6.4
-      '@storybook/theming': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.4
+      '@storybook/core-events': 7.6.5
+      '@storybook/manager-api': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/preview-api': 7.6.5
+      '@storybook/theming': 7.6.5(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/types': 7.6.5
       react: 18.2.0
       react-dom: 18.2.0(react@18.2.0)
     dev: true

From 6df9c79bb7719e4ad01f19827c150a640e268bc3 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 16 Dec 2023 14:00:46 +0900
Subject: [PATCH 251/435] 2023.12.0-beta.5

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

diff --git a/package.json b/package.json
index 7475e77f66..6120c07094 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.12.0-beta.4",
+	"version": "2023.12.0-beta.5",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From c167f206436d43e19f113fab515ff9de524333ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9E=9C=E7=89=A9=E3=83=AA=E3=83=B3?= <nassii74@gmail.com>
Date: Sat, 16 Dec 2023 14:13:38 +0900
Subject: [PATCH 252/435] =?UTF-8?q?feature:=20=E3=83=81=E3=83=A3=E3=83=B3?=
 =?UTF-8?q?=E3=83=8D=E3=83=AB=E6=8A=95=E7=A8=BF=E3=81=AF=E3=83=95=E3=82=A9?=
 =?UTF-8?q?=E3=83=BC=E3=83=A0=E3=81=AB=E8=89=B2=E3=81=8C=E3=81=A4=E3=81=8F?=
 =?UTF-8?q?=E3=81=A8=E3=81=86=E3=82=8C=E3=81=97=E3=81=84=20(#12686)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/components/MkPostForm.vue | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 4a1930ac0b..8a2a21a89e 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -67,6 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo>
 	<input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown">
 	<div :class="[$style.textOuter, { [$style.withCw]: useCw }]">
+		<div v-if="channel" :class="$style.colorBar" :style="{ background: channel.color }"></div>
 		<textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/>
 		<div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div>
 	</div>
@@ -1038,6 +1039,17 @@ defineExpose({
 	}
 }
 
+
+.colorBar {
+	position: absolute;
+	top: 0px;
+	left: 12px;
+	width: 5px;
+	height: 100% ;
+	border-radius: 999px;
+	pointer-events: none;
+}
+
 .submitInner {
 	padding: 0 12px;
 	line-height: 34px;
@@ -1296,5 +1308,6 @@ defineExpose({
 	.headerRight {
 		gap: 0;
 	}
+
 }
 </style>

From 776eea736a2607c62145c01b48a8271dd355f006 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 16 Dec 2023 17:37:50 +0900
Subject: [PATCH 253/435] enhance(frontend): tweak avatar decoration setting ui

---
 locales/index.d.ts                            |  1 +
 locales/ja-JP.yml                             |  1 +
 ...n.vue => avatar-decoration.decoration.vue} |  0
 ...ialog.vue => avatar-decoration.dialog.vue} |  0
 ...r-decoration.vue => avatar-decoration.vue} | 77 ++++++++++++-------
 .../frontend/src/pages/settings/profile.vue   | 28 ++++---
 packages/frontend/src/router.ts               |  4 +
 7 files changed, 68 insertions(+), 43 deletions(-)
 rename packages/frontend/src/pages/settings/{profile.avatar-decoration.decoration.vue => avatar-decoration.decoration.vue} (100%)
 rename packages/frontend/src/pages/settings/{profile.avatar-decoration.dialog.vue => avatar-decoration.dialog.vue} (100%)
 rename packages/frontend/src/pages/settings/{profile.avatar-decoration.vue => avatar-decoration.vue} (58%)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index c8a7b4c70d..cd15bd968f 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1183,6 +1183,7 @@ export interface Locale {
     "remainingN": string;
     "overwriteContentConfirm": string;
     "seasonalScreenEffect": string;
+    "decorate": string;
     "_announcement": {
         "forExistingUsers": string;
         "forExistingUsersDescription": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 25a734ef4f..5537db9d56 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1180,6 +1180,7 @@ reloadRequiredToApplySettings: "設定の反映にはリロードが必要です
 remainingN: "残り: {n}"
 overwriteContentConfirm: "現在の内容に上書きされますがよろしいですか?"
 seasonalScreenEffect: "季節に応じた画面の演出"
+decorate: "デコる"
 
 _announcement:
   forExistingUsers: "既存ユーザーのみ"
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
similarity index 100%
rename from packages/frontend/src/pages/settings/profile.avatar-decoration.decoration.vue
rename to packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
similarity index 100%
rename from packages/frontend/src/pages/settings/profile.avatar-decoration.dialog.vue
rename to packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
diff --git a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue
similarity index 58%
rename from packages/frontend/src/pages/settings/profile.avatar-decoration.vue
rename to packages/frontend/src/pages/settings/avatar-decoration.vue
index 8579acfed8..6551fc917e 100644
--- a/packages/frontend/src/pages/settings/profile.avatar-decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.vue
@@ -4,51 +4,56 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div v-if="!loading" class="_gaps">
-	<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
+<div>
+	<div v-if="!loading" class="_gaps">
+		<MkInfo>{{ i18n.t('_profile.avatarDecorationMax', { max: $i.policies.avatarDecorationLimit }) }} ({{ i18n.t('remainingN', { n: $i.policies.avatarDecorationLimit - $i.avatarDecorations.length }) }})</MkInfo>
 
-	<div v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s">
-		<div>{{ i18n.ts.inUse }}</div>
+		<MkAvatar :class="$style.avatar" :user="$i" forceShowDecoration/>
+
+		<div v-if="$i.avatarDecorations.length > 0" v-panel :class="$style.current" class="_gaps_s">
+			<div>{{ i18n.ts.inUse }}</div>
+
+			<div :class="$style.decorations">
+				<XDecoration
+					v-for="(avatarDecoration, i) in $i.avatarDecorations"
+					:decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)"
+					:angle="avatarDecoration.angle"
+					:flipH="avatarDecoration.flipH"
+					:offsetX="avatarDecoration.offsetX"
+					:offsetY="avatarDecoration.offsetY"
+					:active="true"
+					@click="openDecoration(avatarDecoration, i)"
+				/>
+			</div>
+
+			<MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
+		</div>
 
 		<div :class="$style.decorations">
 			<XDecoration
-				v-for="(avatarDecoration, i) in $i.avatarDecorations"
-				:decoration="avatarDecorations.find(d => d.id === avatarDecoration.id)"
-				:angle="avatarDecoration.angle"
-				:flipH="avatarDecoration.flipH"
-				:offsetX="avatarDecoration.offsetX"
-				:offsetY="avatarDecoration.offsetY"
-				:active="true"
-				@click="openDecoration(avatarDecoration, i)"
+				v-for="avatarDecoration in avatarDecorations"
+				:key="avatarDecoration.id"
+				:decoration="avatarDecoration"
+				@click="openDecoration(avatarDecoration)"
 			/>
 		</div>
-
-		<MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
 	</div>
-
-	<div :class="$style.decorations">
-		<XDecoration
-			v-for="avatarDecoration in avatarDecorations"
-			:key="avatarDecoration.id"
-			:decoration="avatarDecoration"
-			@click="openDecoration(avatarDecoration)"
-		/>
+	<div v-else>
+		<MkLoading/>
 	</div>
 </div>
-<div v-else>
-	<MkLoading/>
-</div>
 </template>
 
 <script lang="ts" setup>
-import { ref, defineAsyncComponent } from 'vue';
+import { ref, defineAsyncComponent, computed } from 'vue';
 import * as Misskey from 'misskey-js';
-import XDecoration from './profile.avatar-decoration.decoration.vue';
+import XDecoration from './avatar-decoration.decoration.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { $i } from '@/account.js';
 import MkInfo from '@/components/MkInfo.vue';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const loading = ref(true);
 const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse>([]);
@@ -59,7 +64,7 @@ os.api('get-avatar-decorations').then(_avatarDecorations => {
 });
 
 function openDecoration(avatarDecoration, index?: number) {
-	os.popup(defineAsyncComponent(() => import('./profile.avatar-decoration.dialog.vue')), {
+	os.popup(defineAsyncComponent(() => import('./avatar-decoration.dialog.vue')), {
 		decoration: avatarDecoration,
 		usingIndex: index,
 	}, {
@@ -115,9 +120,25 @@ function detachAllDecorations() {
 		$i.avatarDecorations = [];
 	});
 }
+
+const headerActions = computed(() => []);
+
+const headerTabs = computed(() => []);
+
+definePageMetadata({
+	title: i18n.ts.avatarDecorations,
+	icon: 'ti ti-sparkles',
+});
 </script>
 
 <style lang="scss" module>
+.avatar {
+	display: inline-block;
+	width: 72px;
+	height: 72px;
+	margin: 16px auto;
+}
+
 .current {
 	padding: 16px;
 	border-radius: var(--radius);
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index 83cd698e66..2ee19b9671 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -5,12 +5,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <div class="_gaps_m">
-	<div :class="$style.avatarAndBanner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
+	<div class="_panel">
+		<div :class="$style.banner" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
+			<MkButton primary rounded :class="$style.bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton>
+		</div>
 		<div :class="$style.avatarContainer">
 			<MkAvatar :class="$style.avatar" :user="$i" forceShowDecoration @click="changeAvatar"/>
-			<MkButton primary rounded @click="changeAvatar">{{ i18n.ts._profile.changeAvatar }}</MkButton>
+			<div class="_buttonsCenter">
+				<MkButton primary rounded @click="changeAvatar">{{ i18n.ts._profile.changeAvatar }}</MkButton>
+				<MkButton primary rounded link to="/settings/avatar-decoration">{{ i18n.ts.decorate }} <i class="ti ti-sparkles"></i></MkButton>
+			</div>
 		</div>
-		<MkButton primary rounded :class="$style.bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton>
 	</div>
 
 	<MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']">
@@ -83,13 +88,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<template #caption>{{ i18n.ts._profile.metadataDescription }}</template>
 	</FormSlot>
 
-	<MkFolder>
-		<template #icon><i class="ti ti-sparkles"></i></template>
-		<template #label>{{ i18n.ts.avatarDecorations }}</template>
-
-		<XAvatarDecoration/>
-	</MkFolder>
-
 	<MkFolder>
 		<template #label>{{ i18n.ts.advancedSettings }}</template>
 
@@ -264,19 +262,19 @@ definePageMetadata({
 </script>
 
 <style lang="scss" module>
-.avatarAndBanner {
+.banner {
 	position: relative;
+	height: 130px;
 	background-size: cover;
 	background-position: center;
-	border: solid 1px var(--divider);
-	border-radius: 10px;
+	border-bottom: solid 1px var(--divider);
 	overflow: clip;
 }
 
 .avatarContainer {
-	display: inline-block;
+	margin-top: -50px;
+	padding-bottom: 16px;
 	text-align: center;
-	padding: 16px;
 }
 
 .avatar {
diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts
index a7a53e97e6..baee85866c 100644
--- a/packages/frontend/src/router.ts
+++ b/packages/frontend/src/router.ts
@@ -54,6 +54,10 @@ export const routes = [{
 		path: '/profile',
 		name: 'profile',
 		component: page(() => import('./pages/settings/profile.vue')),
+	}, {
+		path: '/avatar-decoration',
+		name: 'avatarDecoration',
+		component: page(() => import('./pages/settings/avatar-decoration.vue')),
 	}, {
 		path: '/roles',
 		name: 'roles',

From 67824cfc89742fef040d22f7c6f457abaeb33a9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=9E=9C=E7=89=A9=E3=83=AA=E3=83=B3?= <nassii74@gmail.com>
Date: Sun, 17 Dec 2023 17:41:14 +0900
Subject: [PATCH 254/435] fix lint error (#12692)

---
 packages/frontend/src/components/MkPostForm.vue | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 8a2a21a89e..f800d16524 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -1039,7 +1039,6 @@ defineExpose({
 	}
 }
 
-
 .colorBar {
 	position: absolute;
 	top: 0px;

From 24ba0d1fff7df35739dc770e99a1a2e4cbeeffda Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Mon, 18 Dec 2023 02:46:46 +0100
Subject: [PATCH 255/435] Add .npmrc

---
 .npmrc | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 .npmrc

diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000000..f43bec20e0
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+@sharkey:registry=https://git.joinsharkey.org/api/packages/Sharkey/npm/
\ No newline at end of file

From 24261f6eb3e1b0b164dccf44922ee81af315a912 Mon Sep 17 00:00:00 2001
From: ikasoba <57828948+ikasoba@users.noreply.github.com>
Date: Mon, 18 Dec 2023 10:52:23 +0900
Subject: [PATCH 256/435] =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E3=82=92=E4=BB=98?=
 =?UTF-8?q?=E3=81=91=E3=81=9F=20&=20=E8=A8=AD=E5=AE=9A=E8=87=AA=E4=BD=93?=
 =?UTF-8?q?=E3=82=92=E3=83=95=E3=82=A9=E3=83=BC=E3=83=9E=E3=83=83=E3=83=88?=
 =?UTF-8?q?=20(#12693)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .vscode/settings.json | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/.vscode/settings.json b/.vscode/settings.json
index 71fb02a59d..e2a82b1ffe 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,11 +1,15 @@
 {
-    "search.exclude": {
-        "**/node_modules": true
-    },
-    "typescript.tsdk": "node_modules/typescript/lib",
-    "files.associations": {
-        "*.test.ts": "typescript"
-    },
-    "jest.jestCommandLine": "pnpm run jest",
-    "jest.autoRun": "off"
-}
\ No newline at end of file
+	"search.exclude": {
+		"**/node_modules": true
+	},
+	"typescript.tsdk": "node_modules/typescript/lib",
+	"files.associations": {
+		"*.test.ts": "typescript"
+	},
+	"jest.jestCommandLine": "pnpm run jest",
+	"jest.autoRun": "off",
+	"editor.codeActionsOnSave": {
+		"source.fixAll": "explicit"
+	},
+	"editor.formatOnSave": false
+}

From c92c2d065fd7f39eccd77e1780d5631a91d5315c Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Mon, 18 Dec 2023 03:03:05 +0100
Subject: [PATCH 257/435] upd: replace mfm with sfm

---
 packages/backend/package.json                 |  6 +-
 packages/backend/src/core/MfmService.ts       |  2 +-
 .../backend/src/core/NoteCreateService.ts     |  2 +-
 packages/backend/src/core/NoteEditService.ts  |  2 +-
 .../src/core/activitypub/ApMfmService.ts      |  2 +-
 .../src/core/activitypub/ApRendererService.ts |  2 +-
 .../misc/extract-custom-emojis-from-mfm.ts    |  2 +-
 packages/backend/src/misc/extract-hashtags.ts |  2 +-
 packages/backend/src/misc/extract-mentions.ts |  2 +-
 .../src/server/api/endpoints/i/update.ts      |  2 +-
 .../src/server/api/mastodon/converters.ts     |  2 +-
 packages/backend/test/unit/MfmService.ts      |  2 +-
 .../backend/test/unit/extract-mentions.ts     |  2 +-
 packages/frontend/package.json                |  8 +-
 packages/frontend/src/components/MkNote.vue   |  2 +-
 .../src/components/MkNoteDetailed.vue         |  2 +-
 .../frontend/src/components/MkPostForm.vue    |  2 +-
 .../src/components/MkSubNoteContent.vue       |  2 +-
 packages/frontend/src/components/SkNote.vue   |  2 +-
 .../src/components/SkNoteDetailed.vue         |  2 +-
 .../src/components/SkOldNoteWindow.vue        |  2 +-
 .../global/MkMisskeyFlavoredMarkdown.ts       |  2 +-
 .../src/components/page/page.text.vue         |  2 +-
 .../src/scripts/check-animated-mfm.ts         |  2 +-
 .../frontend/src/scripts/extract-mentions.ts  |  2 +-
 .../src/scripts/extract-url-from-mfm.ts       |  2 +-
 pnpm-lock.yaml                                | 98 ++++---------------
 27 files changed, 49 insertions(+), 111 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 22030d168d..66d8412855 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -73,6 +73,7 @@
 		"@nestjs/core": "10.2.8",
 		"@nestjs/testing": "10.2.8",
 		"@peertube/http-signature": "1.7.0",
+		"@sharkey/sfm-js": "0.23.3",
 		"@simplewebauthn/server": "8.3.5",
 		"@sinonjs/fake-timers": "11.2.2",
 		"@smithy/node-http-handler": "2.1.5",
@@ -97,8 +98,8 @@
 		"content-disposition": "0.5.4",
 		"date-fns": "2.30.0",
 		"deep-email-validator": "0.1.21",
-		"fastify-multer": "^2.0.3",
 		"fastify": "4.24.3",
+		"fastify-multer": "^2.0.3",
 		"fastify-raw-body": "4.3.0",
 		"feed": "4.2.2",
 		"file-type": "18.7.0",
@@ -119,7 +120,6 @@
 		"jsrsasign": "10.8.6",
 		"megalodon": "workspace:*",
 		"meilisearch": "0.35.0",
-		"mfm-js": "0.23.3",
 		"microformats-parser": "1.5.2",
 		"mime-types": "2.1.35",
 		"misskey-js": "workspace:*",
@@ -213,10 +213,10 @@
 		"@types/sinonjs__fake-timers": "8.1.5",
 		"@types/tinycolor2": "1.4.6",
 		"@types/tmp": "0.2.6",
+		"@types/uuid": "^9.0.4",
 		"@types/vary": "1.1.3",
 		"@types/web-push": "3.6.3",
 		"@types/ws": "8.5.9",
-		"@types/uuid": "^9.0.4",
 		"@typescript-eslint/eslint-plugin": "6.11.0",
 		"@typescript-eslint/parser": "6.11.0",
 		"aws-sdk-client-mock": "3.0.0",
diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts
index 178b4cc9a9..12f4353ec4 100644
--- a/packages/backend/src/core/MfmService.ts
+++ b/packages/backend/src/core/MfmService.ts
@@ -13,7 +13,7 @@ import { intersperse } from '@/misc/prelude/array.js';
 import type { IMentionedRemoteUsers } from '@/models/Note.js';
 import { bindThis } from '@/decorators.js';
 import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js';
-import type * as mfm from 'mfm-js';
+import type * as mfm from '@sharkey/sfm-js';
 
 const treeAdapter = TreeAdapter.defaultTreeAdapter;
 
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 837a53cfac..2055a0d210 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -4,7 +4,7 @@
  */
 
 import { setImmediate } from 'node:timers/promises';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import { In, DataSource, IsNull, LessThan } from 'typeorm';
 import * as Redis from 'ioredis';
 import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
diff --git a/packages/backend/src/core/NoteEditService.ts b/packages/backend/src/core/NoteEditService.ts
index e5918e6c2f..c4314c65ac 100644
--- a/packages/backend/src/core/NoteEditService.ts
+++ b/packages/backend/src/core/NoteEditService.ts
@@ -4,7 +4,7 @@
  */
 
 import { setImmediate } from 'node:timers/promises';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import { DataSource, In, IsNull, LessThan } from 'typeorm';
 import * as Redis from 'ioredis';
 import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
diff --git a/packages/backend/src/core/activitypub/ApMfmService.ts b/packages/backend/src/core/activitypub/ApMfmService.ts
index c45c27f863..c19eb310d2 100644
--- a/packages/backend/src/core/activitypub/ApMfmService.ts
+++ b/packages/backend/src/core/activitypub/ApMfmService.ts
@@ -4,7 +4,7 @@
  */
 
 import { Injectable } from '@nestjs/common';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import { MfmService } from '@/core/MfmService.js';
 import type { MiNote } from '@/models/Note.js';
 import { bindThis } from '@/decorators.js';
diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts
index 7464593bb6..f4d39d2408 100644
--- a/packages/backend/src/core/activitypub/ApRendererService.ts
+++ b/packages/backend/src/core/activitypub/ApRendererService.ts
@@ -6,7 +6,7 @@
 import { createPublicKey, randomUUID } from 'node:crypto';
 import { Inject, Injectable } from '@nestjs/common';
 import { In } from 'typeorm';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import type { MiPartialLocalUser, MiLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js';
diff --git a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts
index 0b898d47e8..0e8dfd21f8 100644
--- a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts
+++ b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import { unique } from '@/misc/prelude/array.js';
 
 export function extractCustomEmojisFromMfm(nodes: mfm.MfmNode[]): string[] {
diff --git a/packages/backend/src/misc/extract-hashtags.ts b/packages/backend/src/misc/extract-hashtags.ts
index 3bd56e98eb..3598d90093 100644
--- a/packages/backend/src/misc/extract-hashtags.ts
+++ b/packages/backend/src/misc/extract-hashtags.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import { unique } from '@/misc/prelude/array.js';
 
 export function extractHashtags(nodes: mfm.MfmNode[]): string[] {
diff --git a/packages/backend/src/misc/extract-mentions.ts b/packages/backend/src/misc/extract-mentions.ts
index 272eb92192..b0897b05a8 100644
--- a/packages/backend/src/misc/extract-mentions.ts
+++ b/packages/backend/src/misc/extract-mentions.ts
@@ -5,7 +5,7 @@
 
 // test is located in test/extract-mentions
 
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 
 export function extractMentions(nodes: mfm.MfmNode[]): mfm.MfmMention['props'][] {
 	// TODO: 重複を削除
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index 63f9991b22..aa730716bd 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -4,7 +4,7 @@
  */
 
 import RE2 from 're2';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import { Inject, Injectable } from '@nestjs/common';
 import ms from 'ms';
 import { JSDOM } from 'jsdom';
diff --git a/packages/backend/src/server/api/mastodon/converters.ts b/packages/backend/src/server/api/mastodon/converters.ts
index 9571e14cbe..cce7f798fe 100644
--- a/packages/backend/src/server/api/mastodon/converters.ts
+++ b/packages/backend/src/server/api/mastodon/converters.ts
@@ -1,6 +1,6 @@
 import { Inject, Injectable } from '@nestjs/common';
 import { Entity } from 'megalodon';
-import mfm from 'mfm-js';
+import mfm from '@sharkey/sfm-js';
 import { DI } from '@/di-symbols.js';
 import { MfmService } from '@/core/MfmService.js';
 import type { Config } from '@/config.js';
diff --git a/packages/backend/test/unit/MfmService.ts b/packages/backend/test/unit/MfmService.ts
index bb8e6981d5..49e84ccec8 100644
--- a/packages/backend/test/unit/MfmService.ts
+++ b/packages/backend/test/unit/MfmService.ts
@@ -4,7 +4,7 @@
  */
 
 import * as assert from 'assert';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import { Test } from '@nestjs/testing';
 
 import { CoreModule } from '@/core/CoreModule.js';
diff --git a/packages/backend/test/unit/extract-mentions.ts b/packages/backend/test/unit/extract-mentions.ts
index 5901f33fdc..195e9b8198 100644
--- a/packages/backend/test/unit/extract-mentions.ts
+++ b/packages/backend/test/unit/extract-mentions.ts
@@ -5,7 +5,7 @@
 
 import * as assert from 'assert';
 
-import { parse } from 'mfm-js';
+import { parse } from '@sharkey/sfm-js';
 import { extractMentions } from '@/misc/extract-mentions.js';
 
 describe('Extract mentions', () => {
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 071a8cfa8c..69e3406f94 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -18,18 +18,19 @@
 	"dependencies": {
 		"@discordapp/twemoji": "14.1.2",
 		"@github/webauthn-json": "2.1.1",
+		"@phosphor-icons/web": "^2.0.3",
 		"@rollup/plugin-alias": "5.0.1",
 		"@rollup/plugin-json": "6.0.1",
 		"@rollup/plugin-replace": "5.0.5",
 		"@rollup/pluginutils": "5.0.5",
+		"@sharkey/sfm-js": "0.23.3",
 		"@syuilo/aiscript": "0.16.0",
-		"@phosphor-icons/web": "^2.0.3",
 		"@vitejs/plugin-vue": "4.5.0",
 		"@vue-macros/reactivity-transform": "0.4.0",
 		"@vue/compiler-sfc": "3.3.8",
+		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
 		"astring": "1.8.6",
 		"autosize": "6.0.1",
-		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
 		"broadcast-channel": "6.0.0",
 		"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
 		"buraha": "0.0.1",
@@ -52,15 +53,14 @@
 		"is-file-animated": "1.0.2",
 		"json5": "2.2.3",
 		"matter-js": "0.19.0",
-		"mfm-js": "0.23.3",
 		"misskey-js": "workspace:*",
 		"photoswipe": "5.4.2",
 		"punycode": "2.3.1",
 		"querystring": "0.2.1",
 		"rollup": "4.4.1",
 		"sanitize-html": "2.11.0",
-		"shiki": "^0.14.5",
 		"sass": "1.69.5",
+		"shiki": "^0.14.5",
 		"strict-event-emitter-types": "2.0.0",
 		"textarea-caret": "3.1.0",
 		"three": "0.158.0",
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index dbbff650e0..74edc8903e 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -165,7 +165,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, inject, onMounted, ref, shallowRef, Ref, defineAsyncComponent, watch, provide } from 'vue';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import * as Misskey from 'misskey-js';
 import MkNoteSub from '@/components/MkNoteSub.vue';
 import MkNoteHeader from '@/components/MkNoteHeader.vue';
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 34f782c269..93e39ff033 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -221,7 +221,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, inject, onMounted, provide, ref, shallowRef, watch } from 'vue';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import * as Misskey from 'misskey-js';
 import MkNoteSub from '@/components/MkNoteSub.vue';
 import MkNoteSimple from '@/components/MkNoteSimple.vue';
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 74038cd62c..5c9ac40427 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -100,7 +100,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide } from 'vue';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import * as Misskey from 'misskey-js';
 import insertTextAtCursor from 'insert-text-at-cursor';
 import { toASCII } from 'punycode/';
diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue
index abad074515..a91f1f444c 100644
--- a/packages/frontend/src/components/MkSubNoteContent.vue
+++ b/packages/frontend/src/components/MkSubNoteContent.vue
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import MkMediaList from '@/components/MkMediaList.vue';
 import MkPoll from '@/components/MkPoll.vue';
 import MkButton from '@/components/MkButton.vue';
diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue
index b01e78c436..b308f4a07a 100644
--- a/packages/frontend/src/components/SkNote.vue
+++ b/packages/frontend/src/components/SkNote.vue
@@ -167,7 +167,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, inject, onMounted, ref, shallowRef, Ref, defineAsyncComponent, watch, provide } from 'vue';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import * as Misskey from 'misskey-js';
 import SkNoteSub from '@/components/SkNoteSub.vue';
 import SkNoteHeader from '@/components/SkNoteHeader.vue';
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index 0db4e0a30b..4699eba8f6 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -229,7 +229,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, inject, onMounted, provide, ref, shallowRef, watch } from 'vue';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import * as Misskey from 'misskey-js';
 import SkNoteSub from '@/components/SkNoteSub.vue';
 import SkNoteSimple from '@/components/SkNoteSimple.vue';
diff --git a/packages/frontend/src/components/SkOldNoteWindow.vue b/packages/frontend/src/components/SkOldNoteWindow.vue
index 63d9f4682a..49fbd39812 100644
--- a/packages/frontend/src/components/SkOldNoteWindow.vue
+++ b/packages/frontend/src/components/SkOldNoteWindow.vue
@@ -77,7 +77,7 @@
 
 <script lang="ts" setup>
 import { inject, onMounted, ref, shallowRef } from 'vue';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import * as Misskey from 'misskey-js';
 import MkNoteSimple from '@/components/MkNoteSimple.vue';
 import MkMediaList from '@/components/MkMediaList.vue';
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index d437ea9985..bd6a599a98 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -4,7 +4,7 @@
  */
 
 import { VNode, h } from 'vue';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import * as Misskey from 'misskey-js';
 import MkUrl from '@/components/global/MkUrl.vue';
 import MkTime from '@/components/global/MkTime.vue';
diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue
index e0f1a4af90..6aa2c1c0b7 100644
--- a/packages/frontend/src/components/page/page.text.vue
+++ b/packages/frontend/src/components/page/page.text.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { defineAsyncComponent } from 'vue';
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import * as Misskey from 'misskey-js';
 import { TextBlock } from './block.type';
 import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
diff --git a/packages/frontend/src/scripts/check-animated-mfm.ts b/packages/frontend/src/scripts/check-animated-mfm.ts
index f47c40d4bc..8e3ef7ee46 100644
--- a/packages/frontend/src/scripts/check-animated-mfm.ts
+++ b/packages/frontend/src/scripts/check-animated-mfm.ts
@@ -1,4 +1,4 @@
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 
 export function checkAnimationFromMfm(nodes: mfm.MfmNode[]): boolean {
 	const animatedNodes = mfm.extract(nodes, (node) => {
diff --git a/packages/frontend/src/scripts/extract-mentions.ts b/packages/frontend/src/scripts/extract-mentions.ts
index 74ce45d324..fe60f9a851 100644
--- a/packages/frontend/src/scripts/extract-mentions.ts
+++ b/packages/frontend/src/scripts/extract-mentions.ts
@@ -5,7 +5,7 @@
 
 // test is located in test/extract-mentions
 
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 
 export function extractMentions(nodes: mfm.MfmNode[]): mfm.MfmMention['props'][] {
 	// TODO: 重複を削除
diff --git a/packages/frontend/src/scripts/extract-url-from-mfm.ts b/packages/frontend/src/scripts/extract-url-from-mfm.ts
index c1ed9338f8..d2fbfbcc00 100644
--- a/packages/frontend/src/scripts/extract-url-from-mfm.ts
+++ b/packages/frontend/src/scripts/extract-url-from-mfm.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as mfm from 'mfm-js';
+import * as mfm from '@sharkey/sfm-js';
 import { unique } from '@/scripts/array.js';
 
 // unique without hash
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a7b26f03e7..6395d37cc2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -106,6 +106,9 @@ importers:
       '@peertube/http-signature':
         specifier: 1.7.0
         version: 1.7.0
+      '@sharkey/sfm-js':
+        specifier: 0.23.3
+        version: 0.23.3
       '@simplewebauthn/server':
         specifier: 8.3.5
         version: 8.3.5
@@ -244,9 +247,6 @@ importers:
       meilisearch:
         specifier: 0.35.0
         version: 0.35.0
-      mfm-js:
-        specifier: 0.23.3
-        version: 0.23.3
       microformats-parser:
         specifier: 1.5.2
         version: 1.5.2
@@ -669,6 +669,9 @@ importers:
       '@rollup/pluginutils':
         specifier: 5.0.5
         version: 5.0.5(rollup@4.4.1)
+      '@sharkey/sfm-js':
+        specifier: ^0.23.3
+        version: 0.23.3
       '@syuilo/aiscript':
         specifier: 0.16.0
         version: 0.16.0
@@ -756,9 +759,6 @@ importers:
       matter-js:
         specifier: 0.19.0
         version: 0.19.0
-      mfm-js:
-        specifier: 0.23.3
-        version: 0.23.3
       misskey-js:
         specifier: workspace:*
         version: link:../misskey-js
@@ -1081,7 +1081,7 @@ importers:
         version: 9.0.0(eslint@8.54.0)
       jest:
         specifier: ^29.7.0
-        version: 29.7.0
+        version: 29.7.0(@types/node@20.9.1)
       jest-worker:
         specifier: ^29.7.0
         version: 29.7.0
@@ -5733,6 +5733,12 @@ packages:
       string-argv: 0.3.1
     dev: true
 
+  /@sharkey/sfm-js@0.23.3:
+    resolution: {integrity: sha512-j4VxSmZIA438RKCe16kxaqhl2g+69w89x49ebm22XwRwH/KvYIC6uVQdcBhOovyN7l2xCCLdsPM1+bu8wZIP2A==, tarball: https://git.joinsharkey.org/api/packages/Sharkey/npm/%40sharkey%2Fsfm-js/-/0.23.3/sfm-js-0.23.3.tgz}
+    dependencies:
+      twemoji-parser: 14.0.0
+    dev: false
+
   /@sideway/address@4.1.4:
     resolution: {integrity: sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==}
     dependencies:
@@ -7285,7 +7291,7 @@ packages:
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.3.8(typescript@5.2.2)
-      vue-component-type-helpers: 1.8.24
+      vue-component-type-helpers: 1.8.25
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -7297,7 +7303,7 @@ packages:
     hasBin: true
     peerDependencies:
       '@swc/core': ^1.2.66
-      chokidar: ^3.5.1
+      chokidar: 3.5.3
     peerDependenciesMeta:
       chokidar:
         optional: true
@@ -13857,34 +13863,6 @@ packages:
       - supports-color
     dev: true
 
-  /jest-cli@29.7.0:
-    resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    hasBin: true
-    peerDependencies:
-      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
-    peerDependenciesMeta:
-      node-notifier:
-        optional: true
-    dependencies:
-      '@jest/core': 29.7.0
-      '@jest/test-result': 29.7.0
-      '@jest/types': 29.6.3
-      chalk: 4.1.2
-      create-jest: 29.7.0(@types/node@20.9.1)
-      exit: 0.1.2
-      import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.9.1)
-      jest-util: 29.7.0
-      jest-validate: 29.7.0
-      yargs: 17.7.2
-    transitivePeerDependencies:
-      - '@types/node'
-      - babel-plugin-macros
-      - supports-color
-      - ts-node
-    dev: true
-
   /jest-cli@29.7.0(@types/node@20.9.1):
     resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -14330,27 +14308,6 @@ packages:
       supports-color: 8.1.1
     dev: true
 
-  /jest@29.7.0:
-    resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    hasBin: true
-    peerDependencies:
-      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
-    peerDependenciesMeta:
-      node-notifier:
-        optional: true
-    dependencies:
-      '@jest/core': 29.7.0
-      '@jest/types': 29.6.3
-      import-local: 3.1.0
-      jest-cli: 29.7.0
-    transitivePeerDependencies:
-      - '@types/node'
-      - babel-plugin-macros
-      - supports-color
-      - ts-node
-    dev: true
-
   /jest@29.7.0(@types/node@20.9.1):
     resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -15118,12 +15075,6 @@ packages:
     resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
     engines: {node: '>= 0.6'}
 
-  /mfm-js@0.23.3:
-    resolution: {integrity: sha512-o8scYmbey6rMUmWAlT3k3ntt6khaCLdxlmHhAWV5wTTMj2OK1atQvZfRUq0SIVm1Jig08qlZg/ps71xUqrScNA==}
-    dependencies:
-      twemoji-parser: 14.0.0
-    dev: false
-
   /microformats-parser@1.5.2:
     resolution: {integrity: sha512-EcHm8zxEm3CggOLgILfxCo2wDiJEOnACzpV/FXWGLaRk24ECei+JkoWNdKdo2vzo/Pww9EvrQNeQsdv4JuHy7Q==}
     engines: {node: '>=14'}
@@ -19088,7 +19039,7 @@ packages:
       '@babel/core': 7.23.3
       bs-logger: 0.2.6
       fast-json-stable-stringify: 2.1.0
-      jest: 29.7.0
+      jest: 29.7.0(@types/node@20.9.1)
       jest-util: 29.7.0
       json5: 2.2.3
       lodash.memoize: 4.1.2
@@ -19859,8 +19810,8 @@ packages:
   /vscode-textmate@8.0.0:
     resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
 
-  /vue-component-type-helpers@1.8.24:
-    resolution: {integrity: sha512-lqWs/7fdRXoSBAlbouHBX+LNuaY6gI9xWW34m/ZIz9zVPYHEyw0b2/zaCBwlKx0NtKTeF/6pOpvrxVkh7nhIYg==}
+  /vue-component-type-helpers@1.8.25:
+    resolution: {integrity: sha512-NCA6sekiJIMnMs4DdORxATXD+/NRkQpS32UC+I1KQJUasx+Z7MZUb3Y+MsKsFmX+PgyTYSteb73JW77AibaCCw==}
     dev: true
 
   /vue-component-type-helpers@1.8.4:
@@ -20365,19 +20316,6 @@ packages:
       y18n: 5.0.8
       yargs-parser: 21.1.1
 
-  /yargs@17.7.2:
-    resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
-    engines: {node: '>=12'}
-    dependencies:
-      cliui: 8.0.1
-      escalade: 3.1.1
-      get-caller-file: 2.0.5
-      require-directory: 2.1.1
-      string-width: 4.2.3
-      y18n: 5.0.8
-      yargs-parser: 21.1.1
-    dev: true
-
   /yauzl@2.10.0:
     resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
     dependencies:

From 765038f9a62e1964ab038e76e5ef3e60eed24354 Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Mon, 18 Dec 2023 03:37:03 +0100
Subject: [PATCH 258/435] fix: lockfile

---
 pnpm-lock.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6395d37cc2..f8c3cb57d4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -670,7 +670,7 @@ importers:
         specifier: 5.0.5
         version: 5.0.5(rollup@4.4.1)
       '@sharkey/sfm-js':
-        specifier: ^0.23.3
+        specifier: 0.23.3
         version: 0.23.3
       '@syuilo/aiscript':
         specifier: 0.16.0

From 5150053275594278e9eb23e72d98b16593c4c230 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 18 Dec 2023 12:32:26 +0900
Subject: [PATCH 259/435] fix(backend): add missing kind definition for admin
 endpoints to improve security

---
 CHANGELOG.md                                                  | 1 +
 .../src/server/api/endpoints/admin/abuse-user-reports.ts      | 2 ++
 .../backend/src/server/api/endpoints/admin/accounts/create.ts | 2 ++
 .../backend/src/server/api/endpoints/admin/accounts/delete.ts | 2 ++
 .../src/server/api/endpoints/admin/accounts/find-by-email.ts  | 2 ++
 packages/backend/src/server/api/endpoints/admin/ad/create.ts  | 2 ++
 packages/backend/src/server/api/endpoints/admin/ad/delete.ts  | 2 ++
 packages/backend/src/server/api/endpoints/admin/ad/list.ts    | 2 ++
 packages/backend/src/server/api/endpoints/admin/ad/update.ts  | 2 ++
 .../src/server/api/endpoints/admin/announcements/create.ts    | 2 ++
 .../src/server/api/endpoints/admin/announcements/delete.ts    | 2 ++
 .../src/server/api/endpoints/admin/announcements/list.ts      | 2 ++
 .../src/server/api/endpoints/admin/announcements/update.ts    | 2 ++
 .../server/api/endpoints/admin/avatar-decorations/create.ts   | 2 ++
 .../server/api/endpoints/admin/avatar-decorations/delete.ts   | 2 ++
 .../src/server/api/endpoints/admin/avatar-decorations/list.ts | 2 ++
 .../server/api/endpoints/admin/avatar-decorations/update.ts   | 2 ++
 .../backend/src/server/api/endpoints/admin/delete-account.ts  | 2 ++
 .../server/api/endpoints/admin/delete-all-files-of-a-user.ts  | 2 ++
 .../server/api/endpoints/admin/drive/clean-remote-files.ts    | 2 ++
 .../backend/src/server/api/endpoints/admin/drive/cleanup.ts   | 2 ++
 .../backend/src/server/api/endpoints/admin/drive/files.ts     | 2 ++
 .../backend/src/server/api/endpoints/admin/drive/show-file.ts | 2 ++
 .../src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts  | 2 ++
 packages/backend/src/server/api/endpoints/admin/emoji/add.ts  | 2 ++
 packages/backend/src/server/api/endpoints/admin/emoji/copy.ts | 2 ++
 .../src/server/api/endpoints/admin/emoji/delete-bulk.ts       | 2 ++
 .../backend/src/server/api/endpoints/admin/emoji/delete.ts    | 2 ++
 .../src/server/api/endpoints/admin/emoji/import-zip.ts        | 2 +-
 .../src/server/api/endpoints/admin/emoji/list-remote.ts       | 2 ++
 packages/backend/src/server/api/endpoints/admin/emoji/list.ts | 2 ++
 .../server/api/endpoints/admin/emoji/remove-aliases-bulk.ts   | 2 ++
 .../src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts  | 2 ++
 .../src/server/api/endpoints/admin/emoji/set-category-bulk.ts | 2 ++
 .../src/server/api/endpoints/admin/emoji/set-license-bulk.ts  | 2 ++
 .../backend/src/server/api/endpoints/admin/emoji/update.ts    | 2 ++
 .../server/api/endpoints/admin/federation/delete-all-files.ts | 2 ++
 .../admin/federation/refresh-remote-instance-metadata.ts      | 2 ++
 .../api/endpoints/admin/federation/remove-all-following.ts    | 2 ++
 .../server/api/endpoints/admin/federation/update-instance.ts  | 2 ++
 .../backend/src/server/api/endpoints/admin/get-index-stats.ts | 2 ++
 .../backend/src/server/api/endpoints/admin/get-table-stats.ts | 2 ++
 .../backend/src/server/api/endpoints/admin/get-user-ips.ts    | 2 ++
 .../backend/src/server/api/endpoints/admin/invite/create.ts   | 2 ++
 .../backend/src/server/api/endpoints/admin/invite/list.ts     | 2 ++
 packages/backend/src/server/api/endpoints/admin/meta.ts       | 2 ++
 .../backend/src/server/api/endpoints/admin/promo/create.ts    | 2 ++
 .../backend/src/server/api/endpoints/admin/queue/clear.ts     | 2 ++
 .../src/server/api/endpoints/admin/queue/deliver-delayed.ts   | 2 ++
 .../src/server/api/endpoints/admin/queue/inbox-delayed.ts     | 2 ++
 .../backend/src/server/api/endpoints/admin/queue/promote.ts   | 2 ++
 .../backend/src/server/api/endpoints/admin/queue/stats.ts     | 2 ++
 packages/backend/src/server/api/endpoints/admin/relays/add.ts | 2 ++
 .../backend/src/server/api/endpoints/admin/relays/list.ts     | 2 ++
 .../backend/src/server/api/endpoints/admin/relays/remove.ts   | 2 ++
 .../backend/src/server/api/endpoints/admin/reset-password.ts  | 2 ++
 .../server/api/endpoints/admin/resolve-abuse-user-report.ts   | 2 ++
 .../backend/src/server/api/endpoints/admin/roles/assign.ts    | 2 ++
 .../backend/src/server/api/endpoints/admin/roles/create.ts    | 2 ++
 .../backend/src/server/api/endpoints/admin/roles/delete.ts    | 2 ++
 packages/backend/src/server/api/endpoints/admin/roles/list.ts | 2 ++
 packages/backend/src/server/api/endpoints/admin/roles/show.ts | 2 ++
 .../backend/src/server/api/endpoints/admin/roles/unassign.ts  | 2 ++
 .../api/endpoints/admin/roles/update-default-policies.ts      | 2 ++
 .../backend/src/server/api/endpoints/admin/roles/update.ts    | 2 ++
 .../backend/src/server/api/endpoints/admin/roles/users.ts     | 2 ++
 packages/backend/src/server/api/endpoints/admin/send-email.ts | 2 ++
 .../backend/src/server/api/endpoints/admin/server-info.ts     | 2 ++
 .../src/server/api/endpoints/admin/show-moderation-logs.ts    | 2 ++
 packages/backend/src/server/api/endpoints/admin/show-user.ts  | 2 ++
 packages/backend/src/server/api/endpoints/admin/show-users.ts | 2 ++
 .../backend/src/server/api/endpoints/admin/suspend-user.ts    | 2 ++
 .../src/server/api/endpoints/admin/unset-user-avatar.ts       | 4 +++-
 .../src/server/api/endpoints/admin/unset-user-banner.ts       | 2 ++
 .../backend/src/server/api/endpoints/admin/unsuspend-user.ts  | 2 ++
 .../backend/src/server/api/endpoints/admin/update-meta.ts     | 2 ++
 .../src/server/api/endpoints/admin/update-user-note.ts        | 2 ++
 77 files changed, 153 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67898c604e..e683d9cc79 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -93,6 +93,7 @@
 - Fix: アカウントをブロックした際に、自身のユーザーのページでノートが相手に表示される問題を修正
 - Fix: モデレーションログがモデレーターは閲覧できないように修正
 - Fix: HTTP Digestヘッダのアルゴリズム部分に大文字の"SHA-256"しか使えない
+- Fix: 管理者用APIのアクセス権限が適切に設定されていない問題を修正
 
 ## 2023.11.1
 
diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
index be4fc82f0c..484118cd46 100644
--- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
+++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
@@ -13,6 +13,8 @@ import { AbuseUserReportEntityService } from '@/core/entities/AbuseUserReportEnt
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
index 070e88f6f3..07f24d2995 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
@@ -15,6 +15,8 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	res: {
 		type: 'object',
 		optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts
index 60e928ccbe..86f4b0709b 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts
@@ -14,6 +14,8 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireAdmin: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
index 686341582b..7dc9ca830b 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
@@ -13,6 +13,8 @@ import { ApiError } from '@/server/api/error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireAdmin: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
index 17f792639b..cbe9727c46 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
@@ -13,6 +13,8 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts
index 8097133a4c..ba655a6aa3 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts
@@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
index 8cdeaae179..3bda9fcb02 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
@@ -12,6 +12,8 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts
index d065f9ec50..b83c163004 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts
@@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
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 69c31a05eb..fb432336e4 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts
@@ -10,6 +10,8 @@ import { AnnouncementService } from '@/core/AnnouncementService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
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 80ec281253..e84e63c666 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts
@@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
index 9630299a6e..e98ef0b169 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
@@ -14,6 +14,8 @@ import { IdService } from '@/core/IdService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
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 717866aead..e2ec344899 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts
@@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts
index ec143fcb53..158435ed21 100644
--- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts
@@ -10,6 +10,8 @@ import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageAvatarDecorations',
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts
index 6f1f386871..06083cc180 100644
--- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts
@@ -12,6 +12,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageAvatarDecorations',
 	errors: {
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts
index d9c669377d..49a8718bce 100644
--- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts
@@ -15,6 +15,8 @@ import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageAvatarDecorations',
 
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts
index 5ea9a40762..3d8f3d63de 100644
--- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts
@@ -12,6 +12,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageAvatarDecorations',
 
diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts
index 9ef09b172e..adc446d14b 100644
--- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts
+++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts
@@ -12,6 +12,8 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireAdmin: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts
index e47ecd81cf..1fdbbfb12e 100644
--- a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts
@@ -12,6 +12,8 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireAdmin: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts
index 8af44029c5..3f23319a5f 100644
--- a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts
+++ b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts
@@ -10,6 +10,8 @@ import { QueueService } from '@/core/QueueService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts
index 75d689966f..fd8fa46a47 100644
--- a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts
+++ b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts
@@ -13,6 +13,8 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts
index ac8a70e3da..816bbfbc45 100644
--- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts
+++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts
@@ -13,6 +13,8 @@ import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.j
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts
index 4e5320007e..61cb843558 100644
--- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts
+++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts
@@ -14,6 +14,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
index 66ee4cab3b..5333adb624 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
@@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
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 faab8ee608..360926594a 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
@@ -14,6 +14,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
index 5b41dfb514..87260faa43 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
@@ -16,6 +16,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 
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 e6c1bf317f..c483794a40 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
@@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
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 58aa0b9950..e15af7717b 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
@@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts
index 208616c0ac..b75616f3cc 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts
@@ -8,7 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import { QueueService } from '@/core/QueueService.js';
 
 export const meta = {
-	secure: true,
+	kind: 'write:admin',
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
index 855ab8cd24..a383e09338 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
@@ -15,6 +15,8 @@ import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
index ab16d86a3d..210b3639c3 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
@@ -15,6 +15,8 @@ import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
index a5dd6d5e3a..8e92db1daf 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
@@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
index 515053f57b..5a06b5b32f 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
@@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
index 8e834ad1dd..b3e9c6df13 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
@@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts
index 2dc9595a7e..c59d13ad16 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts
@@ -10,6 +10,8 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
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 04226d8953..61d857b7b0 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
@@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts
index b63f01bec3..b81297413c 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts
@@ -12,6 +12,8 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts
index 6dbfe3c4f5..6cc4e3087f 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts
@@ -13,6 +13,8 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts
index 36ea390e45..18884dfca6 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts
@@ -12,6 +12,8 @@ import { QueueService } from '@/core/QueueService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
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 357bf83e87..4232d42ba5 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
@@ -14,6 +14,8 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts
index 4bd9e7de7f..2de85f655a 100644
--- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts
+++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts
@@ -12,6 +12,8 @@ export const meta = {
 	requireCredential: true,
 	requireAdmin: true,
 
+	kind: 'read:admin',
+
 	tags: ['admin'],
 } as const;
 
diff --git a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts
index f953b889a3..c104f653ef 100644
--- a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts
+++ b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts
@@ -12,6 +12,8 @@ export const meta = {
 	requireCredential: true,
 	requireAdmin: true,
 
+	kind: 'read:admin',
+
 	tags: ['admin'],
 
 	res: {
diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
index 6afa824703..6a404c0c77 100644
--- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
+++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
@@ -12,6 +12,8 @@ import { IdService } from '@/core/IdService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/invite/create.ts b/packages/backend/src/server/api/endpoints/admin/invite/create.ts
index c6ee45735e..96de772edc 100644
--- a/packages/backend/src/server/api/endpoints/admin/invite/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/invite/create.ts
@@ -16,6 +16,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/invite/list.ts b/packages/backend/src/server/api/endpoints/admin/invite/list.ts
index ff57940d48..3b7dc72e11 100644
--- a/packages/backend/src/server/api/endpoints/admin/invite/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/invite/list.ts
@@ -12,6 +12,8 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 8774bcbb67..07912154bd 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -13,6 +13,8 @@ import { DEFAULT_POLICIES } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['meta'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireAdmin: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts
index 4061e1b5df..e2befec50f 100644
--- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts
@@ -13,6 +13,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
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 c9142e9885..1d565e8f24 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts
@@ -11,6 +11,8 @@ import { QueueService } from '@/core/QueueService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts
index 1515ae4c74..30005fc666 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts
@@ -11,6 +11,8 @@ import type { DeliverQueue } from '@/core/QueueModule.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
index febe0d07c6..aa8b6edee5 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
@@ -11,6 +11,8 @@ import type { InboxQueue } from '@/core/QueueModule.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
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 0cba5b4e25..8f46cd6375 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts
@@ -11,6 +11,8 @@ import { QueueService } from '@/core/QueueService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts
index 901195e9a5..1d92e2bf86 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts
@@ -10,6 +10,8 @@ import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, Obj
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/relays/add.ts b/packages/backend/src/server/api/endpoints/admin/relays/add.ts
index b675db2b89..53b83560cf 100644
--- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts
+++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts
@@ -12,6 +12,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/relays/list.ts b/packages/backend/src/server/api/endpoints/admin/relays/list.ts
index 0633c57ed5..35c8e05487 100644
--- a/packages/backend/src/server/api/endpoints/admin/relays/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/relays/list.ts
@@ -10,6 +10,8 @@ import { RelayService } from '@/core/RelayService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts
index 661b4243c4..fdc53cb708 100644
--- a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts
+++ b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts
@@ -10,6 +10,8 @@ import { RelayService } from '@/core/RelayService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
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 13e9c30ed8..73bbd1f091 100644
--- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts
+++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts
@@ -14,6 +14,8 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
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 fb5ac7a335..fb26c82a9d 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
@@ -15,6 +15,8 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
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 a0f3edd867..bbd4cfabbe 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts
@@ -13,6 +13,8 @@ import { RoleService } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
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 fb53815333..ac6085d921 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
@@ -11,6 +11,8 @@ import { RoleService } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireAdmin: true,
 
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 7b989050eb..f60d6754a5 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/delete.ts
@@ -13,6 +13,8 @@ import { RoleService } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireAdmin: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/list.ts b/packages/backend/src/server/api/endpoints/admin/roles/list.ts
index 71b8e44e77..30917ce984 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/list.ts
@@ -12,6 +12,8 @@ import { RoleEntityService } from '@/core/entities/RoleEntityService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/show.ts b/packages/backend/src/server/api/endpoints/admin/roles/show.ts
index 1ca952a3f8..91e32d95be 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/show.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/show.ts
@@ -13,6 +13,8 @@ import { RoleEntityService } from '@/core/entities/RoleEntityService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
+	kind: 'read:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
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 4c27583111..701fea1ed5 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts
@@ -13,6 +13,8 @@ import { RoleService } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts
index b4e7e29e90..066fc73234 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts
@@ -11,6 +11,8 @@ import { MetaService } from '@/core/MetaService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireAdmin: true,
 } as const;
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 6031e2363e..6cfcd8ca4a 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
@@ -14,6 +14,8 @@ import { RoleService } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireAdmin: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
index b7f9aa0495..53145a32d6 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
@@ -16,6 +16,8 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin', 'role', 'users'],
 
+	kind: 'read:admin',
+
 	requireCredential: false,
 	requireAdmin: true,
 
diff --git a/packages/backend/src/server/api/endpoints/admin/send-email.ts b/packages/backend/src/server/api/endpoints/admin/send-email.ts
index b9f2c6a6f1..d22066909e 100644
--- a/packages/backend/src/server/api/endpoints/admin/send-email.ts
+++ b/packages/backend/src/server/api/endpoints/admin/send-email.ts
@@ -10,6 +10,8 @@ import { EmailService } from '@/core/EmailService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts
index 3169373b0e..d3c3bebff6 100644
--- a/packages/backend/src/server/api/endpoints/admin/server-info.ts
+++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts
@@ -17,6 +17,8 @@ export const meta = {
 
 	tags: ['admin', 'meta'],
 
+	kind: 'read:admin',
+
 	res: {
 		type: 'object',
 		optional: false, nullable: false,
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 34c247343a..c82532ed67 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
@@ -16,6 +16,8 @@ export const meta = {
 	requireCredential: true,
 	requireAdmin: true,
 
+	kind: 'read:admin',
+
 	res: {
 		type: 'array',
 		optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts
index f550c4fd28..f1e7b75a32 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts
@@ -17,6 +17,8 @@ export const meta = {
 	requireCredential: true,
 	requireModerator: true,
 
+	kind: 'read:admin',
+
 	res: {
 		type: 'object',
 		nullable: false, optional: false,
diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts
index fc810987d2..5081383687 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts
@@ -17,6 +17,8 @@ export const meta = {
 	requireCredential: true,
 	requireModerator: true,
 
+	kind: 'read:admin',
+
 	res: {
 		type: 'array',
 		nullable: false, optional: false,
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 9464f4b677..35c3f37481 100644
--- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts
@@ -19,6 +19,8 @@ import { QueueService } from '@/core/QueueService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts b/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts
index ac10f1b6fd..2309493937 100644
--- a/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts
@@ -12,6 +12,8 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
@@ -39,7 +41,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			if (user == null) {
 				throw new Error('user not found');
 			}
-	
+
 			if (user.avatarId == null) return;
 
 			await this.usersRepository.update(user.id, {
diff --git a/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts b/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts
index 66acd367df..468c634e5b 100644
--- a/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts
@@ -12,6 +12,8 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
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 5e523bbc31..8cdd317eae 100644
--- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts
@@ -13,6 +13,8 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;
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 d6f9b2cd94..293a95a9a4 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -12,6 +12,8 @@ import { MetaService } from '@/core/MetaService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireAdmin: true,
 } as const;
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 bfccc2a2a5..dd0b777373 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
@@ -12,6 +12,8 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
+	kind: 'write:admin',
+
 	requireCredential: true,
 	requireModerator: true,
 } as const;

From 6672e9efddf60fd641514485fdf4d5d1aeddc6d2 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 18 Dec 2023 13:07:19 +0900
Subject: [PATCH 260/435] refactor(frontend): use GLSL ES 3.0

---
 .../frontend/src/scripts/snowfall-effect.ts   | 39 +++++++++----------
 1 file changed, 18 insertions(+), 21 deletions(-)

diff --git a/packages/frontend/src/scripts/snowfall-effect.ts b/packages/frontend/src/scripts/snowfall-effect.ts
index 54fb48c5e4..a09f02cec0 100644
--- a/packages/frontend/src/scripts/snowfall-effect.ts
+++ b/packages/frontend/src/scripts/snowfall-effect.ts
@@ -4,24 +4,20 @@
  */
 
 export class SnowfallEffect {
-	private VERTEX_SOURCE = `
-		precision highp float;
-
-		attribute vec4 a_position;
-		attribute vec4 a_color;
-		attribute vec3 a_rotation;
-		attribute vec3 a_speed;
-		attribute float a_size;
-
+	private VERTEX_SOURCE = `#version 300 es
+		in vec4 a_position;
+		in vec4 a_color;
+		in vec3 a_rotation;
+		in vec3 a_speed;
+		in float a_size;
+		out vec4 v_color;
+		out float v_rotation;
 		uniform float u_time;
 		uniform mat4 u_projection;
 		uniform vec3 u_worldSize;
 		uniform float u_gravity;
 		uniform float u_wind;
 
-		varying vec4 v_color;
-		varying float v_rotation;
-
 		void main() {
 			v_color = a_color;
 			v_rotation = a_rotation.x + u_time * a_rotation.y;
@@ -41,13 +37,13 @@ export class SnowfallEffect {
 		}
 	`;
 
-	private FRAGMENT_SOURCE = `
+	private FRAGMENT_SOURCE = `#version 300 es
 		precision highp float;
 
+		in vec4 v_color;
+		in float v_rotation;
 		uniform sampler2D u_texture;
-
-		varying vec4 v_color;
-		varying float v_rotation;
+		out vec4 out_color;
 
 		void main() {
 			vec2 rotated = vec2(
@@ -55,9 +51,9 @@ export class SnowfallEffect {
 				cos(v_rotation) * (gl_PointCoord.y - 0.5) - sin(v_rotation) * (gl_PointCoord.x - 0.5) + 0.5
 			);
 
-			vec4 snowflake = texture2D(u_texture, rotated);
+			vec4 snowflake = texture(u_texture, rotated);
 
-			gl_FragColor = vec4(snowflake.rgb * v_color.xyz, snowflake.a * v_color.a);
+			out_color = vec4(snowflake.rgb * v_color.xyz, snowflake.a * v_color.a);
 		}
 	`;
 
@@ -104,9 +100,9 @@ export class SnowfallEffect {
 	private depth = 100;
 	private count = 1000;
 	private gravity = 100;
-	private speed: number = 1 / 15000;
+	private speed: number = 1 / 10000;
 	private color: number[] = [1, 1, 1];
-	private opacity = 0.75;
+	private opacity = 1;
 	private size = 4;
 	private snowflake = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KTMInWQAAErRJREFUeAHdmgnYlmPax5MShaxRKRElPmXJXpaSsRxDU0bTZ+kt65RloiRDltEMQsxYKmS+zzYjxCCamCzV2LchResMIxFRQ1G93+93Pdf5dL9v7zuf4/hm0fc/jt9znddy3/e1nNd53c/7vHXq/AtVWVnZA/bzkaQjoWG298DeMdvrmP6/EIOqC4fBsbAx7Arz4TaYBPXgWVDnO2jSBrB2T0IMIA9mCmmoE8aonPkR6WPZHlp9xSlfeyeBzq9bHBD5feEdUGfDXBgBqnde+a2wvw/dYdNctvZNAp1PnTaFttA6JgP7eVgBM0CNzgO9HNvy0AcYDda6SaDTdXOnz8X+IkZDugAGQmOYA+ob6Ah/MIOMDRPhJjgJ6uV7pXtWt81/50SnY/Wvwn4ZDHAvwJ9ATYcxyaqsnEnqZCyCPaE80BgYZXG/5A3VyyP/b08LHa11z9KmFUwA5eqruRBHYX1s8WSI1Xcbme8Mt8PWUCU+kF8XbFN+dtH+p06OD4IU8EjD/VOZ5bnezq0XHcHuC2oV7BDlkVIWq56uIX8UjAO31GRIMYW0Vo/xXtSXJyTuXVO6xk1qalRTmQ9AfqzEvog2XYpllnsd6Qr4unCPT7NtByu0uU7vuAaOoy1JuvfXpJdTvSX0gI1gCXwGZdFmEFxoQb7Wid8s7lNu+I8wuHGsTqz2zpQ9DAa5R6HC55A2gvCMXthvwi25bjx26H0M9/9f4Rnok9s0zulFlC2HzzP9cnld8nH/p7DVrbmuIfYs6JLz9U3/z+KGadDeCDsmwre7GyEifn/su8HVSsL2HeBn8CK8AW+B7u9R5yrPgyOjvSn5DWAaXAG2UU7CE9Ayt4k4sR1lX4LaLdd9gn2ftsL+Vtuh1Dp/elH1C8lvCdUj8kDK3gbP8XdhCnSC86rcsNSR9pQvhc/gVlB9bUfqoFNAy/mLrUROrpMwCtpBxBbTtLqkF4K6IF9rf57I9pnYekx5AS0P1VhopXso9pR5buC7+kewU86nFcB+BT4EXdIvNO73sRBubGTXLZtTtgp+DEb++bACdqBuJOlAaMMzLVM3whegNznQDtCb+pW5b8YY76euB5+7pxm0IbzCfS8m3Zf2q4T8/+4JNArXGoptpxz8LqDmQJq0Qnostt/sfIn5GygD4/Zeq7B7wljQO2yjB/QGj0Pjxz4wGdqXrkjXtCT/ISyDa6EPpHrSraFjvnecFpMoMx40Br3xSlD262rYObevddHTs2kYwWUG9uP5It/f1eU5Xw9btwoXPALbwYXcg+unG/KB3Rq8n9ddAOpn4Kr8BAaBcltcDo9D7Ouavig1o34x7F94xqPk74eLQH0MH8HvwS3SLPe9iheEG6f70KiuLpZv6sxG/Va5bFJOabaO7ucAvGEbeAH+AN1hV7iDOidQFz4A2oJb6D1YDhXZHkTqpL8EbqHDYRtwW20AsdIb8syl5N2e6dTAPB2mWYa+hE4Qk7I59iMwFZ70GlJlfyuTVfygs7Hyw7HbwI0w3Tak14BqEtdg7wVdIx8pZbtBUbrjZeA3vUPBANkU+sEehev8O4Db6QpwYm+D8II0KPKHwUFeQ3oLDIMN4WgID1yOPQ+MAXMhNAtju3ztmtuAypiAw7EXwo/Am+0NfUG5mknYc6GfGVIjsoFNuyuoh8COuDcd2LmwA9jWE8bB3Q7N4XrwWAz5XOXR+Tx4n6FgdHeB6sF/w2QwhlSXdXvl/jixx4NH8GW5LDzb7GrR4ES4F5QddB99CieAwStOAPegdUZ2B71F3AXbQSn3vJ1bYaYWrayh3NUPTcbYFExVW3CfXwlvgfoavMbnDAY9dxGo6dCt0LeaB54H4UydDEPA2R4PDlrFLB9XuNmTlO+Xr7X9ZNBr9J4+EN8AMcv6ButpMND9FM6EnTOHkLrSnvtzwbbq3vwMB2ow/qWFSC8ZC++ZQaldbquH2afQWbl8TdcvVtC6LtipifAuOKt6gA9Tzqgzb5R2gP1hX3DVtZVHVvdklY5DA5beIkVPuZn8LOgAnWEfeAaUkxCan/voBNkfF+U5cFu5z5XlxZU20OmZtgm1K45VO4naNCukrcBZVk/CD+E/YBjoYjXJY8Zg9DxsDrbbBHTRotxOrug4eBs+hHgWZtKzfHrdXHBi9gDvqzxFHNA5KVfyBCf0ExgB7nkXStLLEKkniNf0AzUs5+ublkVFKiC9FBZAvGxshT0NnN3zoSUYSJQPcjAvm0HmjcIPemNS96F6E36drFLwugx7EEzNZV/l9IjoEPkW4B7eFtYH9QKcBcfA/aCWgpPQOT+zMbb9fS3nDbYR2MdgV0S5aVlUhLs0w45IHi7sqnnGJ2E7CXqHWgZXgJ1y8KqpDUmfSLmSV5yB/XrpDqVP8ofmehNdOv7I0ShfP4yyJdl2a4SchI1gCXgkHgljYfvc1i3cs/SU1A9jQRpfri/b0Sal1RrtSj4ULyHprY5C6+6E1+EBULq0E+DK7A96iwqX0z4td8B3dCdob5gD3UB3j9fUcNuDKFOvgc+bZAZFf4Zgu/q/AGPMgfm+5ShPWay+k6I31BwAvVDRYL2cuqfUVTkfnTqvVFx5ai7/MXn3tp1UrtRkDWRsaAMjzaD08uJ1irz7+8ps/6ZYj90V3FKrQBkvmubULbN7vs7tZRyJV9w0ePLbQ4PcJspqXnkbhbgoGk/AVptZRxpB0hU7Mpc1x34cdgKPm1dzeTts9XPwlFAO5Au4BDbO7ZycO7J9A/Zh2b4A2+ucALefWpTrflDKVq4kHQBOoi9PO1qvsDeGd6AxXAJbQ5VxlFrW8EnDcJlTsOPcjElxL7WNy7AduC4f2+A/rSN/Hyg7YMBTxgqPUT3F2HAqtIb58GvQW86GqyG+ff4UWz0FBuH4UhaTal1vmAGfg98dfP4d4HPGwmwYAg+D2/J7uU0ap/YaolHZVbBj5d1DaSK8ADsmqiH2JIhgNRhbPZrbhSdZ5heVJGw7477VfYuaagMK2sM8iMloga1HXAt/AeWELgQnR/0Z7k3W6pe3xTn/JamTFPGnPMZSj6p90rA8YOziwHcnH/EgTovJlJ0LPSHkyrTKmZNJ+8KrYKBsCQeB0pWdBFNleieMgzjL44jejTK1CPSY0CiMdyOT09g6ni5O3Ceg51U4VNLaPSA3SDNEwwiKFdgHgANNrpjb7UVejYTYCuZ92DR42HYh8gfDJfAMqBi4dqxk+RrKGkD0YXNsA6AT5qCUXhBe5CR0gPCC4dhqKFwI1m1qX0hr94CotDE4aAd3PCyBX4Jyn+sNL5tBDsRAp3S7b5KVYwa2A0nHaO5AXBeDtnlMxizsW+HomLh8zX9R5sTeBSEn/cqc2Tvak9eDXCyP2PgbYWzn2gefHxT7+0Qu/h18DO7XmPWYcYqSXuHz2myb6G7RNs7meLgeMxXugbiPA3clQx0xtgNPGN819L7+oCzvm6zSx+EkI+Du3Pe0LbOd/jqc7dhG9Wib+mJ5jaJBuL8e4B5aAMpAomKlb8d+KZWUVnw+dgzKSdDtvKaLDyJ1ReZB7O0J2EV5Xwd8OsTJExNpu7Q1SJ8zgy7K93UCX4P4mr4udoyhPGDKygOP+tomIFarMw2d+cfgF2DnDVAGoBvzw33YTHgPDoXQ7Fx/Wy6YkdMrcrmrehO4Pz3WvP90cIVPgonwITg4973yu0XTZK0+ZQaQd+K816twVAwKO71ZRj9zeg7lcVzXHghpVN4n2G3BAHQ1NILx4MBjoppgLwL3Ww8IHZsf6vGk3O8fwx9heK7rhD0o2zdg75JtT6GzQQ8KzcZwElSr3M5J85ktYCzEG+Gx2NNzm/Cm5pSp+K2gfLrZbg3RcB2IQcZN1qPM3+l06SjbAltX/TiXe1wtg7+AdR+AcgIs7xUPw94XxuTrnOD4E1bEoe9Rptw+DWGOGeQi7JOs1SfKKfk+epcakPNxbI8uFVdem8vT6aJdq7jASYjOFPdQDP4Q6t+Em8HVutmbkbYH9Tv4LcQW+H6ujy9Wrtxc6A7vQnznb5TbHUPZ0mw7CeoaOBAegmfBIKw8WZzs34M/oNiPGPzB2KHdrVMUlD29VFLLpw2jMWmnaIbdDNxXur+dWgVumTMglI4zMgbUEV5LmjqW7XnRkDS9qhbu/xZlZ8LWuc3UfM22Of80aVcYDJ/lstdIWxXu0TGXm/TO19vveHWuOglUxOo6iMfyBe7JOEp01ech9puuuBCMA8pVcUUNUB5lqgMYwJyE1oXOGTh9v1gO6kmogKEwHtREMHYofz5zAl3lJ2AWqJfgfohJiKB8HWWfg54YA9Zr1fn5Xmm80SdvHhNwVmq2umF8vWxA+WRwwE9BPNhOulrq0nxz97j6Go6DF8HYcBfYyer6MwWuoINeDG6roq4iE97QCtsJuxWc2JrkCeKEbgX7waOgnLiavxdQEWfohtgRwCrygIoxoQv1K0FNgR7gAKPTB+dr5lAWMliqmbAb7AzbgCs42vYK21NmOiwHJ9atpdxqDlhdA75QdYJT4XUYDfbBiVRe5ySoZTAbBpeekp6T4lo5uFnBz0fpJ6P8E9SJufEdXHipdRA/mw2hzmvfhrfgfjCKPwJnwn2g3igldb4hNaD5a6/fz7eHVuAb2wPwPs+4DB7E/hTagd64BbgoC6Ab9IAfgn+OX0p/ppAaGxZjnw6+Ep8DK8Cj0IDrmHw3GaeN9EZ/AlxFfk1RuVGUYu8K00D9Fa6EvrAUVKzO29gXg9vC1VW3g540w0xBcU2hKJnz+FxYvTCXWaduK/StuTZlLcD6JjnfEvsb6A56m32z78q4FMGw1gA4lEa60WmwMeiSnsljIBSDmEOBE3RdfvggbMuMIbNhItgJtbyUpE9ddjA0Bid1sderXDaQ1OdPAO9zH6hDcpuG2Ml7SQfArHRx6Xpf3JTluySrsrIP6Seg9/iMqsEvF6YZoXIDeAZCRmpneAHEnnLQnaEuXATX53schR3n/e7YyuvOT1bpnyV107Io3xZ6QWs4EirAyXkEqqvK3xa9CQ0c5C5xQ+zN8kWjcr2xZxTsBHfmsipbP671ZmW3wHYA58DdEPobhtwVF2HfBE9H3pT8xjkdja3iiDK4PQBO8Dx4B9wiH8JKeANcKTUW9IITwKNMeYrcArfDhVDsb1pVyty26le5D97/zWzrzVUGXyVjI0WjHUgq4CjoAuGiRuuJkN7mSJX7cn+uaZNyfBBgDHZqXvqsU2cZ6aPwChgE/ap8M9wLbSH+0DKOaw18z8N12GPAyf4BfADbwBmwCbxAHY9NvxQXx2GgVLZXPvurZDE0rqk5+NmAm8U2aIbdH9yDalgpSS80ltlB29fPqW9c8XLUHnsIuGquqt8gN7edwtazrOsAn4MysLryX8BD4Ap3y+0dZROIwPsl9h/hHjgit4lXdrdvHN8dc91wyk7JdvIS7VpF46Jb2ZGz4WJIRyBpBKQW3oR8lZuSvwQMhKtAfQUpYuf27cgbNx6EEeDAzgMHPwYMYi2gEcSfxC7B9qicDMoo/1vQI8p9IG88WAY/yeVpYrJdHpf5vytu4Ky7X46xIamrvjDb52OrG3K+HrZt4xq9wYEZPGPVfp7bhsdE2os2ylV6J1n5mbYPUX4S7AkGX+OAk2t6mm1Iw3PtQ+O4LuooK26RYvW3s7nBLZDiAGlbUHYiRV/S5AWk28DTEFqB4eo+B+n1M55Ivhu4kspj92uYCm6Px0Gv61lor0fcDQNBrQQnOr71lVeYsm894L/bkBuFe/u93eBngJtJMlwTDIDKyfDt6n3se8Dt8jHoNU0o70waq34obZ8lPx4coG+LbifrP6Pt0aQvwn65LFzcAHY8ZUtgAnwExp2WoMpeQLvaA12p7bf/pLPFmS3a/ajr750cfE43wX4YYmU9wi7IddHBCsrc69vm8uuwQydYVhQVvmsUn7s+ebfD0GhXrI+yf2jqA4oPKdo+iHxMwHbYRmgjta4cUTqCWXkg0UHatIR4SxxWKK9PeXhgKiZfxWOthzXuGff4p6b54bH3Y3W3pNxJcK8ebgdI44iys0G0N/8qKGOAGg9Ni50n3yjy2GkxSKtMRtT/21I7Fg/H9lRIX6qK5YX6zSjvDL4BGiBfBnUNmFdzwfKX4Ct40OtJv1sDj0Hlzrk6xbM3tob7uCf4amyk96VHvQg7gltGzQG9wpcwX6BCesfJ3/kJiMmgs+Gm4errUeZqF+Up4IoOzoWLcmqETyLve/2BsKkFpGUvK7VYCz6j06RbQx+ogHhN3Qdb3QF+a/wVKF94OhSHR77sWcXytcKm82usHGW9QE2B3skq/QB7APaqnJ9NuvaufnF1GIhxYH3LSAeA+hM0hMfgNzATdHvjgDHDv+qkP8gW77XW2gwmYsJe2F3zZDgxI7NteTo+/1WD/B9Au3Zjh2RyrgAAAABJRU5ErkJggg==';
 
@@ -159,7 +155,7 @@ export class SnowfallEffect {
 
 	constructor() {
 		const canvas = this.initCanvas();
-		const gl = canvas.getContext('webgl', { antialias: true });
+		const gl = canvas.getContext('webgl2', { antialias: true });
 		if (gl == null) throw new Error('Failed to get WebGL context');
 
 		document.body.append(canvas);
@@ -190,6 +186,7 @@ export class SnowfallEffect {
 			height: '100vh',
 			background: 'transparent',
 			'pointer-events': 'none',
+			'z-index': 2147483647,
 		});
 
 		return canvas;

From 8a9f4ef4416997decc68b7cfd3698069e3736a1f Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 18 Dec 2023 13:14:58 +0900
Subject: [PATCH 261/435] update deps

---
 packages/backend/package.json    |   4 +-
 packages/frontend/package.json   |  12 +-
 packages/misskey-js/package.json |   4 +-
 packages/sw/package.json         |   2 +-
 pnpm-lock.yaml                   | 931 ++++++++++++++++++-------------
 5 files changed, 565 insertions(+), 388 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 8a9871b78c..1a435bcc1f 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -122,7 +122,7 @@
 		"jsrsasign": "10.9.0",
 		"meilisearch": "0.36.0",
 		"mfm-js": "0.23.3",
-		"microformats-parser": "1.5.2",
+		"microformats-parser": "2.0.2",
 		"mime-types": "2.1.35",
 		"misskey-js": "workspace:*",
 		"ms": "3.0.0-canary.1",
@@ -195,7 +195,7 @@
 		"@types/jsrsasign": "10.5.12",
 		"@types/mime-types": "2.1.4",
 		"@types/ms": "0.7.34",
-		"@types/node": "20.10.4",
+		"@types/node": "20.10.5",
 		"@types/node-fetch": "3.0.3",
 		"@types/nodemailer": "6.4.14",
 		"@types/oauth": "0.9.4",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index b2ab1eb9ce..48d22869da 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -25,10 +25,10 @@
 		"@syuilo/aiscript": "0.16.0",
 		"@tabler/icons-webfont": "2.44.0",
 		"@vitejs/plugin-vue": "4.5.2",
-		"@vue/compiler-sfc": "3.3.11",
+		"@vue/compiler-sfc": "3.3.12",
 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
 		"astring": "1.8.6",
-		"broadcast-channel": "6.0.0",
+		"broadcast-channel": "7.0.0",
 		"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
 		"buraha": "0.0.1",
 		"canvas-confetti": "1.6.1",
@@ -54,7 +54,7 @@
 		"misskey-js": "workspace:*",
 		"photoswipe": "5.4.3",
 		"punycode": "2.3.1",
-		"rollup": "4.9.0",
+		"rollup": "4.9.1",
 		"sanitize-html": "2.11.0",
 		"sass": "1.69.5",
 		"shiki": "0.14.7",
@@ -70,7 +70,7 @@
 		"uuid": "9.0.1",
 		"v-code-diff": "1.7.2",
 		"vite": "5.0.10",
-		"vue": "3.3.11",
+		"vue": "3.3.12",
 		"vuedraggable": "next"
 	},
 	"devDependencies": {
@@ -97,7 +97,7 @@
 		"@types/estree": "1.0.5",
 		"@types/matter-js": "0.19.5",
 		"@types/micromatch": "4.0.6",
-		"@types/node": "20.10.4",
+		"@types/node": "20.10.5",
 		"@types/punycode": "2.1.3",
 		"@types/sanitize-html": "2.9.5",
 		"@types/throttle-debounce": "5.0.2",
@@ -107,7 +107,7 @@
 		"@typescript-eslint/eslint-plugin": "6.14.0",
 		"@typescript-eslint/parser": "6.14.0",
 		"@vitest/coverage-v8": "0.34.6",
-		"@vue/runtime-core": "3.3.11",
+		"@vue/runtime-core": "3.3.12",
 		"acorn": "8.11.2",
 		"cross-env": "7.0.3",
 		"cypress": "13.6.1",
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index fed440f6db..53d5044d68 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -25,7 +25,7 @@
 		"@microsoft/api-extractor": "7.38.5",
 		"@swc/jest": "0.2.29",
 		"@types/jest": "29.5.11",
-		"@types/node": "20.10.4",
+		"@types/node": "20.10.5",
 		"@typescript-eslint/eslint-plugin": "6.14.0",
 		"@typescript-eslint/parser": "6.14.0",
 		"eslint": "8.56.0",
@@ -35,7 +35,7 @@
 		"mock-socket": "9.3.1",
 		"ncp": "2.0.0",
 		"nodemon": "3.0.2",
-		"tsd": "0.29.0",
+		"tsd": "0.30.0",
 		"typescript": "5.3.3"
 	},
 	"files": [
diff --git a/packages/sw/package.json b/packages/sw/package.json
index e43e1f9a8a..c48efd6ea6 100644
--- a/packages/sw/package.json
+++ b/packages/sw/package.json
@@ -9,7 +9,7 @@
 		"lint": "pnpm typecheck && pnpm eslint"
 	},
 	"dependencies": {
-		"esbuild": "0.19.8",
+		"esbuild": "0.19.9",
 		"idb-keyval": "6.2.1",
 		"misskey-js": "workspace:*"
 	},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 564e5b7929..69b844f2a2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -246,8 +246,8 @@ importers:
         specifier: 0.23.3
         version: 0.23.3
       microformats-parser:
-        specifier: 1.5.2
-        version: 1.5.2
+        specifier: 2.0.2
+        version: 2.0.2
       mime-types:
         specifier: 2.1.35
         version: 2.1.35
@@ -548,8 +548,8 @@ importers:
         specifier: 0.7.34
         version: 0.7.34
       '@types/node':
-        specifier: 20.10.4
-        version: 20.10.4
+        specifier: 20.10.5
+        version: 20.10.5
       '@types/node-fetch':
         specifier: 3.0.3
         version: 3.0.3
@@ -639,7 +639,7 @@ importers:
         version: 8.0.1
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.10.4)
+        version: 29.7.0(@types/node@20.10.5)
       jest-mock:
         specifier: 29.7.0
         version: 29.7.0
@@ -660,13 +660,13 @@ importers:
         version: 2.1.1
       '@rollup/plugin-json':
         specifier: 6.1.0
-        version: 6.1.0(rollup@4.9.0)
+        version: 6.1.0(rollup@4.9.1)
       '@rollup/plugin-replace':
         specifier: 5.0.5
-        version: 5.0.5(rollup@4.9.0)
+        version: 5.0.5(rollup@4.9.1)
       '@rollup/pluginutils':
         specifier: 5.1.0
-        version: 5.1.0(rollup@4.9.0)
+        version: 5.1.0(rollup@4.9.1)
       '@syuilo/aiscript':
         specifier: 0.16.0
         version: 0.16.0
@@ -675,10 +675,10 @@ importers:
         version: 2.44.0
       '@vitejs/plugin-vue':
         specifier: 4.5.2
-        version: 4.5.2(vite@5.0.10)(vue@3.3.11)
+        version: 4.5.2(vite@5.0.10)(vue@3.3.12)
       '@vue/compiler-sfc':
-        specifier: 3.3.11
-        version: 3.3.11
+        specifier: 3.3.12
+        version: 3.3.12
       aiscript-vscode:
         specifier: github:aiscript-dev/aiscript-vscode#v0.0.6
         version: github.com/aiscript-dev/aiscript-vscode/b5a8aa0ad927831a0b867d1c183460a14e6c48cd
@@ -686,8 +686,8 @@ importers:
         specifier: 1.8.6
         version: 1.8.6
       broadcast-channel:
-        specifier: 6.0.0
-        version: 6.0.0
+        specifier: 7.0.0
+        version: 7.0.0
       browser-image-resizer:
         specifier: github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3
         version: github.com/misskey-dev/browser-image-resizer/0227e860621e55cbed0aabe6dc601096a7748c4a
@@ -764,8 +764,8 @@ importers:
         specifier: 2.3.1
         version: 2.3.1
       rollup:
-        specifier: 4.9.0
-        version: 4.9.0
+        specifier: 4.9.1
+        version: 4.9.1
       sanitize-html:
         specifier: 2.11.0
         version: 2.11.0
@@ -807,16 +807,16 @@ importers:
         version: 9.0.1
       v-code-diff:
         specifier: 1.7.2
-        version: 1.7.2(vue@3.3.11)
+        version: 1.7.2(vue@3.3.12)
       vite:
         specifier: 5.0.10
-        version: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
+        version: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
       vue:
-        specifier: 3.3.11
-        version: 3.3.11(typescript@5.3.3)
+        specifier: 3.3.12
+        version: 3.3.12(typescript@5.3.3)
       vuedraggable:
         specifier: next
-        version: 4.1.0(vue@3.3.11)
+        version: 4.1.0(vue@3.3.12)
     devDependencies:
       '@storybook/addon-actions':
         specifier: 7.6.5
@@ -856,7 +856,7 @@ importers:
         version: 7.6.5(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
       '@storybook/react-vite':
         specifier: 7.6.5
-        version: 7.6.5(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.0)(typescript@5.3.3)(vite@5.0.10)
+        version: 7.6.5(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.1)(typescript@5.3.3)(vite@5.0.10)
       '@storybook/testing-library':
         specifier: 0.2.2
         version: 0.2.2
@@ -868,13 +868,13 @@ importers:
         version: 7.6.5
       '@storybook/vue3':
         specifier: 7.6.5
-        version: 7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.11)
+        version: 7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.12)
       '@storybook/vue3-vite':
         specifier: 7.6.5
-        version: 7.6.5(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.11)
+        version: 7.6.5(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12)
       '@testing-library/vue':
         specifier: 8.0.1
-        version: 8.0.1(@vue/compiler-sfc@3.3.11)(vue@3.3.11)
+        version: 8.0.1(@vue/compiler-sfc@3.3.12)(vue@3.3.12)
       '@types/escape-regexp':
         specifier: 0.0.3
         version: 0.0.3
@@ -888,8 +888,8 @@ importers:
         specifier: 4.0.6
         version: 4.0.6
       '@types/node':
-        specifier: 20.10.4
-        version: 20.10.4
+        specifier: 20.10.5
+        version: 20.10.5
       '@types/punycode':
         specifier: 2.1.3
         version: 2.1.3
@@ -918,8 +918,8 @@ importers:
         specifier: 0.34.6
         version: 0.34.6(vitest@0.34.6)
       '@vue/runtime-core':
-        specifier: 3.3.11
-        version: 3.3.11
+        specifier: 3.3.12
+        version: 3.3.12
       acorn:
         specifier: 8.11.2
         version: 8.11.2
@@ -976,7 +976,7 @@ importers:
         version: 7.6.5
       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.6.5)(@storybook/components@7.6.4)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0)
+        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.6.5)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0)
       summaly:
         specifier: github:misskey-dev/summaly
         version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8
@@ -1013,7 +1013,7 @@ importers:
     devDependencies:
       '@microsoft/api-extractor':
         specifier: 7.38.5
-        version: 7.38.5(@types/node@20.10.4)
+        version: 7.38.5(@types/node@20.10.5)
       '@swc/jest':
         specifier: 0.2.29
         version: 0.2.29(@swc/core@1.3.100)
@@ -1021,8 +1021,8 @@ importers:
         specifier: 29.5.11
         version: 29.5.11
       '@types/node':
-        specifier: 20.10.4
-        version: 20.10.4
+        specifier: 20.10.5
+        version: 20.10.5
       '@typescript-eslint/eslint-plugin':
         specifier: 6.14.0
         version: 6.14.0(@typescript-eslint/parser@6.14.0)(eslint@8.56.0)(typescript@5.3.3)
@@ -1034,7 +1034,7 @@ importers:
         version: 8.56.0
       jest:
         specifier: 29.7.0
-        version: 29.7.0(@types/node@20.10.4)
+        version: 29.7.0(@types/node@20.10.5)
       jest-fetch-mock:
         specifier: 3.0.3
         version: 3.0.3
@@ -1051,8 +1051,8 @@ importers:
         specifier: 3.0.2
         version: 3.0.2
       tsd:
-        specifier: 0.29.0
-        version: 0.29.0
+        specifier: 0.30.0
+        version: 0.30.0
       typescript:
         specifier: 5.3.3
         version: 5.3.3
@@ -1093,8 +1093,8 @@ importers:
   packages/sw:
     dependencies:
       esbuild:
-        specifier: 0.19.8
-        version: 0.19.8
+        specifier: 0.19.9
+        version: 0.19.9
       idb-keyval:
         specifier: 6.2.1
         version: 6.2.1
@@ -1808,7 +1808,7 @@ packages:
       '@babel/traverse': 7.22.11
       '@babel/types': 7.22.17
       convert-source-map: 1.9.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -1831,7 +1831,7 @@ packages:
       '@babel/traverse': 7.23.5
       '@babel/types': 7.23.5
       convert-source-map: 2.0.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       gensync: 1.0.0-beta.2
       json5: 2.2.3
       semver: 6.3.1
@@ -1933,7 +1933,7 @@ packages:
       '@babel/core': 7.23.5
       '@babel/helper-compilation-targets': 7.22.15
       '@babel/helper-plugin-utils': 7.22.5
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       lodash.debounce: 4.0.8
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -2175,14 +2175,6 @@ packages:
     dependencies:
       '@babel/types': 7.22.17
 
-  /@babel/parser@7.23.3:
-    resolution: {integrity: sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
-    dependencies:
-      '@babel/types': 7.22.17
-    dev: true
-
   /@babel/parser@7.23.5:
     resolution: {integrity: sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==}
     engines: {node: '>=6.0.0'}
@@ -3287,12 +3279,21 @@ packages:
     engines: {node: '>=6.9.0'}
     dependencies:
       regenerator-runtime: 0.13.11
+    dev: false
 
   /@babel/runtime@7.23.2:
     resolution: {integrity: sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==}
     engines: {node: '>=6.9.0'}
     dependencies:
       regenerator-runtime: 0.14.0
+    dev: true
+
+  /@babel/runtime@7.23.4:
+    resolution: {integrity: sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      regenerator-runtime: 0.14.0
+    dev: false
 
   /@babel/template@7.22.15:
     resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==}
@@ -3308,7 +3309,7 @@ packages:
     engines: {node: '>=6.9.0'}
     dependencies:
       '@babel/code-frame': 7.22.13
-      '@babel/parser': 7.23.3
+      '@babel/parser': 7.23.5
       '@babel/types': 7.22.17
     dev: true
 
@@ -3322,9 +3323,9 @@ packages:
       '@babel/helper-function-name': 7.22.5
       '@babel/helper-hoist-variables': 7.22.5
       '@babel/helper-split-export-declaration': 7.22.6
-      '@babel/parser': 7.23.3
+      '@babel/parser': 7.23.5
       '@babel/types': 7.22.17
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -3342,7 +3343,7 @@ packages:
       '@babel/helper-split-export-declaration': 7.22.6
       '@babel/parser': 7.23.5
       '@babel/types': 7.23.5
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       globals: 11.12.0
     transitivePeerDependencies:
       - supports-color
@@ -3636,6 +3637,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/android-arm64@0.19.9:
+    resolution: {integrity: sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/android-arm@0.18.20:
     resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
     engines: {node: '>=12'}
@@ -3653,6 +3663,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/android-arm@0.19.9:
+    resolution: {integrity: sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/android-x64@0.18.20:
     resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
     engines: {node: '>=12'}
@@ -3670,6 +3689,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/android-x64@0.19.9:
+    resolution: {integrity: sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/darwin-arm64@0.18.20:
     resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
     engines: {node: '>=12'}
@@ -3687,6 +3715,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/darwin-arm64@0.19.9:
+    resolution: {integrity: sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/darwin-x64@0.18.20:
     resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
     engines: {node: '>=12'}
@@ -3704,6 +3741,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/darwin-x64@0.19.9:
+    resolution: {integrity: sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/freebsd-arm64@0.18.20:
     resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
     engines: {node: '>=12'}
@@ -3721,6 +3767,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/freebsd-arm64@0.19.9:
+    resolution: {integrity: sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/freebsd-x64@0.18.20:
     resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
     engines: {node: '>=12'}
@@ -3738,6 +3793,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/freebsd-x64@0.19.9:
+    resolution: {integrity: sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/linux-arm64@0.18.20:
     resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
     engines: {node: '>=12'}
@@ -3755,6 +3819,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/linux-arm64@0.19.9:
+    resolution: {integrity: sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/linux-arm@0.18.20:
     resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
     engines: {node: '>=12'}
@@ -3772,6 +3845,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/linux-arm@0.19.9:
+    resolution: {integrity: sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/linux-ia32@0.18.20:
     resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
     engines: {node: '>=12'}
@@ -3789,6 +3871,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/linux-ia32@0.19.9:
+    resolution: {integrity: sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/linux-loong64@0.18.20:
     resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
     engines: {node: '>=12'}
@@ -3806,6 +3897,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/linux-loong64@0.19.9:
+    resolution: {integrity: sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/linux-mips64el@0.18.20:
     resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
     engines: {node: '>=12'}
@@ -3823,6 +3923,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/linux-mips64el@0.19.9:
+    resolution: {integrity: sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/linux-ppc64@0.18.20:
     resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
     engines: {node: '>=12'}
@@ -3840,6 +3949,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/linux-ppc64@0.19.9:
+    resolution: {integrity: sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/linux-riscv64@0.18.20:
     resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
     engines: {node: '>=12'}
@@ -3857,6 +3975,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/linux-riscv64@0.19.9:
+    resolution: {integrity: sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/linux-s390x@0.18.20:
     resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
     engines: {node: '>=12'}
@@ -3874,6 +4001,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/linux-s390x@0.19.9:
+    resolution: {integrity: sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/linux-x64@0.18.20:
     resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
     engines: {node: '>=12'}
@@ -3891,6 +4027,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/linux-x64@0.19.9:
+    resolution: {integrity: sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/netbsd-x64@0.18.20:
     resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
     engines: {node: '>=12'}
@@ -3908,6 +4053,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/netbsd-x64@0.19.9:
+    resolution: {integrity: sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/openbsd-x64@0.18.20:
     resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
     engines: {node: '>=12'}
@@ -3925,6 +4079,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/openbsd-x64@0.19.9:
+    resolution: {integrity: sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/sunos-x64@0.18.20:
     resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
     engines: {node: '>=12'}
@@ -3942,6 +4105,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/sunos-x64@0.19.9:
+    resolution: {integrity: sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/win32-arm64@0.18.20:
     resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
     engines: {node: '>=12'}
@@ -3959,6 +4131,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/win32-arm64@0.19.9:
+    resolution: {integrity: sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/win32-ia32@0.18.20:
     resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
     engines: {node: '>=12'}
@@ -3976,6 +4157,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/win32-ia32@0.19.9:
+    resolution: {integrity: sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@esbuild/win32-x64@0.18.20:
     resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
     engines: {node: '>=12'}
@@ -3993,6 +4183,15 @@ packages:
     requiresBuild: true
     optional: true
 
+  /@esbuild/win32-x64@0.19.9:
+    resolution: {integrity: sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: false
+    optional: true
+
   /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0):
     resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -4023,7 +4222,7 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dependencies:
       ajv: 6.12.6
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       espree: 9.6.1
       globals: 13.19.0
       ignore: 5.2.4
@@ -4040,7 +4239,7 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dependencies:
       ajv: 6.12.6
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       espree: 9.6.1
       globals: 13.19.0
       ignore: 5.2.4
@@ -4305,7 +4504,7 @@ packages:
     engines: {node: '>=10.10.0'}
     dependencies:
       '@humanwhocodes/object-schema': 2.0.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       minimatch: 3.1.2
     transitivePeerDependencies:
       - supports-color
@@ -4356,7 +4555,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
@@ -4377,14 +4576,14 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       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.10.4)
+      jest-config: 29.7.0(@types/node@20.10.5)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -4419,7 +4618,7 @@ packages:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       jest-mock: 29.7.0
     dev: true
 
@@ -4446,7 +4645,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -4479,7 +4678,7 @@ packages:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.18
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       chalk: 4.1.2
       collect-v8-coverage: 1.0.1
       exit: 0.1.2
@@ -4573,7 +4772,7 @@ packages:
     dependencies:
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       '@types/yargs': 16.0.5
       chalk: 4.1.2
     dev: true
@@ -4585,7 +4784,7 @@ packages:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.1
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       '@types/yargs': 17.0.19
       chalk: 4.1.2
     dev: true
@@ -4604,7 +4803,7 @@ packages:
       magic-string: 0.27.0
       react-docgen-typescript: 2.2.2(typescript@5.3.3)
       typescript: 5.3.3
-      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
+      vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
     dev: true
 
   /@jridgewell/gen-mapping@0.3.2:
@@ -4693,24 +4892,24 @@ packages:
       react: 18.2.0
     dev: true
 
-  /@microsoft/api-extractor-model@7.28.3(@types/node@20.10.4):
+  /@microsoft/api-extractor-model@7.28.3(@types/node@20.10.5):
     resolution: {integrity: sha512-wT/kB2oDbdZXITyDh2SQLzaWwTOFbV326fP0pUwNW00WeliARs0qjmXBWmGWardEzp2U3/axkO3Lboqun6vrig==}
     dependencies:
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.62.0(@types/node@20.10.4)
+      '@rushstack/node-core-library': 3.62.0(@types/node@20.10.5)
     transitivePeerDependencies:
       - '@types/node'
     dev: true
 
-  /@microsoft/api-extractor@7.38.5(@types/node@20.10.4):
+  /@microsoft/api-extractor@7.38.5(@types/node@20.10.5):
     resolution: {integrity: sha512-c/w2zfqBcBJxaCzpJNvFoouWewcYrUOfeu5ZkWCCIXTF9a/gXM85RGevEzlMAIEGM/kssAAZSXRJIZ3Q5vLFow==}
     hasBin: true
     dependencies:
-      '@microsoft/api-extractor-model': 7.28.3(@types/node@20.10.4)
+      '@microsoft/api-extractor-model': 7.28.3(@types/node@20.10.5)
       '@microsoft/tsdoc': 0.14.2
       '@microsoft/tsdoc-config': 0.16.2
-      '@rushstack/node-core-library': 3.62.0(@types/node@20.10.4)
+      '@rushstack/node-core-library': 3.62.0(@types/node@20.10.5)
       '@rushstack/rig-package': 0.5.1
       '@rushstack/ts-command-line': 4.17.1
       colors: 1.2.5
@@ -4813,7 +5012,7 @@ packages:
       '@open-draft/until': 1.0.3
       '@types/debug': 4.1.7
       '@xmldom/xmldom': 0.8.6
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       headers-polyfill: 3.2.5
       outvariant: 1.4.0
       strict-event-emitter: 0.2.8
@@ -5553,7 +5752,7 @@ packages:
       '@babel/runtime': 7.23.2
     dev: true
 
-  /@rollup/plugin-json@6.1.0(rollup@4.9.0):
+  /@rollup/plugin-json@6.1.0(rollup@4.9.1):
     resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5562,11 +5761,11 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.9.0)
-      rollup: 4.9.0
+      '@rollup/pluginutils': 5.1.0(rollup@4.9.1)
+      rollup: 4.9.1
     dev: false
 
-  /@rollup/plugin-replace@5.0.5(rollup@4.9.0):
+  /@rollup/plugin-replace@5.0.5(rollup@4.9.1):
     resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5575,12 +5774,12 @@ packages:
       rollup:
         optional: true
     dependencies:
-      '@rollup/pluginutils': 5.1.0(rollup@4.9.0)
+      '@rollup/pluginutils': 5.1.0(rollup@4.9.1)
       magic-string: 0.30.5
-      rollup: 4.9.0
+      rollup: 4.9.1
     dev: false
 
-  /@rollup/pluginutils@5.1.0(rollup@4.9.0):
+  /@rollup/pluginutils@5.1.0(rollup@4.9.1):
     resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==}
     engines: {node: '>=14.0.0'}
     peerDependencies:
@@ -5592,100 +5791,100 @@ packages:
       '@types/estree': 1.0.5
       estree-walker: 2.0.2
       picomatch: 2.3.1
-      rollup: 4.9.0
+      rollup: 4.9.1
 
-  /@rollup/rollup-android-arm-eabi@4.9.0:
-    resolution: {integrity: sha512-+1ge/xmaJpm1KVBuIH38Z94zj9fBD+hp+/5WLaHgyY8XLq1ibxk/zj6dTXaqM2cAbYKq8jYlhHd6k05If1W5xA==}
+  /@rollup/rollup-android-arm-eabi@4.9.1:
+    resolution: {integrity: sha512-6vMdBZqtq1dVQ4CWdhFwhKZL6E4L1dV6jUjuBvsavvNJSppzi6dLBbuV+3+IyUREaj9ZFvQefnQm28v4OCXlig==}
     cpu: [arm]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-android-arm64@4.9.0:
-    resolution: {integrity: sha512-im6hUEyQ7ZfoZdNvtwgEJvBWZYauC9KVKq1w58LG2Zfz6zMd8gRrbN+xCVoqA2hv/v6fm9lp5LFGJ3za8EQH3A==}
+  /@rollup/rollup-android-arm64@4.9.1:
+    resolution: {integrity: sha512-Jto9Fl3YQ9OLsTDWtLFPtaIMSL2kwGyGoVCmPC8Gxvym9TCZm4Sie+cVeblPO66YZsYH8MhBKDMGZ2NDxuk/XQ==}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-arm64@4.9.0:
-    resolution: {integrity: sha512-u7aTMskN6Dmg1lCT0QJ+tINRt+ntUrvVkhbPfFz4bCwRZvjItx2nJtwJnJRlKMMaQCHRjrNqHRDYvE4mBm3DlQ==}
+  /@rollup/rollup-darwin-arm64@4.9.1:
+    resolution: {integrity: sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA==}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-darwin-x64@4.9.0:
-    resolution: {integrity: sha512-8FvEl3w2ExmpcOmX5RJD0yqXcVSOqAJJUJ29Lca29Ik+3zPS1yFimr2fr5JSZ4Z5gt8/d7WqycpgkX9nocijSw==}
+  /@rollup/rollup-darwin-x64@4.9.1:
+    resolution: {integrity: sha512-KyP/byeXu9V+etKO6Lw3E4tW4QdcnzDG/ake031mg42lob5tN+5qfr+lkcT/SGZaH2PdW4Z1NX9GHEkZ8xV7og==}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm-gnueabihf@4.9.0:
-    resolution: {integrity: sha512-lHoKYaRwd4gge+IpqJHCY+8Vc3hhdJfU6ukFnnrJasEBUvVlydP8PuwndbWfGkdgSvZhHfSEw6urrlBj0TSSfg==}
+  /@rollup/rollup-linux-arm-gnueabihf@4.9.1:
+    resolution: {integrity: sha512-Yqz/Doumf3QTKplwGNrCHe/B2p9xqDghBZSlAY0/hU6ikuDVQuOUIpDP/YcmoT+447tsZTmirmjgG3znvSCR0Q==}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-gnu@4.9.0:
-    resolution: {integrity: sha512-JbEPfhndYeWHfOSeh4DOFvNXrj7ls9S/2omijVsao+LBPTPayT1uKcK3dHW3MwDJ7KO11t9m2cVTqXnTKpeaiw==}
+  /@rollup/rollup-linux-arm64-gnu@4.9.1:
+    resolution: {integrity: sha512-u3XkZVvxcvlAOlQJ3UsD1rFvLWqu4Ef/Ggl40WAVCuogf4S1nJPHh5RTgqYFpCOvuGJ7H5yGHabjFKEZGExk5Q==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-arm64-musl@4.9.0:
-    resolution: {integrity: sha512-ahqcSXLlcV2XUBM3/f/C6cRoh7NxYA/W7Yzuv4bDU1YscTFw7ay4LmD7l6OS8EMhTNvcrWGkEettL1Bhjf+B+w==}
+  /@rollup/rollup-linux-arm64-musl@4.9.1:
+    resolution: {integrity: sha512-0XSYN/rfWShW+i+qjZ0phc6vZ7UWI8XWNz4E/l+6edFt+FxoEghrJHjX1EY/kcUGCnZzYYRCl31SNdfOi450Aw==}
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-riscv64-gnu@4.9.0:
-    resolution: {integrity: sha512-uwvOYNtLw8gVtrExKhdFsYHA/kotURUmZYlinH2VcQxNCQJeJXnkmWgw2hI9Xgzhgu7J9QvWiq9TtTVwWMDa+w==}
+  /@rollup/rollup-linux-riscv64-gnu@4.9.1:
+    resolution: {integrity: sha512-LmYIO65oZVfFt9t6cpYkbC4d5lKHLYv5B4CSHRpnANq0VZUQXGcCPXHzbCXCz4RQnx7jvlYB1ISVNCE/omz5cw==}
     cpu: [riscv64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-gnu@4.9.0:
-    resolution: {integrity: sha512-m6pkSwcZZD2LCFHZX/zW2aLIISyzWLU3hrLLzQKMI12+OLEzgruTovAxY5sCZJkipklaZqPy/2bEEBNjp+Y7xg==}
+  /@rollup/rollup-linux-x64-gnu@4.9.1:
+    resolution: {integrity: sha512-kr8rEPQ6ns/Lmr/hiw8sEVj9aa07gh1/tQF2Y5HrNCCEPiCBGnBUt9tVusrcBBiJfIt1yNaXN6r1CCmpbFEDpg==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-linux-x64-musl@4.9.0:
-    resolution: {integrity: sha512-VFAC1RDRSbU3iOF98X42KaVicAfKf0m0OvIu8dbnqhTe26Kh6Ym9JrDulz7Hbk7/9zGc41JkV02g+p3BivOdAg==}
+  /@rollup/rollup-linux-x64-musl@4.9.1:
+    resolution: {integrity: sha512-t4QSR7gN+OEZLG0MiCgPqMWZGwmeHhsM4AkegJ0Kiy6TnJ9vZ8dEIwHw1LcZKhbHxTY32hp9eVCMdR3/I8MGRw==}
     cpu: [x64]
     os: [linux]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-arm64-msvc@4.9.0:
-    resolution: {integrity: sha512-9jPgMvTKXARz4inw6jezMLA2ihDBvgIU9Ml01hjdVpOcMKyxFBJrn83KVQINnbeqDv0+HdO1c09hgZ8N0s820Q==}
+  /@rollup/rollup-win32-arm64-msvc@4.9.1:
+    resolution: {integrity: sha512-7XI4ZCBN34cb+BH557FJPmh0kmNz2c25SCQeT9OiFWEgf8+dL6ZwJ8f9RnUIit+j01u07Yvrsuu1rZGxJCc51g==}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-ia32-msvc@4.9.0:
-    resolution: {integrity: sha512-WE4pT2kTXQN2bAv40Uog0AsV7/s9nT9HBWXAou8+++MBCnY51QS02KYtm6dQxxosKi1VIz/wZIrTQO5UP2EW+Q==}
+  /@rollup/rollup-win32-ia32-msvc@4.9.1:
+    resolution: {integrity: sha512-yE5c2j1lSWOH5jp+Q0qNL3Mdhr8WuqCNVjc6BxbVfS5cAS6zRmdiw7ktb8GNpDCEUJphILY6KACoFoRtKoqNQg==}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rollup/rollup-win32-x64-msvc@4.9.0:
-    resolution: {integrity: sha512-aPP5Q5AqNGuT0tnuEkK/g4mnt3ZhheiXrDIiSVIHN9mcN21OyXDVbEMqmXPE7e2OplNLDkcvV+ZoGJa2ZImFgw==}
+  /@rollup/rollup-win32-x64-msvc@4.9.1:
+    resolution: {integrity: sha512-PyJsSsafjmIhVgaI1Zdj7m8BB8mMckFah/xbpplObyHfiXzKcI5UOUXRyOdHW7nz4DpMCuzLnF7v5IWHenCwYA==}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
     optional: true
 
-  /@rushstack/node-core-library@3.62.0(@types/node@20.10.4):
+  /@rushstack/node-core-library@3.62.0(@types/node@20.10.5):
     resolution: {integrity: sha512-88aJn2h8UpSvdwuDXBv1/v1heM6GnBf3RjEy6ZPP7UnzHNCqOHA2Ut+ScYUbXcqIdfew9JlTAe3g+cnX9xQ/Aw==}
     peerDependencies:
       '@types/node': '*'
@@ -5693,7 +5892,7 @@ packages:
       '@types/node':
         optional: true
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       colors: 1.2.5
       fs-extra: 7.0.1
       import-lazy: 4.0.0
@@ -6549,23 +6748,12 @@ packages:
       magic-string: 0.30.5
       rollup: 3.29.4
       typescript: 5.3.3
-      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
+      vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
     transitivePeerDependencies:
       - encoding
       - supports-color
     dev: true
 
-  /@storybook/channels@7.6.4:
-    resolution: {integrity: sha512-Z4PY09/Czl70ap4ObmZ4bgin+EQhPaA3HdrEDNwpnH7A9ttfEO5u5KThytIjMq6kApCCihmEPDaYltoVrfYJJA==}
-    dependencies:
-      '@storybook/client-logger': 7.6.4
-      '@storybook/core-events': 7.6.4
-      '@storybook/global': 5.0.0
-      qs: 6.11.1
-      telejson: 7.2.0
-      tiny-invariant: 1.3.1
-    dev: true
-
   /@storybook/channels@7.6.5:
     resolution: {integrity: sha512-FIlNkyfQy9uHoJfAFL2/wO3ASGJELFvBzURBE2rcEF/TS7GcUiqWnBfiDxAbwSEjSOm2F0eEq3UXhaZEjpJHDw==}
     dependencies:
@@ -6629,12 +6817,6 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/client-logger@7.6.4:
-    resolution: {integrity: sha512-vJwMShC98tcoFruRVQ4FphmFqvAZX1FqZqjFyk6IxtFumPKTVSnXJjlU1SnUIkSK2x97rgdUMqkdI+wAv/tugQ==}
-    dependencies:
-      '@storybook/global': 5.0.0
-    dev: true
-
   /@storybook/client-logger@7.6.5:
     resolution: {integrity: sha512-S5aROWgssqg7tcs9lgW5wmCAz4SxMAtioiyVj5oFecmPCbQtFVIAREYzeoxE4GfJL+plrfRkum4BzziANn8EhQ==}
     dependencies:
@@ -6662,29 +6844,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/components@7.6.4(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-K5RvEObJAnX+SbGJbkM1qrZEk+VR2cUhRCSrFnlfMwsn8/60T3qoH7U8bCXf8krDgbquhMwqev5WzDB+T1VV8g==}
-    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.6.4
-      '@storybook/csf': 0.1.2
-      '@storybook/global': 5.0.0
-      '@storybook/theming': 7.6.4(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.6.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/components@7.6.5(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-w4ZucbBBZ+NKMWlJKVj2I/bMBBq7gzDp9lzc4+8QaQ3vUPXKqc1ilIPYo/7UR5oxwDVMZocmMSgl9L8lvf7+Mw==}
     peerDependencies:
@@ -6746,12 +6905,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/core-events@7.6.4:
-    resolution: {integrity: sha512-i3xzcJ19ILSy4oJL5Dz9y0IlyApynn5RsGhAMIsW+mcfri+hGfeakq1stNCo0o7jW4Y3A7oluFTtIoK8DOxQdQ==}
-    dependencies:
-      ts-dedent: 2.2.0
-    dev: true
-
   /@storybook/core-events@7.6.5:
     resolution: {integrity: sha512-zk2q/qicYXAzHA4oV3GDbIql+Kd4TOHUgDE8e4jPCOPp856z2ScqEKUAbiJizs6eEJOH4nW9Db1kuzgrBVEykQ==}
     dependencies:
@@ -6954,7 +7107,7 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/react-vite@7.6.5(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.0)(typescript@5.3.3)(vite@5.0.10):
+  /@storybook/react-vite@7.6.5(react-dom@18.2.0)(react@18.2.0)(rollup@4.9.1)(typescript@5.3.3)(vite@5.0.10):
     resolution: {integrity: sha512-fIoSBbou3rQdOo6qX/nD5givb3qIOSwXeZWjAqRB6560cqmeSQFlRGtKUJ0nzQYADwJ0/iNHz3nOvJOOSnPepA==}
     engines: {node: '>=16'}
     peerDependencies:
@@ -6963,7 +7116,7 @@ packages:
       vite: ^3.0.0 || ^4.0.0 || ^5.0.0
     dependencies:
       '@joshwooding/vite-plugin-react-docgen-typescript': 0.3.0(typescript@5.3.3)(vite@5.0.10)
-      '@rollup/pluginutils': 5.1.0(rollup@4.9.0)
+      '@rollup/pluginutils': 5.1.0(rollup@4.9.1)
       '@storybook/builder-vite': 7.6.5(typescript@5.3.3)(vite@5.0.10)
       '@storybook/react': 7.6.5(react-dom@18.2.0)(react@18.2.0)(typescript@5.3.3)
       '@vitejs/plugin-react': 3.1.0(vite@5.0.10)
@@ -6971,7 +7124,7 @@ packages:
       react: 18.2.0
       react-docgen: 7.0.1
       react-dom: 18.2.0(react@18.2.0)
-      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
+      vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - encoding
@@ -7063,20 +7216,6 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/theming@7.6.4(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-Z/dcC5EpkIXelYCkt9ojnX6D7qGOng8YHxV/OWlVE9TrEGYVGPOEfwQryR0RhmGpDha1TYESLYrsDb4A8nJ1EA==}
-    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.6.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/theming@7.6.5(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-RpcWT0YEgiobO41McVPDfQQHHFnjyr1sJnNTPJIvOUgSfURdgSj17mQVxtD5xcXcPWUdle5UhIOrCixHbL/NNw==}
     peerDependencies:
@@ -7091,15 +7230,6 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/types@7.6.4:
-    resolution: {integrity: sha512-qyiiXPCvol5uVgfubcIMzJBA0awAyFPU+TyUP1mkPYyiTHnsHYel/mKlSdPjc8a97N3SlJXHOCx41Hde4IyJgg==}
-    dependencies:
-      '@storybook/channels': 7.6.4
-      '@types/babel__core': 7.20.0
-      '@types/express': 4.17.17
-      file-system-cache: 2.3.0
-    dev: true
-
   /@storybook/types@7.6.5:
     resolution: {integrity: sha512-Q757v+fYZZSaEpks/zDL5YgXRozxkgKakXFc+BoQHK5q5sVhJ+0jvpLJiAQAniIIaMIkqY/G24Kd6Uo6UdKBCg==}
     dependencies:
@@ -7109,7 +7239,7 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.11):
+  /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12):
     resolution: {integrity: sha512-7wUCq2Lrjlekftd5ha3hG0GSGbbzuc370cKkBqSmwFuOfI38z5+VeYt7nDtAlncxcpVSH7DejTGRuKTlC7NyYg==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
@@ -7117,11 +7247,11 @@ packages:
     dependencies:
       '@storybook/builder-vite': 7.6.5(typescript@5.3.3)(vite@5.0.10)
       '@storybook/core-server': 7.6.5
-      '@storybook/vue3': 7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.11)
-      '@vitejs/plugin-vue': 4.5.2(vite@5.0.10)(vue@3.3.11)
+      '@storybook/vue3': 7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.12)
+      '@vitejs/plugin-vue': 4.5.2(vite@5.0.10)(vue@3.3.12)
       magic-string: 0.30.5
-      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
-      vue-docgen-api: 4.64.1(vue@3.3.11)
+      vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
+      vue-docgen-api: 4.64.1(vue@3.3.12)
     transitivePeerDependencies:
       - '@preact/preset-vite'
       - '@vue/compiler-core'
@@ -7134,7 +7264,7 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.11):
+  /@storybook/vue3@7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.12):
     resolution: {integrity: sha512-tv/9rVc3XXDOJu5hfZtKhrhM8x4GTLKon62Rmaxlq06weqkGlfBi/V/g1EZ7OE71Pi+woKS/TX7p9qbRrvgahg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
@@ -7150,7 +7280,7 @@ packages:
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
-      vue: 3.3.11(typescript@5.3.3)
+      vue: 3.3.12(typescript@5.3.3)
       vue-component-type-helpers: 1.8.25
     transitivePeerDependencies:
       - encoding
@@ -7563,7 +7693,7 @@ packages:
     engines: {node: '>=14'}
     dependencies:
       '@babel/code-frame': 7.21.4
-      '@babel/runtime': 7.21.0
+      '@babel/runtime': 7.23.2
       '@types/aria-query': 5.0.1
       aria-query: 5.1.3
       chalk: 4.1.2
@@ -7625,7 +7755,7 @@ packages:
       '@testing-library/dom': 9.2.0
     dev: true
 
-  /@testing-library/vue@8.0.1(@vue/compiler-sfc@3.3.11)(vue@3.3.11):
+  /@testing-library/vue@8.0.1(@vue/compiler-sfc@3.3.12)(vue@3.3.12):
     resolution: {integrity: sha512-l51ZEpjTQ6glq3wM+asQ1GbKJMGcxwgHEygETx0aCRN4TjFEGvMZy4YdWKs/y7bu4bmLrxcxhbEPP7iPSW/2OQ==}
     engines: {node: '>=14'}
     peerDependencies:
@@ -7634,9 +7764,9 @@ packages:
     dependencies:
       '@babel/runtime': 7.23.2
       '@testing-library/dom': 9.3.3
-      '@vue/compiler-sfc': 3.3.11
-      '@vue/test-utils': 2.4.1(vue@3.3.11)
-      vue: 3.3.11(typescript@5.3.3)
+      '@vue/compiler-sfc': 3.3.12
+      '@vue/test-utils': 2.4.1(vue@3.3.12)
+      vue: 3.3.12(typescript@5.3.3)
     transitivePeerDependencies:
       - '@vue/server-renderer'
     dev: true
@@ -7650,15 +7780,15 @@ packages:
     engines: {node: '>=10.13.0'}
     dev: false
 
-  /@tsd/typescript@5.2.2:
-    resolution: {integrity: sha512-VtjHPAKJqLJoHHKBDNofzvQB2+ZVxjXU/Gw6INAS9aINLQYVsxfzrQ2s84huCeYWZRTtrr7R0J7XgpZHjNwBCw==}
+  /@tsd/typescript@5.3.3:
+    resolution: {integrity: sha512-CQlfzol0ldaU+ftWuG52vH29uRoKboLinLy84wS8TQOu+m+tWoaUfk4svL4ij2V8M5284KymJBlHUusKj6k34w==}
     engines: {node: '>=14.17'}
     dev: true
 
   /@types/accepts@1.3.7:
     resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/archiver@6.0.2:
@@ -7712,7 +7842,7 @@ packages:
     resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
     dependencies:
       '@types/connect': 3.4.35
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/braces@3.0.1:
@@ -7724,7 +7854,7 @@ packages:
     dependencies:
       '@types/http-cache-semantics': 4.0.1
       '@types/keyv': 3.1.4
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       '@types/responselike': 1.0.0
     dev: false
 
@@ -7757,7 +7887,7 @@ packages:
   /@types/connect@3.4.35:
     resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/content-disposition@0.5.8:
@@ -7771,7 +7901,7 @@ packages:
   /@types/cross-spawn@6.0.2:
     resolution: {integrity: sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/debug@4.1.7:
@@ -7829,7 +7959,7 @@ packages:
   /@types/express-serve-static-core@4.17.33:
     resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       '@types/qs': 6.9.7
       '@types/range-parser': 1.2.4
     dev: true
@@ -7850,20 +7980,20 @@ packages:
   /@types/fluent-ffmpeg@2.1.24:
     resolution: {integrity: sha512-g5oQO8Jgi2kFS3tTub7wLvfLztr1s8tdXmRd8PiL/hLMLzTIAyMR2sANkTggM/rdEDAg3d63nYRRVepwBiCw5A==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/glob@7.2.0:
     resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/graceful-fs@4.1.6:
     resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/http-cache-semantics@4.0.1:
@@ -7876,7 +8006,7 @@ packages:
   /@types/http-link-header@1.0.5:
     resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/istanbul-lib-coverage@2.0.4:
@@ -7920,7 +8050,7 @@ packages:
   /@types/jsdom@21.1.6:
     resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       '@types/tough-cookie': 4.0.2
       parse5: 7.1.2
     dev: true
@@ -7944,7 +8074,7 @@ packages:
   /@types/keyv@3.1.4:
     resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: false
 
   /@types/lodash@4.14.191:
@@ -7993,7 +8123,7 @@ packages:
   /@types/node-fetch@2.6.4:
     resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       form-data: 3.0.1
 
   /@types/node-fetch@3.0.3:
@@ -8006,8 +8136,8 @@ packages:
     resolution: {integrity: sha512-2yrWpBk32tvV/JAd3HNHWuZn/VDN1P+72hWirHnvsvTGSqbANi+kSeuQR9yAHnbvaBvHDsoTdXV0Fe+iRtHLKA==}
     dev: true
 
-  /@types/node@20.10.4:
-    resolution: {integrity: sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==}
+  /@types/node@20.10.5:
+    resolution: {integrity: sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==}
     dependencies:
       undici-types: 5.26.5
 
@@ -8020,7 +8150,7 @@ packages:
   /@types/nodemailer@6.4.14:
     resolution: {integrity: sha512-fUWthHO9k9DSdPCSPRqcu6TWhYyxTBg382vlNIttSe9M7XfsT06y0f24KHXtbnijPGGRIcVvdKHTNikOI6qiHA==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/normalize-package-data@2.4.1:
@@ -8037,13 +8167,13 @@ packages:
     resolution: {integrity: sha512-Ali0fUUn+zgr4Yy/pCTFbuiaiJpq7l7OQwFnxYVchNbNGIx0c4Wkcdje6WO89I91RAaYF+gVc1pOaizA4YKZmA==}
     dependencies:
       '@types/express': 4.17.17
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/oauth@0.9.4:
     resolution: {integrity: sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/offscreencanvas@2019.3.0:
@@ -8059,7 +8189,7 @@ packages:
   /@types/pg@8.10.9:
     resolution: {integrity: sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       pg-protocol: 1.6.0
       pg-types: 4.0.1
     dev: true
@@ -8083,7 +8213,7 @@ packages:
   /@types/qrcode@1.5.5:
     resolution: {integrity: sha512-CdfBi/e3Qk+3Z/fXYShipBT13OJ2fDO2Q2w5CIP5anLTLIndQG9z6P1cnm+8zCWSpm5dnxMFd/uREtb0EXuQzg==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/qs@6.9.7:
@@ -8113,7 +8243,7 @@ packages:
   /@types/readdir-glob@1.1.1:
     resolution: {integrity: sha512-ImM6TmoF8bgOwvehGviEj3tRdRBbQujr1N+0ypaln/GWjaerOB26jb93vsRHmdMtvVQZQebOlqt2HROark87mQ==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/rename@1.0.7:
@@ -8127,7 +8257,7 @@ packages:
   /@types/responselike@1.0.0:
     resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: false
 
   /@types/sanitize-html@2.9.5:
@@ -8153,7 +8283,7 @@ packages:
     resolution: {integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==}
     dependencies:
       '@types/mime': 3.0.1
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/serviceworker@0.0.67:
@@ -8163,7 +8293,7 @@ packages:
   /@types/set-cookie-parser@2.4.3:
     resolution: {integrity: sha512-7QhnH7bi+6KAhBB+Auejz1uV9DHiopZqu7LfR/5gZZTkejJV5nYeZZpgfFoE0N8aDsXuiYpfKyfyMatCwQhyTQ==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/sharp@0.32.0:
@@ -8226,13 +8356,13 @@ packages:
   /@types/vary@1.1.3:
     resolution: {integrity: sha512-XJT8/ZQCL7NUut9QDLf6l24JfAEl7bnNdgxfj50cHIpEPRJLHHDDFOAq6i+GsEmeFfH7NamhBE4c4Thtb2egWg==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/web-push@3.6.3:
     resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/webgl-ext@0.0.30:
@@ -8243,7 +8373,7 @@ packages:
   /@types/ws@8.5.10:
     resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /@types/yargs-parser@21.0.0:
@@ -8266,7 +8396,7 @@ packages:
     resolution: {integrity: sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==}
     requiresBuild: true
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
     dev: true
     optional: true
 
@@ -8287,7 +8417,7 @@ packages:
       '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
       graphemer: 1.4.0
       ignore: 5.2.4
@@ -8316,7 +8446,7 @@ packages:
       '@typescript-eslint/type-utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3)
       '@typescript-eslint/utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.14.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.56.0
       graphemer: 1.4.0
       ignore: 5.2.4
@@ -8342,7 +8472,7 @@ packages:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
       typescript: 5.3.3
     transitivePeerDependencies:
@@ -8363,7 +8493,7 @@ packages:
       '@typescript-eslint/types': 6.14.0
       '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3)
       '@typescript-eslint/visitor-keys': 6.14.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.56.0
       typescript: 5.3.3
     transitivePeerDependencies:
@@ -8398,7 +8528,7 @@ packages:
     dependencies:
       '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3)
       '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3)
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.53.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
       typescript: 5.3.3
@@ -8418,7 +8548,7 @@ packages:
     dependencies:
       '@typescript-eslint/typescript-estree': 6.14.0(typescript@5.3.3)
       '@typescript-eslint/utils': 6.14.0(eslint@8.56.0)(typescript@5.3.3)
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.56.0
       ts-api-utils: 1.0.1(typescript@5.3.3)
       typescript: 5.3.3
@@ -8447,7 +8577,7 @@ packages:
     dependencies:
       '@typescript-eslint/types': 6.11.0
       '@typescript-eslint/visitor-keys': 6.11.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
@@ -8468,7 +8598,7 @@ packages:
     dependencies:
       '@typescript-eslint/types': 6.14.0
       '@typescript-eslint/visitor-keys': 6.14.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       globby: 11.1.0
       is-glob: 4.0.3
       semver: 7.5.4
@@ -8547,20 +8677,20 @@ packages:
       '@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.23.5)
       magic-string: 0.27.0
       react-refresh: 0.14.0
-      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
+      vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
 
-  /@vitejs/plugin-vue@4.5.2(vite@5.0.10)(vue@3.3.11):
+  /@vitejs/plugin-vue@4.5.2(vite@5.0.10)(vue@3.3.12):
     resolution: {integrity: sha512-UGR3DlzLi/SaVBPX0cnSyE37vqxU3O6chn8l0HJNzQzDia6/Au2A4xKv+iIJW8w2daf80G7TYHhi1pAUjdZ0bQ==}
     engines: {node: ^14.18.0 || >=16.0.0}
     peerDependencies:
       vite: ^4.0.0 || ^5.0.0
       vue: ^3.2.25
     dependencies:
-      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
-      vue: 3.3.11(typescript@5.3.3)
+      vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
+      vue: 3.3.12(typescript@5.3.3)
 
   /@vitest/coverage-v8@0.34.6(vitest@0.34.6):
     resolution: {integrity: sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==}
@@ -8647,11 +8777,20 @@ packages:
       '@vue/shared': 3.3.11
       estree-walker: 2.0.2
       source-map-js: 1.0.2
+    dev: true
+
+  /@vue/compiler-core@3.3.12:
+    resolution: {integrity: sha512-qAtjyG3GBLG0chzp5xGCyRLLe6wFCHmjI82aGzwuGKyznNP+GJJMxjc0wOYWDB2YKfho7niJFdoFpo0CZZQg9w==}
+    dependencies:
+      '@babel/parser': 7.23.5
+      '@vue/shared': 3.3.12
+      estree-walker: 2.0.2
+      source-map-js: 1.0.2
 
   /@vue/compiler-core@3.3.9:
     resolution: {integrity: sha512-+/Lf68Vr/nFBA6ol4xOtJrW+BQWv3QWKfRwGSm70jtXwfhZNF4R/eRgyVJYoxFRhdCTk/F6g99BP0ffPgZihfQ==}
     dependencies:
-      '@babel/parser': 7.23.3
+      '@babel/parser': 7.23.5
       '@vue/shared': 3.3.9
       estree-walker: 2.0.2
       source-map-js: 1.0.2
@@ -8662,6 +8801,13 @@ packages:
     dependencies:
       '@vue/compiler-core': 3.3.11
       '@vue/shared': 3.3.11
+    dev: true
+
+  /@vue/compiler-dom@3.3.12:
+    resolution: {integrity: sha512-RdJU9oEYaoPKUdGXCy0l+i4clesdDeLmbvRlszoc9iagsnBnMmQtYfCPVQ5BHB6o7K4SCucDdJM2Dh3oXB0D6g==}
+    dependencies:
+      '@vue/compiler-core': 3.3.12
+      '@vue/shared': 3.3.12
 
   /@vue/compiler-dom@3.3.9:
     resolution: {integrity: sha512-nfWubTtLXuT4iBeDSZ5J3m218MjOy42Vp2pmKVuBKo2/BLcrFUX8nCSr/bKRFiJ32R8qbdnnnBgRn9AdU5v0Sg==}
@@ -8670,25 +8816,25 @@ packages:
       '@vue/shared': 3.3.9
     dev: true
 
-  /@vue/compiler-sfc@3.3.11:
-    resolution: {integrity: sha512-U4iqPlHO0KQeK1mrsxCN0vZzw43/lL8POxgpzcJweopmqtoYy9nljJzWDIQS3EfjiYhfdtdk9Gtgz7MRXnz3GA==}
+  /@vue/compiler-sfc@3.3.12:
+    resolution: {integrity: sha512-yy5b9e7b79dsGbMmglCe/YnhCQgBkHO7Uf6JfjWPSf2/5XH+MKn18LhzhHyxbHdJgnA4lZCqtXzLaJz8Pd8lMw==}
     dependencies:
       '@babel/parser': 7.23.5
-      '@vue/compiler-core': 3.3.11
-      '@vue/compiler-dom': 3.3.11
-      '@vue/compiler-ssr': 3.3.11
-      '@vue/reactivity-transform': 3.3.11
-      '@vue/shared': 3.3.11
+      '@vue/compiler-core': 3.3.12
+      '@vue/compiler-dom': 3.3.12
+      '@vue/compiler-ssr': 3.3.12
+      '@vue/reactivity-transform': 3.3.12
+      '@vue/shared': 3.3.12
       estree-walker: 2.0.2
       magic-string: 0.30.5
       postcss: 8.4.32
       source-map-js: 1.0.2
 
-  /@vue/compiler-ssr@3.3.11:
-    resolution: {integrity: sha512-Zd66ZwMvndxRTgVPdo+muV4Rv9n9DwQ4SSgWWKWkPFebHQfVYRrVjeygmmDmPewsHyznCNvJ2P2d6iOOhdv8Qg==}
+  /@vue/compiler-ssr@3.3.12:
+    resolution: {integrity: sha512-adCiMJPznfWcQyk/9HSuXGja859IaMV+b8UNSVzDatqv7h0PvT9BEeS22+gjkWofDiSg5d78/ZLls3sLA+cn3A==}
     dependencies:
-      '@vue/compiler-dom': 3.3.11
-      '@vue/shared': 3.3.11
+      '@vue/compiler-dom': 3.3.12
+      '@vue/shared': 3.3.12
 
   /@vue/language-core@1.8.25(typescript@5.3.3):
     resolution: {integrity: sha512-NJk/5DnAZlpvXX8BdWmHI45bWGLViUaS3R/RMrmFSvFMSbJKuEODpM4kR0F0Ofv5SFzCWuNiMhxameWpVdQsnA==}
@@ -8710,50 +8856,54 @@ packages:
       vue-template-compiler: 2.7.14
     dev: true
 
-  /@vue/reactivity-transform@3.3.11:
-    resolution: {integrity: sha512-fPGjH0wqJo68A0wQ1k158utDq/cRyZNlFoxGwNScE28aUFOKFEnCBsvyD8jHn+0kd0UKVpuGuaZEQ6r9FJRqCg==}
+  /@vue/reactivity-transform@3.3.12:
+    resolution: {integrity: sha512-g5TijmML7FyKkLt6QnpqNmA4KD7K/T5SbXa88Bhq+hydNQEkzA8veVXWAQuNqg9rjaFYD0rPf0a9NofKA0ENgg==}
     dependencies:
       '@babel/parser': 7.23.5
-      '@vue/compiler-core': 3.3.11
-      '@vue/shared': 3.3.11
+      '@vue/compiler-core': 3.3.12
+      '@vue/shared': 3.3.12
       estree-walker: 2.0.2
       magic-string: 0.30.5
 
-  /@vue/reactivity@3.3.11:
-    resolution: {integrity: sha512-D5tcw091f0nuu+hXq5XANofD0OXnBmaRqMYl5B3fCR+mX+cXJIGNw/VNawBqkjLNWETrFW0i+xH9NvDbTPVh7g==}
+  /@vue/reactivity@3.3.12:
+    resolution: {integrity: sha512-vOJORzO8DlIx88cgTnMLIf2GlLYpoXAKsuoQsK6SGdaqODjxO129pVPTd2s/N/Mb6KKZEFIHIEwWGmtN4YPs+g==}
     dependencies:
-      '@vue/shared': 3.3.11
+      '@vue/shared': 3.3.12
 
-  /@vue/runtime-core@3.3.11:
-    resolution: {integrity: sha512-g9ztHGwEbS5RyWaOpXuyIVFTschclnwhqEbdy5AwGhYOgc7m/q3NFwr50MirZwTTzX55JY8pSkeib9BX04NIpw==}
+  /@vue/runtime-core@3.3.12:
+    resolution: {integrity: sha512-5iL4w7MZrSGKEZU2wFAYhDZdZmgn+s//73EfgDXW1M+ZUOl36md7tlWp1QFK/ladiq4FvQ82shVjo0KiPDPr0A==}
     dependencies:
-      '@vue/reactivity': 3.3.11
-      '@vue/shared': 3.3.11
+      '@vue/reactivity': 3.3.12
+      '@vue/shared': 3.3.12
 
-  /@vue/runtime-dom@3.3.11:
-    resolution: {integrity: sha512-OlhtV1PVpbgk+I2zl+Y5rQtDNcCDs12rsRg71XwaA2/Rbllw6mBLMi57VOn8G0AjOJ4Mdb4k56V37+g8ukShpQ==}
+  /@vue/runtime-dom@3.3.12:
+    resolution: {integrity: sha512-8mMzqiIdl+IYa/OXwKwk6/4ebLq7cYV1pUcwCSwBK2KerUa6cwGosen5xrCL9f8o2DJ9TfPFwbPEvH7OXzUpoA==}
     dependencies:
-      '@vue/runtime-core': 3.3.11
-      '@vue/shared': 3.3.11
-      csstype: 3.1.2
+      '@vue/runtime-core': 3.3.12
+      '@vue/shared': 3.3.12
+      csstype: 3.1.3
 
-  /@vue/server-renderer@3.3.11(vue@3.3.11):
-    resolution: {integrity: sha512-AIWk0VwwxCAm4wqtJyxBylRTXSy1wCLOKbWxHaHiu14wjsNYtiRCSgVuqEPVuDpErOlRdNnuRgipQfXRLjLN5A==}
+  /@vue/server-renderer@3.3.12(vue@3.3.12):
+    resolution: {integrity: sha512-OZ0IEK5TU5GXb5J8/wSplyxvGGdIcwEmS8EIO302Vz8K6fGSgSJTU54X0Sb6PaefzZdiN3vHsLXO8XIeF8crQQ==}
     peerDependencies:
-      vue: 3.3.11
+      vue: 3.3.12
     dependencies:
-      '@vue/compiler-ssr': 3.3.11
-      '@vue/shared': 3.3.11
-      vue: 3.3.11(typescript@5.3.3)
+      '@vue/compiler-ssr': 3.3.12
+      '@vue/shared': 3.3.12
+      vue: 3.3.12(typescript@5.3.3)
 
   /@vue/shared@3.3.11:
     resolution: {integrity: sha512-u2G8ZQ9IhMWTMXaWqZycnK4UthG1fA238CD+DP4Dm4WJi5hdUKKLg0RMRaRpDPNMdkTwIDkp7WtD0Rd9BH9fLw==}
+    dev: true
+
+  /@vue/shared@3.3.12:
+    resolution: {integrity: sha512-6p0Yin0pclvnER7BLNOQuod9Z+cxSYh8pSh7CzHnWNjAIP6zrTlCdHRvSCb1aYEx6i3Q3kvfuWU7nG16CgG1ag==}
 
   /@vue/shared@3.3.9:
     resolution: {integrity: sha512-ZE0VTIR0LmYgeyhurPTpy4KzKsuDyQbMSdM49eKkMnT5X4VfFBLysMzjIZhLEFQYjjOVVfbvUDHckwjDFiO2eA==}
     dev: true
 
-  /@vue/test-utils@2.4.1(vue@3.3.11):
+  /@vue/test-utils@2.4.1(vue@3.3.12):
     resolution: {integrity: sha512-VO8nragneNzUZUah6kOjiFmD/gwRjUauG9DROh6oaOeFwX1cZRUNHhdeogE8635cISigXFTtGLUQWx5KCb0xeg==}
     peerDependencies:
       '@vue/server-renderer': ^3.0.1
@@ -8763,7 +8913,7 @@ packages:
         optional: true
     dependencies:
       js-beautify: 1.14.9
-      vue: 3.3.11(typescript@5.3.3)
+      vue: 3.3.12(typescript@5.3.3)
       vue-component-type-helpers: 1.8.4
     dev: true
 
@@ -8902,7 +9052,7 @@ packages:
     engines: {node: '>= 6.0.0'}
     requiresBuild: true
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -8910,7 +9060,7 @@ packages:
     resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
     engines: {node: '>= 14'}
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -9285,7 +9435,7 @@ packages:
     resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==}
     dependencies:
       archy: 1.0.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       fastq: 1.15.0
     transitivePeerDependencies:
       - supports-color
@@ -9597,10 +9747,10 @@ packages:
     dependencies:
       fill-range: 7.0.1
 
-  /broadcast-channel@6.0.0:
-    resolution: {integrity: sha512-h8ki6RYXq502Eb+zAt4Kni2ahL/lulh0ip+mpnvsMSRC2biBo6AkSBfO6JFTelT+FX88VL0SDd3RKpqlPNw4ng==}
+  /broadcast-channel@7.0.0:
+    resolution: {integrity: sha512-a2tW0Ia1pajcPBOGUF2jXlDnvE9d5/dg6BG9h60OmRUcZVr/veUrU8vEQFwwQIhwG3KVzYwSk3v2nRRGFgQDXQ==}
     dependencies:
-      '@babel/runtime': 7.23.2
+      '@babel/runtime': 7.23.4
       oblivious-set: 1.4.0
       p-queue: 6.6.2
       unload: 2.4.1
@@ -10396,7 +10546,7 @@ packages:
       readable-stream: 3.6.0
     dev: false
 
-  /create-jest@29.7.0(@types/node@20.10.4):
+  /create-jest@29.7.0(@types/node@20.10.5):
     resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -10405,7 +10555,7 @@ packages:
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.10.4)
+      jest-config: 29.7.0(@types/node@20.10.5)
       jest-util: 29.7.0
       prompts: 2.4.2
     transitivePeerDependencies:
@@ -10601,6 +10751,10 @@ packages:
 
   /csstype@3.1.2:
     resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
+    dev: true
+
+  /csstype@3.1.3:
+    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 
   /cwise-compiler@1.1.3:
     resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==}
@@ -10728,7 +10882,6 @@ packages:
     dependencies:
       ms: 2.1.2
       supports-color: 5.5.0
-    dev: true
 
   /debug@4.3.4(supports-color@8.1.1):
     resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
@@ -10741,6 +10894,7 @@ packages:
     dependencies:
       ms: 2.1.2
       supports-color: 8.1.1
+    dev: true
 
   /decamelize-keys@1.1.1:
     resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==}
@@ -10957,7 +11111,7 @@ packages:
     hasBin: true
     dependencies:
       address: 1.2.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -11281,7 +11435,7 @@ packages:
     peerDependencies:
       esbuild: '>=0.12 <1'
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       esbuild: 0.18.20
     transitivePeerDependencies:
       - supports-color
@@ -11346,6 +11500,36 @@ packages:
       '@esbuild/win32-ia32': 0.19.8
       '@esbuild/win32-x64': 0.19.8
 
+  /esbuild@0.19.9:
+    resolution: {integrity: sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    optionalDependencies:
+      '@esbuild/android-arm': 0.19.9
+      '@esbuild/android-arm64': 0.19.9
+      '@esbuild/android-x64': 0.19.9
+      '@esbuild/darwin-arm64': 0.19.9
+      '@esbuild/darwin-x64': 0.19.9
+      '@esbuild/freebsd-arm64': 0.19.9
+      '@esbuild/freebsd-x64': 0.19.9
+      '@esbuild/linux-arm': 0.19.9
+      '@esbuild/linux-arm64': 0.19.9
+      '@esbuild/linux-ia32': 0.19.9
+      '@esbuild/linux-loong64': 0.19.9
+      '@esbuild/linux-mips64el': 0.19.9
+      '@esbuild/linux-ppc64': 0.19.9
+      '@esbuild/linux-riscv64': 0.19.9
+      '@esbuild/linux-s390x': 0.19.9
+      '@esbuild/linux-x64': 0.19.9
+      '@esbuild/netbsd-x64': 0.19.9
+      '@esbuild/openbsd-x64': 0.19.9
+      '@esbuild/sunos-x64': 0.19.9
+      '@esbuild/win32-arm64': 0.19.9
+      '@esbuild/win32-ia32': 0.19.9
+      '@esbuild/win32-x64': 0.19.9
+    dev: false
+
   /escalade@3.1.1:
     resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
     engines: {node: '>=6'}
@@ -11526,7 +11710,7 @@ packages:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
@@ -11573,7 +11757,7 @@ packages:
       ajv: 6.12.6
       chalk: 4.1.2
       cross-spawn: 7.0.3
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       doctrine: 3.0.0
       escape-string-regexp: 4.0.0
       eslint-scope: 7.2.2
@@ -12177,7 +12361,7 @@ packages:
       debug:
         optional: true
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
 
   /for-each@0.3.3:
     resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
@@ -12733,7 +12917,6 @@ packages:
   /has-flag@3.0.0:
     resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
     engines: {node: '>=4'}
-    dev: true
 
   /has-flag@4.0.0:
     resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
@@ -12871,7 +13054,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -12941,7 +13124,7 @@ packages:
     engines: {node: '>= 6.0.0'}
     dependencies:
       agent-base: 5.1.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -12951,7 +13134,7 @@ packages:
     engines: {node: '>= 6'}
     dependencies:
       agent-base: 6.0.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -12960,7 +13143,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -12970,7 +13153,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
     dev: false
@@ -13120,7 +13303,7 @@ packages:
     dependencies:
       '@ioredis/commands': 1.2.0
       cluster-key-slot: 1.1.2
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       denque: 2.1.0
       lodash.defaults: 4.2.0
       lodash.isarguments: 3.1.0
@@ -13246,12 +13429,6 @@ packages:
       has: 1.0.3
     dev: true
 
-  /is-core-module@2.13.0:
-    resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==}
-    dependencies:
-      has: 1.0.3
-    dev: true
-
   /is-core-module@2.13.1:
     resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
     dependencies:
@@ -13567,7 +13744,7 @@ packages:
     resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
     engines: {node: '>=10'}
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       istanbul-lib-coverage: 3.2.0
       source-map: 0.6.1
     transitivePeerDependencies:
@@ -13622,7 +13799,7 @@ packages:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.3.0
@@ -13643,7 +13820,7 @@ packages:
       - supports-color
     dev: true
 
-  /jest-cli@29.7.0(@types/node@20.10.4):
+  /jest-cli@29.7.0(@types/node@20.10.5):
     resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -13657,10 +13834,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.10.4)
+      create-jest: 29.7.0(@types/node@20.10.5)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.10.4)
+      jest-config: 29.7.0(@types/node@20.10.5)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.6.2
@@ -13671,7 +13848,7 @@ packages:
       - ts-node
     dev: true
 
-  /jest-config@29.7.0(@types/node@20.10.4):
+  /jest-config@29.7.0(@types/node@20.10.5):
     resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     peerDependencies:
@@ -13686,7 +13863,7 @@ packages:
       '@babel/core': 7.22.11
       '@jest/test-sequencer': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       babel-jest: 29.7.0(@babel/core@7.22.11)
       chalk: 4.1.2
       ci-info: 3.7.1
@@ -13766,7 +13943,7 @@ packages:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       jest-mock: 29.7.0
       jest-util: 29.7.0
     dev: true
@@ -13796,7 +13973,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.6
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -13857,7 +14034,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.10.4
+      '@types/node': 20.10.5
     dev: true
 
   /jest-mock@29.7.0:
@@ -13865,7 +14042,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       jest-util: 29.7.0
     dev: true
 
@@ -13920,7 +14097,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -13951,7 +14128,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       chalk: 4.1.2
       cjs-module-lexer: 1.2.2
       collect-v8-coverage: 1.0.1
@@ -14003,7 +14180,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       chalk: 4.1.2
       ci-info: 3.7.1
       graceful-fs: 4.2.11
@@ -14028,7 +14205,7 @@ packages:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -14047,13 +14224,13 @@ packages:
     resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
     dev: true
 
-  /jest@29.7.0(@types/node@20.10.4):
+  /jest@29.7.0(@types/node@20.10.5):
     resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -14066,7 +14243,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.10.4)
+      jest-cli: 29.7.0(@types/node@20.10.5)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -14245,7 +14422,7 @@ packages:
     resolution: {integrity: sha512-pJ4XLQP4Q9HTxl6RVDLJ8Cyh1uitSs0CzDBAz1uoJ4sRD/Bk7cFSXL1FUXDW3zJ7YnfliJx6eu8Jn283bpZ4Yg==}
     engines: {node: '>=10'}
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       rfdc: 1.3.0
       uri-js: 4.4.1
     transitivePeerDependencies:
@@ -14804,11 +14981,11 @@ packages:
       twemoji-parser: 14.0.0
     dev: false
 
-  /microformats-parser@1.5.2:
-    resolution: {integrity: sha512-EcHm8zxEm3CggOLgILfxCo2wDiJEOnACzpV/FXWGLaRk24ECei+JkoWNdKdo2vzo/Pww9EvrQNeQsdv4JuHy7Q==}
-    engines: {node: '>=14'}
+  /microformats-parser@2.0.2:
+    resolution: {integrity: sha512-tUf9DmN4Jq/tGyp1YH2V6D/Cud+9Uc0WhjjUFirqVeHTRkkfLDacv6BQFT7h7HFsD0Z8wja5eKkRgzZU8bv0Fw==}
+    engines: {node: '>=18'}
     dependencies:
-      parse5: 6.0.1
+      parse5: 7.1.2
     dev: false
 
   /micromatch@4.0.5:
@@ -15443,7 +15620,7 @@ packages:
     engines: {node: '>=10'}
     dependencies:
       hosted-git-info: 4.1.0
-      is-core-module: 2.13.0
+      is-core-module: 2.13.1
       semver: 7.5.4
       validate-npm-package-license: 3.0.4
     dev: true
@@ -16817,7 +16994,7 @@ packages:
     engines: {node: '>=8.16.0'}
     dependencies:
       '@types/mime-types': 2.1.4
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       extract-zip: 1.7.0
       https-proxy-agent: 4.0.0
       mime: 2.6.0
@@ -17274,6 +17451,7 @@ packages:
 
   /regenerator-runtime@0.13.11:
     resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
+    dev: false
 
   /regenerator-runtime@0.14.0:
     resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==}
@@ -17520,24 +17698,24 @@ packages:
       fsevents: 2.3.3
     dev: true
 
-  /rollup@4.9.0:
-    resolution: {integrity: sha512-bUHW/9N21z64gw8s6tP4c88P382Bq/L5uZDowHlHx6s/QWpjJXivIAbEw6LZthgSvlEizZBfLC4OAvWe7aoF7A==}
+  /rollup@4.9.1:
+    resolution: {integrity: sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw==}
     engines: {node: '>=18.0.0', npm: '>=8.0.0'}
     hasBin: true
     optionalDependencies:
-      '@rollup/rollup-android-arm-eabi': 4.9.0
-      '@rollup/rollup-android-arm64': 4.9.0
-      '@rollup/rollup-darwin-arm64': 4.9.0
-      '@rollup/rollup-darwin-x64': 4.9.0
-      '@rollup/rollup-linux-arm-gnueabihf': 4.9.0
-      '@rollup/rollup-linux-arm64-gnu': 4.9.0
-      '@rollup/rollup-linux-arm64-musl': 4.9.0
-      '@rollup/rollup-linux-riscv64-gnu': 4.9.0
-      '@rollup/rollup-linux-x64-gnu': 4.9.0
-      '@rollup/rollup-linux-x64-musl': 4.9.0
-      '@rollup/rollup-win32-arm64-msvc': 4.9.0
-      '@rollup/rollup-win32-ia32-msvc': 4.9.0
-      '@rollup/rollup-win32-x64-msvc': 4.9.0
+      '@rollup/rollup-android-arm-eabi': 4.9.1
+      '@rollup/rollup-android-arm64': 4.9.1
+      '@rollup/rollup-darwin-arm64': 4.9.1
+      '@rollup/rollup-darwin-x64': 4.9.1
+      '@rollup/rollup-linux-arm-gnueabihf': 4.9.1
+      '@rollup/rollup-linux-arm64-gnu': 4.9.1
+      '@rollup/rollup-linux-arm64-musl': 4.9.1
+      '@rollup/rollup-linux-riscv64-gnu': 4.9.1
+      '@rollup/rollup-linux-x64-gnu': 4.9.1
+      '@rollup/rollup-linux-x64-musl': 4.9.1
+      '@rollup/rollup-win32-arm64-msvc': 4.9.1
+      '@rollup/rollup-win32-ia32-msvc': 4.9.1
+      '@rollup/rollup-win32-x64-msvc': 4.9.1
       fsevents: 2.3.3
 
   /rrweb-cssom@0.6.0:
@@ -17830,7 +18008,7 @@ packages:
     dependencies:
       '@hapi/hoek': 10.0.1
       '@hapi/wreck': 18.0.1
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       joi: 17.7.0
     transitivePeerDependencies:
       - supports-color
@@ -18030,7 +18208,7 @@ packages:
     engines: {node: '>= 14'}
     dependencies:
       agent-base: 7.1.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       socks: 2.7.1
     transitivePeerDependencies:
       - supports-color
@@ -18183,7 +18361,7 @@ packages:
       arg: 5.0.2
       bluebird: 3.7.2
       check-more-types: 2.24.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       execa: 5.1.1
       lazy-ass: 1.6.0
       ps-tree: 1.2.0
@@ -18448,7 +18626,6 @@ packages:
     engines: {node: '>=4'}
     dependencies:
       has-flag: 3.0.0
-    dev: true
 
   /supports-color@7.2.0:
     resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
@@ -18857,15 +19034,15 @@ packages:
       strip-bom: 3.0.0
     dev: false
 
-  /tsd@0.29.0:
-    resolution: {integrity: sha512-5B7jbTj+XLMg6rb9sXRBGwzv7h8KJlGOkTHxY63eWpZJiQ5vJbXEjL0u7JkIxwi5EsrRE1kRVUWmy6buK/ii8A==}
+  /tsd@0.30.0:
+    resolution: {integrity: sha512-aHL4rEuf3wwRzKCH8yqsE1oMAJYn7SAQ2JfWSgjr1e5/fqr+ggohQazECMpSoRAqSQeM/iIFugoyL/0eFwdTcA==}
     engines: {node: '>=14.16'}
     hasBin: true
     dependencies:
-      '@tsd/typescript': 5.2.2
+      '@tsd/typescript': 5.3.3
       eslint-formatter-pretty: 4.1.0
       globby: 11.1.0
-      jest-diff: 29.6.4
+      jest-diff: 29.7.0
       meow: 9.0.0
       path-exists: 4.0.0
       read-pkg-up: 7.0.1
@@ -19069,7 +19246,7 @@ packages:
       chalk: 4.1.2
       cli-highlight: 2.1.11
       date-fns: 2.30.0
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       dotenv: 16.0.3
       glob: 8.1.0
       ioredis: 5.3.2
@@ -19371,7 +19548,7 @@ packages:
     resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
     hasBin: true
 
-  /v-code-diff@1.7.2(vue@3.3.11):
+  /v-code-diff@1.7.2(vue@3.3.12):
     resolution: {integrity: sha512-y+q8ZHf8GfphYLhcZbjAKcId/h6vZujS71Ryq5u+dI6Jg4ZLTdLrBNVSzYpHywHSSFFfBMdilm6XvVryEaH4+A==}
     requiresBuild: true
     peerDependencies:
@@ -19384,8 +19561,8 @@ packages:
       diff: 5.1.0
       diff-match-patch: 1.0.5
       highlight.js: 11.8.0
-      vue: 3.3.11(typescript@5.3.3)
-      vue-demi: 0.13.11(vue@3.3.11)
+      vue: 3.3.12(typescript@5.3.3)
+      vue-demi: 0.13.11(vue@3.3.12)
     dev: false
 
   /v8-to-istanbul@9.1.0:
@@ -19421,17 +19598,17 @@ packages:
       core-util-is: 1.0.2
       extsprintf: 1.3.0
 
-  /vite-node@0.34.6(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0):
+  /vite-node@0.34.6(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0):
     resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
     engines: {node: '>=v14.18.0'}
     hasBin: true
     dependencies:
       cac: 6.7.14
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       mlly: 1.4.0
       pathe: 1.1.1
       picocolors: 1.0.0
-      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
+      vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
     transitivePeerDependencies:
       - '@types/node'
       - less
@@ -19447,7 +19624,7 @@ packages:
     resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==}
     dev: true
 
-  /vite@5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0):
+  /vite@5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0):
     resolution: {integrity: sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==}
     engines: {node: ^18.0.0 || >=20.0.0}
     hasBin: true
@@ -19475,10 +19652,10 @@ packages:
       terser:
         optional: true
     dependencies:
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       esbuild: 0.19.8
       postcss: 8.4.32
-      rollup: 4.9.0
+      rollup: 4.9.1
       sass: 1.69.5
       terser: 5.26.0
     optionalDependencies:
@@ -19529,7 +19706,7 @@ packages:
     dependencies:
       '@types/chai': 4.3.5
       '@types/chai-subset': 1.3.3
-      '@types/node': 20.10.4
+      '@types/node': 20.10.5
       '@vitest/expect': 0.34.6
       '@vitest/runner': 0.34.6
       '@vitest/snapshot': 0.34.6
@@ -19539,7 +19716,7 @@ packages:
       acorn-walk: 8.2.0
       cac: 6.7.14
       chai: 4.3.10
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       happy-dom: 10.0.3
       local-pkg: 0.4.3
       magic-string: 0.30.3
@@ -19549,8 +19726,8 @@ packages:
       strip-literal: 1.0.1
       tinybench: 2.5.0
       tinypool: 0.7.0
-      vite: 5.0.10(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
-      vite-node: 0.34.6(@types/node@20.10.4)(sass@1.69.5)(terser@5.26.0)
+      vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
+      vite-node: 0.34.6(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
       why-is-node-running: 2.2.2
     transitivePeerDependencies:
       - less
@@ -19582,7 +19759,7 @@ packages:
     resolution: {integrity: sha512-6bnLkn8O0JJyiFSIF0EfCogzeqNXpnjJ0vW/SZzNHfe6sPx30lTtTXlE5TFs2qhJlAtDFybStVNpL73cPe3OMQ==}
     dev: true
 
-  /vue-demi@0.13.11(vue@3.3.11):
+  /vue-demi@0.13.11(vue@3.3.12):
     resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
     engines: {node: '>=12'}
     hasBin: true
@@ -19594,23 +19771,23 @@ packages:
       '@vue/composition-api':
         optional: true
     dependencies:
-      vue: 3.3.11(typescript@5.3.3)
+      vue: 3.3.12(typescript@5.3.3)
     dev: false
 
-  /vue-docgen-api@4.64.1(vue@3.3.11):
+  /vue-docgen-api@4.64.1(vue@3.3.12):
     resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==}
     dependencies:
       '@babel/parser': 7.23.5
       '@babel/types': 7.23.5
       '@vue/compiler-dom': 3.3.11
-      '@vue/compiler-sfc': 3.3.11
+      '@vue/compiler-sfc': 3.3.12
       ast-types: 0.14.2
       hash-sum: 2.0.0
       lru-cache: 8.0.4
       pug: 3.0.2
       recast: 0.22.0
       ts-map: 1.0.3
-      vue-inbrowser-compiler-independent-utils: 4.64.1(vue@3.3.11)
+      vue-inbrowser-compiler-independent-utils: 4.64.1(vue@3.3.12)
     transitivePeerDependencies:
       - vue
     dev: true
@@ -19621,7 +19798,7 @@ packages:
     peerDependencies:
       eslint: '>=6.0.0'
     dependencies:
-      debug: 4.3.4(supports-color@8.1.1)
+      debug: 4.3.4(supports-color@5.5.0)
       eslint: 8.56.0
       eslint-scope: 7.2.2
       eslint-visitor-keys: 3.4.3
@@ -19633,12 +19810,12 @@ packages:
       - supports-color
     dev: true
 
-  /vue-inbrowser-compiler-independent-utils@4.64.1(vue@3.3.11):
+  /vue-inbrowser-compiler-independent-utils@4.64.1(vue@3.3.12):
     resolution: {integrity: sha512-Hn32n07XZ8j9W8+fmOXPQL+i+W2e/8i6mkH4Ju3H6nR0+cfvmWM95GhczYi5B27+Y8JlCKgAo04IUiYce4mKAw==}
     peerDependencies:
       vue: '>=2'
     dependencies:
-      vue: 3.3.11(typescript@5.3.3)
+      vue: 3.3.12(typescript@5.3.3)
     dev: true
 
   /vue-template-compiler@2.7.14:
@@ -19660,28 +19837,28 @@ packages:
       typescript: 5.3.3
     dev: true
 
-  /vue@3.3.11(typescript@5.3.3):
-    resolution: {integrity: sha512-d4oBctG92CRO1cQfVBZp6WJAs0n8AK4Xf5fNjQCBeKCvMI1efGQ5E3Alt1slFJS9fZuPcFoiAiqFvQlv1X7t/w==}
+  /vue@3.3.12(typescript@5.3.3):
+    resolution: {integrity: sha512-jYNv2QmET2OTHsFzfWHMnqgCfqL4zfo97QwofdET+GBRCHhSCHuMTTvNIgeSn0/xF3JRT5OGah6MDwUFN7MPlg==}
     peerDependencies:
       typescript: '*'
     peerDependenciesMeta:
       typescript:
         optional: true
     dependencies:
-      '@vue/compiler-dom': 3.3.11
-      '@vue/compiler-sfc': 3.3.11
-      '@vue/runtime-dom': 3.3.11
-      '@vue/server-renderer': 3.3.11(vue@3.3.11)
-      '@vue/shared': 3.3.11
+      '@vue/compiler-dom': 3.3.12
+      '@vue/compiler-sfc': 3.3.12
+      '@vue/runtime-dom': 3.3.12
+      '@vue/server-renderer': 3.3.12(vue@3.3.12)
+      '@vue/shared': 3.3.12
       typescript: 5.3.3
 
-  /vuedraggable@4.1.0(vue@3.3.11):
+  /vuedraggable@4.1.0(vue@3.3.12):
     resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==}
     peerDependencies:
       vue: ^3.0.1
     dependencies:
       sortablejs: 1.14.0
-      vue: 3.3.11(typescript@5.3.3)
+      vue: 3.3.12(typescript@5.3.3)
     dev: false
 
   /w3c-xmlserializer@5.0.0:
@@ -20156,7 +20333,7 @@ packages:
       sharp: 0.31.3
     dev: false
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.6.4)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0):
+  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.6.5)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(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
@@ -20178,7 +20355,7 @@ packages:
         optional: true
     dependencies:
       '@storybook/blocks': 7.6.5(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/components': 7.6.4(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/components': 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/core-events': 7.6.5
       '@storybook/manager-api': 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/preview-api': 7.6.5

From 507d436699ff3ba31587150b73a3d279e3e8ef75 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 18 Dec 2023 13:22:34 +0900
Subject: [PATCH 262/435] update mfm-js

---
 packages/backend/package.json  |  2 +-
 packages/frontend/package.json |  2 +-
 pnpm-lock.yaml                 | 30 +++++++++++++++++-------------
 3 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 1a435bcc1f..9f015f8ee9 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -121,7 +121,7 @@
 		"jsonld": "8.3.2",
 		"jsrsasign": "10.9.0",
 		"meilisearch": "0.36.0",
-		"mfm-js": "0.23.3",
+		"mfm-js": "0.24.0",
 		"microformats-parser": "2.0.2",
 		"mime-types": "2.1.35",
 		"misskey-js": "workspace:*",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 48d22869da..e1647d6a59 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -50,7 +50,7 @@
 		"is-file-animated": "1.0.2",
 		"json5": "2.2.3",
 		"matter-js": "0.19.0",
-		"mfm-js": "0.23.3",
+		"mfm-js": "0.24.0",
 		"misskey-js": "workspace:*",
 		"photoswipe": "5.4.3",
 		"punycode": "2.3.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 69b844f2a2..a38b644b48 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -243,8 +243,8 @@ importers:
         specifier: 0.36.0
         version: 0.36.0
       mfm-js:
-        specifier: 0.23.3
-        version: 0.23.3
+        specifier: 0.24.0
+        version: 0.24.0
       microformats-parser:
         specifier: 2.0.2
         version: 2.0.2
@@ -752,8 +752,8 @@ importers:
         specifier: 0.19.0
         version: 0.19.0
       mfm-js:
-        specifier: 0.23.3
-        version: 0.23.3
+        specifier: 0.24.0
+        version: 0.24.0
       misskey-js:
         specifier: workspace:*
         version: link:../misskey-js
@@ -868,10 +868,10 @@ importers:
         version: 7.6.5
       '@storybook/vue3':
         specifier: 7.6.5
-        version: 7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.12)
+        version: 7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12)
       '@storybook/vue3-vite':
         specifier: 7.6.5
-        version: 7.6.5(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12)
+        version: 7.6.5(@vue/compiler-core@3.3.12)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12)
       '@testing-library/vue':
         specifier: 8.0.1
         version: 8.0.1(@vue/compiler-sfc@3.3.12)(vue@3.3.12)
@@ -7239,7 +7239,7 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.3.11)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12):
+  /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.3.12)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12):
     resolution: {integrity: sha512-7wUCq2Lrjlekftd5ha3hG0GSGbbzuc370cKkBqSmwFuOfI38z5+VeYt7nDtAlncxcpVSH7DejTGRuKTlC7NyYg==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
@@ -7247,7 +7247,7 @@ packages:
     dependencies:
       '@storybook/builder-vite': 7.6.5(typescript@5.3.3)(vite@5.0.10)
       '@storybook/core-server': 7.6.5
-      '@storybook/vue3': 7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.12)
+      '@storybook/vue3': 7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12)
       '@vitejs/plugin-vue': 4.5.2(vite@5.0.10)(vue@3.3.12)
       magic-string: 0.30.5
       vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
@@ -7264,7 +7264,7 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.6.5(@vue/compiler-core@3.3.11)(vue@3.3.12):
+  /@storybook/vue3@7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12):
     resolution: {integrity: sha512-tv/9rVc3XXDOJu5hfZtKhrhM8x4GTLKon62Rmaxlq06weqkGlfBi/V/g1EZ7OE71Pi+woKS/TX7p9qbRrvgahg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
@@ -7276,7 +7276,7 @@ packages:
       '@storybook/global': 5.0.0
       '@storybook/preview-api': 7.6.5
       '@storybook/types': 7.6.5
-      '@vue/compiler-core': 3.3.11
+      '@vue/compiler-core': 3.3.12
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
@@ -7785,6 +7785,10 @@ packages:
     engines: {node: '>=14.17'}
     dev: true
 
+  /@twemoji/parser@15.0.0:
+    resolution: {integrity: sha512-lh9515BNsvKSNvyUqbj5yFu83iIDQ77SwVcsN/SnEGawczhsKU6qWuogewN1GweTi5Imo5ToQ9s+nNTf97IXvg==}
+    dev: false
+
   /@types/accepts@1.3.7:
     resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
     dependencies:
@@ -14975,10 +14979,10 @@ packages:
     resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
     engines: {node: '>= 0.6'}
 
-  /mfm-js@0.23.3:
-    resolution: {integrity: sha512-o8scYmbey6rMUmWAlT3k3ntt6khaCLdxlmHhAWV5wTTMj2OK1atQvZfRUq0SIVm1Jig08qlZg/ps71xUqrScNA==}
+  /mfm-js@0.24.0:
+    resolution: {integrity: sha512-6m8N0ElH9/4CA1izhVqmxTfLj5Z9RspdqM/lMew4xU/UTgm4Pf//VpDunpasxbRFjeJSVW+zoVwL4ZPfPtfiQg==}
     dependencies:
-      twemoji-parser: 14.0.0
+      '@twemoji/parser': 15.0.0
     dev: false
 
   /microformats-parser@2.0.2:

From 13990279c35938d15e20cc8aba1342d355d0f61a Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Mon, 18 Dec 2023 14:51:29 +0900
Subject: [PATCH 263/435] =?UTF-8?q?enhance:=20Unicode=2015.0=E3=81=A7?=
 =?UTF-8?q?=E6=96=B0=E3=81=9F=E3=81=AB=E8=BF=BD=E5=8A=A0=E3=81=95=E3=82=8C?=
 =?UTF-8?q?=E3=81=9F=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=92=E3=83=AA=E3=82=A2?=
 =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AB=E4=BD=BF=E3=81=88?=
 =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#12683)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/package.json                 |  4 +--
 packages/backend/src/misc/emoji-regex.ts      |  4 +--
 packages/frontend/.storybook/mocks.ts         |  2 +-
 packages/frontend/package.json                |  4 +--
 packages/frontend/src/emojilist.json          | 21 +++++++++++++++
 .../src/unicode-emoji-indexes/en-US.json      | 21 +++++++++++++++
 pnpm-lock.yaml                                | 26 +++++++++----------
 7 files changed, 62 insertions(+), 20 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 9f015f8ee9..6848d88e03 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -65,7 +65,7 @@
 		"@bull-board/api": "5.10.2",
 		"@bull-board/fastify": "5.10.2",
 		"@bull-board/ui": "5.10.2",
-		"@discordapp/twemoji": "14.1.2",
+		"@discordapp/twemoji": "15.0.2",
 		"@fastify/accepts": "4.3.0",
 		"@fastify/cookie": "9.2.0",
 		"@fastify/cors": "8.4.2",
@@ -83,6 +83,7 @@
 		"@smithy/node-http-handler": "2.1.10",
 		"@swc/cli": "0.1.63",
 		"@swc/core": "1.3.100",
+		"@twemoji/parser": "15.0.0",
 		"accepts": "1.3.8",
 		"ajv": "8.12.0",
 		"archiver": "6.0.1",
@@ -166,7 +167,6 @@
 		"tmp": "0.2.1",
 		"tsc-alias": "1.8.8",
 		"tsconfig-paths": "4.2.0",
-		"twemoji-parser": "14.0.0",
 		"typeorm": "0.3.17",
 		"typescript": "5.3.3",
 		"ulid": "2.3.0",
diff --git a/packages/backend/src/misc/emoji-regex.ts b/packages/backend/src/misc/emoji-regex.ts
index 24e4092aeb..37ecde6eb1 100644
--- a/packages/backend/src/misc/emoji-regex.ts
+++ b/packages/backend/src/misc/emoji-regex.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-// taken from twemoji-parser/dist/lib/regex.js
-const twemojiRegex = /(?:\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d[\udc68\udc69]|\ud83e\udef1\ud83c\udffb\u200d\ud83e\udef2\ud83c[\udffc-\udfff]|\ud83e\udef1\ud83c\udffc\u200d\ud83e\udef2\ud83c[\udffb\udffd-\udfff]|\ud83e\udef1\ud83c\udffd\u200d\ud83e\udef2\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\udef1\ud83c\udffe\u200d\ud83e\udef2\ud83c[\udffb-\udffd\udfff]|\ud83e\udef1\ud83c\udfff\u200d\ud83e\udef2\ud83c[\udffb-\udffe]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d[\udc68\udc69]|\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1|\ud83d\udc6b\ud83c[\udffb-\udfff]|\ud83d\udc6c\ud83c[\udffb-\udfff]|\ud83d\udc6d\ud83c[\udffb-\udfff]|\ud83d\udc8f\ud83c[\udffb-\udfff]|\ud83d\udc91\ud83c[\udffb-\udfff]|\ud83e\udd1d\ud83c[\udffb-\udfff]|\ud83d[\udc6b-\udc6d\udc8f\udc91]|\ud83e\udd1d)|(?:\ud83d[\udc68\udc69]|\ud83e\uddd1)(?:\ud83c[\udffb-\udfff])?\u200d(?:\u2695\ufe0f|\u2696\ufe0f|\u2708\ufe0f|\ud83c[\udf3e\udf73\udf7c\udf84\udf93\udfa4\udfa8\udfeb\udfed]|\ud83d[\udcbb\udcbc\udd27\udd2c\ude80\ude92]|\ud83e[\uddaf-\uddb3\uddbc\uddbd])|(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75]|\u26f9)((?:\ud83c[\udffb-\udfff]|\ufe0f)\u200d[\u2640\u2642]\ufe0f)|(?:\ud83c[\udfc3\udfc4\udfca]|\ud83d[\udc6e\udc70\udc71\udc73\udc77\udc81\udc82\udc86\udc87\ude45-\ude47\ude4b\ude4d\ude4e\udea3\udeb4-\udeb6]|\ud83e[\udd26\udd35\udd37-\udd39\udd3d\udd3e\uddb8\uddb9\uddcd-\uddcf\uddd4\uddd6-\udddd])(?:\ud83c[\udffb-\udfff])?\u200d[\u2640\u2642]\ufe0f|(?:\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f|\ud83c\udff3\ufe0f\u200d\ud83c\udf08|\ud83d\ude36\u200d\ud83c\udf2b\ufe0f|\u2764\ufe0f\u200d\ud83d\udd25|\u2764\ufe0f\u200d\ud83e\ude79|\ud83c\udff4\u200d\u2620\ufe0f|\ud83d\udc15\u200d\ud83e\uddba|\ud83d\udc3b\u200d\u2744\ufe0f|\ud83d\udc41\u200d\ud83d\udde8|\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc6f\u200d\u2640\ufe0f|\ud83d\udc6f\u200d\u2642\ufe0f|\ud83d\ude2e\u200d\ud83d\udca8|\ud83d\ude35\u200d\ud83d\udcab|\ud83e\udd3c\u200d\u2640\ufe0f|\ud83e\udd3c\u200d\u2642\ufe0f|\ud83e\uddde\u200d\u2640\ufe0f|\ud83e\uddde\u200d\u2642\ufe0f|\ud83e\udddf\u200d\u2640\ufe0f|\ud83e\udddf\u200d\u2642\ufe0f|\ud83d\udc08\u200d\u2b1b)|[#*0-9]\ufe0f?\u20e3|(?:[©®\u2122\u265f]\ufe0f)|(?:\ud83c[\udc04\udd70\udd71\udd7e\udd7f\ude02\ude1a\ude2f\ude37\udf21\udf24-\udf2c\udf36\udf7d\udf96\udf97\udf99-\udf9b\udf9e\udf9f\udfcd\udfce\udfd4-\udfdf\udff3\udff5\udff7]|\ud83d[\udc3f\udc41\udcfd\udd49\udd4a\udd6f\udd70\udd73\udd76-\udd79\udd87\udd8a-\udd8d\udda5\udda8\uddb1\uddb2\uddbc\uddc2-\uddc4\uddd1-\uddd3\udddc-\uddde\udde1\udde3\udde8\uddef\uddf3\uddfa\udecb\udecd-\udecf\udee0-\udee5\udee9\udef0\udef3]|[\u203c\u2049\u2139\u2194-\u2199\u21a9\u21aa\u231a\u231b\u2328\u23cf\u23ed-\u23ef\u23f1\u23f2\u23f8-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267b\u267f\u2692-\u2697\u2699\u269b\u269c\u26a0\u26a1\u26a7\u26aa\u26ab\u26b0\u26b1\u26bd\u26be\u26c4\u26c5\u26c8\u26cf\u26d1\u26d3\u26d4\u26e9\u26ea\u26f0-\u26f5\u26f8\u26fa\u26fd\u2702\u2708\u2709\u270f\u2712\u2714\u2716\u271d\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u2764\u27a1\u2934\u2935\u2b05-\u2b07\u2b1b\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299])(?:\ufe0f|(?!\ufe0e))|(?:(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75\udd90]|[\u261d\u26f7\u26f9\u270c\u270d])(?:\ufe0f|(?!\ufe0e))|(?:\ud83c[\udf85\udfc2-\udfc4\udfc7\udfca]|\ud83d[\udc42\udc43\udc46-\udc50\udc66-\udc69\udc6e\udc70-\udc78\udc7c\udc81-\udc83\udc85-\udc87\udcaa\udd7a\udd95\udd96\ude45-\ude47\ude4b-\ude4f\udea3\udeb4-\udeb6\udec0\udecc]|\ud83e[\udd0c\udd0f\udd18-\udd1c\udd1e\udd1f\udd26\udd30-\udd39\udd3d\udd3e\udd77\uddb5\uddb6\uddb8\uddb9\uddbb\uddcd-\uddcf\uddd1-\udddd\udec3-\udec5\udef0-\udef6]|[\u270a\u270b]))(?:\ud83c[\udffb-\udfff])?|(?:\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f|\ud83c\udde6\ud83c[\udde8-\uddec\uddee\uddf1\uddf2\uddf4\uddf6-\uddfa\uddfc\uddfd\uddff]|\ud83c\udde7\ud83c[\udde6\udde7\udde9-\uddef\uddf1-\uddf4\uddf6-\uddf9\uddfb\uddfc\uddfe\uddff]|\ud83c\udde8\ud83c[\udde6\udde8\udde9\uddeb-\uddee\uddf0-\uddf5\uddf7\uddfa-\uddff]|\ud83c\udde9\ud83c[\uddea\uddec\uddef\uddf0\uddf2\uddf4\uddff]|\ud83c\uddea\ud83c[\udde6\udde8\uddea\uddec\udded\uddf7-\uddfa]|\ud83c\uddeb\ud83c[\uddee-\uddf0\uddf2\uddf4\uddf7]|\ud83c\uddec\ud83c[\udde6\udde7\udde9-\uddee\uddf1-\uddf3\uddf5-\uddfa\uddfc\uddfe]|\ud83c\udded\ud83c[\uddf0\uddf2\uddf3\uddf7\uddf9\uddfa]|\ud83c\uddee\ud83c[\udde8-\uddea\uddf1-\uddf4\uddf6-\uddf9]|\ud83c\uddef\ud83c[\uddea\uddf2\uddf4\uddf5]|\ud83c\uddf0\ud83c[\uddea\uddec-\uddee\uddf2\uddf3\uddf5\uddf7\uddfc\uddfe\uddff]|\ud83c\uddf1\ud83c[\udde6-\udde8\uddee\uddf0\uddf7-\uddfb\uddfe]|\ud83c\uddf2\ud83c[\udde6\udde8-\udded\uddf0-\uddff]|\ud83c\uddf3\ud83c[\udde6\udde8\uddea-\uddec\uddee\uddf1\uddf4\uddf5\uddf7\uddfa\uddff]|\ud83c\uddf4\ud83c\uddf2|\ud83c\uddf5\ud83c[\udde6\uddea-\udded\uddf0-\uddf3\uddf7-\uddf9\uddfc\uddfe]|\ud83c\uddf6\ud83c\udde6|\ud83c\uddf7\ud83c[\uddea\uddf4\uddf8\uddfa\uddfc]|\ud83c\uddf8\ud83c[\udde6-\uddea\uddec-\uddf4\uddf7-\uddf9\uddfb\uddfd-\uddff]|\ud83c\uddf9\ud83c[\udde6\udde8\udde9\uddeb-\udded\uddef-\uddf4\uddf7\uddf9\uddfb\uddfc\uddff]|\ud83c\uddfa\ud83c[\udde6\uddec\uddf2\uddf3\uddf8\uddfe\uddff]|\ud83c\uddfb\ud83c[\udde6\udde8\uddea\uddec\uddee\uddf3\uddfa]|\ud83c\uddfc\ud83c[\uddeb\uddf8]|\ud83c\uddfd\ud83c\uddf0|\ud83c\uddfe\ud83c[\uddea\uddf9]|\ud83c\uddff\ud83c[\udde6\uddf2\uddfc]|\ud83c[\udccf\udd8e\udd91-\udd9a\udde6-\uddff\ude01\ude32-\ude36\ude38-\ude3a\ude50\ude51\udf00-\udf20\udf2d-\udf35\udf37-\udf7c\udf7e-\udf84\udf86-\udf93\udfa0-\udfc1\udfc5\udfc6\udfc8\udfc9\udfcf-\udfd3\udfe0-\udff0\udff4\udff8-\udfff]|\ud83d[\udc00-\udc3e\udc40\udc44\udc45\udc51-\udc65\udc6a\udc6f\udc79-\udc7b\udc7d-\udc80\udc84\udc88-\udc8e\udc90\udc92-\udca9\udcab-\udcfc\udcff-\udd3d\udd4b-\udd4e\udd50-\udd67\udda4\uddfb-\ude44\ude48-\ude4a\ude80-\udea2\udea4-\udeb3\udeb7-\udebf\udec1-\udec5\uded0-\uded2\uded5-\uded7\udedd-\udedf\udeeb\udeec\udef4-\udefc\udfe0-\udfeb\udff0]|\ud83e[\udd0d\udd0e\udd10-\udd17\udd20-\udd25\udd27-\udd2f\udd3a\udd3c\udd3f-\udd45\udd47-\udd76\udd78-\uddb4\uddb7\uddba\uddbc-\uddcc\uddd0\uddde-\uddff\ude70-\ude74\ude78-\ude7c\ude80-\ude86\ude90-\udeac\udeb0-\udeba\udec0-\udec2\uded0-\uded9\udee0-\udee7]|[\u23e9-\u23ec\u23f0\u23f3\u267e\u26ce\u2705\u2728\u274c\u274e\u2753-\u2755\u2795-\u2797\u27b0\u27bf\ue50a])|\ufe0f/g;
+// taken from @twemoji/parser/dist/lib/regex.js
+const twemojiRegex = /(?:\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83d\udc68\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc68\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc68\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc68\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffc-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffd-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb\udffc\udffe\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffd\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc68\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83d\udc69\ud83c[\udffb-\udfff]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc68\ud83c[\udffb-\udffe]|\ud83d\udc69\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83d\udc69\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udffb\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffc-\udfff]|\ud83e\uddd1\ud83c\udffb\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffd-\udfff]|\ud83e\uddd1\ud83c\udffc\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\uddd1\ud83c\udffd\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffd\udfff]|\ud83e\uddd1\ud83c\udffe\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83e\uddd1\ud83c\udfff\u200d\u2764\ufe0f\u200d\ud83e\uddd1\ud83c[\udffb-\udffe]|\ud83e\uddd1\ud83c\udfff\u200d\ud83e\udd1d\u200d\ud83e\uddd1\ud83c[\udffb-\udfff]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d\udc8b\u200d\ud83d[\udc68\udc69]|\ud83e\udef1\ud83c\udffb\u200d\ud83e\udef2\ud83c[\udffc-\udfff]|\ud83e\udef1\ud83c\udffc\u200d\ud83e\udef2\ud83c[\udffb\udffd-\udfff]|\ud83e\udef1\ud83c\udffd\u200d\ud83e\udef2\ud83c[\udffb\udffc\udffe\udfff]|\ud83e\udef1\ud83c\udffe\u200d\ud83e\udef2\ud83c[\udffb-\udffd\udfff]|\ud83e\udef1\ud83c\udfff\u200d\ud83e\udef2\ud83c[\udffb-\udffe]|\ud83d\udc68\u200d\u2764\ufe0f\u200d\ud83d\udc68|\ud83d\udc69\u200d\u2764\ufe0f\u200d\ud83d[\udc68\udc69]|\ud83e\uddd1\u200d\ud83e\udd1d\u200d\ud83e\uddd1|\ud83d\udc6b\ud83c[\udffb-\udfff]|\ud83d\udc6c\ud83c[\udffb-\udfff]|\ud83d\udc6d\ud83c[\udffb-\udfff]|\ud83d\udc8f\ud83c[\udffb-\udfff]|\ud83d\udc91\ud83c[\udffb-\udfff]|\ud83e\udd1d\ud83c[\udffb-\udfff]|\ud83d[\udc6b-\udc6d\udc8f\udc91]|\ud83e\udd1d)|(?:\ud83d[\udc68\udc69]|\ud83e\uddd1)(?:\ud83c[\udffb-\udfff])?\u200d(?:\u2695\ufe0f|\u2696\ufe0f|\u2708\ufe0f|\ud83c[\udf3e\udf73\udf7c\udf84\udf93\udfa4\udfa8\udfeb\udfed]|\ud83d[\udcbb\udcbc\udd27\udd2c\ude80\ude92]|\ud83e[\uddaf-\uddb3\uddbc\uddbd])|(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75]|\u26f9)((?:\ud83c[\udffb-\udfff]|\ufe0f)\u200d[\u2640\u2642]\ufe0f)|(?:\ud83c[\udfc3\udfc4\udfca]|\ud83d[\udc6e\udc70\udc71\udc73\udc77\udc81\udc82\udc86\udc87\ude45-\ude47\ude4b\ude4d\ude4e\udea3\udeb4-\udeb6]|\ud83e[\udd26\udd35\udd37-\udd39\udd3d\udd3e\uddb8\uddb9\uddcd-\uddcf\uddd4\uddd6-\udddd])(?:\ud83c[\udffb-\udfff])?\u200d[\u2640\u2642]\ufe0f|(?:\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc68\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc68\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc66\u200d\ud83d\udc66|\ud83d\udc69\u200d\ud83d\udc67\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f|\ud83c\udff3\ufe0f\u200d\ud83c\udf08|\ud83d\ude36\u200d\ud83c\udf2b\ufe0f|\u2764\ufe0f\u200d\ud83d\udd25|\u2764\ufe0f\u200d\ud83e\ude79|\ud83c\udff4\u200d\u2620\ufe0f|\ud83d\udc15\u200d\ud83e\uddba|\ud83d\udc3b\u200d\u2744\ufe0f|\ud83d\udc41\u200d\ud83d\udde8|\ud83d\udc68\u200d\ud83d[\udc66\udc67]|\ud83d\udc69\u200d\ud83d[\udc66\udc67]|\ud83d\udc6f\u200d\u2640\ufe0f|\ud83d\udc6f\u200d\u2642\ufe0f|\ud83d\ude2e\u200d\ud83d\udca8|\ud83d\ude35\u200d\ud83d\udcab|\ud83e\udd3c\u200d\u2640\ufe0f|\ud83e\udd3c\u200d\u2642\ufe0f|\ud83e\uddde\u200d\u2640\ufe0f|\ud83e\uddde\u200d\u2642\ufe0f|\ud83e\udddf\u200d\u2640\ufe0f|\ud83e\udddf\u200d\u2642\ufe0f|\ud83d\udc08\u200d\u2b1b|\ud83d\udc26\u200d\u2b1b)|[#*0-9]\ufe0f?\u20e3|(?:[©®\u2122\u265f]\ufe0f)|(?:\ud83c[\udc04\udd70\udd71\udd7e\udd7f\ude02\ude1a\ude2f\ude37\udf21\udf24-\udf2c\udf36\udf7d\udf96\udf97\udf99-\udf9b\udf9e\udf9f\udfcd\udfce\udfd4-\udfdf\udff3\udff5\udff7]|\ud83d[\udc3f\udc41\udcfd\udd49\udd4a\udd6f\udd70\udd73\udd76-\udd79\udd87\udd8a-\udd8d\udda5\udda8\uddb1\uddb2\uddbc\uddc2-\uddc4\uddd1-\uddd3\udddc-\uddde\udde1\udde3\udde8\uddef\uddf3\uddfa\udecb\udecd-\udecf\udee0-\udee5\udee9\udef0\udef3]|[\u203c\u2049\u2139\u2194-\u2199\u21a9\u21aa\u231a\u231b\u2328\u23cf\u23ed-\u23ef\u23f1\u23f2\u23f8-\u23fa\u24c2\u25aa\u25ab\u25b6\u25c0\u25fb-\u25fe\u2600-\u2604\u260e\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262a\u262e\u262f\u2638-\u263a\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267b\u267f\u2692-\u2697\u2699\u269b\u269c\u26a0\u26a1\u26a7\u26aa\u26ab\u26b0\u26b1\u26bd\u26be\u26c4\u26c5\u26c8\u26cf\u26d1\u26d3\u26d4\u26e9\u26ea\u26f0-\u26f5\u26f8\u26fa\u26fd\u2702\u2708\u2709\u270f\u2712\u2714\u2716\u271d\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u2764\u27a1\u2934\u2935\u2b05-\u2b07\u2b1b\u2b1c\u2b50\u2b55\u3030\u303d\u3297\u3299])(?:\ufe0f|(?!\ufe0e))|(?:(?:\ud83c[\udfcb\udfcc]|\ud83d[\udd74\udd75\udd90]|\ud83e\udef0|[\u261d\u26f7\u26f9\u270c\u270d])(?:\ufe0f|(?!\ufe0e))|(?:\ud83c[\udf85\udfc2-\udfc4\udfc7\udfca]|\ud83d[\udc42\udc43\udc46-\udc50\udc66-\udc69\udc6e\udc70-\udc78\udc7c\udc81-\udc83\udc85-\udc87\udcaa\udd7a\udd95\udd96\ude45-\ude47\ude4b-\ude4f\udea3\udeb4-\udeb6\udec0\udecc]|\ud83e[\udd0c\udd0f\udd18-\udd1c\udd1e\udd1f\udd26\udd30-\udd39\udd3d\udd3e\udd77\uddb5\uddb6\uddb8\uddb9\uddbb\uddcd-\uddcf\uddd1-\udddd\udec3-\udec5\udef1-\udef8]|[\u270a\u270b]))(?:\ud83c[\udffb-\udfff])?|(?:\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc65\udb40\udc6e\udb40\udc67\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc73\udb40\udc63\udb40\udc74\udb40\udc7f|\ud83c\udff4\udb40\udc67\udb40\udc62\udb40\udc77\udb40\udc6c\udb40\udc73\udb40\udc7f|\ud83c\udde6\ud83c[\udde8-\uddec\uddee\uddf1\uddf2\uddf4\uddf6-\uddfa\uddfc\uddfd\uddff]|\ud83c\udde7\ud83c[\udde6\udde7\udde9-\uddef\uddf1-\uddf4\uddf6-\uddf9\uddfb\uddfc\uddfe\uddff]|\ud83c\udde8\ud83c[\udde6\udde8\udde9\uddeb-\uddee\uddf0-\uddf5\uddf7\uddfa-\uddff]|\ud83c\udde9\ud83c[\uddea\uddec\uddef\uddf0\uddf2\uddf4\uddff]|\ud83c\uddea\ud83c[\udde6\udde8\uddea\uddec\udded\uddf7-\uddfa]|\ud83c\uddeb\ud83c[\uddee-\uddf0\uddf2\uddf4\uddf7]|\ud83c\uddec\ud83c[\udde6\udde7\udde9-\uddee\uddf1-\uddf3\uddf5-\uddfa\uddfc\uddfe]|\ud83c\udded\ud83c[\uddf0\uddf2\uddf3\uddf7\uddf9\uddfa]|\ud83c\uddee\ud83c[\udde8-\uddea\uddf1-\uddf4\uddf6-\uddf9]|\ud83c\uddef\ud83c[\uddea\uddf2\uddf4\uddf5]|\ud83c\uddf0\ud83c[\uddea\uddec-\uddee\uddf2\uddf3\uddf5\uddf7\uddfc\uddfe\uddff]|\ud83c\uddf1\ud83c[\udde6-\udde8\uddee\uddf0\uddf7-\uddfb\uddfe]|\ud83c\uddf2\ud83c[\udde6\udde8-\udded\uddf0-\uddff]|\ud83c\uddf3\ud83c[\udde6\udde8\uddea-\uddec\uddee\uddf1\uddf4\uddf5\uddf7\uddfa\uddff]|\ud83c\uddf4\ud83c\uddf2|\ud83c\uddf5\ud83c[\udde6\uddea-\udded\uddf0-\uddf3\uddf7-\uddf9\uddfc\uddfe]|\ud83c\uddf6\ud83c\udde6|\ud83c\uddf7\ud83c[\uddea\uddf4\uddf8\uddfa\uddfc]|\ud83c\uddf8\ud83c[\udde6-\uddea\uddec-\uddf4\uddf7-\uddf9\uddfb\uddfd-\uddff]|\ud83c\uddf9\ud83c[\udde6\udde8\udde9\uddeb-\udded\uddef-\uddf4\uddf7\uddf9\uddfb\uddfc\uddff]|\ud83c\uddfa\ud83c[\udde6\uddec\uddf2\uddf3\uddf8\uddfe\uddff]|\ud83c\uddfb\ud83c[\udde6\udde8\uddea\uddec\uddee\uddf3\uddfa]|\ud83c\uddfc\ud83c[\uddeb\uddf8]|\ud83c\uddfd\ud83c\uddf0|\ud83c\uddfe\ud83c[\uddea\uddf9]|\ud83c\uddff\ud83c[\udde6\uddf2\uddfc]|\ud83c[\udccf\udd8e\udd91-\udd9a\udde6-\uddff\ude01\ude32-\ude36\ude38-\ude3a\ude50\ude51\udf00-\udf20\udf2d-\udf35\udf37-\udf7c\udf7e-\udf84\udf86-\udf93\udfa0-\udfc1\udfc5\udfc6\udfc8\udfc9\udfcf-\udfd3\udfe0-\udff0\udff4\udff8-\udfff]|\ud83d[\udc00-\udc3e\udc40\udc44\udc45\udc51-\udc65\udc6a\udc6f\udc79-\udc7b\udc7d-\udc80\udc84\udc88-\udc8e\udc90\udc92-\udca9\udcab-\udcfc\udcff-\udd3d\udd4b-\udd4e\udd50-\udd67\udda4\uddfb-\ude44\ude48-\ude4a\ude80-\udea2\udea4-\udeb3\udeb7-\udebf\udec1-\udec5\uded0-\uded2\uded5-\uded7\udedc-\udedf\udeeb\udeec\udef4-\udefc\udfe0-\udfeb\udff0]|\ud83e[\udd0d\udd0e\udd10-\udd17\udd20-\udd25\udd27-\udd2f\udd3a\udd3c\udd3f-\udd45\udd47-\udd76\udd78-\uddb4\uddb7\uddba\uddbc-\uddcc\uddd0\uddde-\uddff\ude70-\ude7c\ude80-\ude88\ude90-\udebd\udebf-\udec2\udece-\udedb\udee0-\udee8]|[\u23e9-\u23ec\u23f0\u23f3\u267e\u26ce\u2705\u2728\u274c\u274e\u2753-\u2755\u2795-\u2797\u27b0\u27bf\ue50a])|\ufe0f/g;
 
 export const emojiRegex = new RegExp(`(${twemojiRegex.source})`);
diff --git a/packages/frontend/.storybook/mocks.ts b/packages/frontend/.storybook/mocks.ts
index b60755feea..80e5157c5a 100644
--- a/packages/frontend/.storybook/mocks.ts
+++ b/packages/frontend/.storybook/mocks.ts
@@ -25,7 +25,7 @@ export const commonHandlers = [
 	}),
 	rest.get('/twemoji/:codepoints.svg', async (req, res, ctx) => {
 		const { codepoints } = req.params;
-		const value = await fetch(`https://unpkg.com/@discordapp/twemoji@14.1.2/dist/svg/${codepoints}.svg`).then((response) => response.blob());
+		const value = await fetch(`https://unpkg.com/@discordapp/twemoji@15.0.2/dist/svg/${codepoints}.svg`).then((response) => response.blob());
 		return res(ctx.set('Content-Type', 'image/svg+xml'), ctx.body(value));
 	}),
 ];
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index e1647d6a59..523fc281b3 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -17,13 +17,14 @@
 		"lint": "pnpm typecheck && pnpm eslint"
 	},
 	"dependencies": {
-		"@discordapp/twemoji": "14.1.2",
+		"@discordapp/twemoji": "15.0.2",
 		"@github/webauthn-json": "2.1.1",
 		"@rollup/plugin-json": "6.1.0",
 		"@rollup/plugin-replace": "5.0.5",
 		"@rollup/pluginutils": "5.1.0",
 		"@syuilo/aiscript": "0.16.0",
 		"@tabler/icons-webfont": "2.44.0",
+		"@twemoji/parser": "15.0.0",
 		"@vitejs/plugin-vue": "4.5.2",
 		"@vue/compiler-sfc": "3.3.12",
 		"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.0.6",
@@ -65,7 +66,6 @@
 		"tinycolor2": "1.6.0",
 		"tsc-alias": "1.8.8",
 		"tsconfig-paths": "4.2.0",
-		"twemoji-parser": "14.0.0",
 		"typescript": "5.3.3",
 		"uuid": "9.0.1",
 		"v-code-diff": "1.7.2",
diff --git a/packages/frontend/src/emojilist.json b/packages/frontend/src/emojilist.json
index fe1d884ebe..75d5c34d71 100644
--- a/packages/frontend/src/emojilist.json
+++ b/packages/frontend/src/emojilist.json
@@ -103,6 +103,7 @@
 	["🫥", "dotted_line_face", 0],
 	["🫤", "face_with_diagonal_mouth", 0],
 	["🥹", "face_holding_back_tears", 0],
+	["🫨", "shaking_face", 0],
 	["💩", "poop", 0],
 	["😈", "smiling_imp", 0],
 	["👿", "imp", 0],
@@ -132,6 +133,8 @@
 	["✊", "fist", 1],
 	["🤛", "fist_left", 1],
 	["🤜", "fist_right", 1],
+	["🫷", "leftwards_pushing_hand", 1],
+	["🫸", "rightwards_pushing_hand", 1],
 	["✌", "v", 1],
 	["👌", "ok_hand", 1],
 	["✋", "raised_hand", 1],
@@ -453,6 +456,7 @@
 	["🐸", "frog", 2],
 	["🦑", "squid", 2],
 	["🐙", "octopus", 2],
+	["🪼", "jellyfish", 2],
 	["🦐", "shrimp", 2],
 	["🐵", "monkey_face", 2],
 	["🦍", "gorilla", 2],
@@ -466,7 +470,9 @@
 	["🐤", "baby_chick", 2],
 	["🐣", "hatching_chick", 2],
 	["🐥", "hatched_chick", 2],
+	["🪿", "goose", 2],
 	["🦆", "duck", 2],
+	["🐦‍⬛", "black_bird", 2],
 	["🦅", "eagle", 2],
 	["🦉", "owl", 2],
 	["🦇", "bat", 2],
@@ -474,6 +480,7 @@
 	["🐗", "boar", 2],
 	["🐴", "horse", 2],
 	["🦄", "unicorn", 2],
+	["🫎", "moose", 2],
 	["🐝", "honeybee", 2],
 	["🐛", "bug", 2],
 	["🦋", "butterfly", 2],
@@ -516,6 +523,7 @@
 	["🐐", "goat", 2],
 	["🐏", "ram", 2],
 	["🐑", "sheep", 2],
+	["🫏", "donkey", 2],
 	["🐎", "racehorse", 2],
 	["🐖", "pig2", 2],
 	["🐀", "rat", 2],
@@ -546,6 +554,7 @@
 	["🐻‍❄️", "polar_bear", 2],
 	["🦤", "dodo", 2],
 	["🪶", "feather", 2],
+	["🪽", "wing", 2],
 	["🦭", "seal", 2],
 	["🐾", "paw_prints", 2],
 	["🐉", "dragon", 2],
@@ -576,6 +585,7 @@
 	["🌻", "sunflower", 2],
 	["🌹", "rose", 2],
 	["🥀", "wilted_flower", 2],
+	["🪻", "hyacinth", 2],
 	["🌷", "tulip", 2],
 	["🌼", "blossom", 2],
 	["🌸", "cherry_blossom", 2],
@@ -655,6 +665,7 @@
 	["🥝", "kiwi_fruit", 3],
 	["🥭", "mango", 3],
 	["🥑", "avocado", 3],
+	["🫛", "pea_pod", 3],
 	["🥦", "broccoli", 3],
 	["🍅", "tomato", 3],
 	["🍆", "eggplant", 3],
@@ -668,6 +679,7 @@
 	["🌽", "corn", 3],
 	["🥬", "leafy_greens", 3],
 	["🍠", "sweet_potato", 3],
+	["🫚", "ginger_root", 3],
 	["🥜", "peanuts", 3],
 	["🧄", "garlic", 3],
 	["🧅", "onion", 3],
@@ -850,9 +862,11 @@
 	["🎧", "headphones", 4],
 	["🎼", "musical_score", 4],
 	["🎹", "musical_keyboard", 4],
+	["🪇", "maracas", 4],
 	["🥁", "drum", 4],
 	["🎷", "saxophone", 4],
 	["🎺", "trumpet", 4],
+	["🪈", "flute", 4],
 	["🎸", "guitar", 4],
 	["🎻", "violin", 4],
 	["🪕", "banjo", 4],
@@ -1108,6 +1122,7 @@
 	["🩹", "adhesive_bandage", 6],
 	["🩺", "stethoscope", 6],
 	["🪒", "razor", 6],
+	["🪮", "hair_pick", 6],
 	["🩻", "xray", 6],
 	["🩼", "crutch", 6],
 	["🧬", "dna", 6],
@@ -1156,6 +1171,7 @@
 	["🎊", "confetti_ball", 6],
 	["🎉", "tada", 6],
 	["🎎", "dolls", 6],
+	["🪭", "folding_hand_fan", 6],
 	["🎐", "wind_chime", 6],
 	["🎌", "crossed_flags", 6],
 	["🏮", "izakaya_lantern", 6],
@@ -1237,14 +1253,17 @@
 	["🪧", "placard", 6],
 	["💯", "100", 7],
 	["🔢", "1234", 7],
+	["🩷", "pink_heart", 7],
 	["❤️", "heart", 7],
 	["🧡", "orange_heart", 7],
 	["💛", "yellow_heart", 7],
 	["💚", "green_heart", 7],
+	["🩵", "light_blue_heart", 7],
 	["💙", "blue_heart", 7],
 	["💜", "purple_heart", 7],
 	["🤎", "brown_heart", 7],
 	["🖤", "black_heart", 7],
+	["🩶", "grey_heart", 7],
 	["🤍", "white_heart", 7],
 	["💔", "broken_heart", 7],
 	["❣", "heavy_heart_exclamation", 7],
@@ -1263,6 +1282,7 @@
 	["☪", "star_and_crescent", 7],
 	["🕉", "om", 7],
 	["☸", "wheel_of_dharma", 7],
+	["🪯", "khanda", 7],
 	["✡", "star_of_david", 7],
 	["🔯", "six_pointed_star", 7],
 	["🕎", "menorah", 7],
@@ -1358,6 +1378,7 @@
 	["🛃", "customs", 7],
 	["🛄", "baggage_claim", 7],
 	["🛅", "left_luggage", 7],
+	["🛜", "wireless", 7],
 	["♿", "wheelchair", 7],
 	["🚭", "no_smoking", 7],
 	["🚾", "wc", 7],
diff --git a/packages/frontend/src/unicode-emoji-indexes/en-US.json b/packages/frontend/src/unicode-emoji-indexes/en-US.json
index 567125c4c7..ad406dc462 100644
--- a/packages/frontend/src/unicode-emoji-indexes/en-US.json
+++ b/packages/frontend/src/unicode-emoji-indexes/en-US.json
@@ -103,6 +103,7 @@
 	"🫥": ["depressed", "disappear", "hide", "introvert", "invisible", "tensen"],
 	"🫤": ["disappointed", "meh", "skeptical", "unsure"],
 	"🥹": ["angry", "cry", "proud", "resist", "sad"],
+	"🫨": ["earthquake", "face", "shaking", "shock", "vibrate"],
 	"💩": ["hankey", "shitface", "fail", "turd", "shit"],
 	"😈": ["devil", "horns"],
 	"👿": ["devil", "angry", "horns"],
@@ -132,6 +133,8 @@
 	"✊": ["fingers", "hand", "grasp"],
 	"🤛": ["hand", "fistbump"],
 	"🤜": ["hand", "fistbump"],
+	"🫷": ["hand", "high_five", "leftward", "push", "refuse", "stop", "wait"],
+	"🫸": ["hand", "high_five", "push", "refuse", "rightward", "stop", "wait"],
 	"✌": ["fingers", "ohyeah", "hand", "peace", "victory", "two"],
 	"👌": ["fingers", "limbs", "perfect", "ok", "okay"],
 	"✋": ["fingers", "stop", "highfive", "palm", "ban"],
@@ -453,6 +456,7 @@
 	"🐸": ["animal", "nature", "croak", "toad"],
 	"🦑": ["animal", "nature", "ocean", "sea"],
 	"🐙": ["animal", "creature", "ocean", "sea", "nature", "beach"],
+	"🪼": ["animal", "creature", "ocean", "sea", "nature", "beach"],
 	"🦐": ["animal", "ocean", "nature", "seafood"],
 	"🐵": ["animal", "nature", "circus"],
 	"🦍": ["animal", "nature", "circus"],
@@ -466,7 +470,9 @@
 	"🐤": ["animal", "chicken", "bird"],
 	"🐣": ["animal", "chicken", "egg", "born", "baby", "bird"],
 	"🐥": ["animal", "chicken", "baby", "bird"],
+	"🪿": ["animal", "nature", "bird", "fowl", "goose", "honk", "silly"],
 	"🦆": ["animal", "nature", "bird", "mallard"],
+	"🐦‍⬛": ["animal", "nature", "bird", "black", "crow", "raven", "rook"],
 	"🦅": ["animal", "nature", "bird"],
 	"🦉": ["animal", "nature", "bird", "hoot"],
 	"🦇": ["animal", "nature", "blind", "vampire"],
@@ -474,6 +480,7 @@
 	"🐗": ["animal", "nature"],
 	"🐴": ["animal", "brown", "nature"],
 	"🦄": ["animal", "nature", "mystical"],
+	"🫎": ["animal", "nature", "antlers", "elk", "mammal"],
 	"🐝": ["animal", "insect", "nature", "bug", "spring", "honey"],
 	"🐛": ["animal", "insect", "nature", "worm"],
 	"🦋": ["animal", "insect", "nature", "caterpillar"],
@@ -516,6 +523,7 @@
 	"🐐": ["animal", "nature"],
 	"🐏": ["animal", "sheep", "nature"],
 	"🐑": ["animal", "nature", "wool", "shipit"],
+	"🫏": ["animal", "ass", "burro", "mammal", "mule", "stubborn"],
 	"🐎": ["animal", "gamble", "luck"],
 	"🐖": ["animal", "nature"],
 	"🐀": ["animal", "mouse", "rodent"],
@@ -546,6 +554,7 @@
 	"🐻‍❄️": ["animal", "nature"],
 	"🦤": ["animal", "nature"],
 	"🪶": ["animal", "nature"],
+	"🪽": ["angelic", "aviation", "bird", "flying", "mythology"],
 	"🦭": ["animal", "nature"],
 	"🐾": ["animal", "tracking", "footprints", "dog", "cat", "pet", "feet"],
 	"🐉": ["animal", "myth", "nature", "chinese", "green"],
@@ -576,6 +585,7 @@
 	"🌻": ["nature", "plant", "fall"],
 	"🌹": ["flowers", "valentines", "love", "spring"],
 	"🥀": ["plant", "nature", "flower"],
+	"🪻": ["plant", "nature", "flower", "bluebonnet", "lavender", "lupine", "snapdragon"],
 	"🌷": ["flowers", "plant", "nature", "summer", "spring"],
 	"🌼": ["nature", "flowers", "yellow"],
 	"🌸": ["nature", "plant", "spring", "flower"],
@@ -655,6 +665,7 @@
 	"🥝": ["fruit", "food"],
 	"🥭": ["fruit", "food", "tropical"],
 	"🥑": ["fruit", "food"],
+	"🫛": ["beans", "edamame", "legume", "pea", "pod", "vegetable", "food"],
 	"🥦": ["fruit", "food", "vegetable"],
 	"🍅": ["fruit", "vegetable", "nature", "food"],
 	"🍆": ["vegetable", "nature", "food", "aubergine"],
@@ -668,6 +679,7 @@
 	"🌽": ["food", "vegetable", "plant"],
 	"🥬": ["food", "vegetable", "plant", "bok choy", "cabbage", "kale", "lettuce"],
 	"🍠": ["food", "nature"],
+	"🫚": ["food", "nature", "beer", "root", "spice"],
 	"🥜": ["food", "nut"],
 	"🧄": ["food"],
 	"🧅": ["food"],
@@ -850,9 +862,11 @@
 	"🎧": ["music", "score", "gadgets"],
 	"🎼": ["treble", "clef", "compose"],
 	"🎹": ["piano", "instrument", "compose"],
+	"🪇": ["instrument", "music", "percussion", "rattle", "shake"],
 	"🥁": ["music", "instrument", "drumsticks", "snare"],
 	"🎷": ["music", "instrument", "jazz", "blues"],
 	"🎺": ["music", "brass"],
+	"🪈": ["music", "fife", "pipe", "recorder", "woodwind"],
 	"🎸": ["music", "instrument"],
 	"🎻": ["music", "instrument", "orchestra", "symphony"],
 	"🪕": ["music", "instrument"],
@@ -1108,6 +1122,7 @@
 	"🩹": ["health", "hospital", "medicine", "needle", "doctor", "nurse"],
 	"🩺": ["health", "hospital", "medicine", "needle", "doctor", "nurse"],
 	"🪒": ["health"],
+	"🪮": ["afro", "comb", "hair", "pick"],
 	"🩻": [],
 	"🩼": [],
 	"🧬": ["biologist", "genetics", "life"],
@@ -1156,6 +1171,7 @@
 	"🎊": ["festival", "party", "birthday", "circus"],
 	"🎉": ["party", "congratulations", "birthday", "magic", "circus", "celebration"],
 	"🎎": ["japanese", "toy", "kimono"],
+	"🪭": ["cooling", "dance", "fan", "flutter", "hot", "shy"],
 	"🎐": ["nature", "ding", "spring", "bell"],
 	"🎌": ["japanese", "nation", "country", "border"],
 	"🏮": ["light", "paper", "halloween", "spooky"],
@@ -1237,14 +1253,17 @@
 	"🪧": [],
 	"💯": ["score", "perfect", "numbers", "century", "exam", "quiz", "test", "pass", "hundred"],
 	"🔢": ["numbers", "blue-square"],
+	"🩷": ["love", "like", "affection", "valentines"],
 	"❤️": ["love", "like", "affection", "valentines"],
 	"🧡": ["love", "like", "affection", "valentines"],
 	"💛": ["love", "like", "affection", "valentines"],
 	"💚": ["love", "like", "affection", "valentines"],
+	"🩵": ["love", "like", "affection", "valentines"],
 	"💙": ["love", "like", "affection", "valentines"],
 	"💜": ["love", "like", "affection", "valentines"],
 	"🤎": ["love", "like", "affection", "valentines"],
 	"🖤": ["love", "like", "affection", "valentines"],
+	"🩶": ["love", "like", "affection", "valentines"],
 	"🤍": ["love", "like", "affection", "valentines"],
 	"💔": ["sad", "sorry", "break", "heart", "heartbreak"],
 	"❣": ["decoration", "love"],
@@ -1263,6 +1282,7 @@
 	"☪": ["islam"],
 	"🕉": ["hinduism", "buddhism", "sikhism", "jainism"],
 	"☸": ["hinduism", "buddhism", "sikhism", "jainism"],
+	"🪯": ["religion", "sikh"],
 	"✡": ["judaism"],
 	"🔯": ["purple-square", "religion", "jewish", "hexagram"],
 	"🕎": ["hanukkah", "candles", "jewish"],
@@ -1358,6 +1378,7 @@
 	"🛃": ["passport", "border", "blue-square"],
 	"🛄": ["blue-square", "airport", "transport"],
 	"🛅": ["blue-square", "travel"],
+	"🛜": ["blue-square", "computer", "internet", "network"],
 	"♿": ["blue-square", "disabled", "a11y", "accessibility"],
 	"🚭": ["cigarette", "blue-square", "smell", "smoke"],
 	"🚾": ["toilet", "restroom", "blue-square"],
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a38b644b48..278109f12d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -75,8 +75,8 @@ importers:
         specifier: 5.10.2
         version: 5.10.2
       '@discordapp/twemoji':
-        specifier: 14.1.2
-        version: 14.1.2
+        specifier: 15.0.2
+        version: 15.0.2
       '@fastify/accepts':
         specifier: 4.3.0
         version: 4.3.0
@@ -128,6 +128,9 @@ importers:
       '@swc/core':
         specifier: 1.3.100
         version: 1.3.100
+      '@twemoji/parser':
+        specifier: 15.0.0
+        version: 15.0.0
       accepts:
         specifier: 1.3.8
         version: 1.3.8
@@ -377,9 +380,6 @@ importers:
       tsconfig-paths:
         specifier: 4.2.0
         version: 4.2.0
-      twemoji-parser:
-        specifier: 14.0.0
-        version: 14.0.0
       typeorm:
         specifier: 0.3.17
         version: 0.3.17(ioredis@5.3.2)(pg@8.11.3)
@@ -653,8 +653,8 @@ importers:
   packages/frontend:
     dependencies:
       '@discordapp/twemoji':
-        specifier: 14.1.2
-        version: 14.1.2
+        specifier: 15.0.2
+        version: 15.0.2
       '@github/webauthn-json':
         specifier: 2.1.1
         version: 2.1.1
@@ -673,6 +673,9 @@ importers:
       '@tabler/icons-webfont':
         specifier: 2.44.0
         version: 2.44.0
+      '@twemoji/parser':
+        specifier: 15.0.0
+        version: 15.0.0
       '@vitejs/plugin-vue':
         specifier: 4.5.2
         version: 4.5.2(vite@5.0.10)(vue@3.3.12)
@@ -796,9 +799,6 @@ importers:
       tsconfig-paths:
         specifier: 4.2.0
         version: 4.2.0
-      twemoji-parser:
-        specifier: 14.0.0
-        version: 14.0.0
       typescript:
         specifier: 5.3.3
         version: 5.3.3
@@ -3598,12 +3598,12 @@ packages:
       - web-streams-polyfill
     dev: false
 
-  /@discordapp/twemoji@14.1.2:
-    resolution: {integrity: sha512-Rkuu30/biwy8Zss0r5qfFvQzoQGPTHXzA7Y/MPMkCQqFd0WskoYvjfJRTz0iuZwUpMfrgbM8eakSsptCxmOqog==}
+  /@discordapp/twemoji@15.0.2:
+    resolution: {integrity: sha512-SrWKcv3SrGfrLQ/vfUnA+bAG73Q6Yjys01UuoY5SzUlc9iS03amQ6DxLhzVsjW/aTdgiMQdUatLidD+YPfYMCw==}
     dependencies:
+      '@twemoji/parser': 15.0.0
       fs-extra: 8.1.0
       jsonfile: 5.0.0
-      twemoji-parser: 14.0.0
       universalify: 0.1.2
     dev: false
 

From 6b39ba918fe1a3cab4d5146babbe4576ecabb017 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Mon, 18 Dec 2023 14:52:27 +0900
Subject: [PATCH 264/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e683d9cc79..cf3b56e602 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -58,6 +58,7 @@
 - Enhance: ユーザー名、プロフィール、お知らせ、ページの編集画面でMFMや絵文字のオートコンプリートが使用できるように
 - Enhance: プロフィール、お知らせの編集画面でMFMのプレビューを表示できるように
 - Enhance: 絵文字の詳細ページに記載される情報を追加
+- Enhance: Unicode 15.0のサポート
 - Enhance: コードブロックのハイライト機能を利用するには言語を明示的に指定させるように
 	- MFMでコードブロックを利用する際に意図しないハイライトが起こらないようになりました
 	- 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります  

From 38b82b85829bd198b7b23ae1a4aacb073fe11ff1 Mon Sep 17 00:00:00 2001
From: 1STEP621 <86859447+1STEP621@users.noreply.github.com>
Date: Mon, 18 Dec 2023 14:59:55 +0900
Subject: [PATCH 265/435] =?UTF-8?q?Enhance(frontend):=20Shift+Tab=E3=81=A7?=
 =?UTF-8?q?=E5=89=8D=E3=81=AE=E8=A3=9C=E5=AE=8C=E5=80=99=E8=A3=9C=E3=81=8C?=
 =?UTF-8?q?=E9=81=B8=E6=8A=9E=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#12704)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Shift+Tabで前の補完候補が選択できるように

* update CHANGELOG.md
---
 CHANGELOG.md                                      |  1 +
 .../frontend/src/components/MkAutocomplete.vue    | 15 ++++++++++++++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cf3b56e602..0d6df0a167 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -63,6 +63,7 @@
 	- MFMでコードブロックを利用する際に意図しないハイライトが起こらないようになりました
 	- 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります  
 	(例: ` ```js ` → Javascript, ` ```ais ` → AiScript)
+-	Enhance: 絵文字などのオートコンプリートでShift+Tabを押すと前の候補を選択できるように
 - Fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index c1fcbd7ac1..494d120a93 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -359,12 +359,25 @@ function onKeydown(event: KeyboardEvent) {
 			}
 			break;
 
-		case 'Tab':
 		case 'ArrowDown':
 			cancel();
 			selectNext();
 			break;
 
+		case 'Tab':
+			if (event.shiftKey) {
+				if (select.value !== -1) {
+					cancel();
+					selectPrev();
+				} else {
+					props.close();
+				}
+			} else {
+				cancel();
+				selectNext();
+			}
+			break;
+
 		default:
 			event.stopPropagation();
 			props.textarea.focus();

From f6ff3b1f1a7047a29337575c08849be99ac15a4b Mon Sep 17 00:00:00 2001
From: 1STEP621 <86859447+1STEP621@users.noreply.github.com>
Date: Mon, 18 Dec 2023 19:49:19 +0900
Subject: [PATCH 266/435] =?UTF-8?q?Fix:=20Renote=E3=81=AE=E5=88=A4?=
 =?UTF-8?q?=E5=AE=9A=E3=81=8C=E9=96=93=E9=81=95=E3=81=A3=E3=81=A6=E3=81=84?=
 =?UTF-8?q?=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12706)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* RNと引用RNの判定が間違っているのを修正

* remove dump.rdb

* update CHANGELOG.md

* lint fix
---
 CHANGELOG.md                                    |  2 ++
 packages/backend/src/core/NoteCreateService.ts  | 11 ++++++++---
 packages/frontend/src/components/MkCwButton.vue |  2 ++
 packages/frontend/src/components/MkNote.vue     |  3 ++-
 4 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d6df0a167..6a99a6ca2c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -76,6 +76,8 @@
 - Fix: ノート中の絵文字をタップして「リアクションする」からリアクションした際にリアクションサウンドが鳴らない不具合を修正
 - Fix: ノート中のリアクションの表示を微調整 #12650
 - Fix: AiScriptの`readline`が不正な値を返すことがある問題を修正
+- Fix: 投票のみ/画像のみの引用RNが、通知欄でただのRNとして判定されるバグを修正
+- Fix: CWをつけて引用RNしても、普通のRNとして扱われてしまうバグを修正しました。
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 45dfbb87aa..9fe965b139 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -293,7 +293,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 		}
 
 		// Check blocking
-		if (data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0)) {
+		if (data.renote && this.isQuote(data)) {
 			if (data.renote.userHost === null) {
 				if (data.renote.userId !== user.id) {
 					const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id);
@@ -622,7 +622,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 
 			// If it is renote
 			if (data.renote) {
-				const type = data.text ? 'quote' : 'renote';
+				const type = this.isQuote(data) ? 'quote' : 'renote';
 
 				// Notify
 				if (data.renote.userHost === null) {
@@ -729,6 +729,11 @@ export class NoteCreateService implements OnApplicationShutdown {
 		return false;
 	}
 
+	@bindThis
+	private isQuote(note: Option): boolean {
+		return !!note.text || !!note.cw || !!note.files || !!note.poll;
+	}
+
 	@bindThis
 	private incRenoteCount(renote: MiNote) {
 		this.notesRepository.createQueryBuilder().update()
@@ -794,7 +799,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 	private async renderNoteOrRenoteActivity(data: Option, note: MiNote) {
 		if (data.localOnly) return null;
 
-		const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0)
+		const content = data.renote && this.isQuote(data)
 			? this.apRendererService.renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note)
 			: this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note);
 
diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue
index 70b7bc8295..a25193a57c 100644
--- a/packages/frontend/src/components/MkCwButton.vue
+++ b/packages/frontend/src/components/MkCwButton.vue
@@ -17,6 +17,7 @@ import MkButton from '@/components/MkButton.vue';
 const props = defineProps<{
 	modelValue: boolean;
 	text: string | null;
+	renote: Misskey.entities.Note | null;
 	files: Misskey.entities.DriveFile[];
 	poll?: {
 		expiresAt: string | null;
@@ -41,6 +42,7 @@ const emit = defineEmits<{
 const label = computed(() => {
 	return concat([
 		props.text ? [i18n.t('_cw.chars', { count: props.text.length })] : [],
+		props.renote != null ? [i18n.ts.quote] : [],
 		props.files.length !== 0 ? [i18n.t('_cw.files', { count: props.files.length })] : [],
 		props.poll != null ? [i18n.ts.poll] : [],
 	] as string[][]).join(' / ');
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index e723198a17..609b381598 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div style="container-type: inline-size;">
 				<p v-if="appearNote.cw != null" :class="$style.cw">
 					<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
-					<MkCwButton v-model="showContent" :text="appearNote.text" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;"/>
+					<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;"/>
 				</p>
 				<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]">
 					<div :class="$style.text">
@@ -229,6 +229,7 @@ if (noteViewInterruptors.length > 0) {
 const isRenote = (
 	note.value.renote != null &&
 	note.value.text == null &&
+	note.value.cw == null &&
 	note.value.fileIds.length === 0 &&
 	note.value.poll == null
 );

From 4e2d8029678951ca3b8b9b40e62901b0c67618ed Mon Sep 17 00:00:00 2001
From: zawa-ch <lunatic.zawa.ch@gmail.com>
Date: Mon, 18 Dec 2023 20:59:20 +0900
Subject: [PATCH 267/435] =?UTF-8?q?enhance:=20=E2=80=9C=E3=81=A4=E3=81=AA?=
 =?UTF-8?q?=E3=81=8C=E3=82=8A=E3=81=AE=E5=85=AC=E9=96=8B=E7=AF=84=E5=9B=B2?=
 =?UTF-8?q?=E2=80=9D=E3=81=8C=E3=83=95=E3=82=A9=E3=83=AD=E3=83=BC=E3=83=BB?=
 =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=AD=E3=83=AF=E3=83=BC=E5=80=8B=E5=88=A5?=
 =?UTF-8?q?=E8=A8=AD=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?=
 =?UTF-8?q?=E3=81=AB=20(#12702)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Enhance: “つながりの公開範囲”がフォロー・フォロワー個別設定できるように (#12072)

* refactor: crowdin 編集部分のコミットを打ち消し

https://github.com/misskey-dev/misskey/pull/12702#issuecomment-1859417158

* refactor: オブジェクトの名前修正

https://github.com/misskey-dev/misskey/pull/12702#issuecomment-1859417158

* fix: 設定項目の説明を削除

名称が具体的になって必要なくなったため
https://github.com/misskey-dev/misskey/pull/12702#discussion_r1429932463
---
 CHANGELOG.md                                  |   1 +
 locales/index.d.ts                            |   4 +-
 locales/ja-JP.yml                             |   4 +-
 .../migration/1702718871541-ffVisibility.js   |  35 ++
 .../src/core/entities/UserEntityService.ts    |  11 +-
 packages/backend/src/models/UserProfile.ts    |  12 +-
 .../backend/src/models/json-schema/user.ts    |   7 +-
 .../src/server/ActivityPubServerService.ts    |   8 +-
 .../src/server/api/endpoints/i/update.ts      |   6 +-
 .../server/api/endpoints/users/followers.ts   |   4 +-
 .../server/api/endpoints/users/following.ts   |   4 +-
 .../backend/src/server/web/FeedService.ts     |   2 +-
 packages/backend/src/types.ts                 |   3 +-
 packages/backend/test/e2e/ff-visibility.ts    | 543 +++++++++++++++++-
 packages/backend/test/e2e/users.ts            |  15 +-
 packages/frontend/.storybook/fakes.ts         |   3 +-
 .../frontend/src/components/MkUserInfo.vue    |   6 +-
 .../frontend/src/components/MkUserPopup.vue   |   6 +-
 .../frontend/src/pages/settings/privacy.vue   |  18 +-
 packages/frontend/src/pages/user/home.vue     |   6 +-
 .../frontend/src/scripts/isFfVisibleForMe.ts  |  14 +-
 packages/misskey-js/src/consts.ts             |   4 +-
 packages/misskey-js/src/index.ts              |   3 +-
 23 files changed, 648 insertions(+), 71 deletions(-)
 create mode 100644 packages/backend/migration/1702718871541-ffVisibility.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6a99a6ca2c..dd8c492782 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,7 @@
 - Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加
 - Enhance: アイコンデコレーションを複数設定できるように
 - Enhance: アイコンデコレーションの位置を微調整できるように
+- Enhance: つながりの公開範囲をフォロー/フォロワーで個別に設定可能に #12072
 - Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
 
 ### Client
diff --git a/locales/index.d.ts b/locales/index.d.ts
index cd15bd968f..25a16d4a4d 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -884,8 +884,8 @@ export interface Locale {
     "classic": string;
     "muteThread": string;
     "unmuteThread": string;
-    "ffVisibility": string;
-    "ffVisibilityDescription": string;
+    "followingVisibility": string;
+    "followersVisibility": string;
     "continueThread": string;
     "deleteAccountConfirm": string;
     "incorrectPassword": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 5537db9d56..308b7ae67d 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -881,8 +881,8 @@ makeReactionsPublicDescription: "あなたがしたリアクション一覧を
 classic: "クラシック"
 muteThread: "スレッドをミュート"
 unmuteThread: "スレッドのミュートを解除"
-ffVisibility: "つながりの公開範囲"
-ffVisibilityDescription: "自分のフォロー/フォロワー情報の公開範囲を設定できます。"
+followingVisibility: "フォローの公開範囲"
+followersVisibility: "フォロワーの公開範囲"
 continueThread: "さらにスレッドを見る"
 deleteAccountConfirm: "アカウントが削除されます。よろしいですか?"
 incorrectPassword: "パスワードが間違っています。"
diff --git a/packages/backend/migration/1702718871541-ffVisibility.js b/packages/backend/migration/1702718871541-ffVisibility.js
new file mode 100644
index 0000000000..24b1873134
--- /dev/null
+++ b/packages/backend/migration/1702718871541-ffVisibility.js
@@ -0,0 +1,35 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class ffVisibility1702718871541 {
+	constructor() {
+			this.name = 'ffVisibility1702718871541';
+	}
+	async up(queryRunner) {
+		await queryRunner.query(`CREATE TYPE "public"."user_profile_followingvisibility_enum" AS ENUM('public', 'followers', 'private')`);
+		await queryRunner.query(`CREATE CAST ("public"."user_profile_ffvisibility_enum" AS "public"."user_profile_followingvisibility_enum") WITH INOUT AS ASSIGNMENT`);
+		await queryRunner.query(`CREATE TYPE "public"."user_profile_followersVisibility_enum" AS ENUM('public', 'followers', 'private')`);
+		await queryRunner.query(`CREATE CAST ("public"."user_profile_ffvisibility_enum" AS "public"."user_profile_followersVisibility_enum") WITH INOUT AS ASSIGNMENT`);
+		await queryRunner.query(`ALTER TABLE "user_profile" ADD "followingVisibility" "public"."user_profile_followingvisibility_enum" NOT NULL DEFAULT 'public'`);
+		await queryRunner.query(`ALTER TABLE "user_profile" ADD "followersVisibility" "public"."user_profile_followersVisibility_enum" NOT NULL DEFAULT 'public'`);
+		await queryRunner.query(`UPDATE "user_profile" SET "followingVisibility" = "ffVisibility"`);
+		await queryRunner.query(`UPDATE "user_profile" SET "followersVisibility" = "ffVisibility"`);
+		await queryRunner.query(`DROP CAST ("public"."user_profile_ffvisibility_enum" AS "public"."user_profile_followersVisibility_enum")`);
+		await queryRunner.query(`DROP CAST ("public"."user_profile_ffvisibility_enum" AS "public"."user_profile_followingvisibility_enum")`);
+		await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "ffVisibility"`);
+		await queryRunner.query(`DROP TYPE "public"."user_profile_ffvisibility_enum"`);
+	}
+	async down(queryRunner) {
+		await queryRunner.query(`CREATE TYPE "public"."user_profile_ffvisibility_enum" AS ENUM('public', 'followers', 'private')`);
+		await queryRunner.query(`ALTER TABLE "user_profile" ADD "ffVisibility" "public"."user_profile_ffvisibility_enum" NOT NULL DEFAULT 'public'`);
+		await queryRunner.query(`CREATE CAST ("public"."user_profile_followingvisibility_enum" AS "public"."user_profile_ffvisibility_enum") WITH INOUT AS ASSIGNMENT`);
+		await queryRunner.query(`UPDATE "user_profile" SET ffVisibility = "user_profile"."followingVisibility"`);
+		await queryRunner.query(`DROP CAST ("public"."user_profile_followingvisibility_enum" AS "public"."user_profile_ffvisibility_enum")`);
+		await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "followersVisibility"`);
+		await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "followingVisibility"`);
+		await queryRunner.query(`DROP TYPE "public"."user_profile_followersVisibility_enum"`);
+		await queryRunner.query(`DROP TYPE "public"."user_profile_followingvisibility_enum"`);
+	}
+}
diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts
index fb7aa0c244..ef815a388a 100644
--- a/packages/backend/src/core/entities/UserEntityService.ts
+++ b/packages/backend/src/core/entities/UserEntityService.ts
@@ -332,13 +332,13 @@ export class UserEntityService implements OnModuleInit {
 		const profile = opts.detail ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) : null;
 
 		const followingCount = profile == null ? null :
-			(profile.ffVisibility === 'public') || isMe ? user.followingCount :
-			(profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followingCount :
+			(profile.followingVisibility === 'public') || isMe ? user.followingCount :
+			(profile.followingVisibility === 'followers') && (relation && relation.isFollowing) ? user.followingCount :
 			null;
 
 		const followersCount = profile == null ? null :
-			(profile.ffVisibility === 'public') || isMe ? user.followersCount :
-			(profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
+			(profile.followersVisibility === 'public') || isMe ? user.followersCount :
+			(profile.followersVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
 			null;
 
 		const isModerator = isMe && opts.detail ? this.roleService.isModerator(user) : null;
@@ -417,7 +417,8 @@ export class UserEntityService implements OnModuleInit {
 				pinnedPageId: profile!.pinnedPageId,
 				pinnedPage: profile!.pinnedPageId ? this.pageEntityService.pack(profile!.pinnedPageId, me) : null,
 				publicReactions: profile!.publicReactions,
-				ffVisibility: profile!.ffVisibility,
+				followersVisibility: profile!.followersVisibility,
+				followingVisibility: profile!.followingVisibility,
 				twoFactorEnabled: profile!.twoFactorEnabled,
 				usePasswordLessLogin: profile!.usePasswordLessLogin,
 				securityKeys: profile!.twoFactorEnabled
diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts
index 6659a01412..328dbeaa1c 100644
--- a/packages/backend/src/models/UserProfile.ts
+++ b/packages/backend/src/models/UserProfile.ts
@@ -4,7 +4,7 @@
  */
 
 import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
-import { obsoleteNotificationTypes, ffVisibility, notificationTypes } from '@/types.js';
+import { obsoleteNotificationTypes, followingVisibilities, followersVisibilities, notificationTypes } from '@/types.js';
 import { id } from './util/id.js';
 import { MiUser } from './User.js';
 import { MiPage } from './Page.js';
@@ -94,10 +94,16 @@ export class MiUserProfile {
 	public publicReactions: boolean;
 
 	@Column('enum', {
-		enum: ffVisibility,
+		enum: followingVisibilities,
 		default: 'public',
 	})
-	public ffVisibility: typeof ffVisibility[number];
+	public followingVisibility: typeof followingVisibilities[number];
+
+	@Column('enum', {
+		enum: followersVisibilities,
+		default: 'public',
+	})
+	public followersVisibility: typeof followersVisibilities[number];
 
 	@Column('varchar', {
 		length: 128, nullable: true,
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index 7a3ca58269..1b86b1bf10 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -311,7 +311,12 @@ export const packedUserDetailedNotMeOnlySchema = {
 			type: 'boolean',
 			nullable: false, optional: false,
 		},
-		ffVisibility: {
+		followingVisibility: {
+			type: 'string',
+			nullable: false, optional: false,
+			enum: ['public', 'followers', 'private'],
+		},
+		followersVisibility: {
 			type: 'string',
 			nullable: false, optional: false,
 			enum: ['public', 'followers', 'private'],
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 2bc7e1136a..68e426b5bc 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -195,11 +195,11 @@ export class ActivityPubServerService {
 		//#region Check ff visibility
 		const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
 
-		if (profile.ffVisibility === 'private') {
+		if (profile.followersVisibility === 'private') {
 			reply.code(403);
 			reply.header('Cache-Control', 'public, max-age=30');
 			return;
-		} else if (profile.ffVisibility === 'followers') {
+		} else if (profile.followersVisibility === 'followers') {
 			reply.code(403);
 			reply.header('Cache-Control', 'public, max-age=30');
 			return;
@@ -287,11 +287,11 @@ export class ActivityPubServerService {
 		//#region Check ff visibility
 		const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
 
-		if (profile.ffVisibility === 'private') {
+		if (profile.followingVisibility === 'private') {
 			reply.code(403);
 			reply.header('Cache-Control', 'public, max-age=30');
 			return;
-		} else if (profile.ffVisibility === 'followers') {
+		} else if (profile.followingVisibility === 'followers') {
 			reply.code(403);
 			reply.header('Cache-Control', 'public, max-age=30');
 			return;
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index a56f50115b..eed3082258 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -176,7 +176,8 @@ export const paramDef = {
 		receiveAnnouncementEmail: { type: 'boolean' },
 		alwaysMarkNsfw: { type: 'boolean' },
 		autoSensitive: { type: 'boolean' },
-		ffVisibility: { type: 'string', enum: ['public', 'followers', 'private'] },
+		followingVisibility: { type: 'string', enum: ['public', 'followers', 'private'] },
+		followersVisibility: { type: 'string', enum: ['public', 'followers', 'private'] },
 		pinnedPageId: { type: 'string', format: 'misskey:id', nullable: true },
 		mutedWords: muteWords,
 		hardMutedWords: muteWords,
@@ -241,7 +242,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			if (ps.lang !== undefined) profileUpdates.lang = ps.lang;
 			if (ps.location !== undefined) profileUpdates.location = ps.location;
 			if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
-			if (ps.ffVisibility !== undefined) profileUpdates.ffVisibility = ps.ffVisibility;
+			if (ps.followingVisibility !== undefined) profileUpdates.followingVisibility = ps.followingVisibility;
+			if (ps.followersVisibility !== undefined) profileUpdates.followersVisibility = ps.followersVisibility;
 
 			function checkMuteWordCount(mutedWords: (string[] | string)[], limit: number) {
 				// TODO: ちゃんと数える
diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts
index b22fd2ff7a..5706e46b96 100644
--- a/packages/backend/src/server/api/endpoints/users/followers.ts
+++ b/packages/backend/src/server/api/endpoints/users/followers.ts
@@ -93,11 +93,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
 
-			if (profile.ffVisibility === 'private') {
+			if (profile.followersVisibility === 'private') {
 				if (me == null || (me.id !== user.id)) {
 					throw new ApiError(meta.errors.forbidden);
 				}
-			} else if (profile.ffVisibility === 'followers') {
+			} else if (profile.followersVisibility === 'followers') {
 				if (me == null) {
 					throw new ApiError(meta.errors.forbidden);
 				} else if (me.id !== user.id) {
diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts
index ead7ba8c40..794fb04f10 100644
--- a/packages/backend/src/server/api/endpoints/users/following.ts
+++ b/packages/backend/src/server/api/endpoints/users/following.ts
@@ -101,11 +101,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });
 
-			if (profile.ffVisibility === 'private') {
+			if (profile.followingVisibility === 'private') {
 				if (me == null || (me.id !== user.id)) {
 					throw new ApiError(meta.errors.forbidden);
 				}
-			} else if (profile.ffVisibility === 'followers') {
+			} else if (profile.followingVisibility === 'followers') {
 				if (me == null) {
 					throw new ApiError(meta.errors.forbidden);
 				} else if (me.id !== user.id) {
diff --git a/packages/backend/src/server/web/FeedService.ts b/packages/backend/src/server/web/FeedService.ts
index dd4304e6ef..dfda85aac9 100644
--- a/packages/backend/src/server/web/FeedService.ts
+++ b/packages/backend/src/server/web/FeedService.ts
@@ -60,7 +60,7 @@ export class FeedService {
 			title: `${author.name} (@${user.username}@${this.config.host})`,
 			updated: notes.length !== 0 ? this.idService.parse(notes[0].id).date : undefined,
 			generator: 'Misskey',
-			description: `${user.notesCount} Notes, ${profile.ffVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.ffVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`,
+			description: `${user.notesCount} Notes, ${profile.followingVisibility === 'public' ? user.followingCount : '?'} Following, ${profile.followersVisibility === 'public' ? user.followersCount : '?'} Followers${profile.description ? ` · ${profile.description}` : ''}`,
 			link: author.link,
 			image: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user),
 			feedLinks: {
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index 1fb3d6a6ce..e085407de0 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -25,7 +25,8 @@ 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 followingVisibilities = ['public', 'followers', 'private'] as const;
+export const followersVisibilities = ['public', 'followers', 'private'] as const;
 
 export const moderationLogTypes = [
 	'updateServerSettings',
diff --git a/packages/backend/test/e2e/ff-visibility.ts b/packages/backend/test/e2e/ff-visibility.ts
index 7841e057bf..1fbd45c741 100644
--- a/packages/backend/test/e2e/ff-visibility.ts
+++ b/packages/backend/test/e2e/ff-visibility.ts
@@ -26,9 +26,10 @@ describe('FF visibility', () => {
 		await app.close();
 	});
 
-	test('ffVisibility が public なユーザーのフォロー/フォロワーを誰でも見れる', async () => {
+	test('followingVisibility, followersVisibility がともに public なユーザーのフォロー/フォロワーを誰でも見れる', async () => {
 		await api('/i/update', {
-			ffVisibility: 'public',
+			followingVisibility: 'public',
+			followersVisibility: 'public',
 		}, alice);
 
 		const followingRes = await api('/users/following', {
@@ -44,9 +45,88 @@ describe('FF visibility', () => {
 		assert.strictEqual(Array.isArray(followersRes.body), true);
 	});
 
-	test('ffVisibility が followers なユーザーのフォロー/フォロワーを自分で見れる', async () => {
+	test('followingVisibility が public であれば followersVisibility の設定に関わらずユーザーのフォローを誰でも見れる', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'public',
+				followersVisibility: 'public',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'public',
+				followersVisibility: 'followers',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'public',
+				followersVisibility: 'private',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+	});
+
+	test('followersVisibility が public であれば followingVisibility の設定に関わらずユーザーのフォロワーを誰でも見れる', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'public',
+				followersVisibility: 'public',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'public',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'public',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+	});
+
+	test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーを自分で見れる', async () => {
 		await api('/i/update', {
-			ffVisibility: 'followers',
+			followingVisibility: 'followers',
+			followersVisibility: 'followers',
 		}, alice);
 
 		const followingRes = await api('/users/following', {
@@ -62,9 +142,88 @@ describe('FF visibility', () => {
 		assert.strictEqual(Array.isArray(followersRes.body), true);
 	});
 
-	test('ffVisibility が followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => {
+	test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらず自分で見れる', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'public',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'followers',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'private',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+	});
+
+	test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらず自分で見れる', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'public',
+				followersVisibility: 'followers',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'followers',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'followers',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+	});
+
+	test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => {
 		await api('/i/update', {
-			ffVisibility: 'followers',
+			followingVisibility: 'followers',
+			followersVisibility: 'followers',
 		}, alice);
 
 		const followingRes = await api('/users/following', {
@@ -78,9 +237,82 @@ describe('FF visibility', () => {
 		assert.strictEqual(followersRes.status, 400);
 	});
 
-	test('ffVisibility が followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => {
+	test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらず非フォロワーが見れない', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'public',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 400);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'followers',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 400);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'private',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 400);
+		}
+	});
+
+	test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらず非フォロワーが見れない', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'public',
+				followersVisibility: 'followers',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 400);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'followers',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 400);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'followers',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 400);
+		}
+	});
+
+	test('followingVisibility, followersVisibility がともに followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => {
 		await api('/i/update', {
-			ffVisibility: 'followers',
+			followingVisibility: 'followers',
+			followersVisibility: 'followers',
 		}, alice);
 
 		await api('/following/create', {
@@ -100,9 +332,106 @@ describe('FF visibility', () => {
 		assert.strictEqual(Array.isArray(followersRes.body), true);
 	});
 
-	test('ffVisibility が private なユーザーのフォロー/フォロワーを自分で見れる', async () => {
+	test('followingVisibility が followers なユーザーのフォローを followersVisibility の設定に関わらずフォロワーが見れる', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'public',
+			}, alice);
+			await api('/following/create', {
+				userId: alice.id,
+			}, bob);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'followers',
+			}, alice);
+			await api('/following/create', {
+				userId: alice.id,
+			}, bob);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'private',
+			}, alice);
+			await api('/following/create', {
+				userId: alice.id,
+			}, bob);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+	});
+
+	test('followersVisibility が followers なユーザーのフォロワーを followingVisibility の設定に関わらずフォロワーが見れる', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'public',
+				followersVisibility: 'followers',
+			}, alice);
+			await api('/following/create', {
+				userId: alice.id,
+			}, bob);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'followers',
+			}, alice);
+			await api('/following/create', {
+				userId: alice.id,
+			}, bob);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'followers',
+			}, alice);
+			await api('/following/create', {
+				userId: alice.id,
+			}, bob);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+	});
+
+	test('followingVisibility, followersVisibility がともに private なユーザーのフォロー/フォロワーを自分で見れる', async () => {
 		await api('/i/update', {
-			ffVisibility: 'private',
+			followingVisibility: 'private',
+			followersVisibility: 'private',
 		}, alice);
 
 		const followingRes = await api('/users/following', {
@@ -118,9 +447,88 @@ describe('FF visibility', () => {
 		assert.strictEqual(Array.isArray(followersRes.body), true);
 	});
 
-	test('ffVisibility が private なユーザーのフォロー/フォロワーを他人が見れない', async () => {
+	test('followingVisibility が private なユーザーのフォローを followersVisibility の設定に関わらず自分で見れる', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'public',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'followers',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'private',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followingRes.status, 200);
+			assert.strictEqual(Array.isArray(followingRes.body), true);
+		}
+	});
+
+	test('followersVisibility が private なユーザーのフォロワーを followingVisibility の設定に関わらず自分で見れる', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'public',
+				followersVisibility: 'private',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'private',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'private',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, alice);
+			assert.strictEqual(followersRes.status, 200);
+			assert.strictEqual(Array.isArray(followersRes.body), true);
+		}
+	});
+
+	test('followingVisibility, followersVisibility がともに private なユーザーのフォロー/フォロワーを他人が見れない', async () => {
 		await api('/i/update', {
-			ffVisibility: 'private',
+			followingVisibility: 'private',
+			followersVisibility: 'private',
 		}, alice);
 
 		const followingRes = await api('/users/following', {
@@ -134,36 +542,129 @@ describe('FF visibility', () => {
 		assert.strictEqual(followersRes.status, 400);
 	});
 
+	test('followingVisibility が private なユーザーのフォローを followersVisibility の設定に関わらず他人が見れない', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'public',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 400);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'followers',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 400);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'private',
+			}, alice);
+
+			const followingRes = await api('/users/following', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followingRes.status, 400);
+		}
+	});
+
+	test('followersVisibility が private なユーザーのフォロワーを followingVisibility の設定に関わらず他人が見れない', async () => {
+		{
+			await api('/i/update', {
+				followingVisibility: 'public',
+				followersVisibility: 'private',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 400);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'followers',
+				followersVisibility: 'private',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 400);
+		}
+		{
+			await api('/i/update', {
+				followingVisibility: 'private',
+				followersVisibility: 'private',
+			}, alice);
+
+			const followersRes = await api('/users/followers', {
+				userId: alice.id,
+			}, bob);
+			assert.strictEqual(followersRes.status, 400);
+		}
+	});
+
 	describe('AP', () => {
-		test('ffVisibility が public 以外ならばAPからは取得できない', async () => {
+		test('followingVisibility が public 以外ならばAPからはフォローを取得できない', async () => {
 			{
 				await api('/i/update', {
-					ffVisibility: 'public',
+					followingVisibility: 'public',
 				}, alice);
 
 				const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json');
-				const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json');
 				assert.strictEqual(followingRes.status, 200);
+			}
+			{
+				await api('/i/update', {
+					followingVisibility: 'followers',
+				}, alice);
+
+				const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json');
+				assert.strictEqual(followingRes.status, 403);
+			}
+			{
+				await api('/i/update', {
+					followingVisibility: 'private',
+				}, alice);
+
+				const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json');
+				assert.strictEqual(followingRes.status, 403);
+			}
+		});
+
+		test('followersVisibility が public 以外ならばAPからはフォロワーを取得できない', async () => {
+			{
+				await api('/i/update', {
+					followersVisibility: 'public',
+				}, alice);
+
+				const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json');
 				assert.strictEqual(followersRes.status, 200);
 			}
 			{
 				await api('/i/update', {
-					ffVisibility: 'followers',
+					followersVisibility: 'followers',
 				}, alice);
 
-				const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json');
 				const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json');
-				assert.strictEqual(followingRes.status, 403);
 				assert.strictEqual(followersRes.status, 403);
 			}
 			{
 				await api('/i/update', {
-					ffVisibility: 'private',
+					followersVisibility: 'private',
 				}, alice);
 
-				const followingRes = await simpleGet(`/users/${alice.id}/following`, 'application/activity+json');
 				const followersRes = await simpleGet(`/users/${alice.id}/followers`, 'application/activity+json');
-				assert.strictEqual(followingRes.status, 403);
 				assert.strictEqual(followersRes.status, 403);
 			}
 		});
diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts
index 2ce8fbc129..9c4cbac368 100644
--- a/packages/backend/test/e2e/users.ts
+++ b/packages/backend/test/e2e/users.ts
@@ -112,7 +112,8 @@ describe('ユーザー', () => {
 			pinnedPageId: user.pinnedPageId,
 			pinnedPage: user.pinnedPage,
 			publicReactions: user.publicReactions,
-			ffVisibility: user.ffVisibility,
+			followingVisibility: user.followingVisibility,
+			followersVisibility: user.followersVisibility,
 			twoFactorEnabled: user.twoFactorEnabled,
 			usePasswordLessLogin: user.usePasswordLessLogin,
 			securityKeys: user.securityKeys,
@@ -386,7 +387,8 @@ describe('ユーザー', () => {
 		assert.strictEqual(response.pinnedPageId, null);
 		assert.strictEqual(response.pinnedPage, null);
 		assert.strictEqual(response.publicReactions, true);
-		assert.strictEqual(response.ffVisibility, 'public');
+		assert.strictEqual(response.followingVisibility, 'public');
+		assert.strictEqual(response.followersVisibility, 'public');
 		assert.strictEqual(response.twoFactorEnabled, false);
 		assert.strictEqual(response.usePasswordLessLogin, false);
 		assert.strictEqual(response.securityKeys, false);
@@ -495,9 +497,12 @@ describe('ユーザー', () => {
 		{ parameters: (): object => ({ alwaysMarkNsfw: false }) },
 		{ parameters: (): object => ({ autoSensitive: true }) },
 		{ parameters: (): object => ({ autoSensitive: false }) },
-		{ parameters: (): object => ({ ffVisibility: 'private' }) },
-		{ parameters: (): object => ({ ffVisibility: 'followers' }) },
-		{ parameters: (): object => ({ ffVisibility: 'public' }) },
+		{ parameters: (): object => ({ followingVisibility: 'private' }) },
+		{ parameters: (): object => ({ followingVisibility: 'followers' }) },
+		{ parameters: (): object => ({ followingVisibility: 'public' }) },
+		{ parameters: (): object => ({ followersVisibility: 'private' }) },
+		{ parameters: (): object => ({ followersVisibility: 'followers' }) },
+		{ parameters: (): object => ({ followersVisibility: 'public' }) },
 		{ parameters: (): object => ({ mutedWords: Array(19).fill(['xxxxx']) }) },
 		{ parameters: (): object => ({ mutedWords: [['x'.repeat(194)]] }) },
 		{ parameters: (): object => ({ mutedWords: [] }) },
diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts
index c2e6ee52f3..2960489c77 100644
--- a/packages/frontend/.storybook/fakes.ts
+++ b/packages/frontend/.storybook/fakes.ts
@@ -82,7 +82,8 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host = 'mi
 		birthday: '2014-06-20',
 		createdAt: '2016-12-28T22:49:51.000Z',
 		description: 'I am a cool user!',
-		ffVisibility: 'public',
+		followingVisibility: 'public',
+		followersVisibility: 'public',
 		roles: [],
 		fields: [
 			{
diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue
index eaebbf03e7..762b9b4316 100644
--- a/packages/frontend/src/components/MkUserInfo.vue
+++ b/packages/frontend/src/components/MkUserInfo.vue
@@ -22,10 +22,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div :class="$style.statusItem">
 			<p :class="$style.statusItemLabel">{{ i18n.ts.notes }}</p><span :class="$style.statusItemValue">{{ number(user.notesCount) }}</span>
 		</div>
-		<div v-if="isFfVisibleForMe(user)" :class="$style.statusItem">
+		<div v-if="isFollowingVisibleForMe(user)" :class="$style.statusItem">
 			<p :class="$style.statusItemLabel">{{ i18n.ts.following }}</p><span :class="$style.statusItemValue">{{ number(user.followingCount) }}</span>
 		</div>
-		<div v-if="isFfVisibleForMe(user)" :class="$style.statusItem">
+		<div v-if="isFollowersVisibleForMe(user)" :class="$style.statusItem">
 			<p :class="$style.statusItemLabel">{{ i18n.ts.followers }}</p><span :class="$style.statusItemValue">{{ number(user.followersCount) }}</span>
 		</div>
 	</div>
@@ -40,7 +40,7 @@ import number from '@/filters/number.js';
 import { userPage } from '@/filters/user.js';
 import { i18n } from '@/i18n.js';
 import { $i } from '@/account.js';
-import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
+import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
 
 defineProps<{
 	user: Misskey.entities.UserDetailed;
diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue
index b703369433..df8252fb14 100644
--- a/packages/frontend/src/components/MkUserPopup.vue
+++ b/packages/frontend/src/components/MkUserPopup.vue
@@ -35,11 +35,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<div :class="$style.statusItemLabel">{{ i18n.ts.notes }}</div>
 					<div>{{ number(user.notesCount) }}</div>
 				</div>
-				<div v-if="isFfVisibleForMe(user)" :class="$style.statusItem">
+				<div v-if="isFollowingVisibleForMe(user)" :class="$style.statusItem">
 					<div :class="$style.statusItemLabel">{{ i18n.ts.following }}</div>
 					<div>{{ number(user.followingCount) }}</div>
 				</div>
-				<div v-if="isFfVisibleForMe(user)" :class="$style.statusItem">
+				<div v-if="isFollowersVisibleForMe(user)" :class="$style.statusItem">
 					<div :class="$style.statusItemLabel">{{ i18n.ts.followers }}</div>
 					<div>{{ number(user.followersCount) }}</div>
 				</div>
@@ -65,7 +65,7 @@ import number from '@/filters/number.js';
 import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
 import { $i } from '@/account.js';
-import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
+import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
 
 const props = defineProps<{
 	showing: boolean;
diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue
index 971881ea24..67a2f2cb40 100644
--- a/packages/frontend/src/pages/settings/privacy.vue
+++ b/packages/frontend/src/pages/settings/privacy.vue
@@ -13,12 +13,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<template #caption>{{ i18n.ts.makeReactionsPublicDescription }}</template>
 	</MkSwitch>
 
-	<MkSelect v-model="ffVisibility" @update:modelValue="save()">
-		<template #label>{{ i18n.ts.ffVisibility }}</template>
+	<MkSelect v-model="followingVisibility" @update:modelValue="save()">
+		<template #label>{{ i18n.ts.followingVisibility }}</template>
+		<option value="public">{{ i18n.ts._ffVisibility.public }}</option>
+		<option value="followers">{{ i18n.ts._ffVisibility.followers }}</option>
+		<option value="private">{{ i18n.ts._ffVisibility.private }}</option>
+	</MkSelect>
+
+	<MkSelect v-model="followersVisibility" @update:modelValue="save()">
+		<template #label>{{ i18n.ts.followersVisibility }}</template>
 		<option value="public">{{ i18n.ts._ffVisibility.public }}</option>
 		<option value="followers">{{ i18n.ts._ffVisibility.followers }}</option>
 		<option value="private">{{ i18n.ts._ffVisibility.private }}</option>
-		<template #caption>{{ i18n.ts.ffVisibilityDescription }}</template>
 	</MkSelect>
 
 	<MkSwitch v-model="hideOnlineStatus" @update:modelValue="save()">
@@ -84,7 +90,8 @@ const preventAiLearning = ref($i.preventAiLearning);
 const isExplorable = ref($i.isExplorable);
 const hideOnlineStatus = ref($i.hideOnlineStatus);
 const publicReactions = ref($i.publicReactions);
-const ffVisibility = ref($i.ffVisibility);
+const followingVisibility = ref($i?.followingVisibility);
+const followersVisibility = ref($i?.followersVisibility);
 
 const defaultNoteVisibility = computed(defaultStore.makeGetterSetter('defaultNoteVisibility'));
 const defaultNoteLocalOnly = computed(defaultStore.makeGetterSetter('defaultNoteLocalOnly'));
@@ -100,7 +107,8 @@ function save() {
 		isExplorable: !!isExplorable.value,
 		hideOnlineStatus: !!hideOnlineStatus.value,
 		publicReactions: !!publicReactions.value,
-		ffVisibility: ffVisibility.value,
+		followingVisibility: followingVisibility.value,
+		followersVisibility: followersVisibility.value,
 	});
 }
 
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index a87e03e761..a9497f4fe0 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -110,11 +110,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<b>{{ number(user.notesCount) }}</b>
 							<span>{{ i18n.ts.notes }}</span>
 						</MkA>
-						<MkA v-if="isFfVisibleForMe(user)" :to="userPage(user, 'following')">
+						<MkA v-if="isFollowingVisibleForMe(user)" :to="userPage(user, 'following')">
 							<b>{{ number(user.followingCount) }}</b>
 							<span>{{ i18n.ts.following }}</span>
 						</MkA>
-						<MkA v-if="isFfVisibleForMe(user)" :to="userPage(user, 'followers')">
+						<MkA v-if="isFollowersVisibleForMe(user)" :to="userPage(user, 'followers')">
 							<b>{{ number(user.followersCount) }}</b>
 							<span>{{ i18n.ts.followers }}</span>
 						</MkA>
@@ -173,7 +173,7 @@ import { dateString } from '@/filters/date.js';
 import { confetti } from '@/scripts/confetti.js';
 import MkNotes from '@/components/MkNotes.vue';
 import { api } from '@/os.js';
-import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
+import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
 
 function calcAge(birthdate: string): number {
 	const date = new Date(birthdate);
diff --git a/packages/frontend/src/scripts/isFfVisibleForMe.ts b/packages/frontend/src/scripts/isFfVisibleForMe.ts
index 0567f3b34a..dc0e90d20a 100644
--- a/packages/frontend/src/scripts/isFfVisibleForMe.ts
+++ b/packages/frontend/src/scripts/isFfVisibleForMe.ts
@@ -6,11 +6,19 @@
 import * as Misskey from 'misskey-js';
 import { $i } from '@/account.js';
 
-export function isFfVisibleForMe(user: Misskey.entities.UserDetailed): boolean {
+export function isFollowingVisibleForMe(user: Misskey.entities.UserDetailed): boolean {
 	if ($i && $i.id === user.id) return true;
 
-	if (user.ffVisibility === 'private') return false;
-	if (user.ffVisibility === 'followers' && !user.isFollowing) return false;
+	if (user.followingVisibility === 'private') return false;
+	if (user.followingVisibility === 'followers' && !user.isFollowing) return false;
+
+	return true;
+}
+export function isFollowersVisibleForMe(user: Misskey.entities.UserDetailed): boolean {
+	if ($i && $i.id === user.id) return true;
+
+	if (user.followersVisibility === 'private') return false;
+	if (user.followersVisibility === 'followers' && !user.isFollowing) return false;
 
 	return true;
 }
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index a8f0b96d5d..83d313a5fe 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -4,7 +4,9 @@ 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 followingVisibilities = ['public', 'followers', 'private'] as const;
+
+export const followersVisibilities = ['public', 'followers', 'private'] as const;
 
 export const permissions = [
 	'read:account',
diff --git a/packages/misskey-js/src/index.ts b/packages/misskey-js/src/index.ts
index e78501fdfd..54cae8ec03 100644
--- a/packages/misskey-js/src/index.ts
+++ b/packages/misskey-js/src/index.ts
@@ -16,7 +16,8 @@ export const permissions = consts.permissions;
 export const notificationTypes = consts.notificationTypes;
 export const noteVisibilities = consts.noteVisibilities;
 export const mutedNoteReasons = consts.mutedNoteReasons;
-export const ffVisibility = consts.ffVisibility;
+export const followingVisibilities = consts.followingVisibilities;
+export const followersVisibilities = consts.followersVisibilities;
 export const moderationLogTypes = consts.moderationLogTypes;
 
 // api extractor not supported yet

From eeedef59c4ec2684e119dab0429dadcb13285469 Mon Sep 17 00:00:00 2001
From: 1STEP621 <86859447+1STEP621@users.noreply.github.com>
Date: Tue, 19 Dec 2023 18:30:31 +0900
Subject: [PATCH 268/435] =?UTF-8?q?Fix(frontend):=20CW=E3=83=9C=E3=82=BF?=
 =?UTF-8?q?=E3=83=B3=E3=81=AB=E5=B8=B8=E3=81=AB=E3=80=8C=E5=BC=95=E7=94=A8?=
 =?UTF-8?q?=E3=80=8D=E3=81=8C=E5=87=BA=E3=81=A6=E3=81=97=E3=81=BE=E3=81=86?=
 =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12715)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* RNと引用RNの判定が間違っているのを修正

* remove dump.rdb

* update CHANGELOG.md

* lint fix

* fix cw button label
---
 packages/frontend/src/components/MkCwButton.vue | 2 +-
 packages/frontend/src/components/MkNote.vue     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue
index a25193a57c..4a6d2dfba2 100644
--- a/packages/frontend/src/components/MkCwButton.vue
+++ b/packages/frontend/src/components/MkCwButton.vue
@@ -42,7 +42,7 @@ const emit = defineEmits<{
 const label = computed(() => {
 	return concat([
 		props.text ? [i18n.t('_cw.chars', { count: props.text.length })] : [],
-		props.renote != null ? [i18n.ts.quote] : [],
+		props.renote ? [i18n.ts.quote] : [],
 		props.files.length !== 0 ? [i18n.t('_cw.files', { count: props.files.length })] : [],
 		props.poll != null ? [i18n.ts.poll] : [],
 	] as string[][]).join(' / ');
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 609b381598..bb834a3845 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div style="container-type: inline-size;">
 				<p v-if="appearNote.cw != null" :class="$style.cw">
 					<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/>
-					<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;"/>
+					<MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;"/>
 				</p>
 				<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]">
 					<div :class="$style.text">

From b7bfdd5d0c170a123fdb4584e89641fdee4d45cd Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Tue, 19 Dec 2023 11:24:38 +0100
Subject: [PATCH 269/435] upd: remove outdated search example from MfM
 Cheatsheet

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

diff --git a/packages/frontend/src/components/MkMfmWindow.vue b/packages/frontend/src/components/MkMfmWindow.vue
index 314808dcf6..ce2a0e7391 100644
--- a/packages/frontend/src/components/MkMfmWindow.vue
+++ b/packages/frontend/src/components/MkMfmWindow.vue
@@ -391,7 +391,7 @@ const preview_inlineMath = ref(
 const preview_blockMath = ref("\\[x= \\frac{-b' \\pm \\sqrt{(b')^2-ac}}{a}\\]");
 const preview_quote = ref(`> ${i18n.ts._mfm.dummy}`);
 const preview_search = ref(
-	`${i18n.ts._mfm.dummy} [search]\n${i18n.ts._mfm.dummy} [検索]\n${i18n.ts._mfm.dummy} 検索`,
+	`${i18n.ts._mfm.dummy} [search]\n${i18n.ts._mfm.dummy} [検索]`,
 );
 const preview_jelly = ref(
 	"$[jelly 🍮] $[jelly.speed=3s 🍮] $[jelly.delay=3s 🍮] $[jelly.loop=3 🍮]",

From ea41df538eea155353e5fdd65110d82c48997b22 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Tue, 19 Dec 2023 19:40:53 +0900
Subject: [PATCH 270/435] New Crowdin updates (#12652)

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Romanian)

* 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 (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 (Korean (Gyeongsang))

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Korean)

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

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Korean)

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

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Italian)

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

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Ukrainian)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Korean)

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

* New translations ja-jp.yml (French)

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

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Ukrainian)

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

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

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

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Arabic)

* New translations ja-jp.yml (Italian)

* New translations ja-jp.yml (Indonesian)

* 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 (Portuguese)

* 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 (Bengali)

* New translations ja-jp.yml (Thai)

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

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Chinese Traditional)
---
 locales/ar-SA.yml | 18 ++++++++++--
 locales/bn-BD.yml |  3 --
 locales/ca-ES.yml |  1 -
 locales/cs-CZ.yml |  3 --
 locales/de-DE.yml |  3 --
 locales/el-GR.yml |  1 -
 locales/en-US.yml |  3 --
 locales/es-ES.yml |  3 --
 locales/fr-FR.yml | 17 +++++++++--
 locales/id-ID.yml | 24 +++++++++++++--
 locales/it-IT.yml | 17 +++++++++--
 locales/ja-KS.yml | 17 +++++++++--
 locales/ko-GS.yml | 74 +++++++++++++++++++++++++++++++++++++++++------
 locales/ko-KR.yml | 26 ++++++++++++-----
 locales/nl-NL.yml |  1 -
 locales/no-NO.yml |  1 -
 locales/pl-PL.yml |  3 --
 locales/pt-PT.yml |  3 --
 locales/ro-RO.yml |  1 -
 locales/ru-RU.yml |  3 --
 locales/sk-SK.yml |  3 --
 locales/sv-SE.yml |  1 -
 locales/th-TH.yml |  3 --
 locales/tr-TR.yml |  1 -
 locales/uk-UA.yml |  7 +++--
 locales/uz-UZ.yml |  1 -
 locales/vi-VN.yml |  3 --
 locales/zh-CN.yml |  5 +---
 locales/zh-TW.yml | 20 +++++++++----
 29 files changed, 181 insertions(+), 85 deletions(-)

diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index 6ac56ffc29..0a7d86cc89 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -120,7 +120,6 @@ sensitive: "محتوى حساس"
 add: "إضافة"
 reaction: "التفاعلات"
 reactions: "التفاعلات"
-reactionSetting: "التفاعلات المراد عرضها في منتقي التفاعلات."
 reactionSettingDescription2: "اسحب لترتيب ، انقر للحذف ، استخدم \"+\" للإضافة."
 rememberNoteVisibility: "تذكر إعدادت مدى رؤية الملاحظات"
 attachCancel: "أزل المرفق"
@@ -817,8 +816,6 @@ makeReactionsPublicDescription: "هذا سيجعل قائمة تفاعلاتك 
 classic: "تقليدي"
 muteThread: "اكتم النقاش"
 unmuteThread: "ارفع الكتم عن النقاش"
-ffVisibility: "مرئية المتابِعين/المتابَعين"
-ffVisibilityDescription: "يسمح لك بتحديد من يمكنهم رؤية متابِعيك ومتابَعيك."
 continueThread: "اعرض بقية النقاش"
 deleteAccountConfirm: "سيحذف حسابك نهائيًا، أتريد المتابعة؟"
 incorrectPassword: "كلمة السر خاطئة."
@@ -947,9 +944,12 @@ rolesAssignedToMe: "الأدوار المسندة إلي"
 resetPasswordConfirm: "هل تريد إعادة تعيين كلمة السر؟"
 license: "الرخصة"
 unfavoriteConfirm: "أتريد إزالتها من المفضلة؟"
+reactionsDisplaySize: "حجم التفاعلات"
+limitWidthOfReaction: "تصغير حجم التفاعلات"
 noteIdOrUrl: "معرف الملاحظة أو رابطها"
 video: "فيديو"
 videos: "فيديوهات"
+dataSaver: "موفر البيانات"
 accountMigration: "ترحيل الحساب"
 accountMoved: "نقل هذا المستخدم حسابه:"
 accountMovedShort: "رُحل هذا الحساب."
@@ -957,6 +957,7 @@ operationForbidden: "عملية ممنوعة"
 forceShowAds: "أظهر الإعلانات التجارية دائما"
 reactionsList: "التفاعلات"
 renotesList: "إعادات النشر"
+notificationDisplay: "إشعارات"
 leftTop: "أعلى اليسار"
 rightTop: "أعلى اليمين"
 leftBottom: "أسفل اليسار"
@@ -979,6 +980,7 @@ thisChannelArchived: "أُرشفت هذه القناة."
 displayOfNote: "عرض الملاحظة"
 initialAccountSetting: "إعداد الملف الشخصي"
 youFollowing: "متابَع"
+preventAiLearning: "منع استخدام البيانات في تعليم الآلة"
 options: "خيارات"
 specifyUser: "مستخدم محدد"
 failedToPreviewUrl: "تتعذر المعاينة"
@@ -992,7 +994,16 @@ later: "لاحقاً"
 goToMisskey: "لميسكي"
 additionalEmojiDictionary: "قواميس إيموجي إضافية"
 installed: "مُثبت"
+enableServerMachineStats: "نشر إحصائيات عتاد الخادم"
+turnOffToImprovePerformance: "تفعيله قد يزيد الأداء."
+createInviteCode: "ولِّد دعوة"
+inviteCodeCreated: "ولِّدت دعوة"
+inviteLimitExceeded: "وصلتَ لحد عدد الدعوات المسموح لك توليدها."
+createLimitRemaining: "حد عدد الدعوات: {limit} دعوة"
 expirationDate: "تاريخ انتهاء الصلاحية"
+noExpirationDate: "لا نهاية لصلاحيتها"
+inviteCodeUsedAt: "اُستخدم رمز الدعوة في"
+registeredUserUsingInviteCode: "اِستخدم رمز الدعوة"
 unused: "غير مستعمَل"
 expired: "منتهية صلاحيته"
 icon: "الصورة الرمزية"
@@ -1549,3 +1560,4 @@ _webhookSettings:
 _moderationLogTypes:
   suspend: "علِق"
   resetPassword: "أعد تعيين كلمتك السرية"
+  createInvitation: "ولِّد دعوة"
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index c6784269c4..c659e13250 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -108,7 +108,6 @@ sensitive: "সংবেদনশীল বিষয়বস্তু"
 add: "যুক্ত করুন"
 reaction: "প্রতিক্রিয়া"
 reactions: "প্রতিক্রিয়া"
-reactionSetting: "রিঅ্যাকশন পিকারে যেসকল প্রতিক্রিয়া দেখানো হবে"
 reactionSettingDescription2: "পুনরায় সাজাতে টেনে আনুন, মুছতে ক্লিক করুন, যোগ করতে + টিপুন।"
 rememberNoteVisibility: "নোটের দৃশ্যমান্যতার সেটিংস মনে রাখুন"
 attachCancel: "অ্যাটাচমেন্ট সরান "
@@ -794,8 +793,6 @@ makeReactionsPublicDescription: "আপনার পূর্ববর্তী
 classic: "ক্লাসিক"
 muteThread: "থ্রেড মিউট করুন"
 unmuteThread: "থ্রেড আনমিউট করুন"
-ffVisibility: "অনুসরণ/অনুসরণকারীদের দৃশ্যমান্যতা"
-ffVisibilityDescription: "আপনি কাকে অনুসরণ করেন এবং কে আপনাকে অনুসরণ করে, সেটা কারা দেখতে পাবে তা নির্ধারণ করে।"
 continueThread: "আরো থ্রেড দেখুন"
 deleteAccountConfirm: "আপনার অ্যাকাউন্ট মুছে ফেলা হবে। ঠিক আছে?"
 incorrectPassword: "আপনার দেওয়া পাসওয়ার্ডটি ভুল।"
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index 018645768d..b4fa799ada 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -121,7 +121,6 @@ sensitive: "NSFW"
 add: "Afegir"
 reaction: "Reaccions"
 reactions: "Reaccions"
-reactionSetting: "Reaccions a mostrar al selector de reaccions"
 reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem \"+\" per afegir."
 rememberNoteVisibility: "Recorda la configuració de visibilitat de les notes"
 attachCancel: "Eliminar el fitxer adjunt"
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 8221da44ea..1e064c4911 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -120,7 +120,6 @@ sensitive: "NSFW"
 add: "Přidat"
 reaction: "Reakce"
 reactions: "Reakce"
-reactionSetting: "Reakce zobrazené ve výběru reakcí"
 reactionSettingDescription2: "Přetažením změníte pořadí, kliknutím smažete, zmáčkněte \"+\" k přidání"
 rememberNoteVisibility: "Zapamatovat nastavení zobrazení poznámky"
 attachCancel: "Odstranit přílohu"
@@ -855,8 +854,6 @@ makeReactionsPublicDescription: "Tohle zviditelný seznam vašich předchozích
 classic: "Klasický"
 muteThread: "Ztlumit vlákno"
 unmuteThread: "Zrušit ztlumení vlákna"
-ffVisibility: "Viditelnost Sledovaných/Sledujících"
-ffVisibilityDescription: "Umožní vám nastavit kdo uvidí koho sledujete a kdo vás sleduje."
 continueThread: "Zobrazit pokračování vlákna"
 deleteAccountConfirm: "Tohle nenávratně smaže váš účet, chcete pokračovat?"
 incorrectPassword: "Nesprávné heslo."
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index db6aea29c4..4c32b3dda4 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -121,7 +121,6 @@ sensitive: "Sensibel"
 add: "Hinzufügen"
 reaction: "Reaktionen"
 reactions: "Reaktionen"
-reactionSetting: "In der Reaktionsauswahl anzuzeigende Reaktionen"
 reactionSettingDescription2: "Ziehe um Anzuordnen, klicke um zu löschen, drücke „+“ um hinzuzufügen"
 rememberNoteVisibility: "Notizsichtbarkeit merken"
 attachCancel: "Anhang entfernen"
@@ -874,8 +873,6 @@ makeReactionsPublicDescription: "Jeder wird die Liste deiner gesendeten Reaktion
 classic: "Classic"
 muteThread: "Thread stummschalten"
 unmuteThread: "Threadstummschaltung aufheben"
-ffVisibility: "Sichtbarkeit von Gefolgten/Followern"
-ffVisibilityDescription: "Konfiguriere wer sehen kann, wem du folgst sowie wer dir folgt."
 continueThread: "Weiteren Threadverlauf anzeigen"
 deleteAccountConfirm: "Dein Benutzerkonto wird unwiderruflich gelöscht. Trotzdem fortfahren?"
 incorrectPassword: "Falsches Passwort."
diff --git a/locales/el-GR.yml b/locales/el-GR.yml
index a1c2d25391..30a52b726e 100644
--- a/locales/el-GR.yml
+++ b/locales/el-GR.yml
@@ -104,7 +104,6 @@ clickToShow: "Κάντε κλικ για εμφάνιση"
 add: "Προσθέστε"
 reaction: "Αντιδράσεις"
 reactions: "Αντιδράσεις"
-reactionSetting: "Αντιδράσεις για εμφάνιση στην επιλογή αντίδρασης"
 reactionSettingDescription2: "Σύρετε για να αλλάξετε τη σειρά, κάντε κλικ για να διαγράψετε, πατήστε \"+\" για να προσθέσετε."
 rememberNoteVisibility: "Θυμήσου τις ρυθμίσεις ορατότητας σημειώματος"
 attachCancel: "Διαγραφή αρχείου"
diff --git a/locales/en-US.yml b/locales/en-US.yml
index da9b0fb7bc..65fe07b6d0 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -121,7 +121,6 @@ sensitive: "Sensitive"
 add: "Add"
 reaction: "Reactions"
 reactions: "Reactions"
-reactionSetting: "Reactions to show in the reaction picker"
 reactionSettingDescription2: "Drag to reorder, click to delete, press \"+\" to add."
 rememberNoteVisibility: "Remember note visibility settings"
 attachCancel: "Remove attachment"
@@ -875,8 +874,6 @@ makeReactionsPublicDescription: "This will make the list of all your past reacti
 classic: "Classic"
 muteThread: "Mute thread"
 unmuteThread: "Unmute thread"
-ffVisibility: "Follows/Followers Visibility"
-ffVisibilityDescription: "Allows you to configure who can see who you follow and who follows you."
 continueThread: "View thread continuation"
 deleteAccountConfirm: "This will irreversibly delete your account. Proceed?"
 incorrectPassword: "Incorrect password."
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 3c36e2b29f..a079cf01f9 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -121,7 +121,6 @@ sensitive: "Marcado como sensible"
 add: "Agregar"
 reaction: "Reacción"
 reactions: "Reacción"
-reactionSetting: "Reacciones para mostrar en el menú de reacciones"
 reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir."
 rememberNoteVisibility: "Recordar visibilidad"
 attachCancel: "Quitar adjunto"
@@ -874,8 +873,6 @@ makeReactionsPublicDescription: "Todas las reacciones que hayas hecho serán pú
 classic: "Clásico"
 muteThread: "Silenciar hilo"
 unmuteThread: "Mostrar hilo"
-ffVisibility: "Visibilidad de seguidores y seguidos"
-ffVisibilityDescription: "Puedes configurar quien puede ver a quienes sigues y quienes te siguen"
 continueThread: "Ver la continuación del hilo"
 deleteAccountConfirm: "La cuenta será borrada. ¿Está seguro?"
 incorrectPassword: "La contraseña es incorrecta"
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index bc0676eb0a..8acbc7d7a6 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -121,7 +121,12 @@ sensitive: "Contenu sensible"
 add: "Ajouter"
 reaction: "Réactions"
 reactions: "Réactions"
-reactionSetting: "Réactions à afficher dans le sélecteur de réactions"
+emojiPicker: "Sélecteur d’émojis"
+pinnedEmojisForReactionSettingDescription: "Vous pouvez définir les émojis épinglés lors de la réaction"
+pinnedEmojisSettingDescription: "Vous pouvez définir les émojis épinglés lors de la saisie de l'émoji"
+emojiPickerDisplay: "Affichage du sélecteur d'émojis"
+overwriteFromPinnedEmojisForReaction: "Remplacer par les émojis épinglés pour la réaction"
+overwriteFromPinnedEmojis: "Remplacer par les émojis épinglés globalement"
 reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser « + » pour ajouter."
 rememberNoteVisibility: "Se souvenir de la visibilité des notes"
 attachCancel: "Supprimer le fichier attaché"
@@ -873,8 +878,8 @@ makeReactionsPublicDescription: "Ceci rendra la liste de toutes vos réactions d
 classic: "Classique"
 muteThread: "Masquer cette discussion"
 unmuteThread: "Ne plus masquer le fil"
-ffVisibility: "Visibilité des abonnés/abonnements"
-ffVisibilityDescription: "Permet de configurer qui peut voir les personnes que tu suis et les personnes qui te suivent."
+followingVisibility: "Visibilité des abonnements"
+followersVisibility: "Visibilité des abonnés"
 continueThread: "Afficher la suite du fil"
 deleteAccountConfirm: "Votre compte sera supprimé. Êtes vous certain ?"
 incorrectPassword: "Le mot de passe est incorrect."
@@ -1024,6 +1029,8 @@ license: "Licence"
 myClips: "Mes clips"
 drivecleaner: "Nettoyeur du Disque"
 retryAllQueuesConfirmText: "Cela peut augmenter temporairement la charge du serveur."
+enableChartsForRemoteUser: "Générer les graphiques pour les utilisateurs distants"
+enableChartsForFederatedInstances: "Générer les graphiques pour les instances distantes"
 showClipButtonInNoteFooter: "Ajouter « Clip » au menu d'action de la note"
 reactionsDisplaySize: "Taille de l'affichage des réactions"
 limitWidthOfReaction: "Limiter la largeur maximale des réactions et les afficher en taille réduite"
@@ -1067,6 +1074,7 @@ options: "Options"
 specifyUser: "Spécifier l'utilisateur·rice"
 failedToPreviewUrl: "Aperçu d'URL échoué"
 update: "Mettre à jour"
+rolesThatCanBeUsedThisEmojiAsReaction: "Rôles qui peuvent utiliser cet émoji comme réaction"
 later: "Plus tard"
 goToMisskey: "Retour vers Misskey"
 additionalEmojiDictionary: "Dictionnaires d'émojis additionnels"
@@ -1129,6 +1137,9 @@ doReaction: "Réagir"
 code: "Code"
 reloadRequiredToApplySettings: "Le rafraîchissement est nécessaire pour que les paramètres prennent effet."
 remainingN: "Restants : {n}"
+overwriteContentConfirm: "Voulez-vous remplacer le contenu actuel ?"
+seasonalScreenEffect: "Effet d'écran saisonnier"
+decorate: "Décorer"
 _announcement:
   readConfirmTitle: "Marquer comme lu ?"
   shouldNotBeUsedToPresentPermanentInfo: "Puisque cela pourrait nuire considérablement à l'expérience utilisateur pour les nouveaux utilisateurs, il est recommandé d'utiliser les annonces pour afficher des informations temporaires plutôt que des informations persistantes."
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index eebdf90646..dc5600151a 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -121,7 +121,6 @@ sensitive: "Konten sensitif"
 add: "Tambahkan"
 reaction: "Reaksi"
 reactions: "Reaksi"
-reactionSetting: "Reaksi untuk dimunculkan di bilah reaksi"
 reactionSettingDescription2: "Geser untuk memindah urutan emoji, klik untuk menghapus, tekan \"+\" untuk menambahkan"
 rememberNoteVisibility: "Ingat pengaturan visibilitas catatan"
 attachCancel: "Hapus lampiran"
@@ -261,6 +260,7 @@ removed: "Telah dihapus"
 removeAreYouSure: "Apakah kamu yakin ingin menghapus \"{x}\"?"
 deleteAreYouSure: "Apakah kamu yakin ingin menghapus \"{x}\"?"
 resetAreYouSure: "Yakin mau atur ulang?"
+areYouSure: "Apakah kamu yakin?"
 saved: "Telah disimpan"
 messaging: "Pesan"
 upload: "Unggah"
@@ -311,6 +311,7 @@ folderName: "Nama folder"
 createFolder: "Buat folder"
 renameFolder: "Ubah nama folder"
 deleteFolder: "Hapus folder"
+folder: "Folder"
 addFile: "Tambahkan berkas"
 emptyDrive: "Drive kosong"
 emptyFolder: "Folder kosong"
@@ -543,6 +544,8 @@ showInPage: "Tampilkan di halaman"
 popout: "Pop-out"
 volume: "Volume"
 masterVolume: "Master volume"
+notUseSound: "Tidak ada keluaran suara"
+useSoundOnlyWhenActive: "Hanya keluarkan suara jika Misskey sedang aktif"
 details: "Selengkapnya"
 chooseEmoji: "Pilih emoji"
 unableToProcess: "Operasi tersebut tidak dapat diselesaikan."
@@ -871,8 +874,6 @@ makeReactionsPublicDescription: "Pengaturan ini akan membuat daftar dari semua r
 classic: "Klasik"
 muteThread: "Bisukan thread"
 unmuteThread: "Suarakan thread"
-ffVisibility: "Visibilitas Mengikuti/Pengikut"
-ffVisibilityDescription: "Mengatur siapa yang dapat melihat pengikutmu dan yang kamu ikuti."
 continueThread: "Lihat lanjutan thread"
 deleteAccountConfirm: "Akun akan dihapus. Apakah kamu yakin?"
 incorrectPassword: "Kata sandi salah."
@@ -1023,6 +1024,8 @@ resetPasswordConfirm: "Yakin untuk mereset kata sandimu?"
 sensitiveWords: "Kata sensitif"
 sensitiveWordsDescription: "Visibilitas dari semua catatan mengandung kata yang telah diatur akan dijadikan \"Beranda\" secara otomatis. Kamu dapat mendaftarkan kata tersebut lebih dari satu dengan menuliskannya di baris baru."
 sensitiveWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
+hiddenTags: "Tagar tersembunyi"
+hiddenTagsDescription: "Pilih tanda yang mana akan tidak diperlihatkan dalam daftar tren.\nTanda lebih dari satu dapat didaftarkan dengan tiap baris."
 notesSearchNotAvailable: "Pencarian catatan tidak tersedia."
 license: "Lisensi"
 unfavoriteConfirm: "Yakin ingin menghapusnya dari favorit?"
@@ -1035,6 +1038,7 @@ enableChartsForRemoteUser: "Buat bagan data pengguna instansi luar"
 enableChartsForFederatedInstances: "Buat bagan data peladen instansi luar"
 showClipButtonInNoteFooter: "Tambahkan \"Klip\" ke menu aksi catatan"
 reactionsDisplaySize: "Ukuran tampilan reaksi"
+limitWidthOfReaction: "Batasi lebar maksimum reaksi dan tampilkan dalam ukuran terbatasi."
 noteIdOrUrl: "ID catatan atau URL"
 video: "Video"
 videos: "Video"
@@ -1161,6 +1165,9 @@ useGroupedNotifications: "Tampilkan notifikasi secara dikelompokkan"
 signupPendingError: "Terdapat masalah ketika memverifikasi alamat surel. Tautan kemungkinan telah kedaluwarsa."
 cwNotationRequired: "Jika \"Sembunyikan konten\" diaktifkan, deskripsi harus disediakan."
 doReaction: "Tambahkan reaksi"
+code: "Kode"
+reloadRequiredToApplySettings: "Muat ulang diperlukan untuk menerapkan pengaturan."
+remainingN: "Sisa : {n}"
 _announcement:
   forExistingUsers: "Hanya pengguna yang telah ada"
   forExistingUsersDescription: "Pengumuman ini akan dimunculkan ke pengguna yang sudah ada dari titik waktu publikasi jika dinyalakan. Apabila dimatikan, mereka yang baru mendaftar setelah publikasi ini akan juga melihatnya."
@@ -1189,12 +1196,17 @@ _initialAccountSetting:
 _initialTutorial:
   launchTutorial: "Lihat Tutorial"
   title: "Tutorial"
+  wellDone: "Kerja bagus!"
   skipAreYouSure: "Berhenti dari Tutorial?"
   _landing:
     title: "Selamat datang di Tutorial"
     description: "Di sini kamu dapat mempelajari dasar-dasar dari penggunaan Misskey dan fitur-fiturnya."
   _note:
     title: "Apa itu Catatan?"
+  _reaction:
+    title: "Apa itu Reaksi?"
+  _timeline:
+    title: "Konsep Lini Masa"
   _postNote:
     title: "Pengaturan posting Catatan"
     _visibility:
@@ -1202,6 +1214,12 @@ _initialTutorial:
       home: "Hanya publik ke lini masa Beranda. Pengguna yang mengunjungi profilmu melalui pengikut dan renote dapat melihatnya."
       followers: "Perlihatkan ke pengikut saja. Hanya pengikut yang dapat melihat postinganmu dan tidak dapat direnote oleh siapapun."
       direct: "Hanya perlihatkan ke pengguna spesifik dan penerima akan diberi tahu. Dapat juga digunakan sebagai alternatif dari pesan langsung."
+    _cw:
+      _exampleNote:
+        cw: "Peringatan: Bikin Lapar!"
+        note: "Baru aja makan donat berlapis coklat 🍩😋"
+  _howToMakeAttachmentsSensitive:
+    title: "Bagaimana menandai lampiran sebagai sensitif?"
 _serverRules:
   description: "Daftar peraturan akan ditampilkan sebelum pendaftaran. Mengatur ringkasan dari Syarat dan Ketentuan sangat direkomendasikan."
 _serverSettings:
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 8c233dd66a..6ce6cd5604 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -121,7 +121,12 @@ sensitive: "Allegato esplicito"
 add: "Aggiungi"
 reaction: "Reazioni"
 reactions: "Reazioni"
-reactionSetting: "Reazioni visualizzate sul pannello"
+emojiPicker: "Selettore emoji"
+pinnedEmojisForReactionSettingDescription: "Scegli quale sia l'emoji in cima, quando reagisci"
+pinnedEmojisSettingDescription: "Scegli quale sia l'emoji in cima, quando reagisci"
+emojiPickerDisplay: "Visualizza selettore"
+overwriteFromPinnedEmojisForReaction: "Sovrascrivi con le impostazioni reazioni"
+overwriteFromPinnedEmojis: "Sovrascrivi con le impostazioni globali"
 reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere."
 rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note"
 attachCancel: "Rimuovi allegato"
@@ -261,6 +266,7 @@ removed: "Eliminato con successo"
 removeAreYouSure: "Vuoi davvero eliminare \"{x}\"?"
 deleteAreYouSure: "Vuoi davvero eliminare \"{x}\"?"
 resetAreYouSure: "Ripristinare?"
+areYouSure: "Confermi?"
 saved: "Salvato"
 messaging: "Messaggi"
 upload: "Carica"
@@ -875,8 +881,6 @@ makeReactionsPublicDescription: "La lista delle reazioni che avete fatto è a di
 classic: "Classico"
 muteThread: "Silenzia conversazione"
 unmuteThread: "Riattiva la conversazione"
-ffVisibility: "Visibilità delle connessioni"
-ffVisibilityDescription: "Puoi scegliere a chi mostrare le tue relazioni con altri profili nel fediverso."
 continueThread: "Altre conversazioni"
 deleteAccountConfirm: "Così verrà eliminato il profilo. Vuoi procedere?"
 incorrectPassword: "La password è errata."
@@ -1157,6 +1161,7 @@ tosAndPrivacyPolicy: "Condizioni d'uso e informativa privacy"
 avatarDecorations: "Decorazioni foto profilo"
 attach: "Applica"
 detach: "Rimuovi"
+detachAll: "Togli tutto"
 angle: "Angolo"
 flip: "Inverti"
 showAvatarDecorations: "Mostra decorazione della foto profilo"
@@ -1170,6 +1175,10 @@ cwNotationRequired: "Devi indicare perché il contenuto è indicato come esplici
 doReaction: "Reagisci"
 code: "Codice"
 reloadRequiredToApplySettings: "Per applicare le impostazioni, occorre ricaricare."
+remainingN: "Rimangono: {n}"
+overwriteContentConfirm: "Vuoi davvero sostituire l'attuale contenuto?"
+seasonalScreenEffect: "Schermate in base alla stagione"
+decorate: "Decora"
 _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."
@@ -1601,6 +1610,7 @@ _role:
     canHideAds: "Nascondere i banner"
     canSearchNotes: "Ricercare nelle Note"
     canUseTranslator: "Tradurre le Note"
+    avatarDecorationLimit: "Numero massimo di decorazioni foto profilo installabili"
   _condition:
     isLocal: "Profilo locale"
     isRemote: "Profilo remoto"
@@ -2037,6 +2047,7 @@ _profile:
   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."
+  avatarDecorationMax: "Puoi aggiungere fino a {max} decorazioni."
 _exportOrImport:
   allNotes: "Tutte le note"
   favoritedNotes: "Note preferite"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index c1d60d8a4c..4bd44f56d9 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -121,7 +121,12 @@ sensitive: "気いつけて見いや"
 add: "増やす"
 reaction: "ツッコミ"
 reactions: "ツッコミ"
-reactionSetting: "ピッカーに出しとくツッコミ"
+emojiPicker: "絵文字ピッカー"
+pinnedEmojisForReactionSettingDescription: "リアクションしたときにピンで留めてる表示をする絵文字を設定するで"
+pinnedEmojisSettingDescription: "絵文字打ったときにピン留め表示する絵文字設定できるで"
+emojiPickerDisplay: "ピッカーの表示"
+overwriteFromPinnedEmojisForReaction: "リアクション設定から上書きする"
+overwriteFromPinnedEmojis: "全般設定から上書きする"
 reactionSettingDescription2: "ドラッグで並び替え、クリックで削除、+を押して追加やで。"
 rememberNoteVisibility: "公開範囲覚えといて"
 attachCancel: "のっけるのやめる"
@@ -261,6 +266,7 @@ removed: "ほかしたで!"
 removeAreYouSure: "「{x}」はほかしてええか?"
 deleteAreYouSure: "「{x}」はほかしてええか?"
 resetAreYouSure: "リセットしてええん?"
+areYouSure: "いいん?"
 saved: "保存したで!"
 messaging: "チャット"
 upload: "アップロード"
@@ -875,8 +881,6 @@ makeReactionsPublicDescription: "あんたがしたツッコミ一覧を誰で
 classic: "クラシック"
 muteThread: "スレッドをミュート"
 unmuteThread: "スレッドのミュートを解除"
-ffVisibility: "つながりの公開範囲"
-ffVisibilityDescription: "あんたのフォロー/フォロワー情報の公開範囲を設定できるで。"
 continueThread: "さらにスレッドを見るで"
 deleteAccountConfirm: "アカウントを消すで?ええんか?"
 incorrectPassword: "パスワードがちゃうわ。"
@@ -1157,6 +1161,7 @@ tosAndPrivacyPolicy: "利用規約・プライバシーポリシー"
 avatarDecorations: "アイコンデコレーション"
 attach: "のっける"
 detach: "取る"
+detachAll: "全部とる"
 angle: "角度"
 flip: "反転"
 showAvatarDecorations: "アイコンのデコレーション映す"
@@ -1170,6 +1175,10 @@ cwNotationRequired: "「内容を隠す」んやったら注釈書かなアカ
 doReaction: "ツッコむで"
 code: "コード"
 reloadRequiredToApplySettings: "設定を見るんにはリロードが必要やで。"
+remainingN: "残り:{n}"
+overwriteContentConfirm: "今の内容に上書きされるけどいい?"
+seasonalScreenEffect: "季節にあった画面の動き"
+decorate: "デコる"
 _announcement:
   forExistingUsers: "もうおるユーザーのみ"
   forExistingUsersDescription: "オンにしたらこのお知らせができた時点でおる人らにだけお知らせが行くで。切ったらこの知らせが行ったあとにアカウント作った人にもちゃんとお知らせが行くで。"
@@ -1601,6 +1610,7 @@ _role:
     canHideAds: "広告映さへん"
     canSearchNotes: "ノート探せるかどうか"
     canUseTranslator: "翻訳使えるかどうか"
+    avatarDecorationLimit: "アイコンデコのいっちばんつけれる数"
   _condition:
     isLocal: "ローカルユーザー"
     isRemote: "リモートユーザー"
@@ -2037,6 +2047,7 @@ _profile:
   changeAvatar: "アバター画像を変更するで"
   changeBanner: "バナー画像を変更するで"
   verifiedLinkDescription: "内容をURLに設定すると、リンク先のwebサイトに自分のプロフのリンクが含まれてる場合に所有者確認済みアイコンを表示させることができるで。"
+  avatarDecorationMax: "最大{max}つまでデコつけれんで"
 _exportOrImport:
   allNotes: "全てのノート"
   favoritedNotes: "お気に入りにしたノート"
diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml
index 760247c483..9b113ad1b9 100644
--- a/locales/ko-GS.yml
+++ b/locales/ko-GS.yml
@@ -15,7 +15,7 @@ gotIt: "알것어예"
 cancel: "아이예"
 noThankYou: "뎃어예"
 enterUsername: "사용자 이럼 서기"
-renotedBy: "{user}님이 리노트햇십니다"
+renotedBy: "{user}님이 리노트햇어예"
 noNotes: "노트가 없십니다"
 noNotifications: "알림이 없십니다"
 instance: "서버"
@@ -76,7 +76,7 @@ export: "내가기"
 files: "파일"
 download: "내리받기"
 driveFileDeleteConfirm: "‘{name}’ 파일얼 뭉캡니꺼? 요 파일얼 서넌 콘텐츠도 뭉캐집니다."
-unfollowConfirm: "{name}님얼 고만 팔로잉합니꺼?"
+unfollowConfirm: "{name}님얼 고마 팔로잉합니꺼?"
 exportRequested: "내가기 요청얼 햇십니다. 시간이 쪼매 걸릴 깁니다. 요청이 껕나모 ‘드라이브’에 옇십니다."
 importRequested: "가오기 요청얼 햇십니다. 시간이 쪼매 걸릴 깁니다."
 lists: "리스트"
@@ -113,7 +113,7 @@ cantReRenote: "리노트넌 지럴 리노트 몬 합니다."
 quote: "따오기"
 inChannelRenote: "채널 안 리노트"
 inChannelQuote: "채널 안 따오기"
-pinnedNote: "프로필에 붙인 노트"
+pinnedNote: "붙인 노트"
 pinned: "프로필에 붙이기"
 you: "나"
 clickToShow: "누질라서 보기"
@@ -121,7 +121,6 @@ sensitive: "수ᇚ힛섭니다"
 add: "옇기"
 reaction: "반엉"
 reactions: "반엉"
-reactionSetting: "모엄함서 포시할 반엉"
 reactionSettingDescription2: "꺼시서 두고, 누질라서 뭉캐고,  ‘+’럴 누질라서 옇십니다."
 rememberNoteVisibility: "공개 범위럴 기억하기"
 attachCancel: "붙임 빼기"
@@ -330,7 +329,7 @@ whenServerDisconnected: "서버하고 옌겔이 껂기모"
 disconnectedFromServer: "서버하고 옌겔이 껂깃십니다"
 reload: "새로곤침"
 doNothing: "무시하기"
-reloadConfirm: "새로곤침합니까?"
+reloadConfirm: "새로곤침합니꺼?"
 watch: "간심 갖기"
 unwatch: "간심 고마 갖기"
 accept: "받기"
@@ -368,7 +367,7 @@ pinnedUsersDescription: "‘살펴보기’서 붙일라넌 사용자럴 줄 바
 pinnedPages: "붙인 바닥"
 pinnedPagesDescription: "서버으 대문서 붙일라넌 바닥으 겡로럴 줄 바꿈해서로 적십니다."
 pinnedClipId: "붙일 클립으 아이디"
-pinnedNotes: "프로필에 붙인 노트"
+pinnedNotes: "붙인 노트"
 hcaptcha: "에이치캡차"
 enableHcaptcha: "에이치캡차 키기"
 hcaptchaSiteKey: "사이트키"
@@ -381,7 +380,7 @@ turnstile: "턴스타일"
 enableTurnstile: "턴스타일 키기"
 turnstileSiteKey: "사이트키"
 turnstileSecretKey: "시크릿키"
-avoidMultiCaptchaConfirm: "오만 캡차럴 서모 간섭이 잇얼 깁니다. 다린 캡차를 껍니까? ‘아이예’럴 누질리모 오만 캡차럴 키 둘 수도 잇십니다."
+avoidMultiCaptchaConfirm: "오만 캡차럴 서모 간섭이 잇얼 깁니다. 다린 캡차를 껍니꺼? ‘아이예’럴 누질리모 오만 캡차럴 키 둘 수도 잇십니다."
 antennas: "안테나"
 manageAntennas: "안테나 간리"
 name: "이럼"
@@ -413,7 +412,7 @@ userList: "리스트"
 about: "정보"
 aboutMisskey: "Misskey넌예"
 administrator: "간리자"
-token: "학인 코드"
+token: "학인 기호"
 2fa: "두 단게 정멩"
 setupOf2fa: "두 단게 정멩 설정"
 totp: "정멩 앱"
@@ -426,13 +425,45 @@ moderationLogs: "중재 일지"
 nUsersMentioned: "{n}멩이 이바구하고 잇어예"
 securityKeyAndPasskey: "보안키·패스키"
 securityKey: "보안키"
+unregister: "맨걸기 무루기"
+share: "노누기"
+notFound: "몬 찾앗십니다"
+help: "도움말"
 invites: "초대하기"
+retype: "다시 서기"
+noteOf: "{user}님으 노트"
 invitations: "초대하기"
+checking: "학인하고 잇십니다"
+passwordMatched: "맞십니다"
+passwordNotMatched: "안 맞십니다"
 language: "언어"
+remote: "웬겍"
+script: "스크립트"
 manage: "간리"
+emailServer: "전자우펜 서버"
+email: "전자우펜"
+emailAddress: "전자우펜 주소"
 smtpHost: "호스트 이럼"
+smtpPort: "포트"
 smtpUser: "사용자 이럼"
 smtpPass: "비밀번호"
+abuseReports: "신고하기"
+reportAbuse: "신고하기"
+reportAbuseRenote: "리노트 신고하기"
+reportAbuseOf: "{name}님얼 신고하기"
+reporter: "신고한 사람"
+reporteeOrigin: "신고덴 사람"
+reporterOrigin: "신고한 곳"
+forwardReport: "웬겍 서버에 신고 보내기"
+random: "무작이"
+system: "시스템"
+clip: "클립 맨걸기"
+notesCount: "노트 수"
+renotesCount: "리노트한 수"
+renotedCount: "리노트덴 수"
+followingCount: "팔로우 수"
+followersCount: "팔로워 수"
+clips: "클립 맨걸기"
 clearCache: "캐시 비우기"
 unlikeConfirm: "좋네예럴 무룹니꺼?"
 info: "정보"
@@ -440,6 +471,7 @@ user: "사용자"
 administration: "간리"
 on: "킴"
 off: "껌"
+clickToFinishEmailVerification: "[{ok}]럴 누질라서 전자우펜 정멩얼 껕내이소."
 searchByGoogle: "찾기"
 tenMinutes: "십 분"
 oneHour: "한 시간"
@@ -459,6 +491,20 @@ likeOnly: "좋네예마"
 icon: "아바타"
 replies: "답하기"
 renotes: "리노트"
+_initialAccountSetting:
+  startTutorial: "길라잡이 하기"
+_initialTutorial:
+  launchTutorial: "길라잡이 보기"
+  title: "길라잡이"
+  skipAreYouSure: "길라잡이럴 껕냅니까?"
+  _landing:
+    title: "길라잡이에 어서 오이소"
+  _done:
+    title: "길라잡이가 껕낫십니다!🎉"
+_achievements:
+  _types:
+    _tutorialCompleted:
+      description: "길라잡이럴 껕냇십니다"
 _gallery:
   liked: "좋네예한 걸"
   like: "좋네예!"
@@ -466,13 +512,16 @@ _gallery:
 _email:
   _follow:
     title: "새 팔로워가 잇십니다"
+_channel:
+  removeBanner: "배너 뭉캐기"
 _theme:
   keys:
     mention: "멘션"
 _sfx:
-  note: "노트"
+  note: "새 노트"
   notification: "알림"
 _2fa:
+  step3Title: "학인 기호럴 서기"
   renewTOTPCancel: "뎃어예"
 _widgets:
   profile: "프로필"
@@ -501,11 +550,15 @@ _charts:
   federation: "옌합"
 _timelines:
   home: "덜머리"
+_play:
+  script: "스크립트"
 _pages:
   like: "좋네예"
   unlike: "좋네예 무루기"
   blocks:
     image: "이미지"
+    _note:
+      id: "노트 아이디"
 _notification:
   youWereFollowed: "새 팔로워가 잇십니다"
   _types:
@@ -526,3 +579,6 @@ _webhookSettings:
   name: "이럼"
 _moderationLogTypes:
   suspend: "얼우기"
+  deleteNote: "노트 뭉캐기"
+  deleteUserAnnouncement: "사용자 공지 걸 뭉캐기"
+  resolveAbuseReport: "신고 해겔하기"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 2673e947f3..ae612baecb 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -121,7 +121,12 @@ sensitive: "열람 주의"
 add: "추가"
 reaction: "리액션"
 reactions: "리액션"
-reactionSetting: "선택기에 표시할 리액션"
+emojiPicker: "이모지 선택기"
+pinnedEmojisForReactionSettingDescription: "리액션을 할 때 프로필에 고정하여 표시할 이모지를 설정할 수 있습니다"
+pinnedEmojisSettingDescription: "이모지를 입력할 때 프로필에 고정하여 표시할 이모지를 설정할 수 있습니다"
+emojiPickerDisplay: "선택기 표시"
+overwriteFromPinnedEmojisForReaction: "리액션 설정을 덮어쓰기"
+overwriteFromPinnedEmojis: "일반 설정을 덮어쓰기"
 reactionSettingDescription2: "끌어서 순서 변경, 클릭해서 삭제, +를 눌러서 추가할 수 있습니다."
 rememberNoteVisibility: "공개 범위를 기억하기"
 attachCancel: "첨부 취소"
@@ -261,6 +266,7 @@ removed: "삭제하였습니다"
 removeAreYouSure: "\"{x}\" 을(를) 삭제하시겠습니까?"
 deleteAreYouSure: "\"{x}\" 을(를) 삭제하시겠습니까?"
 resetAreYouSure: "초기화 하시겠습니까?"
+areYouSure: "계속 진행하시겠습니까?"
 saved: "저장하였습니다"
 messaging: "대화"
 upload: "업로드"
@@ -686,7 +692,7 @@ defaultNavigationBehaviour: "기본 탐색 동작"
 editTheseSettingsMayBreakAccount: "이 설정을 변경하면 계정이 손상될 수 있습니다."
 instanceTicker: "노트의 서버 정보"
 waitingFor: "{x}을(를) 기다리고 있습니다"
-random: "랜덤"
+random: "무작위"
 system: "시스템"
 switchUi: "UI 전환"
 desktop: "데스크탑"
@@ -875,8 +881,6 @@ makeReactionsPublicDescription: "나의 리액션을 누구나 볼 수 있게 
 classic: "클래식"
 muteThread: "글타래 뮤트"
 unmuteThread: "글타래 뮤트 해제"
-ffVisibility: "내 인맥의 공개 범위"
-ffVisibilityDescription: "나의 팔로우와 팔로워 정보에 대한 공개 범위를 설정할 수 있습니다."
 continueThread: "글타래 더 보기"
 deleteAccountConfirm: "계정이 삭제되고 되돌릴 수 없게 됩니다. 계속하시겠습니까? "
 incorrectPassword: "비밀번호가 올바르지 않습니다."
@@ -1156,7 +1160,8 @@ privacyPolicyUrl: "개인정보 보호 정책 URL"
 tosAndPrivacyPolicy: "약관 및 개인정보 보호 정책"
 avatarDecorations: "아바타 장식"
 attach: "붙이기"
-detach: "떼기"
+detach: "빼기"
+detachAll: "모두 빼기"
 angle: "각도"
 flip: "플립"
 showAvatarDecorations: "아바타 장식 표시"
@@ -1170,6 +1175,9 @@ cwNotationRequired: "'내용을 숨기기'를 체크한 경우 주석을 써야
 doReaction: "리액션 추가"
 code: "문자열"
 reloadRequiredToApplySettings: "설정을 적용하려면 새로고침을 해야 합니다."
+remainingN: "나머지: {n}"
+overwriteContentConfirm: "현재 내용을 덮어쓰기 합니다. 계속 진행하시겠습니까?"
+seasonalScreenEffect: "철에 맞는 화면으로 꾸미기"
 _announcement:
   forExistingUsers: "기존 유저에게만 알림"
   forExistingUsersDescription: "활성화하면 이 공지사항을 게시한 시점에서 이미 가입한 유저에게만 표시합니다. 비활성화하면 게시 후에 가입한 유저에게도 표시합니다."
@@ -1601,6 +1609,7 @@ _role:
     canHideAds: "광고 숨기기"
     canSearchNotes: "노트 검색 이용 가능 여부"
     canUseTranslator: "번역 기능의 사용"
+    avatarDecorationLimit: "아바타 장식의 최대 붙임 개수"
   _condition:
     isLocal: "로컬 사용자"
     isRemote: "리모트 사용자"
@@ -2037,6 +2046,7 @@ _profile:
   changeAvatar: "아바타 이미지 변경"
   changeBanner: "배너 이미지 변경"
   verifiedLinkDescription: "내용에 자신의 프로필로 향하는 링크가 포함된 페이지의 URL을 삽입하면 소유자 인증 마크가 표시됩니다."
+  avatarDecorationMax: "최대 {max}개까지 장식을 할 수 있습니다."
 _exportOrImport:
   allNotes: "모든 노트"
   favoritedNotes: "즐겨찾기한 노트"
@@ -2270,9 +2280,9 @@ _moderationLogTypes:
   createAd: "광고 생성"
   deleteAd: "광고 삭제"
   updateAd: "광고 수정"
-  createAvatarDecoration: "아이콘 장식 추가"
-  updateAvatarDecoration: "아이콘 장식 수정"
-  deleteAvatarDecoration: "아이콘 장식 삭제"
+  createAvatarDecoration: "아바타 장식 만들기"
+  updateAvatarDecoration: "아바타 장식 수정"
+  deleteAvatarDecoration: "아바타 장식 삭제"
   unsetUserAvatar: "유저 아바타 제거"
   unsetUserBanner: "유저 배너 제거"
 _fileViewer:
diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml
index c1bdbede2c..98f1693129 100644
--- a/locales/nl-NL.yml
+++ b/locales/nl-NL.yml
@@ -119,7 +119,6 @@ sensitive: "NSFW"
 add: "Toevoegen"
 reaction: "Reacties"
 reactions: "Reacties"
-reactionSetting: "Reacties die in de reactie-selector worden getoond"
 reactionSettingDescription2: "Sleep om opnieuw te ordenen, Klik om te verwijderen, Druk op \"+\" om toe te voegen"
 rememberNoteVisibility: "Vergeet niet de notitie zichtbaarheidsinstellingen"
 attachCancel: "Verwijder bijlage"
diff --git a/locales/no-NO.yml b/locales/no-NO.yml
index 44944f8465..195b1d0717 100644
--- a/locales/no-NO.yml
+++ b/locales/no-NO.yml
@@ -102,7 +102,6 @@ clickToShow: "Klikk for å vise"
 add: "Legg til"
 reaction: "Reaksjon"
 reactions: "Reaksjoner"
-reactionSetting: "Reaksjoner som vises i reaksjonsvelgeren"
 reactionSettingDescription2: "Dra for å endre rekkefølgen, klikk for å slette, trykk \"+\" for å legge til."
 rememberNoteVisibility: "Husk innstillingene for synlighet av Notes"
 attachCancel: "Fjern vedlegg"
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index 3a83f9b7ee..496e9bfc30 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -111,7 +111,6 @@ sensitive: "NSFW"
 add: "Dodaj"
 reaction: "Reakcja"
 reactions: "Reakcja"
-reactionSetting: "Reakcje do pokazania w wyborniku reakcji"
 reactionSettingDescription2: "Przeciągnij aby zmienić kolejność, naciśnij aby usunąć, naciśnij „+” aby dodać"
 rememberNoteVisibility: "Zapamiętuj ustawienia widoczności wpisu"
 attachCancel: "Usuń załącznik"
@@ -807,8 +806,6 @@ makeReactionsPublicDescription: "To spowoduje, że lista wszystkich Twoich dotyc
 classic: "Klasyczny"
 muteThread: "Wycisz wątek"
 unmuteThread: "Wyłącz wyciszenie wątku"
-ffVisibility: "Widoczność obserwowanych/obserwujących"
-ffVisibilityDescription: "Pozwala skonfigurować, kto może zobaczyć, kogo obserwujesz i kto Cię obserwuje."
 continueThread: "Pokaż kontynuację wątku"
 deleteAccountConfirm: "Spowoduje to nieodwracalne usunięcie Twojego konta. Kontynuować?"
 incorrectPassword: "Nieprawidłowe hasło."
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index b7a333f9e7..f969d711c0 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -121,7 +121,6 @@ sensitive: "Conteúdo sensível"
 add: "Adicionar"
 reaction: "Reações"
 reactions: "Reações"
-reactionSetting: "Quais reações exibir no seletor de reações"
 reactionSettingDescription2: "Arraste para reordenar, clique para excluir, pressione + para adicionar."
 rememberNoteVisibility: "Lembrar das configurações de visibilidade de notas"
 attachCancel: "Remover anexo"
@@ -859,8 +858,6 @@ makeReactionsPublicDescription: "Isto vai deixar o histórico de todas as suas r
 classic: "Clássico"
 muteThread: "Silenciar esta conversa"
 unmuteThread: "Desativar silêncio desta conversa"
-ffVisibility: "Visibilidade de Seguidos/Seguidores"
-ffVisibilityDescription: "Permite configurar quem pode ver quem lhe segue e quem você está seguindo."
 continueThread: "Ver mais desta conversa"
 deleteAccountConfirm: "Deseja realmente excluir a conta?"
 incorrectPassword: "Senha inválida."
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index 4a90d1e006..10be9539cf 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -121,7 +121,6 @@ sensitive: "NSFW"
 add: "Adaugă"
 reaction: "Reacție"
 reactions: "Reacție"
-reactionSetting: "Reacții care să apară in selectorul de reacții"
 reactionSettingDescription2: "Trage pentru a rearanja, apasă pe \"+\" pentru a adăuga."
 rememberNoteVisibility: "Amintește setarea de vizibilitate a notelor"
 attachCancel: "Înlătură atașament"
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index bea08a19fd..b8095d7256 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -120,7 +120,6 @@ sensitive: "Содержимое не для всех"
 add: "Добавить"
 reaction: "Реакции"
 reactions: "Реакции"
-reactionSetting: "Реакции, отображаемые в палитре"
 reactionSettingDescription2: "Расставляйте перетаскиванием, удаляйте нажатием, добавляйте кнопкой «+»."
 rememberNoteVisibility: "Запоминать видимость заметок"
 attachCancel: "Удалить вложение"
@@ -857,8 +856,6 @@ makeReactionsPublicDescription: "Список сделанных вами реа
 classic: "Классика"
 muteThread: "Скрыть цепочку"
 unmuteThread: "Отменить сокрытие цепочки"
-ffVisibility: "Видимость подписок и подписчиков"
-ffVisibilityDescription: "Здесь можно настроить, кто будет видеть ваши подписки и подписчиков."
 continueThread: "Показать следующие ответы"
 deleteAccountConfirm: "Учётная запись будет безвозвратно удалена. Подтверждаете?"
 incorrectPassword: "Пароль неверен."
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index 19b06b475a..e8401af1cd 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -113,7 +113,6 @@ sensitive: "NSFW"
 add: "Pridať"
 reaction: "Reakcie"
 reactions: "Reakcie"
-reactionSetting: "Reakcie zobrazené vo výbere reakcií"
 reactionSettingDescription2: "Ťahaním preusporiadate, kliknutím odstránite, Stlačením \"+\" pridáte"
 rememberNoteVisibility: "Zapamätať nastavenia viditeľnosti poznámky"
 attachCancel: "Odstrániť prílohu"
@@ -822,8 +821,6 @@ makeReactionsPublicDescription: "Toto spraví všetky vaše minulé reakcie vidi
 classic: "Klasika"
 muteThread: "Ztíšiť vlákno"
 unmuteThread: "Zrušiť stíšenie vlákna"
-ffVisibility: "Viditeľnosť sledujúcich/sledovaných"
-ffVisibilityDescription: "Umožňuje nastaviť kto vidí koho sledujete a kto vás sleduje."
 continueThread: "Zobraziť pokračovanie vlákna"
 deleteAccountConfirm: "Toto nezvrátiteľne vymaže váš účet. Pokračovať?"
 incorrectPassword: "Nesprávne heslo."
diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml
index 92678afef8..e5816ba105 100644
--- a/locales/sv-SE.yml
+++ b/locales/sv-SE.yml
@@ -118,7 +118,6 @@ sensitive: "Känsligt innehåll"
 add: "Lägg till"
 reaction: "Reaktioner"
 reactions: "Reaktioner"
-reactionSetting: "Reaktioner som ska visas i reaktionsväljaren"
 reactionSettingDescription2: "Dra för att omordna, klicka för att radera, tryck \"+\" för att lägga till."
 rememberNoteVisibility: "Komihåg notvisningsinställningar"
 attachCancel: "Ta bort bilaga"
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index d27e90b855..7cb2d68321 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -121,7 +121,6 @@ sensitive: "เนื้อหาที่ละเอียดอ่อน NSFW
 add: "เพิ่ม"
 reaction: "รีแอคชั่น"
 reactions: "รีแอคชั่น"
-reactionSetting: "รีแอคชั่นไปยังแสดงผลในตัวเลือกการรีแอคชั่น"
 reactionSettingDescription2: "กดลากเพื่อจัดลำดับใหม่ กดคลิกเพื่อลบ กด \"+\" เพื่อเพิ่ม"
 rememberNoteVisibility: "จดจำการตั้งค่าการมองเห็นตัวโน้ต"
 attachCancel: "ลบไฟล์ออกที่แนบมา"
@@ -870,8 +869,6 @@ makeReactionsPublicDescription: "การทำเช่นนี้จะท
 classic: "คลาสสิค"
 muteThread: "ปิดเสียงเธรด"
 unmuteThread: "เปิดเสียงเธรด"
-ffVisibility: "การมองเห็นผู้ติดตาม/ผู้ติดตาม"
-ffVisibilityDescription: "ช่วยให้คุณสามารถกำหนดค่าได้ว่าใครสามารถดูได้ว่าคุณติดตามใครและใครติดตามคุณบ้าง"
 continueThread: "ดูความต่อเนื่องเธรด"
 deleteAccountConfirm: "การดำเนินการนี้จะลบบัญชีของคุณอย่างถาวรเลยนะ แน่ใจหรอดำเนินการ?"
 incorrectPassword: "รหัสผ่านไม่ถูกต้อง"
diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml
index 3dd7a5b797..0793592d34 100644
--- a/locales/tr-TR.yml
+++ b/locales/tr-TR.yml
@@ -121,7 +121,6 @@ sensitive: "Hassas içerik"
 add: "Ekle"
 reaction: "Tepkiler"
 reactions: "Tepkiler"
-reactionSetting: "Palette görünecek tepkiler"
 reactionSettingDescription2: "Sıralamak için sürükleyin, silmek için tıklayın, eklemek için \"+\" tuşuna tıklayın."
 rememberNoteVisibility: "Görünürlük ayarlarını hatırla"
 attachCancel: "Eki sil"
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index f10f257fa0..9b609edebb 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -55,6 +55,7 @@ copyRSS: "Скопіювати RSS"
 copyUsername: "Скопіювати ім’я користувача"
 copyUserId: "Копіювати ID користувача"
 copyNoteId: "блокнот ID користувача"
+copyFileId: "Скопіювати ідентифікатор файлу."
 searchUser: "Пошук користувачів"
 reply: "Відповісти"
 loadMore: "Показати більше"
@@ -115,7 +116,6 @@ sensitive: "NSFW"
 add: "Додати"
 reaction: "Реакції"
 reactions: "Реакції"
-reactionSetting: "Налаштування реакцій"
 reactionSettingDescription2: "Перемістити щоб змінити порядок, Клацнути мишою щоб видалити, Натиснути \"+\" щоб додати."
 rememberNoteVisibility: "Пам’ятати параметри видимісті"
 attachCancel: "Видалити вкладення"
@@ -133,6 +133,7 @@ unblockConfirm: "Ви впевнені, що хочете розблокуват
 suspendConfirm: "Ви впевнені, що хочете призупинити цей акаунт?"
 unsuspendConfirm: "Ви впевнені, що хочете відновити цей акаунт?"
 selectList: "Виберіть список"
+editList: "Редагувати список."
 selectChannel: "Виберіть канал"
 selectAntenna: "Виберіть антену"
 selectWidget: "Виберіть віджет"
@@ -448,6 +449,7 @@ or: "або"
 language: "Мова"
 uiLanguage: "Мова інтерфейсу"
 aboutX: "Про {x}"
+native: "місцевий"
 disableDrawer: "Не використовувати висувні меню"
 noHistory: "Історія порожня"
 signinHistory: "Історія входів"
@@ -526,6 +528,8 @@ output: "Вихід"
 script: "Скрипт"
 disablePagesScript: "Вимкнути AiScript на Сторінках"
 updateRemoteUser: "Оновити інформацію про віддаленого користувача"
+unsetUserAvatar: "Деактивувати піктограму."
+unsetUserBanner: "Випустити прапор."
 deleteAllFiles: "Видалити всі файли"
 deleteAllFilesConfirm: "Ви дійсно хочете видалити всі файли?"
 removeAllFollowing: "Скасувати всі підписки"
@@ -813,7 +817,6 @@ makeReactionsPublicDescription: "Це зробить список усіх ва
 classic: "Класичний"
 muteThread: "Приглушити тред"
 unmuteThread: "Скасувати глушіння"
-ffVisibility: "Видимість підписок/підписників"
 continueThread: "Показати продовження треду"
 deleteAccountConfirm: "Це незворотно видалить ваш акаунт. Продовжити?"
 incorrectPassword: "Неправильний пароль."
diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml
index 8d3e8043f3..54e20b001d 100644
--- a/locales/uz-UZ.yml
+++ b/locales/uz-UZ.yml
@@ -120,7 +120,6 @@ sensitive: "Sezuvchan"
 add: "Qo'shish"
 reaction: "Reaktsiyalar"
 reactions: "Reaktsiyalar"
-reactionSetting: "Reaksiyalar ro'yxati"
 reactionSettingDescription2: "Qayta tartiblash uchun ushlab turib siljiting, oʻchirish uchun bosing, qoʻshish uchun “+” tugmasini bosing."
 rememberNoteVisibility: "Qaydning ko'rinish sozlamarini eslab qolish"
 attachCancel: "Qo'shimchani olib tashlash"
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index 0f60578963..c2d68d8b27 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -121,7 +121,6 @@ sensitive: "Nhạy cảm"
 add: "Thêm"
 reaction: "Biểu cảm"
 reactions: "Biểu cảm"
-reactionSetting: "Chọn những biểu cảm hiển thị"
 reactionSettingDescription2: "Kéo để sắp xếp, nhấn để xóa, nhấn \"+\" để thêm."
 rememberNoteVisibility: "Lưu kiểu tút mặc định"
 attachCancel: "Gỡ tập tin đính kèm"
@@ -858,8 +857,6 @@ makeReactionsPublicDescription: "Điều này sẽ hiển thị công khai danh
 classic: "Cổ điển"
 muteThread: "Không quan tâm nữa"
 unmuteThread: "Quan tâm tút này"
-ffVisibility: "Hiển thị Theo dõi/Người theo dõi"
-ffVisibilityDescription: "Quyết định ai có thể xem những người bạn theo dõi và những người theo dõi bạn."
 continueThread: "Tiếp tục xem chuỗi tút"
 deleteAccountConfirm: "Điều này sẽ khiến tài khoản bị xóa vĩnh viễn. Vẫn tiếp tục?"
 incorrectPassword: "Sai mật khẩu."
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 1b440284ab..bfacc03e0a 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -121,7 +121,6 @@ sensitive: "敏感内容"
 add: "添加"
 reaction: "回应"
 reactions: "回应"
-reactionSetting: "在选择器中显示回应"
 reactionSettingDescription2: "拖动重新排序,单击删除,点击 + 添加。"
 rememberNoteVisibility: "保存上次设置的可见性"
 attachCancel: "删除附件"
@@ -867,8 +866,6 @@ makeReactionsPublicDescription: "将您发表过的回应设置成公开可见
 classic: "经典"
 muteThread: "屏蔽帖子列表"
 unmuteThread: "取消屏蔽帖子列表"
-ffVisibility: "关注关系的可见范围"
-ffVisibilityDescription: "您可以设置您的关注/关注者信息的公开范围"
 continueThread: "查看更多帖子"
 deleteAccountConfirm: "将要删除账户。是否确认?"
 incorrectPassword: "密码错误"
@@ -1164,7 +1161,7 @@ _serverSettings:
   appIconUsageExample: "例如:作为书签添加到 PWA 或手机主屏幕的时候"
   appIconStyleRecommendation: "因为有可能会被裁切为圆形或者圆角矩形,建议使用边缘带有留白背景的图标。"
   appIconResolutionMustBe: "分辨率必须为 {resolution}。"
-  manifestJsonOverride: "覆盖 mainfest.json"
+  manifestJsonOverride: "覆盖 manifest.json"
   shortName: "简称"
   shortNameDescription: "如果服务器的正式名称很长,可以用简称或者別名来替代。"
 _accountMigration:
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 7f3399ed90..8fbf036385 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -121,7 +121,12 @@ sensitive: "敏感內容"
 add: "新增"
 reaction: "反應"
 reactions: "反應"
-reactionSetting: "在選擇器中顯示反應"
+emojiPicker: "表情符號選擇器"
+pinnedEmojisForReactionSettingDescription: "選擇反應時可以設定要固定顯示在頂端的表情符號"
+pinnedEmojisSettingDescription: "輸入表情符號時可以設定要固定顯示在頂端的表情符號"
+emojiPickerDisplay: "顯示表情符號選擇器"
+overwriteFromPinnedEmojisForReaction: "從反應複寫設定"
+overwriteFromPinnedEmojis: "從一般複寫設定"
 reactionSettingDescription2: "拖動以交換,點擊以刪除,按下「+」以新增。"
 rememberNoteVisibility: "記住貼文可見性"
 attachCancel: "移除附件"
@@ -261,7 +266,7 @@ removed: "已刪除"
 removeAreYouSure: "確定要刪掉「{x}」嗎?"
 deleteAreYouSure: "確定要刪掉「{x}」嗎?"
 resetAreYouSure: "確定要重設嗎?"
-areYouSure: "您確定要移除所有裝飾嗎?"
+areYouSure: "是否確定?"
 saved: "已儲存"
 messaging: "聊天"
 upload: "上傳"
@@ -782,7 +787,7 @@ receiveAnnouncementFromInstance: "接收由本實例發出的電郵通知"
 emailNotification: "郵件通知"
 publish: "發布"
 inChannelSearch: "頻道内搜尋"
-useReactionPickerForContextMenu: "點擊右鍵開啟反應工具欄"
+useReactionPickerForContextMenu: "點擊右鍵開啟反應選擇器"
 typingUsers: "{users}輸入中"
 jumpToSpecifiedDate: "跳轉到特定日期"
 showingPastTimeline: "顯示過往的時間軸"
@@ -876,8 +881,8 @@ makeReactionsPublicDescription: "將您做過的反應設為公開可見。"
 classic: "經典"
 muteThread: "將貼文串設為靜音"
 unmuteThread: "將貼文串的靜音解除"
-ffVisibility: "連繫的可見性"
-ffVisibilityDescription: "您可以設定追隨或追隨者資訊的公開範圍"
+followingVisibility: "追隨中的可見性"
+followersVisibility: "追隨者的可見性"
 continueThread: "查看更多貼文"
 deleteAccountConfirm: "將要刪除帳戶。是否確定?"
 incorrectPassword: "密碼錯誤。"
@@ -1173,6 +1178,9 @@ doReaction: "做出反應"
 code: "程式碼"
 reloadRequiredToApplySettings: "需要重新載入頁面設定才能生效。"
 remainingN: "剩餘:{n}"
+overwriteContentConfirm: "確定要覆蓋目前的內容嗎?"
+seasonalScreenEffect: "隨季節變換畫面的呈現"
+decorate: "設置頭像裝飾"
 _announcement:
   forExistingUsers: "僅限既有的使用者"
   forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。"
@@ -2041,7 +2049,7 @@ _profile:
   changeAvatar: "更換大頭貼"
   changeBanner: "變更橫幅圖像"
   verifiedLinkDescription: "如果輸入包含您個人資料的網站 URL,欄位旁邊將出現驗證圖示。"
-  avatarDecorationMax: "最多可以設置{max}個裝飾。"
+  avatarDecorationMax: "最多可以設置 {max} 個裝飾。"
 _exportOrImport:
   allNotes: "所有貼文"
   favoritedNotes: "「我的最愛」貼文"

From 21882b69ec6f093ce76e02c751927eeb6de08e0c Mon Sep 17 00:00:00 2001
From: GrapeApple0 <84321396+GrapeApple0@users.noreply.github.com>
Date: Tue, 19 Dec 2023 22:44:42 +0900
Subject: [PATCH 271/435] =?UTF-8?q?fix:=20=E8=BF=BD=E5=8A=A0=E7=B5=B5?=
 =?UTF-8?q?=E6=96=87=E5=AD=97=E5=80=99=E8=A3=9C=E8=BE=9E=E6=9B=B8=E3=81=AE?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3(=E4=B8=BB=E3=81=AB=E5=9B=BD=E6=97=97)=20(#12?=
 =?UTF-8?q?716)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: 絵文字候補辞書の修正(主に国旗)

* 間違って削除してしまった絵文字の復旧
---
 .../src/unicode-emoji-indexes/en-US.json      | 500 +++++++++---------
 1 file changed, 250 insertions(+), 250 deletions(-)

diff --git a/packages/frontend/src/unicode-emoji-indexes/en-US.json b/packages/frontend/src/unicode-emoji-indexes/en-US.json
index ad406dc462..4d8b040ad2 100644
--- a/packages/frontend/src/unicode-emoji-indexes/en-US.json
+++ b/packages/frontend/src/unicode-emoji-indexes/en-US.json
@@ -484,6 +484,7 @@
 	"🐝": ["animal", "insect", "nature", "bug", "spring", "honey"],
 	"🐛": ["animal", "insect", "nature", "worm"],
 	"🦋": ["animal", "insect", "nature", "caterpillar"],
+	"🫏": ["animal", "ass", "burro", "mammal", "mule", "stubborn"],
 	"🐌": ["slow", "animal", "shell"],
 	"🐞": ["animal", "insect", "nature", "ladybug"],
 	"🐜": ["animal", "insect", "nature", "bug"],
@@ -523,7 +524,6 @@
 	"🐐": ["animal", "nature"],
 	"🐏": ["animal", "sheep", "nature"],
 	"🐑": ["animal", "nature", "wool", "shipit"],
-	"🫏": ["animal", "ass", "burro", "mammal", "mule", "stubborn"],
 	"🐎": ["animal", "gamble", "luck"],
 	"🐖": ["animal", "nature"],
 	"🐀": ["animal", "mouse", "rodent"],
@@ -1548,258 +1548,258 @@
 	"🕥": ["time", "late", "early", "schedule"],
 	"🕦": ["time", "late", "early", "schedule"],
 	"🕧": ["time", "late", "early", "schedule"],
-	"🇦🇫": ["af", "flag", "nation", "country", "banner"],
-	"🇦🇽": ["Åland", "islands", "flag", "nation", "country", "banner"],
-	"🇦🇱": ["al", "flag", "nation", "country", "banner"],
-	"🇩🇿": ["dz", "flag", "nation", "country", "banner"],
-	"🇦🇸": ["american", "ws", "flag", "nation", "country", "banner"],
+	"🇦🇫": ["af", "afghanistan", "flag", "nation", "country", "banner"],
+	"🇦🇽": ["ax", "Åland", "aland", "islands", "flag", "nation", "country", "banner"],
+	"🇦🇱": ["al", "albania", "flag", "nation", "country", "banner"],
+	"🇩🇿": ["dz", "algeria", "flag", "nation", "country", "banner"],
+	"🇦🇸": ["as", "american", "samoa", "flag", "nation", "country", "banner"],
 	"🇦🇩": ["ad", "flag", "nation", "country", "banner"],
-	"🇦🇴": ["ao", "flag", "nation", "country", "banner"],
-	"🇦🇮": ["ai", "flag", "nation", "country", "banner"],
-	"🇦🇶": ["aq", "flag", "nation", "country", "banner"],
-	"🇦🇬": ["antigua", "barbuda", "flag", "nation", "country", "banner"],
-	"🇦🇷": ["ar", "flag", "nation", "country", "banner"],
-	"🇦🇲": ["am", "flag", "nation", "country", "banner"],
-	"🇦🇼": ["aw", "flag", "nation", "country", "banner"],
-	"🇦🇨": ["flag", "nation", "country", "banner"],
-	"🇦🇺": ["au", "flag", "nation", "country", "banner"],
-	"🇦🇹": ["at", "flag", "nation", "country", "banner"],
-	"🇦🇿": ["az", "flag", "nation", "country", "banner"],
-	"🇧🇸": ["bs", "flag", "nation", "country", "banner"],
-	"🇧🇭": ["bh", "flag", "nation", "country", "banner"],
-	"🇧🇩": ["bd", "flag", "nation", "country", "banner"],
-	"🇧🇧": ["bb", "flag", "nation", "country", "banner"],
-	"🇧🇾": ["by", "flag", "nation", "country", "banner"],
-	"🇧🇪": ["be", "flag", "nation", "country", "banner"],
-	"🇧🇿": ["bz", "flag", "nation", "country", "banner"],
-	"🇧🇯": ["bj", "flag", "nation", "country", "banner"],
-	"🇧🇲": ["bm", "flag", "nation", "country", "banner"],
-	"🇧🇹": ["bt", "flag", "nation", "country", "banner"],
-	"🇧🇴": ["bo", "flag", "nation", "country", "banner"],
-	"🇧🇶": ["bonaire", "flag", "nation", "country", "banner"],
-	"🇧🇦": ["bosnia", "herzegovina", "flag", "nation", "country", "banner"],
-	"🇧🇼": ["bw", "flag", "nation", "country", "banner"],
-	"🇧🇷": ["br", "flag", "nation", "country", "banner"],
-	"🇮🇴": ["british", "indian", "ocean", "territory", "flag", "nation", "country", "banner"],
-	"🇻🇬": ["british", "virgin", "islands", "bvi", "flag", "nation", "country", "banner"],
-	"🇧🇳": ["bn", "darussalam", "flag", "nation", "country", "banner"],
-	"🇧🇬": ["bg", "flag", "nation", "country", "banner"],
-	"🇧🇫": ["burkina", "faso", "flag", "nation", "country", "banner"],
-	"🇧🇮": ["bi", "flag", "nation", "country", "banner"],
-	"🇨🇻": ["cabo", "verde", "flag", "nation", "country", "banner"],
-	"🇰🇭": ["kh", "flag", "nation", "country", "banner"],
-	"🇨🇲": ["cm", "flag", "nation", "country", "banner"],
-	"🇨🇦": ["ca", "flag", "nation", "country", "banner"],
-	"🇮🇨": ["canary", "islands", "flag", "nation", "country", "banner"],
-	"🇰🇾": ["cayman", "islands", "flag", "nation", "country", "banner"],
-	"🇨🇫": ["central", "african", "republic", "flag", "nation", "country", "banner"],
-	"🇹🇩": ["td", "flag", "nation", "country", "banner"],
-	"🇨🇱": ["flag", "nation", "country", "banner"],
-	"🇨🇳": ["china", "chinese", "prc", "flag", "country", "nation", "banner"],
-	"🇨🇽": ["christmas", "island", "flag", "nation", "country", "banner"],
-	"🇨🇨": ["cocos", "keeling", "islands", "flag", "nation", "country", "banner"],
-	"🇨🇴": ["co", "flag", "nation", "country", "banner"],
-	"🇰🇲": ["km", "flag", "nation", "country", "banner"],
-	"🇨🇬": ["congo", "flag", "nation", "country", "banner"],
-	"🇨🇩": ["congo", "democratic", "republic", "flag", "nation", "country", "banner"],
-	"🇨🇰": ["cook", "islands", "flag", "nation", "country", "banner"],
-	"🇨🇷": ["costa", "rica", "flag", "nation", "country", "banner"],
-	"🇭🇷": ["hr", "flag", "nation", "country", "banner"],
-	"🇨🇺": ["cu", "flag", "nation", "country", "banner"],
-	"🇨🇼": ["curaçao", "flag", "nation", "country", "banner"],
-	"🇨🇾": ["cy", "flag", "nation", "country", "banner"],
-	"🇨🇿": ["cz", "flag", "nation", "country", "banner"],
-	"🇩🇰": ["dk", "flag", "nation", "country", "banner"],
-	"🇩🇯": ["dj", "flag", "nation", "country", "banner"],
-	"🇩🇲": ["dm", "flag", "nation", "country", "banner"],
-	"🇩🇴": ["dominican", "republic", "flag", "nation", "country", "banner"],
-	"🇪🇨": ["ec", "flag", "nation", "country", "banner"],
-	"🇪🇬": ["eg", "flag", "nation", "country", "banner"],
-	"🇸🇻": ["el", "salvador", "flag", "nation", "country", "banner"],
-	"🇬🇶": ["equatorial", "gn", "flag", "nation", "country", "banner"],
-	"🇪🇷": ["er", "flag", "nation", "country", "banner"],
-	"🇪🇪": ["ee", "flag", "nation", "country", "banner"],
-	"🇪🇹": ["et", "flag", "nation", "country", "banner"],
-	"🇪🇺": ["european", "union", "flag", "banner"],
-	"🇫🇰": ["falkland", "islands", "malvinas", "flag", "nation", "country", "banner"],
-	"🇫🇴": ["faroe", "islands", "flag", "nation", "country", "banner"],
-	"🇫🇯": ["fj", "flag", "nation", "country", "banner"],
-	"🇫🇮": ["fi", "flag", "nation", "country", "banner"],
-	"🇫🇷": ["banner", "flag", "nation", "france", "french", "country"],
-	"🇬🇫": ["french", "guiana", "flag", "nation", "country", "banner"],
-	"🇵🇫": ["french", "polynesia", "flag", "nation", "country", "banner"],
-	"🇹🇫": ["french", "southern", "territories", "flag", "nation", "country", "banner"],
-	"🇬🇦": ["ga", "flag", "nation", "country", "banner"],
-	"🇬🇲": ["gm", "flag", "nation", "country", "banner"],
-	"🇬🇪": ["ge", "flag", "nation", "country", "banner"],
-	"🇩🇪": ["german", "nation", "flag", "country", "banner"],
-	"🇬🇭": ["gh", "flag", "nation", "country", "banner"],
-	"🇬🇮": ["gi", "flag", "nation", "country", "banner"],
-	"🇬🇷": ["gr", "flag", "nation", "country", "banner"],
-	"🇬🇱": ["gl", "flag", "nation", "country", "banner"],
-	"🇬🇩": ["gd", "flag", "nation", "country", "banner"],
-	"🇬🇵": ["gp", "flag", "nation", "country", "banner"],
-	"🇬🇺": ["gu", "flag", "nation", "country", "banner"],
-	"🇬🇹": ["gt", "flag", "nation", "country", "banner"],
-	"🇬🇬": ["gg", "flag", "nation", "country", "banner"],
-	"🇬🇳": ["gn", "flag", "nation", "country", "banner"],
-	"🇬🇼": ["gw", "bissau", "flag", "nation", "country", "banner"],
-	"🇬🇾": ["gy", "flag", "nation", "country", "banner"],
-	"🇭🇹": ["ht", "flag", "nation", "country", "banner"],
-	"🇭🇳": ["hn", "flag", "nation", "country", "banner"],
-	"🇭🇰": ["hong", "kong", "flag", "nation", "country", "banner"],
-	"🇭🇺": ["hu", "flag", "nation", "country", "banner"],
-	"🇮🇸": ["is", "flag", "nation", "country", "banner"],
-	"🇮🇳": ["in", "flag", "nation", "country", "banner"],
-	"🇮🇩": ["flag", "nation", "country", "banner"],
-	"🇮🇷": ["iran, ", "islamic", "republic", "flag", "nation", "country", "banner"],
-	"🇮🇶": ["iq", "flag", "nation", "country", "banner"],
-	"🇮🇪": ["ie", "flag", "nation", "country", "banner"],
-	"🇮🇲": ["isle", "man", "flag", "nation", "country", "banner"],
-	"🇮🇱": ["il", "flag", "nation", "country", "banner"],
-	"🇮🇹": ["italy", "flag", "nation", "country", "banner"],
-	"🇨🇮": ["ivory", "coast", "flag", "nation", "country", "banner"],
-	"🇯🇲": ["jm", "flag", "nation", "country", "banner"],
-	"🇯🇵": ["japanese", "nation", "flag", "country", "banner"],
-	"🇯🇪": ["je", "flag", "nation", "country", "banner"],
-	"🇯🇴": ["jo", "flag", "nation", "country", "banner"],
-	"🇰🇿": ["kz", "flag", "nation", "country", "banner"],
-	"🇰🇪": ["ke", "flag", "nation", "country", "banner"],
-	"🇰🇮": ["ki", "flag", "nation", "country", "banner"],
-	"🇽🇰": ["xk", "flag", "nation", "country", "banner"],
-	"🇰🇼": ["kw", "flag", "nation", "country", "banner"],
-	"🇰🇬": ["kg", "flag", "nation", "country", "banner"],
-	"🇱🇦": ["lao", "democratic", "republic", "flag", "nation", "country", "banner"],
-	"🇱🇻": ["lv", "flag", "nation", "country", "banner"],
-	"🇱🇧": ["lb", "flag", "nation", "country", "banner"],
-	"🇱🇸": ["ls", "flag", "nation", "country", "banner"],
-	"🇱🇷": ["lr", "flag", "nation", "country", "banner"],
-	"🇱🇾": ["ly", "flag", "nation", "country", "banner"],
-	"🇱🇮": ["li", "flag", "nation", "country", "banner"],
-	"🇱🇹": ["lt", "flag", "nation", "country", "banner"],
-	"🇱🇺": ["lu", "flag", "nation", "country", "banner"],
-	"🇲🇴": ["macao", "flag", "nation", "country", "banner"],
-	"🇲🇰": ["macedonia, ", "flag", "nation", "country", "banner"],
-	"🇲🇬": ["mg", "flag", "nation", "country", "banner"],
-	"🇲🇼": ["mw", "flag", "nation", "country", "banner"],
-	"🇲🇾": ["my", "flag", "nation", "country", "banner"],
-	"🇲🇻": ["mv", "flag", "nation", "country", "banner"],
-	"🇲🇱": ["ml", "flag", "nation", "country", "banner"],
-	"🇲🇹": ["mt", "flag", "nation", "country", "banner"],
-	"🇲🇭": ["marshall", "islands", "flag", "nation", "country", "banner"],
-	"🇲🇶": ["mq", "flag", "nation", "country", "banner"],
-	"🇲🇷": ["mr", "flag", "nation", "country", "banner"],
-	"🇲🇺": ["mu", "flag", "nation", "country", "banner"],
-	"🇾🇹": ["yt", "flag", "nation", "country", "banner"],
-	"🇲🇽": ["mx", "flag", "nation", "country", "banner"],
-	"🇫🇲": ["micronesia, ", "federated", "states", "flag", "nation", "country", "banner"],
-	"🇲🇩": ["moldova, ", "republic", "flag", "nation", "country", "banner"],
-	"🇲🇨": ["mc", "flag", "nation", "country", "banner"],
-	"🇲🇳": ["mn", "flag", "nation", "country", "banner"],
-	"🇲🇪": ["me", "flag", "nation", "country", "banner"],
-	"🇲🇸": ["ms", "flag", "nation", "country", "banner"],
-	"🇲🇦": ["ma", "flag", "nation", "country", "banner"],
-	"🇲🇿": ["mz", "flag", "nation", "country", "banner"],
-	"🇲🇲": ["mm", "flag", "nation", "country", "banner"],
-	"🇳🇦": ["na", "flag", "nation", "country", "banner"],
-	"🇳🇷": ["nr", "flag", "nation", "country", "banner"],
-	"🇳🇵": ["np", "flag", "nation", "country", "banner"],
-	"🇳🇱": ["nl", "flag", "nation", "country", "banner"],
-	"🇳🇨": ["new", "caledonia", "flag", "nation", "country", "banner"],
-	"🇳🇿": ["new", "zealand", "flag", "nation", "country", "banner"],
-	"🇳🇮": ["ni", "flag", "nation", "country", "banner"],
-	"🇳🇪": ["ne", "flag", "nation", "country", "banner"],
-	"🇳🇬": ["flag", "nation", "country", "banner"],
-	"🇳🇺": ["nu", "flag", "nation", "country", "banner"],
-	"🇳🇫": ["norfolk", "island", "flag", "nation", "country", "banner"],
-	"🇲🇵": ["northern", "mariana", "islands", "flag", "nation", "country", "banner"],
-	"🇰🇵": ["north", "korea", "nation", "flag", "country", "banner"],
-	"🇳🇴": ["no", "flag", "nation", "country", "banner"],
-	"🇴🇲": ["om_symbol", "flag", "nation", "country", "banner"],
-	"🇵🇰": ["pk", "flag", "nation", "country", "banner"],
-	"🇵🇼": ["pw", "flag", "nation", "country", "banner"],
-	"🇵🇸": ["palestine", "palestinian", "territories", "flag", "nation", "country", "banner"],
-	"🇵🇦": ["pa", "flag", "nation", "country", "banner"],
-	"🇵🇬": ["papua", "new", "guinea", "flag", "nation", "country", "banner"],
-	"🇵🇾": ["py", "flag", "nation", "country", "banner"],
-	"🇵🇪": ["pe", "flag", "nation", "country", "banner"],
-	"🇵🇭": ["ph", "flag", "nation", "country", "banner"],
-	"🇵🇳": ["pitcairn", "flag", "nation", "country", "banner"],
-	"🇵🇱": ["pl", "flag", "nation", "country", "banner"],
-	"🇵🇹": ["pt", "flag", "nation", "country", "banner"],
-	"🇵🇷": ["puerto", "rico", "flag", "nation", "country", "banner"],
-	"🇶🇦": ["qa", "flag", "nation", "country", "banner"],
-	"🇷🇪": ["réunion", "flag", "nation", "country", "banner"],
-	"🇷🇴": ["ro", "flag", "nation", "country", "banner"],
-	"🇷🇺": ["russian", "federation", "flag", "nation", "country", "banner"],
-	"🇷🇼": ["rw", "flag", "nation", "country", "banner"],
-	"🇧🇱": ["saint", "barthélemy", "flag", "nation", "country", "banner"],
-	"🇸🇭": ["saint", "helena", "ascension", "tristan", "cunha", "flag", "nation", "country", "banner"],
-	"🇰🇳": ["saint", "kitts", "nevis", "flag", "nation", "country", "banner"],
-	"🇱🇨": ["saint", "lucia", "flag", "nation", "country", "banner"],
-	"🇵🇲": ["saint", "pierre", "miquelon", "flag", "nation", "country", "banner"],
-	"🇻🇨": ["saint", "vincent", "grenadines", "flag", "nation", "country", "banner"],
-	"🇼🇸": ["ws", "flag", "nation", "country", "banner"],
-	"🇸🇲": ["san", "marino", "flag", "nation", "country", "banner"],
-	"🇸🇹": ["sao", "tome", "principe", "flag", "nation", "country", "banner"],
-	"🇸🇦": ["flag", "nation", "country", "banner"],
-	"🇸🇳": ["sn", "flag", "nation", "country", "banner"],
-	"🇷🇸": ["rs", "flag", "nation", "country", "banner"],
-	"🇸🇨": ["sc", "flag", "nation", "country", "banner"],
-	"🇸🇱": ["sierra", "leone", "flag", "nation", "country", "banner"],
-	"🇸🇬": ["sg", "flag", "nation", "country", "banner"],
-	"🇸🇽": ["sint", "maarten", "dutch", "flag", "nation", "country", "banner"],
-	"🇸🇰": ["sk", "flag", "nation", "country", "banner"],
-	"🇸🇮": ["si", "flag", "nation", "country", "banner"],
-	"🇸🇧": ["solomon", "islands", "flag", "nation", "country", "banner"],
-	"🇸🇴": ["so", "flag", "nation", "country", "banner"],
-	"🇿🇦": ["south", "africa", "flag", "nation", "country", "banner"],
-	"🇬🇸": ["south", "georgia", "sandwich", "islands", "flag", "nation", "country", "banner"],
-	"🇰🇷": ["south", "korea", "nation", "flag", "country", "banner"],
-	"🇸🇸": ["south", "sd", "flag", "nation", "country", "banner"],
-	"🇪🇸": ["spain", "flag", "nation", "country", "banner"],
-	"🇱🇰": ["sri", "lanka", "flag", "nation", "country", "banner"],
-	"🇸🇩": ["sd", "flag", "nation", "country", "banner"],
-	"🇸🇷": ["sr", "flag", "nation", "country", "banner"],
-	"🇸🇿": ["sz", "flag", "nation", "country", "banner"],
-	"🇸🇪": ["se", "flag", "nation", "country", "banner"],
-	"🇨🇭": ["ch", "flag", "nation", "country", "banner"],
-	"🇸🇾": ["syrian", "arab", "republic", "flag", "nation", "country", "banner"],
-	"🇹🇼": ["tw", "flag", "nation", "country", "banner"],
-	"🇹🇯": ["tj", "flag", "nation", "country", "banner"],
-	"🇹🇿": ["tanzania, ", "united", "republic", "flag", "nation", "country", "banner"],
-	"🇹🇭": ["th", "flag", "nation", "country", "banner"],
-	"🇹🇱": ["timor", "leste", "flag", "nation", "country", "banner"],
-	"🇹🇬": ["tg", "flag", "nation", "country", "banner"],
-	"🇹🇰": ["tk", "flag", "nation", "country", "banner"],
-	"🇹🇴": ["to", "flag", "nation", "country", "banner"],
-	"🇹🇹": ["trinidad", "tobago", "flag", "nation", "country", "banner"],
-	"🇹🇦": ["flag", "nation", "country", "banner"],
-	"🇹🇳": ["tn", "flag", "nation", "country", "banner"],
-	"🇹🇷": ["turkey", "flag", "nation", "country", "banner"],
-	"🇹🇲": ["flag", "nation", "country", "banner"],
-	"🇹🇨": ["turks", "caicos", "islands", "flag", "nation", "country", "banner"],
-	"🇹🇻": ["flag", "nation", "country", "banner"],
-	"🇺🇬": ["ug", "flag", "nation", "country", "banner"],
-	"🇺🇦": ["ua", "flag", "nation", "country", "banner"],
-	"🇦🇪": ["united", "arab", "emirates", "flag", "nation", "country", "banner"],
-	"🇬🇧": ["united", "kingdom", "great", "britain", "northern", "ireland", "flag", "nation", "country", "banner", "british", "UK", "english", "england", "union jack"],
+	"🇦🇴": ["ao", "angola", "flag", "nation", "country", "banner"],
+	"🇦🇮": ["ai", "anguilla", "flag", "nation", "country", "banner"],
+	"🇦🇶": ["aq", "antarctique", "flag", "nation", "country", "banner"],
+	"🇦🇬": ["ag", "antigua", "barbuda", "flag", "nation", "country", "banner"],
+	"🇦🇷": ["ar", "argentina", "flag", "nation", "country", "banner"],
+	"🇦🇲": ["am", "armenia", "flag", "nation", "country", "banner"],
+	"🇦🇼": ["aw", "aruba", "flag", "nation", "country", "banner"],
+	"🇦🇨": ["ac", "ascension", "island", "flag", "nation", "country", "banner"],
+	"🇦🇺": ["au", "australia", "flag", "nation", "country", "banner"],
+	"🇦🇹": ["at", "austria", "flag", "nation", "country", "banner"],
+	"🇦🇿": ["az", "azerbaijan", "flag", "nation", "country", "banner"],
+	"🇧🇸": ["bs", "bahamas", "flag", "nation", "country", "banner"],
+	"🇧🇭": ["bh", "bahrain", "flag", "nation", "country", "banner"],
+	"🇧🇩": ["bd", "bangladesh", "flag", "nation", "country", "banner"],
+	"🇧🇧": ["bb", "barbados", "flag", "nation", "country", "banner"],
+	"🇧🇾": ["by", "belarus", "flag", "nation", "country", "banner"],
+	"🇧🇪": ["be", "belgium", "flag", "nation", "country", "banner"],
+	"🇧🇿": ["bz", "belize", "flag", "nation", "country", "banner"],
+	"🇧🇯": ["bj", "benin", "flag", "nation", "country", "banner"],
+	"🇧🇲": ["bm", "bermuda", "flag", "nation", "country", "banner"],
+	"🇧🇹": ["bt", "bhutan", "flag", "nation", "country", "banner"],
+	"🇧🇴": ["bo", "bolivia", "flag", "nation", "country", "banner"],
+	"🇧🇶": ["bq", "bonaire", "flag", "nation", "country", "banner"],
+	"🇧🇦": ["ba", "bosnia", "herzegovina", "flag", "nation", "country", "banner"],
+	"🇧🇼": ["bw", "botswana", "flag", "nation", "country", "banner"],
+	"🇧🇷": ["br", "brazil", "flag", "nation", "country", "banner"],
+	"🇮🇴": ["io", "british", "indian", "ocean", "territory", "flag", "nation", "country", "banner"],
+	"🇻🇬": ["vg", "british", "virgin", "islands", "bvi", "flag", "nation", "country", "banner"],
+	"🇧🇳": ["bn", "brunei", "darussalam", "flag", "nation", "country", "banner"],
+	"🇧🇬": ["bg", "bulgaria", "flag", "nation", "country", "banner"],
+	"🇧🇫": ["bf", "burkina", "faso", "flag", "nation", "country", "banner"],
+	"🇧🇮": ["bi", "burundi", "flag", "nation", "country", "banner"],
+	"🇨🇻": ["cv", "cabo", "verde", "flag", "nation", "country", "banner"],
+	"🇰🇭": ["kh", "cambodia", "flag", "nation", "country", "banner"],
+	"🇨🇲": ["cm", "cameroon", "flag", "nation", "country", "banner"],
+	"🇨🇦": ["ca", "canada", "flag", "nation", "country", "banner"],
+	"🇮🇨": ["ic", "canary", "islands", "flag", "nation", "country", "banner"],
+	"🇰🇾": ["ky", "cayman", "islands", "flag", "nation", "country", "banner"],
+	"🇨🇫": ["cf", "central", "african", "republic", "flag", "nation", "country", "banner"],
+	"🇹🇩": ["td", "chad", "flag", "nation", "country", "banner"],
+	"🇨🇱": ["cl", "chile", "flag", "nation", "country", "banner"],
+	"🇨🇳": ["cn", "china", "chinese", "prc", "flag", "country", "nation", "banner"],
+	"🇨🇽": ["cx", "christmas", "island", "flag", "nation", "country", "banner"],
+	"🇨🇨": ["cc", "cocos", "keeling", "islands", "flag", "nation", "country", "banner"],
+	"🇨🇴": ["co", "colombia", "flag", "nation", "country", "banner"],
+	"🇰🇲": ["km", "comoros", "flag", "nation", "country", "banner"],
+	"🇨🇬": ["cg", "republic", "congo", "flag", "nation", "country", "banner"],
+	"🇨🇩": ["cd", "democratic", "republic", "congo", "flag", "nation", "country", "banner"],
+	"🇨🇰": ["ck", "cook", "islands", "flag", "nation", "country", "banner"],
+	"🇨🇷": ["cr", "costa", "rica", "flag", "nation", "country", "banner"],
+	"🇭🇷": ["hr", "croatia", "flag", "nation", "country", "banner"],
+	"🇨🇺": ["cu", "cuba", "flag", "nation", "country", "banner"],
+	"🇨🇼": ["cw", "curacao", "curaçao", "flag", "nation", "country", "banner"],
+	"🇨🇾": ["cy", "cyprus", "flag", "nation", "country", "banner"],
+	"🇨🇿": ["cz", "czech", "republic", "flag", "nation", "country", "banner"],
+	"🇩🇰": ["dk", "denmark", "flag", "nation", "country", "banner"],
+	"🇩🇯": ["dj", "djibouti", "flag", "nation", "country", "banner"],
+	"🇩🇲": ["dm", "dominica", "flag", "nation", "country", "banner"],
+	"🇩🇴": ["do", "dominican", "republic", "flag", "nation", "country", "banner"],
+	"🇪🇨": ["ec", "ecuador", "flag", "nation", "country", "banner"],
+	"🇪🇬": ["eg", "egypt", "flag", "nation", "country", "banner"],
+	"🇸🇻": ["sv", "el", "salvador", "flag", "nation", "country", "banner"],
+	"🇬🇶": ["gq", "equatorial", "guinea", "flag", "nation", "country", "banner"],
+	"🇪🇷": ["er", "eritrea", "flag", "nation", "country", "banner"],
+	"🇪🇪": ["ee", "estonia", "flag", "nation", "country", "banner"],
+	"🇪🇹": ["et", "ethiopia", "flag", "nation", "country", "banner"],
+	"🇪🇺": ["eu", "european", "union", "flag", "banner"],
+	"🇫🇰": ["fk", "falkland", "islands", "malvinas", "flag", "nation", "country", "banner"],
+	"🇫🇴": ["fo", "faroe", "islands", "flag", "nation", "country", "banner"],
+	"🇫🇯": ["fj", "fiji", "flag", "nation", "country", "banner"],
+	"🇫🇮": ["fi", "finland", "flag", "nation", "country", "banner"],
+	"🇫🇷": ["fr", "banner", "flag", "nation", "france", "french", "country"],
+	"🇬🇫": ["gf", "french", "guiana", "flag", "nation", "country", "banner"],
+	"🇵🇫": ["pf", "french", "polynesia", "flag", "nation", "country", "banner"],
+	"🇹🇫": ["tf", "french", "southern", "territories", "flag", "nation", "country", "banner"],
+	"🇬🇦": ["ga", "gabon", "flag", "nation", "country", "banner"],
+	"🇬🇲": ["gm", "gambia", "flag", "nation", "country", "banner"],
+	"🇬🇪": ["ge", "georgia", "flag", "nation", "country", "banner"],
+	"🇩🇪": ["de", "deutschland", "german", "nation", "flag", "country", "banner"],
+	"🇬🇭": ["gh", "ghana", "flag", "nation", "country", "banner"],
+	"🇬🇮": ["gi", "gibraltar", "flag", "nation", "country", "banner"],
+	"🇬🇷": ["gr", "greece", "flag", "nation", "country", "banner"],
+	"🇬🇱": ["gl", "green", "land", "flag", "nation", "country", "banner"],
+	"🇬🇩": ["gd", "grenada", "flag", "nation", "country", "banner"],
+	"🇬🇵": ["gp", "guadeloupe", "flag", "nation", "country", "banner"],
+	"🇬🇺": ["gu", "guam", "flag", "nation", "country", "banner"],
+	"🇬🇹": ["gt", "guatemala", "flag", "nation", "country", "banner"],
+	"🇬🇬": ["gg", "guernsey", "flag", "nation", "country", "banner"],
+	"🇬🇳": ["gn", "guinea", "flag", "nation", "country", "banner"],
+	"🇬🇼": ["gw", "guiana", "bissau", "flag", "nation", "country", "banner"],
+	"🇬🇾": ["gy", "guyana", "flag", "nation", "country", "banner"],
+	"🇭🇹": ["ht", "haiti", "flag", "nation", "country", "banner"],
+	"🇭🇳": ["hn", "honduras", "flag", "nation", "country", "banner"],
+	"🇭🇰": ["hk", "hong", "kong", "flag", "nation", "country", "banner"],
+	"🇭🇺": ["hu", "hungary", "flag", "nation", "country", "banner"],
+	"🇮🇸": ["is", "iceland", "Ísland", "flag", "nation", "country", "banner"],
+	"🇮🇳": ["in", "india", "flag", "nation", "country", "banner"],
+	"🇮🇩": ["id", "indonesia", "flag", "nation", "country", "banner"],
+	"🇮🇷": ["ir", "iran", "islamic", "republic", "flag", "nation", "country", "banner"],
+	"🇮🇶": ["iq", "iraq", "flag", "nation", "country", "banner"],
+	"🇮🇪": ["ie", "ireland", "flag", "nation", "country", "banner"],
+	"🇮🇲": ["im", "isle", "man", "flag", "nation", "country", "banner"],
+	"🇮🇱": ["il", "israel", "flag", "nation", "country", "banner"],
+	"🇮🇹": ["it", "italy", "flag", "nation", "country", "banner"],
+	"🇨🇮": ["ci", "cote", "divoire", "Côte", "d'Ivoire", "ivory", "coast", "flag", "nation", "country", "banner"],
+	"🇯🇲": ["jm", "jamaica", "flag", "nation", "country", "banner"],
+	"🇯🇵": ["jp", "japan", "japanese", "nation", "flag", "country", "banner"],
+	"🇯🇪": ["je", "jersey", "flag", "nation", "country", "banner"],
+	"🇯🇴": ["jo", "jordan", "flag", "nation", "country", "banner"],
+	"🇰🇿": ["kz", "kazakhstan", "flag", "nation", "country", "banner"],
+	"🇰🇪": ["ke", "kenya", "flag", "nation", "country", "banner"],
+	"🇰🇮": ["ki", "kiribati", "flag", "nation", "country", "banner"],
+	"🇽🇰": ["xk", "kosovo", "flag", "nation", "country", "banner"],
+	"🇰🇼": ["kw", "kuwait", "flag", "nation", "country", "banner"],
+	"🇰🇬": ["kg", "kyrgyzstan", "kyrgyz", "flag", "nation", "country", "banner"],
+	"🇱🇦": ["la", "laos", "lao", "democratic", "republic", "flag", "nation", "country", "banner"],
+	"🇱🇻": ["lv", "latvia", "flag", "nation", "country", "banner"],
+	"🇱🇧": ["lb", "lebanon", "flag", "nation", "country", "banner"],
+	"🇱🇸": ["ls", "lesotho", "flag", "nation", "country", "banner"],
+	"🇱🇷": ["lr", "liberia", "flag", "nation", "country", "banner"],
+	"🇱🇾": ["ly", "libya", "flag", "nation", "country", "banner"],
+	"🇱🇮": ["li", "liechtenstein", "flag", "nation", "country", "banner"],
+	"🇱🇹": ["lt", "lithuania", "flag", "nation", "country", "banner"],
+	"🇱🇺": ["lu", "luxembourg", "flag", "nation", "country", "banner"],
+	"🇲🇴": ["mo", "macao", "macau", "flag", "nation", "country", "banner"],
+	"🇲🇰": ["mk", "north", "macedonia", "flag", "nation", "country", "banner"],
+	"🇲🇬": ["mg", "madagascar", "flag", "nation", "country", "banner"],
+	"🇲🇼": ["mw", "malawi", "flag", "nation", "country", "banner"],
+	"🇲🇾": ["my", "malaysia", "flag", "nation", "country", "banner"],
+	"🇲🇻": ["mv", "maldives", "republic", "flag", "nation", "country", "banner"],
+	"🇲🇱": ["ml", "mali", "flag", "nation", "country", "banner"],
+	"🇲🇹": ["mt", "malta", "flag", "nation", "country", "banner"],
+	"🇲🇭": ["mh", "marshall", "islands", "flag", "nation", "country", "banner"],
+	"🇲🇶": ["mq", "martinique", "flag", "nation", "country", "banner"],
+	"🇲🇷": ["mr", "mauritania", "flag", "nation", "country", "banner"],
+	"🇲🇺": ["mu", "mauritius", "flag", "nation", "country", "banner"],
+	"🇾🇹": ["yt", "mayotte", "flag", "nation", "country", "banner"],
+	"🇲🇽": ["mx", "mexico", "flag", "nation", "country", "banner"],
+	"🇫🇲": ["fm", "micronesia", "federated", "states", "flag", "nation", "country", "banner"],
+	"🇲🇩": ["md", "moldova", "republic", "flag", "nation", "country", "banner"],
+	"🇲🇨": ["mc", "monaco", "flag", "nation", "country", "banner"],
+	"🇲🇳": ["mn", "mongolia", "flag", "nation", "country", "banner"],
+	"🇲🇪": ["me", "montenegro", "flag", "nation", "country", "banner"],
+	"🇲🇸": ["ms", "montserrat", "flag", "nation", "country", "banner"],
+	"🇲🇦": ["ma", "morocco", "flag", "nation", "country", "banner"],
+	"🇲🇿": ["mz", "mozambique", "flag", "nation", "country", "banner"],
+	"🇲🇲": ["mm", "myanmar", "flag", "nation", "country", "banner"],
+	"🇳🇦": ["na", "namibia", "flag", "nation", "country", "banner"],
+	"🇳🇷": ["nr", "nauru", "flag", "nation", "country", "banner"],
+	"🇳🇵": ["np", "nepal", "flag", "nation", "country", "banner"],
+	"🇳🇱": ["nl", "netherlands", "flag", "nation", "country", "banner"],
+	"🇳🇨": ["nc", "new", "caledonia", "flag", "nation", "country", "banner"],
+	"🇳🇿": ["nz", "new", "zealand", "flag", "nation", "country", "banner"],
+	"🇳🇮": ["ni", "nicaragua", "flag", "nation", "country", "banner"],
+	"🇳🇪": ["ne", "niger", "flag", "nation", "country", "banner"],
+	"🇳🇬": ["ng", "nigeria", "flag", "nation", "country", "banner"],
+	"🇳🇺": ["nu", "niue", "flag", "nation", "country", "banner"],
+	"🇳🇫": ["nf", "norfolk", "island", "flag", "nation", "country", "banner"],
+	"🇲🇵": ["mp", "northern", "mariana", "islands", "flag", "nation", "country", "banner"],
+	"🇰🇵": ["kp", "democratic", "people", "republic", "north", "korea", "nation", "flag", "country", "banner"],
+	"🇳🇴": ["no", "norway", "flag", "nation", "country", "banner"],
+	"🇴🇲": ["om", "oman", "flag", "nation", "country", "banner"],
+	"🇵🇰": ["pk", "pakistan", "flag", "nation", "country", "banner"],
+	"🇵🇼": ["pw", "palau", "flag", "nation", "country", "banner"],
+	"🇵🇸": ["ps", "palestine", "palestinian", "territories", "flag", "nation", "country", "banner"],
+	"🇵🇦": ["pa", "panama", "flag", "nation", "country", "banner"],
+	"🇵🇬": ["pg", "papua", "new", "guinea", "flag", "nation", "country", "banner"],
+	"🇵🇾": ["py", "paraguay", "flag", "nation", "country", "banner"],
+	"🇵🇪": ["pe", "peru", "flag", "nation", "country", "banner"],
+	"🇵🇭": ["ph", "philippines", "flag", "nation", "country", "banner"],
+	"🇵🇳": ["pn", "pitcairn", "flag", "nation", "country", "banner"],
+	"🇵🇱": ["pl", "poland", "flag", "nation", "country", "banner"],
+	"🇵🇹": ["pt", "portugal", "flag", "nation", "country", "banner"],
+	"🇵🇷": ["pr", "puerto", "rico", "flag", "nation", "country", "banner"],
+	"🇶🇦": ["qa", "qatar", "flag", "nation", "country", "banner"],
+	"🇷🇪": ["re", "reunion", "réunion", "flag", "nation", "country", "banner"],
+	"🇷🇴": ["ro", "romania", "flag", "nation", "country", "banner"],
+	"🇷🇺": ["ru", "russian", "federation", "flag", "nation", "country", "banner"],
+	"🇷🇼": ["rw", "rwanda", "flag", "nation", "country", "banner"],
+	"🇧🇱": ["bl", "saint", "barthélemy", "flag", "nation", "country", "banner"],
+	"🇸🇭": ["sh", "saint", "helena", "ascension", "tristan", "cunha", "flag", "nation", "country", "banner"],
+	"🇰🇳": ["kn", "saint", "kitts", "nevis", "flag", "nation", "country", "banner"],
+	"🇱🇨": ["lc", "saint", "lucia", "flag", "nation", "country", "banner"],
+	"🇵🇲": ["pm", "saint", "pierre", "miquelon", "flag", "nation", "country", "banner"],
+	"🇻🇨": ["vc", "saint", "vincent", "grenadines", "flag", "nation", "country", "banner"],
+	"🇼🇸": ["ws", "western", "samoa", "flag", "nation", "country", "banner"],
+	"🇸🇲": ["sm", "san", "marino", "flag", "nation", "country", "banner"],
+	"🇸🇹": ["st", "sao", "tome", "principe", "flag", "nation", "country", "banner"],
+	"🇸🇦": ["saudi", "arabia", "flag", "nation", "country", "banner"],
+	"🇸🇳": ["sn", "senegal", "flag", "nation", "country", "banner"],
+	"🇷🇸": ["rs", "serbia", "flag", "nation", "country", "banner"],
+	"🇸🇨": ["sc", "seychelles", "flag", "nation", "country", "banner"],
+	"🇸🇱": ["sl", "sierra", "leone", "flag", "nation", "country", "banner"],
+	"🇸🇬": ["sg", "singapore", "flag", "nation", "country", "banner"],
+	"🇸🇽": ["sx", "sint", "maarten", "dutch", "flag", "nation", "country", "banner"],
+	"🇸🇰": ["sk", "slovakia", "flag", "nation", "country", "banner"],
+	"🇸🇮": ["si", "slovenia", "flag", "nation", "country", "banner"],
+	"🇸🇧": ["sb", "solomon", "islands", "flag", "nation", "country", "banner"],
+	"🇸🇴": ["so", "somalia", "flag", "nation", "country", "banner"],
+	"🇿🇦": ["za", "south", "africa", "flag", "nation", "country", "banner"],
+	"🇬🇸": ["gs", "south", "georgia", "sandwich", "islands", "flag", "nation", "country", "banner"],
+	"🇰🇷": ["kr", "south", "korea", "nation", "flag", "country", "banner"],
+	"🇸🇸": ["ss", "south", "sudan", "flag", "nation", "country", "banner"],
+	"🇪🇸": ["es", "spain", "españa", "flag", "nation", "country", "banner"],
+	"🇱🇰": ["lk", "sri", "lanka", "flag", "nation", "country", "banner"],
+	"🇸🇩": ["sd", "sudan", "flag", "nation", "country", "banner"],
+	"🇸🇷": ["sr", "suriname", "flag", "nation", "country", "banner"],
+	"🇸🇿": ["sz", "eswatini", "flag", "nation", "country", "banner"],
+	"🇸🇪": ["se", "sweden", "flag", "nation", "country", "banner"],
+	"🇨🇭": ["ch", "switzerland", "confoederatio", "helvetica", "flag", "nation", "country", "banner"],
+	"🇸🇾": ["sy", "syrian", "arab", "republic", "flag", "nation", "country", "banner"],
+	"🇹🇼": ["tw", "taiwan", "flag", "nation", "country", "banner"],
+	"🇹🇯": ["tj", "tajikistan", "flag", "nation", "country", "banner"],
+	"🇹🇿": ["tz", "tanzania", "united", "republic", "flag", "nation", "country", "banner"],
+	"🇹🇭": ["th", "thailand", "flag", "nation", "country", "banner"],
+	"🇹🇱": ["tl", "timor", "leste", "flag", "nation", "country", "banner"],
+	"🇹🇬": ["tg", "togo", "flag", "nation", "country", "banner"],
+	"🇹🇰": ["tk", "tokelau", "flag", "nation", "country", "banner"],
+	"🇹🇴": ["to", "tonga", "flag", "nation", "country", "banner"],
+	"🇹🇹": ["tt", "trinidad", "tobago", "flag", "nation", "country", "banner"],
+	"🇹🇦": ["ta", "tristan", "da", "cunha", "flag", "nation", "country", "banner"],
+	"🇹🇳": ["tn", "tunisia", "flag", "nation", "country", "banner"],
+	"🇹🇷": ["tr", "turkey", "türkiye", "flag", "nation", "country", "banner"],
+	"🇹🇲": ["tm", "turkmenistan", "flag", "nation", "country", "banner"],
+	"🇹🇨": ["tc", "turks", "caicos", "islands", "flag", "nation", "country", "banner"],
+	"🇹🇻": ["tv", "tuvalu", "flag", "nation", "country", "banner"],
+	"🇺🇬": ["ug", "uganda", "flag", "nation", "country", "banner"],
+	"🇺🇦": ["ua", "ukraine", "flag", "nation", "country", "banner"],
+	"🇦🇪": ["ae", "united", "arab", "emirates", "flag", "nation", "country", "banner"],
+	"🇬🇧": ["gb", "united", "kingdom", "great", "britain", "northern", "ireland", "flag", "nation", "country", "banner", "british", "uk", "english", "england", "union jack"],
 	"🏴󠁧󠁢󠁥󠁮󠁧󠁿": ["flag", "english"],
 	"🏴󠁧󠁢󠁳󠁣󠁴󠁿": ["flag", "scottish"],
 	"🏴󠁧󠁢󠁷󠁬󠁳󠁿": ["flag", "welsh"],
-	"🇺🇸": ["united", "states", "america", "flag", "nation", "country", "banner"],
-	"🇻🇮": ["virgin", "islands", "us", "flag", "nation", "country", "banner"],
-	"🇺🇾": ["uy", "flag", "nation", "country", "banner"],
-	"🇺🇿": ["uz", "flag", "nation", "country", "banner"],
-	"🇻🇺": ["vu", "flag", "nation", "country", "banner"],
-	"🇻🇦": ["vatican", "city", "flag", "nation", "country", "banner"],
-	"🇻🇪": ["ve", "bolivarian", "republic", "flag", "nation", "country", "banner"],
-	"🇻🇳": ["viet", "nam", "flag", "nation", "country", "banner"],
-	"🇼🇫": ["wallis", "futuna", "flag", "nation", "country", "banner"],
-	"🇪🇭": ["western", "sahara", "flag", "nation", "country", "banner"],
-	"🇾🇪": ["ye", "flag", "nation", "country", "banner"],
-	"🇿🇲": ["zm", "flag", "nation", "country", "banner"],
-	"🇿🇼": ["zw", "flag", "nation", "country", "banner"],
-	"🇺🇳": ["un", "flag", "banner"],
+	"🇺🇸": ["us", "usa", "united", "states", "america", "flag", "nation", "country", "banner"],
+	"🇻🇮": ["vi", "virgin", "islands", "us", "flag", "nation", "country", "banner"],
+	"🇺🇾": ["uy", "uruguay", "flag", "nation", "country", "banner"],
+	"🇺🇿": ["uz", "uzbekistan", "flag", "nation", "country", "banner"],
+	"🇻🇺": ["vu", "vanuatu", "flag", "nation", "country", "banner"],
+	"🇻🇦": ["va", "vatican", "city", "flag", "nation", "country", "banner"],
+	"🇻🇪": ["ve", "venezuela", "flag", "nation", "country", "banner"],
+	"🇻🇳": ["vn", "viet", "nam", "flag", "nation", "country", "banner"],
+	"🇼🇫": ["wf", "wallis", "futuna", "flag", "nation", "country", "banner"],
+	"🇪🇭": ["eh", "western", "sahara", "flag", "nation", "country", "banner"],
+	"🇾🇪": ["ye", "yemen", "flag", "nation", "country", "banner"],
+	"🇿🇲": ["zm", "zambia", "flag", "nation", "country", "banner"],
+	"🇿🇼": ["zw", "zimbabwe", "flag", "nation", "country", "banner"],
+	"🇺🇳": ["un", "united", "nation", "flag", "banner"],
 	"🏴‍☠️": ["skull", "crossbones", "flag", "banner"]
 }

From 3ba54afc722b0944d6eb5a5647be05136b297154 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Tue, 19 Dec 2023 21:39:19 +0100
Subject: [PATCH 272/435] chore: remove unused env_file line

---
 docker-compose.yml.example | 2 --
 1 file changed, 2 deletions(-)

diff --git a/docker-compose.yml.example b/docker-compose.yml.example
index b0d68ce88e..5a8560bb42 100644
--- a/docker-compose.yml.example
+++ b/docker-compose.yml.example
@@ -55,8 +55,6 @@ services:
 #    environment:
 #      - MEILI_NO_ANALYTICS=true
 #      - MEILI_ENV=production
-#    env_file:
-#      - .config/meilisearch.env
 #    networks:
 #      - shonk
 #    volumes:

From 0d23e7a27829e3793c591e16aa334d872b2a4e44 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Tue, 19 Dec 2023 22:03:43 +0100
Subject: [PATCH 273/435] fix: autocomplete not showing up if dot in username

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

diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts
index 0b4ebb4410..0d6756d498 100644
--- a/packages/frontend/src/scripts/autocomplete.ts
+++ b/packages/frontend/src/scripts/autocomplete.ts
@@ -97,7 +97,7 @@ export class Autocomplete {
 
 		if (isMention) {
 			const username = text.substring(mentionIndex + 1);
-			if (username !== '' && username.match(/^[a-zA-Z0-9_]+$/)) {
+			if (username !== '' && username.match(/^[a-zA-Z0-9_.]+$/)) {
 				this.open('user', username);
 				opened = true;
 			} else if (username === '') {

From 10d4ae107bef82f988d39c40aa528c720612209a Mon Sep 17 00:00:00 2001
From: 1STEP621 <86859447+1STEP621@users.noreply.github.com>
Date: Wed, 20 Dec 2023 16:19:49 +0900
Subject: [PATCH 274/435] =?UTF-8?q?Fix(frontend):=20CW=E5=86=85=E3=81=AA?=
 =?UTF-8?q?=E3=81=A9=E3=81=AE=E7=94=BB=E5=83=8F=E3=81=8C=E8=A1=A8=E7=A4=BA?=
 =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E3=81=93=E3=81=A8=E3=81=8C?=
 =?UTF-8?q?=E3=81=82=E3=82=8B=E3=83=90=E3=82=B0=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#12721)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 画像のアスペクト比をデフォルト以外に設定しているとCW内の画像が表示されないのを修正

* fix
---
 CHANGELOG.md                                  |  1 +
 .../frontend/src/components/MkMediaList.vue   | 77 ++-----------------
 2 files changed, 9 insertions(+), 69 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index dd8c492782..7251fd2219 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -79,6 +79,7 @@
 - Fix: AiScriptの`readline`が不正な値を返すことがある問題を修正
 - Fix: 投票のみ/画像のみの引用RNが、通知欄でただのRNとして判定されるバグを修正
 - Fix: CWをつけて引用RNしても、普通のRNとして扱われてしまうバグを修正しました。
+- Fix: 「画像が1枚のみのメディアリストの高さ」を「デフォルト」以外に設定していると、CWの中などに添付された画像が見られないバグを修正
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index b154eb0202..c5be2a2f62 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div ref="root">
+<div>
 	<XBanner v-for="media in mediaList.filter(media => !previewable(media))" :key="media.id" :media="media"/>
 	<div v-if="mediaList.filter(media => previewable(media)).length > 0" :class="$style.container">
 		<div
@@ -27,41 +27,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 </div>
 </template>
 
-<script lang="ts">
-/**
- * アスペクト比算出のためにHTMLElement.clientWidthを使うが、
- * 大変重たいのでコンテナ要素とメディアリスト幅のペアをキャッシュする
- * (タイムラインごとにスクロールコンテナが存在する前提だが……)
- */
-const widthCache = new Map<Element, number>();
-
-/**
- * コンテナ要素がリサイズされたらキャッシュを削除する
- */
-const ro = new ResizeObserver(entries => {
-	for (const entry of entries) {
-		widthCache.delete(entry.target);
-	}
-});
-
-async function getClientWidthWithCache(targetEl: HTMLElement, containerEl: HTMLElement, count = 0) {
-	if (_DEV_) console.log('getClientWidthWithCache', { targetEl, containerEl, count, cache: widthCache.get(containerEl) });
-	if (widthCache.has(containerEl)) return widthCache.get(containerEl)!;
-
-	const width = targetEl.clientWidth;
-
-	if (count <= 10 && width < 64) {
-		// widthが64未満はおかしいのでリトライする
-		await new Promise(resolve => setTimeout(resolve, 50));
-		return getClientWidthWithCache(targetEl, containerEl, count + 1);
-	}
-
-	widthCache.set(containerEl, width);
-	ro.observe(containerEl);
-	return width;
-}
-</script>
-
 <script lang="ts" setup>
 import { computed, onMounted, onUnmounted, shallowRef } from 'vue';
 import * as Misskey from 'misskey-js';
@@ -74,15 +39,12 @@ import XVideo from '@/components/MkMediaVideo.vue';
 import * as os from '@/os.js';
 import { FILE_TYPE_BROWSERSAFE } from '@/const';
 import { defaultStore } from '@/store.js';
-import { getScrollContainer, getBodyScrollHeight } from '@/scripts/scroll.js';
 
 const props = defineProps<{
 	mediaList: Misskey.entities.DriveFile[];
 	raw?: boolean;
 }>();
 
-const root = shallowRef<HTMLDivElement>();
-const container = shallowRef<HTMLElement | null | undefined>(undefined);
 const gallery = shallowRef<HTMLDivElement>();
 const pswpZIndex = os.claimZIndex('middle');
 document.documentElement.style.setProperty('--mk-pswp-root-z-index', pswpZIndex.toString());
@@ -95,12 +57,8 @@ const popstateHandler = (): void => {
 	}
 };
 
-/**
- * アスペクト比をmediaListWithOneImageAppearanceに基づいていい感じに調整する
- * aspect-ratioではなくheightを使う
- */
 async function calcAspectRatio() {
-	if (!gallery.value || !root.value) return;
+	if (!gallery.value) return;
 
 	let img = props.mediaList[0];
 
@@ -109,41 +67,22 @@ async function calcAspectRatio() {
 		return;
 	}
 
-	if (!container.value) container.value = getScrollContainer(root.value);
-	const width = container.value ? await getClientWidthWithCache(root.value, container.value) : root.value.clientWidth;
-
-	const heightMin = (ratio: number) => {
-		const imgResizeRatio = width / img.properties.width;
-		const imgDrawHeight = img.properties.height * imgResizeRatio;
-		const maxHeight = width * ratio;
-		const height = Math.min(imgDrawHeight, maxHeight);
-		if (_DEV_) console.log('Image height calculated:', { width, properties: img.properties, imgResizeRatio, imgDrawHeight, maxHeight, height });
-		return `${height}px`;
-	};
+	const ratioMax = (ratio: number) => `${Math.max(ratio, img.properties.width / img.properties.height).toString()} / 1`;
 
 	switch (defaultStore.state.mediaListWithOneImageAppearance) {
 		case '16_9':
-			gallery.value.style.height = heightMin(9 / 16);
+			gallery.value.style.aspectRatio = ratioMax(16 / 9);
 			break;
 		case '1_1':
-			gallery.value.style.height = heightMin(1);
+			gallery.value.style.aspectRatio = ratioMax(1 / 1);
 			break;
 		case '2_3':
-			gallery.value.style.height = heightMin(3 / 2);
+			gallery.value.style.aspectRatio = ratioMax(2 / 3);
 			break;
-		default: {
-			const maxHeight = Math.max(64, (container.value ? container.value.clientHeight : getBodyScrollHeight()) * 0.5 || 360);
-			if (width === 0 || !maxHeight) return;
-			const imgResizeRatio = width / img.properties.width;
-			const imgDrawHeight = img.properties.height * imgResizeRatio;
-			gallery.value.style.height = `${Math.max(64, Math.min(imgDrawHeight, maxHeight))}px`;
-			gallery.value.style.minHeight = 'initial';
-			gallery.value.style.maxHeight = 'initial';
+		default:
+			gallery.value.style.aspectRatio = '';
 			break;
-		}
 	}
-
-	gallery.value.style.aspectRatio = 'initial';
 }
 
 onMounted(() => {

From 75637b0f481e6c71e8bbb654354cbfce21391d4f Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Wed, 20 Dec 2023 09:16:20 +0000
Subject: [PATCH 275/435] =?UTF-8?q?remove=20"=F0=9F=91=81=20preview"=20but?=
 =?UTF-8?q?ton=20from=20theme=20editor?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

it stopped working back in May, see
3d4a90b08a284f416564fc950a9216936c1a8e99

Thanks Amelia for noticing!
---
 packages/frontend/src/pages/theme-editor.vue | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue
index b78cf531a5..1bc5eaddd9 100644
--- a/packages/frontend/src/pages/theme-editor.vue
+++ b/packages/frontend/src/pages/theme-editor.vue
@@ -134,10 +134,6 @@ let changed = $ref(false);
 
 useLeaveGuard($$(changed));
 
-function showPreview() {
-	os.pageWindow('/preview');
-}
-
 function setBgColor(color: typeof bgColors[number]) {
 	if (theme.base !== color.kind) {
 		const base = color.kind === 'dark' ? darkTheme : lightTheme;
@@ -214,11 +210,6 @@ async function saveAs() {
 watch($$(theme), apply, { deep: true });
 
 const headerActions = $computed(() => [{
-	asFullButton: true,
-	icon: 'ph-eye ph-bold ph-lg',
-	text: i18n.ts.preview,
-	handler: showPreview,
-}, {
 	asFullButton: true,
 	icon: 'ph-check ph-bold ph-lg',
 	text: i18n.ts.saveAs,

From 4175b7809b3c7841423450032ec73380f674599b Mon Sep 17 00:00:00 2001
From: Kagami Sascha Rosylight <saschanaz@outlook.com>
Date: Thu, 21 Dec 2023 02:29:30 +0100
Subject: [PATCH 276/435] chore(QueueProcessorService): show error stack for
 failures (#12727)

---
 .../src/queue/QueueProcessorService.ts        | 28 +++++++++----------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts
index ee081ccaad..b872dd65f7 100644
--- a/packages/backend/src/queue/QueueProcessorService.ts
+++ b/packages/backend/src/queue/QueueProcessorService.ts
@@ -153,8 +153,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 		this.systemQueueWorker
 			.on('active', (job) => systemLogger.debug(`active id=${job.id}`))
 			.on('completed', (job, result) => systemLogger.debug(`completed(${result}) id=${job.id}`))
-			.on('failed', (job, err) => systemLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
-			.on('error', (err: Error) => systemLogger.error(`error ${err}`, { e: renderError(err) }))
+			.on('failed', (job, err) => systemLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+			.on('error', (err: Error) => systemLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 			.on('stalled', (jobId) => systemLogger.warn(`stalled id=${jobId}`));
 		//#endregion
 
@@ -191,8 +191,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 		this.dbQueueWorker
 			.on('active', (job) => dbLogger.debug(`active id=${job.id}`))
 			.on('completed', (job, result) => dbLogger.debug(`completed(${result}) id=${job.id}`))
-			.on('failed', (job, err) => dbLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
-			.on('error', (err: Error) => dbLogger.error(`error ${err}`, { e: renderError(err) }))
+			.on('failed', (job, err) => dbLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+			.on('error', (err: Error) => dbLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 			.on('stalled', (jobId) => dbLogger.warn(`stalled id=${jobId}`));
 		//#endregion
 
@@ -215,8 +215,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 		this.deliverQueueWorker
 			.on('active', (job) => deliverLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
 			.on('completed', (job, result) => deliverLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
-			.on('failed', (job, err) => deliverLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
-			.on('error', (err: Error) => deliverLogger.error(`error ${err}`, { e: renderError(err) }))
+			.on('failed', (job, err) => deliverLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
+			.on('error', (err: Error) => deliverLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 			.on('stalled', (jobId) => deliverLogger.warn(`stalled id=${jobId}`));
 		//#endregion
 
@@ -239,8 +239,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 		this.inboxQueueWorker
 			.on('active', (job) => inboxLogger.debug(`active ${getJobInfo(job, true)}`))
 			.on('completed', (job, result) => inboxLogger.debug(`completed(${result}) ${getJobInfo(job, true)}`))
-			.on('failed', (job, err) => inboxLogger.warn(`failed(${err}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) }))
-			.on('error', (err: Error) => inboxLogger.error(`error ${err}`, { e: renderError(err) }))
+			.on('failed', (job, err) => inboxLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} activity=${job ? (job.data.activity ? job.data.activity.id : 'none') : '-'}`, { job, e: renderError(err) }))
+			.on('error', (err: Error) => inboxLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 			.on('stalled', (jobId) => inboxLogger.warn(`stalled id=${jobId}`));
 		//#endregion
 
@@ -263,8 +263,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 		this.webhookDeliverQueueWorker
 			.on('active', (job) => webhookLogger.debug(`active ${getJobInfo(job, true)} to=${job.data.to}`))
 			.on('completed', (job, result) => webhookLogger.debug(`completed(${result}) ${getJobInfo(job, true)} to=${job.data.to}`))
-			.on('failed', (job, err) => webhookLogger.warn(`failed(${err}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
-			.on('error', (err: Error) => webhookLogger.error(`error ${err}`, { e: renderError(err) }))
+			.on('failed', (job, err) => webhookLogger.warn(`failed(${err.stack}) ${getJobInfo(job)} to=${job ? job.data.to : '-'}`))
+			.on('error', (err: Error) => webhookLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 			.on('stalled', (jobId) => webhookLogger.warn(`stalled id=${jobId}`));
 		//#endregion
 
@@ -292,8 +292,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 		this.relationshipQueueWorker
 			.on('active', (job) => relationshipLogger.debug(`active id=${job.id}`))
 			.on('completed', (job, result) => relationshipLogger.debug(`completed(${result}) id=${job.id}`))
-			.on('failed', (job, err) => relationshipLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
-			.on('error', (err: Error) => relationshipLogger.error(`error ${err}`, { e: renderError(err) }))
+			.on('failed', (job, err) => relationshipLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+			.on('error', (err: Error) => relationshipLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 			.on('stalled', (jobId) => relationshipLogger.warn(`stalled id=${jobId}`));
 		//#endregion
 
@@ -315,8 +315,8 @@ export class QueueProcessorService implements OnApplicationShutdown {
 		this.objectStorageQueueWorker
 			.on('active', (job) => objectStorageLogger.debug(`active id=${job.id}`))
 			.on('completed', (job, result) => objectStorageLogger.debug(`completed(${result}) id=${job.id}`))
-			.on('failed', (job, err) => objectStorageLogger.warn(`failed(${err}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
-			.on('error', (err: Error) => objectStorageLogger.error(`error ${err}`, { e: renderError(err) }))
+			.on('failed', (job, err) => objectStorageLogger.warn(`failed(${err.stack}) id=${job ? job.id : '-'}`, { job, e: renderError(err) }))
+			.on('error', (err: Error) => objectStorageLogger.error(`error ${err.stack}`, { e: renderError(err) }))
 			.on('stalled', (jobId) => objectStorageLogger.warn(`stalled id=${jobId}`));
 		//#endregion
 

From d14eb20122411b8ab53920ae9d48580db512650c Mon Sep 17 00:00:00 2001
From: Kagami Sascha Rosylight <saschanaz@outlook.com>
Date: Thu, 21 Dec 2023 02:29:51 +0100
Subject: [PATCH 277/435] chore(workflows): use postgres 15 everywhere (#12726)

---
 .github/workflows/test-backend.yml       | 2 +-
 .github/workflows/test-frontend.yml      | 2 +-
 chart/templates/Deployment.yml           | 4 ++--
 packages/backend/test/docker-compose.yml | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index 6e8327ca07..1b0f22c8e9 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -17,7 +17,7 @@ jobs:
 
     services:
       postgres:
-        image: postgres:13
+        image: postgres:15
         ports:
           - 54312:5432
         env:
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index e5c461e6d1..18b2a8c202 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -56,7 +56,7 @@ jobs:
 
     services:
       postgres:
-        image: postgres:13
+        image: postgres:15
         ports:
           - 54312:5432
         env:
diff --git a/chart/templates/Deployment.yml b/chart/templates/Deployment.yml
index d5dd14f59e..3c73837801 100644
--- a/chart/templates/Deployment.yml
+++ b/chart/templates/Deployment.yml
@@ -27,7 +27,7 @@ spec:
           ports:
             - containerPort: 3000
         - name: postgres
-          image: postgres:14-alpine
+          image: postgres:15-alpine
           env:
             - name: POSTGRES_USER
               value: "example-misskey-user"
@@ -38,7 +38,7 @@ spec:
           ports:
             - containerPort: 5432
         - name: redis
-          image: redis:alpine
+          image: redis:7-alpine
           ports:
             - containerPort: 6379
       volumes:
diff --git a/packages/backend/test/docker-compose.yml b/packages/backend/test/docker-compose.yml
index da6c01dda1..f2d8990758 100644
--- a/packages/backend/test/docker-compose.yml
+++ b/packages/backend/test/docker-compose.yml
@@ -7,7 +7,7 @@ services:
       - "127.0.0.1:56312:6379"
 
   dbtest:
-    image: postgres:13
+    image: postgres:15
     ports:
       - "127.0.0.1:54312:5432"
     environment:

From 15b0d2aff2011935f212db19feab3bec97979ae1 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 21 Dec 2023 10:39:11 +0900
Subject: [PATCH 278/435] =?UTF-8?q?enhance:=20=E3=83=AD=E3=83=BC=E3=83=AB?=
 =?UTF-8?q?=E3=81=AB=E3=82=A2=E3=82=B5=E3=82=A4=E3=83=B3=E3=81=95=E3=82=8C?=
 =?UTF-8?q?=E3=81=9F=E3=81=A8=E3=81=8D=E3=81=AE=E9=80=9A=E7=9F=A5=20(#1260?=
 =?UTF-8?q?7)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* wip

* Update misskey-js.api.md

* Update CHANGELOG.md

* Update RoleService.ts

* Update locales/ja-JP.yml

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>

* Update UserListService.ts

* Update misskey-js.api.md

* fix (#12724)

---------

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
Co-authored-by: おさむのひと <46447427+samunohito@users.noreply.github.com>
---
 CHANGELOG.md                                  |  1 +
 locales/index.d.ts                            |  1 +
 locales/ja-JP.yml                             |  1 +
 packages/backend/src/core/RoleService.ts      | 29 ++++++++--
 packages/backend/src/core/UserListService.ts  |  4 +-
 .../entities/NotificationEntityService.ts     | 11 ++--
 packages/backend/src/models/Notification.ts   |  8 ++-
 .../backend/src/models/json-schema/user.ts    |  2 -
 packages/backend/src/types.ts                 | 17 +++++-
 packages/backend/test/unit/RoleService.ts     | 54 +++++++++++++++++++
 .../src/components/MkNotification.vue         |  6 +++
 packages/frontend/src/const.ts                | 16 +++++-
 .../src/pages/settings/notifications.vue      |  2 +-
 packages/misskey-js/etc/misskey-js.api.md     | 11 ++--
 packages/misskey-js/src/consts.ts             |  2 +-
 15 files changed, 143 insertions(+), 22 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7251fd2219..9a62cbefa0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,7 @@
 - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
 - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
 - Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加
+- Enhance: 公開ロールにアサインされたときに通知が作成されるように
 - Enhance: アイコンデコレーションを複数設定できるように
 - Enhance: アイコンデコレーションの位置を微調整できるように
 - Enhance: つながりの公開範囲をフォロー/フォロワーで個別に設定可能に #12072
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 25a16d4a4d..f22b7f1c4a 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2325,6 +2325,7 @@ export interface Locale {
         "pollEnded": string;
         "newNote": string;
         "unreadAntennaNote": string;
+        "roleAssigned": string;
         "emptyPushNotificationMessage": string;
         "achievementEarned": string;
         "testNotification": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 308b7ae67d..2185183c98 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2227,6 +2227,7 @@ _notification:
   pollEnded: "アンケートの結果が出ました"
   newNote: "新しい投稿"
   unreadAntennaNote: "アンテナ {name}"
+  roleAssigned: "ロールが付与されました"
   emptyPushNotificationMessage: "プッシュ通知の更新をしました"
   achievementEarned: "実績を獲得"
   testNotification: "通知テスト"
diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts
index 4de719d6a0..d354faa7c2 100644
--- a/packages/backend/src/core/RoleService.ts
+++ b/packages/backend/src/core/RoleService.ts
@@ -6,7 +6,14 @@
 import { Inject, Injectable } from '@nestjs/common';
 import * as Redis from 'ioredis';
 import { In } from 'typeorm';
-import type { MiRole, MiRoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/_.js';
+import { ModuleRef } from '@nestjs/core';
+import type {
+	MiRole,
+	MiRoleAssignment,
+	RoleAssignmentsRepository,
+	RolesRepository,
+	UsersRepository,
+} from '@/models/_.js';
 import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js';
 import type { MiUser } from '@/models/User.js';
 import { DI } from '@/di-symbols.js';
@@ -16,12 +23,13 @@ import { CacheService } from '@/core/CacheService.js';
 import type { RoleCondFormulaValue } from '@/models/Role.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import type { GlobalEvents } from '@/core/GlobalEventService.js';
-import { IdService } from '@/core/IdService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
+import { IdService } from '@/core/IdService.js';
 import { ModerationLogService } from '@/core/ModerationLogService.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { FanoutTimelineService } from '@/core/FanoutTimelineService.js';
-import type { OnApplicationShutdown } from '@nestjs/common';
+import { NotificationService } from '@/core/NotificationService.js';
+import type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common';
 
 export type RolePolicies = {
 	gtlAvailable: boolean;
@@ -78,14 +86,17 @@ export const DEFAULT_POLICIES: RolePolicies = {
 };
 
 @Injectable()
-export class RoleService implements OnApplicationShutdown {
+export class RoleService implements OnApplicationShutdown, OnModuleInit {
 	private rolesCache: MemorySingleCache<MiRole[]>;
 	private roleAssignmentByUserIdCache: MemoryKVCache<MiRoleAssignment[]>;
+	private notificationService: NotificationService;
 
 	public static AlreadyAssignedError = class extends Error {};
 	public static NotAssignedError = class extends Error {};
 
 	constructor(
+		private moduleRef: ModuleRef,
+
 		@Inject(DI.redis)
 		private redisClient: Redis.Redis,
 
@@ -120,6 +131,10 @@ export class RoleService implements OnApplicationShutdown {
 		this.redisForSub.on('message', this.onMessage);
 	}
 
+	async onModuleInit() {
+		this.notificationService = this.moduleRef.get(NotificationService.name);
+	}
+
 	@bindThis
 	private async onMessage(_: string, data: string): Promise<void> {
 		const obj = JSON.parse(data);
@@ -427,6 +442,12 @@ export class RoleService implements OnApplicationShutdown {
 
 		this.globalEventService.publishInternalEvent('userRoleAssigned', created);
 
+		if (role.isPublic) {
+			this.notificationService.createNotification(userId, 'roleAssigned', {
+				roleId: roleId,
+			});
+		}
+
 		if (moderator) {
 			const user = await this.usersRepository.findOneByOrFail({ id: userId });
 			this.moderationLogService.log(moderator, 'assignRole', {
diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts
index 702c731fc3..832b715d97 100644
--- a/packages/backend/src/core/UserListService.ts
+++ b/packages/backend/src/core/UserListService.ts
@@ -10,15 +10,15 @@ import type { MiUser } from '@/models/User.js';
 import type { MiUserList } from '@/models/UserList.js';
 import type { MiUserListMembership } from '@/models/UserListMembership.js';
 import { IdService } from '@/core/IdService.js';
+import type { GlobalEvents } from '@/core/GlobalEventService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { DI } from '@/di-symbols.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { ProxyAccountService } from '@/core/ProxyAccountService.js';
 import { bindThis } from '@/decorators.js';
-import { RoleService } from '@/core/RoleService.js';
 import { QueueService } from '@/core/QueueService.js';
 import { RedisKVCache } from '@/misc/cache.js';
-import type { GlobalEvents } from '@/core/GlobalEventService.js';
+import { RoleService } from '@/core/RoleService.js';
 
 @Injectable()
 export class UserListService implements OnApplicationShutdown {
diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts
index e723ea5a55..f2124998ac 100644
--- a/packages/backend/src/core/entities/NotificationEntityService.ts
+++ b/packages/backend/src/core/entities/NotificationEntityService.ts
@@ -15,8 +15,8 @@ import type { Packed } from '@/misc/json-schema.js';
 import { bindThis } from '@/decorators.js';
 import { isNotNull } from '@/misc/is-not-null.js';
 import { FilterUnionByProperty, notificationTypes } from '@/types.js';
+import { RoleEntityService } from './RoleEntityService.js';
 import type { OnModuleInit } from '@nestjs/common';
-import type { CustomEmojiService } from '../CustomEmojiService.js';
 import type { UserEntityService } from './UserEntityService.js';
 import type { NoteEntityService } from './NoteEntityService.js';
 
@@ -27,7 +27,7 @@ const NOTE_REQUIRED_GROUPED_NOTIFICATION_TYPES = new Set(['note', 'mention', 're
 export class NotificationEntityService implements OnModuleInit {
 	private userEntityService: UserEntityService;
 	private noteEntityService: NoteEntityService;
-	private customEmojiService: CustomEmojiService;
+	private roleEntityService: RoleEntityService;
 
 	constructor(
 		private moduleRef: ModuleRef,
@@ -43,14 +43,13 @@ export class NotificationEntityService implements OnModuleInit {
 
 		//private userEntityService: UserEntityService,
 		//private noteEntityService: NoteEntityService,
-		//private customEmojiService: CustomEmojiService,
 	) {
 	}
 
 	onModuleInit() {
 		this.userEntityService = this.moduleRef.get('UserEntityService');
 		this.noteEntityService = this.moduleRef.get('NoteEntityService');
-		this.customEmojiService = this.moduleRef.get('CustomEmojiService');
+		this.roleEntityService = this.moduleRef.get('RoleEntityService');
 	}
 
 	@bindThis
@@ -81,6 +80,7 @@ export class NotificationEntityService implements OnModuleInit {
 					detail: false,
 				})
 		) : undefined;
+		const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined;
 
 		return await awaitAll({
 			id: notification.id,
@@ -92,6 +92,9 @@ export class NotificationEntityService implements OnModuleInit {
 			...(notification.type === 'reaction' ? {
 				reaction: notification.reaction,
 			} : {}),
+			...(notification.type === 'roleAssigned' ? {
+				role: role,
+			} : {}),
 			...(notification.type === 'achievementEarned' ? {
 				achievement: notification.achievement,
 			} : {}),
diff --git a/packages/backend/src/models/Notification.ts b/packages/backend/src/models/Notification.ts
index 1d5fc124e2..3bc2edaa0d 100644
--- a/packages/backend/src/models/Notification.ts
+++ b/packages/backend/src/models/Notification.ts
@@ -3,11 +3,10 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { notificationTypes } from '@/types.js';
 import { MiUser } from './User.js';
 import { MiNote } from './Note.js';
-import { MiFollowRequest } from './FollowRequest.js';
 import { MiAccessToken } from './AccessToken.js';
+import { MiRole } from './Role.js';
 
 export type MiNotification = {
 	type: 'note';
@@ -68,6 +67,11 @@ export type MiNotification = {
 	id: string;
 	createdAt: string;
 	notifierId: MiUser['id'];
+} | {
+	type: 'roleAssigned';
+	id: string;
+	createdAt: string;
+	roleId: MiRole['id'];
 } | {
 	type: 'achievementEarned';
 	id: string;
diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts
index 1b86b1bf10..6a0d43b1ac 100644
--- a/packages/backend/src/models/json-schema/user.ts
+++ b/packages/backend/src/models/json-schema/user.ts
@@ -554,9 +554,7 @@ export const packedMeDetailedOnlySchema = {
 				mention: notificationRecieveConfig,
 				reaction: notificationRecieveConfig,
 				pollEnded: notificationRecieveConfig,
-				achievementEarned: notificationRecieveConfig,
 				receiveFollowRequest: notificationRecieveConfig,
-				followRequestAccepted: notificationRecieveConfig,
 			},
 		},
 		emailNotificationTypes: {
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index e085407de0..361a4931eb 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -14,11 +14,26 @@
  * pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した
  * receiveFollowRequest - フォローリクエストされた
  * followRequestAccepted - 自分の送ったフォローリクエストが承認された
+ * roleAssigned - ロールが付与された
  * achievementEarned - 実績を獲得
  * app - アプリ通知
  * test - テスト通知(サーバー側)
  */
-export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app', 'test'] as const;
+export const notificationTypes = [
+	'note',
+	'follow',
+	'mention',
+	'reply',
+	'renote',
+	'quote',
+	'reaction',
+	'pollEnded',
+	'receiveFollowRequest',
+	'followRequestAccepted',
+	'roleAssigned',
+	'achievementEarned',
+	'app',
+	'test'] as const;
 export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const;
 
 export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts
index f644312bc9..99c6912116 100644
--- a/packages/backend/test/unit/RoleService.ts
+++ b/packages/backend/test/unit/RoleService.ts
@@ -19,6 +19,7 @@ import { CacheService } from '@/core/CacheService.js';
 import { IdService } from '@/core/IdService.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { secureRndstr } from '@/misc/secure-rndstr.js';
+import { NotificationService } from '@/core/NotificationService.js';
 import { sleep } from '../utils.js';
 import type { TestingModule } from '@nestjs/testing';
 import type { MockFunctionMetadata } from 'jest-mock';
@@ -32,6 +33,7 @@ describe('RoleService', () => {
 	let rolesRepository: RolesRepository;
 	let roleAssignmentsRepository: RoleAssignmentsRepository;
 	let metaService: jest.Mocked<MetaService>;
+	let notificationService: jest.Mocked<NotificationService>;
 	let clock: lolex.InstalledClock;
 
 	function createUser(data: Partial<MiUser> = {}) {
@@ -76,6 +78,8 @@ describe('RoleService', () => {
 			.useMocker((token) => {
 				if (token === MetaService) {
 					return { fetch: jest.fn() };
+				} else if (token === NotificationService) {
+					return { createNotification: jest.fn() };
 				}
 				if (typeof token === 'function') {
 					const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
@@ -93,6 +97,7 @@ describe('RoleService', () => {
 		roleAssignmentsRepository = app.get<RoleAssignmentsRepository>(DI.roleAssignmentsRepository);
 
 		metaService = app.get<MetaService>(MetaService) as jest.Mocked<MetaService>;
+		notificationService = app.get<NotificationService>(NotificationService) as jest.Mocked<NotificationService>;
 	});
 
 	afterEach(async () => {
@@ -273,4 +278,53 @@ describe('RoleService', () => {
 			expect(resultAfter25hAgain.canManageCustomEmojis).toBe(true);
 		});
 	});
+
+	describe('assign', () => {
+		test('公開ロールの場合は通知される', async () => {
+			const user = await createUser();
+			const role = await createRole({
+				isPublic: true,
+			});
+
+			await roleService.assign(user.id, role.id);
+
+			await sleep(100);
+
+			const assignments = await roleAssignmentsRepository.find({
+				where: {
+					userId: user.id,
+					roleId: role.id,
+				},
+			});
+			expect(assignments).toHaveLength(1);
+
+			expect(notificationService.createNotification).toHaveBeenCalled();
+			expect(notificationService.createNotification.mock.lastCall![0]).toBe(user.id);
+			expect(notificationService.createNotification.mock.lastCall![1]).toBe('roleAssigned');
+			expect(notificationService.createNotification.mock.lastCall![2]).toBe({
+				roleId: role.id,
+			});
+		});
+
+		test('非公開ロールの場合は通知されない', async () => {
+			const user = await createUser();
+			const role = await createRole({
+				isPublic: false,
+			});
+
+			await roleService.assign(user.id, role.id);
+
+			await sleep(100);
+
+			const assignments = await roleAssignmentsRepository.find({
+				where: {
+					userId: user.id,
+					roleId: role.id,
+				},
+			});
+			expect(assignments).toHaveLength(1);
+
+			expect(notificationService.createNotification).not.toHaveBeenCalled();
+		});
+	});
 });
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index fcf4791240..2b9af26654 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<div :class="$style.head">
 		<MkAvatar v-if="notification.type === 'pollEnded'" :class="$style.icon" :user="notification.note.user" link preview/>
 		<MkAvatar v-else-if="notification.type === 'note'" :class="$style.icon" :user="notification.note.user" link preview/>
+		<MkAvatar v-else-if="notification.type === 'roleAssigned'" :class="$style.icon" :user="$i" link preview/>
 		<MkAvatar v-else-if="notification.type === 'achievementEarned'" :class="$style.icon" :user="$i" link preview/>
 		<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div>
 		<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
@@ -36,6 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<i v-else-if="notification.type === 'quote'" class="ti ti-quote"></i>
 			<i v-else-if="notification.type === 'pollEnded'" class="ti ti-chart-arrows"></i>
 			<i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i>
+			<img v-else-if="notification.type === 'roleAssigned'" :src="notification.role.iconUrl" alt=""/>
 			<!-- notification.reaction が null になることはまずないが、ここでoptional chaining使うと一部ブラウザで刺さるので念の為 -->
 			<MkReactionIcon
 				v-else-if="notification.type === 'reaction'"
@@ -50,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<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 }}: <MkUserName :user="notification.note.user"/></span>
+			<span v-else-if="notification.type === 'roleAssigned'">{{ i18n.ts._notification.roleAssigned }}</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>
@@ -86,6 +89,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="true" :author="notification.note.user"/>
 				<i class="ti ti-quote" :class="$style.quote"></i>
 			</MkA>
+			<div v-else-if="notification.type === 'roleAssigned'" :class="$style.text">
+				{{ notification.role.name }}
+			</div>
 			<MkA v-else-if="notification.type === 'achievementEarned'" :class="$style.text" to="/my/achievements">
 				{{ i18n.ts._achievements._types['_' + notification.achievement].title }}
 			</MkA>
diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts
index f016b7aa02..01c224ae2d 100644
--- a/packages/frontend/src/const.ts
+++ b/packages/frontend/src/const.ts
@@ -54,7 +54,21 @@ https://github.com/sindresorhus/file-type/blob/main/core.js
 https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
 */
 
-export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const;
+export const notificationTypes = [
+	'note',
+	'follow',
+	'mention',
+	'reply',
+	'renote',
+	'quote',
+	'reaction',
+	'pollEnded',
+	'receiveFollowRequest',
+	'followRequestAccepted',
+	'roleAssigned',
+	'achievementEarned',
+	'app',
+] as const;
 export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const;
 
 export const ROLE_POLICIES = [
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index 394e428eda..def8fd3e69 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -68,7 +68,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
 import { notificationTypes } from '@/const.js';
 
-const nonConfigurableNotificationTypes = ['note'];
+const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'achievementEarned'];
 
 const allowButton = shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
 const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrationInServer);
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index abb3cae4b1..ea4e0c4163 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1635,9 +1635,6 @@ type FetchLike = (input: string, init?: {
 // @public (undocumented)
 type FetchRssRequest = operations['fetch-rss']['requestBody']['content']['application/json'];
 
-// @public (undocumented)
-export const ffVisibility: readonly ["public", "followers", "private"];
-
 // @public (undocumented)
 type Flash = components['schemas']['Flash'];
 
@@ -1677,6 +1674,9 @@ type FlashUnlikeRequest = operations['flash/unlike']['requestBody']['content']['
 // @public (undocumented)
 type FlashUpdateRequest = operations['flash/update']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+export const followersVisibilities: readonly ["public", "followers", "private"];
+
 // @public (undocumented)
 type Following = components['schemas']['Following'];
 
@@ -1725,6 +1725,9 @@ type FollowingUpdateRequest = operations['following/update']['requestBody']['con
 // @public (undocumented)
 type FollowingUpdateResponse = operations['following/update']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+export const followingVisibilities: readonly ["public", "followers", "private"];
+
 // @public (undocumented)
 type GalleryFeaturedRequest = operations['gallery/featured']['requestBody']['content']['application/json'];
 
@@ -2337,7 +2340,7 @@ type Notification_2 = components['schemas']['Notification'];
 type NotificationsCreateRequest = operations['notifications/create']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
-export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "achievementEarned"];
+export const notificationTypes: readonly ["note", "follow", "mention", "reply", "renote", "quote", "reaction", "pollVote", "pollEnded", "receiveFollowRequest", "followRequestAccepted", "groupInvited", "app", "roleAssigned", "achievementEarned"];
 
 // @public (undocumented)
 type Page = components['schemas']['Page'];
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 83d313a5fe..e769bb9e6d 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -1,4 +1,4 @@
-export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'achievementEarned'] as const;
+export const notificationTypes = ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'roleAssigned', 'achievementEarned'] as const;
 
 export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
 

From b3ab96b5ee9e84a3393c20f917ebf7f1fa178347 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 21 Dec 2023 11:23:31 +0900
Subject: [PATCH 279/435] =?UTF-8?q?fix(backend):=20=E3=83=AD=E3=83=BC?=
 =?UTF-8?q?=E3=83=AB=E3=82=A2=E3=82=B5=E3=82=A4=E3=83=B3=E3=81=AE=E9=80=9A?=
 =?UTF-8?q?=E7=9F=A5=E3=81=8C=E3=81=82=E3=82=8B=E7=8A=B6=E6=85=8B=E3=81=A7?=
 =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=82=92=E3=83=AA=E3=83=AD=E3=83=BC?=
 =?UTF-8?q?=E3=83=89=E3=81=99=E3=82=8B=E3=81=A8=E9=80=9A=E7=9F=A5=E6=AC=84?=
 =?UTF-8?q?=E3=81=AB=E4=BD=95=E3=82=82=E3=81=A7=E3=81=AA=E3=81=8F=E3=81=AA?=
 =?UTF-8?q?=E3=82=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Fix #12729
---
 .../backend/src/core/entities/NotificationEntityService.ts   | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts
index f2124998ac..704081ed00 100644
--- a/packages/backend/src/core/entities/NotificationEntityService.ts
+++ b/packages/backend/src/core/entities/NotificationEntityService.ts
@@ -219,6 +219,8 @@ export class NotificationEntityService implements OnModuleInit {
 			});
 		}
 
+		const role = notification.type === 'roleAssigned' ? await this.roleEntityService.pack(notification.roleId) : undefined;
+
 		return await awaitAll({
 			id: notification.id,
 			createdAt: new Date(notification.createdAt).toISOString(),
@@ -229,6 +231,9 @@ export class NotificationEntityService implements OnModuleInit {
 			...(notification.type === 'reaction' ? {
 				reaction: notification.reaction,
 			} : {}),
+			...(notification.type === 'roleAssigned' ? {
+				role: role,
+			} : {}),
 			...(notification.type === 'achievementEarned' ? {
 				achievement: notification.achievement,
 			} : {}),

From c307dd4fe8a48d4b95fea916d7a449a19adee22b Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 21 Dec 2023 11:26:41 +0900
Subject: [PATCH 280/435] perf(frontend): import snowfall-effect dynamically to
 reduce bundle size

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

diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index e3fd6d5fca..8826413f45 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -20,7 +20,6 @@ import { mainRouter } from '@/router.js';
 import { initializeSw } from '@/scripts/initialize-sw.js';
 import { deckStore } from '@/ui/deck/deck-store.js';
 import { emojiPicker } from '@/scripts/emoji-picker.js';
-import { SnowfallEffect } from '@/scripts/snowfall-effect.js';
 
 export async function mainBoot() {
 	const { isClientUpdated } = await common(() => createApp(
@@ -79,6 +78,7 @@ export async function mainBoot() {
 	if (defaultStore.state.enableSeasonalScreenEffect) {
 		const month = new Date().getMonth() + 1;
 		if (month === 12 || month === 1) {
+			const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect;
 			new SnowfallEffect().render();
 		}
 	}

From 757dee5664fbeb9c5f67d4b0e38d13ab2bbd8688 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 21 Dec 2023 11:28:30 +0900
Subject: [PATCH 281/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a62cbefa0..819c655202 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,8 @@
 - Enhance: アイコンデコレーションを複数設定できるように
 - Enhance: アイコンデコレーションの位置を微調整できるように
 - Enhance: つながりの公開範囲をフォロー/フォロワーで個別に設定可能に #12072
+- Enhance: ローカリゼーションの更新
+- Enhance: 依存関係の更新
 - Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正
 
 ### Client

From b2254a66d32bf553a16af3b584e6c5a69e64efc4 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 21 Dec 2023 11:34:19 +0900
Subject: [PATCH 282/435] chore: remove hashtag from featured immediately
 (#12668)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* chore: remove hashtag from featured immediately

* docs(changelog): ハッシュタグのトレンド除外設定が即時に効果を持つように修正

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                 |  1 +
 packages/backend/src/core/FeaturedService.ts | 16 +++++++++++++
 packages/backend/src/core/MetaService.ts     | 25 +++++++++++++++++---
 3 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 819c655202..f535aa3d9c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -101,6 +101,7 @@
 - Fix: 「みつける」が年越し時に壊れる問題を修正
 - Fix: アカウントをブロックした際に、自身のユーザーのページでノートが相手に表示される問題を修正
 - Fix: モデレーションログがモデレーターは閲覧できないように修正
+- Fix: ハッシュタグのトレンド除外設定が即時に効果を持つように修正
 - Fix: HTTP Digestヘッダのアルゴリズム部分に大文字の"SHA-256"しか使えない
 - Fix: 管理者用APIのアクセス権限が適切に設定されていない問題を修正
 
diff --git a/packages/backend/src/core/FeaturedService.ts b/packages/backend/src/core/FeaturedService.ts
index d970ffa43b..595383c82c 100644
--- a/packages/backend/src/core/FeaturedService.ts
+++ b/packages/backend/src/core/FeaturedService.ts
@@ -77,6 +77,17 @@ export class FeaturedService {
 		return Array.from(ranking.keys());
 	}
 
+	@bindThis
+	private async removeFromRanking(name: string, windowRange: number, element: string): Promise<void> {
+		const currentWindow = this.getCurrentWindow(windowRange);
+		const previousWindow = currentWindow - 1;
+
+		const redisPipeline = this.redisClient.pipeline();
+		redisPipeline.zrem(`${name}:${currentWindow}`, element);
+		redisPipeline.zrem(`${name}:${previousWindow}`, element);
+		await redisPipeline.exec();
+	}
+
 	@bindThis
 	public updateGlobalNotesRanking(noteId: MiNote['id'], score = 1): Promise<void> {
 		return this.updateRankingOf('featuredGlobalNotesRanking', GLOBAL_NOTES_RANKING_WINDOW, noteId, score);
@@ -126,4 +137,9 @@ export class FeaturedService {
 	public getHashtagsRanking(threshold: number): Promise<string[]> {
 		return this.getRankingOf('featuredHashtagsRanking', HASHTAG_RANKING_WINDOW, threshold);
 	}
+
+	@bindThis
+	public removeHashtagsFromRanking(hashtag: string): Promise<void> {
+		return this.removeFromRanking('featuredHashtagsRanking', HASHTAG_RANKING_WINDOW, hashtag);
+	}
 }
diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts
index 508544dc07..80e8020961 100644
--- a/packages/backend/src/core/MetaService.ts
+++ b/packages/backend/src/core/MetaService.ts
@@ -11,6 +11,7 @@ import { MiMeta } from '@/models/Meta.js';
 import { GlobalEventService } from '@/core/GlobalEventService.js';
 import { bindThis } from '@/decorators.js';
 import type { GlobalEvents } from '@/core/GlobalEventService.js';
+import { FeaturedService } from '@/core/FeaturedService.js';
 import type { OnApplicationShutdown } from '@nestjs/common';
 
 @Injectable()
@@ -25,6 +26,7 @@ export class MetaService implements OnApplicationShutdown {
 		@Inject(DI.db)
 		private db: DataSource,
 
+		private featuredService: FeaturedService,
 		private globalEventService: GlobalEventService,
 	) {
 		//this.onMessage = this.onMessage.bind(this);
@@ -95,6 +97,8 @@ export class MetaService implements OnApplicationShutdown {
 
 	@bindThis
 	public async update(data: Partial<MiMeta>): Promise<MiMeta> {
+		let before: MiMeta | undefined;
+
 		const updated = await this.db.transaction(async transactionalEntityManager => {
 			const metas = await transactionalEntityManager.find(MiMeta, {
 				order: {
@@ -102,10 +106,10 @@ export class MetaService implements OnApplicationShutdown {
 				},
 			});
 
-			const meta = metas[0];
+			before = metas[0];
 
-			if (meta) {
-				await transactionalEntityManager.update(MiMeta, meta.id, data);
+			if (before) {
+				await transactionalEntityManager.update(MiMeta, before.id, data);
 
 				const metas = await transactionalEntityManager.find(MiMeta, {
 					order: {
@@ -119,6 +123,21 @@ export class MetaService implements OnApplicationShutdown {
 			}
 		});
 
+		if (data.hiddenTags) {
+			process.nextTick(() => {
+				const hiddenTags = new Set<string>(data.hiddenTags);
+				if (before) {
+					for (const previousHiddenTag of before.hiddenTags) {
+						hiddenTags.delete(previousHiddenTag);
+					}
+				}
+
+				for (const hiddenTag of hiddenTags) {
+					this.featuredService.removeHashtagsFromRanking(hiddenTag);
+				}
+			});
+		}
+
 		this.globalEventService.publishInternalEvent('metaUpdated', updated);
 
 		return updated;

From c92508a57588f9237ec61a17b521e45e73672393 Mon Sep 17 00:00:00 2001
From: woxtu <woxtup@gmail.com>
Date: Thu, 21 Dec 2023 11:36:45 +0900
Subject: [PATCH 283/435] Remove unused imports (#12730)

---
 packages/frontend/src/boot/common.ts                  |  8 ++++----
 packages/frontend/src/boot/main-boot.ts               |  8 ++++----
 packages/frontend/src/boot/sub-boot.ts                |  2 +-
 packages/frontend/src/components/MkColorInput.vue     |  3 +--
 packages/frontend/src/components/MkMediaBanner.vue    |  2 +-
 packages/frontend/src/components/MkMenu.vue           |  2 +-
 packages/frontend/src/components/MkNote.vue           |  2 +-
 packages/frontend/src/components/MkNoteDetailed.vue   |  3 +--
 packages/frontend/src/components/MkNoteSimple.vue     |  1 -
 packages/frontend/src/components/MkNoteSub.vue        |  1 -
 packages/frontend/src/components/MkNotification.vue   |  2 +-
 packages/frontend/src/components/MkNotifications.vue  |  5 ++---
 packages/frontend/src/components/MkPullToRefresh.vue  |  3 +--
 .../frontend/src/components/MkSignupDialog.form.vue   |  1 -
 .../frontend/src/components/MkSignupDialog.rules.vue  |  2 +-
 packages/frontend/src/components/MkSignupDialog.vue   |  1 -
 packages/frontend/src/components/MkSubNoteContent.vue |  1 -
 .../src/components/MkUserSetupDialog.Follow.vue       |  6 ------
 .../src/components/MkUserSetupDialog.Privacy.vue      |  4 +---
 .../src/components/MkUserSetupDialog.Profile.vue      |  3 +--
 .../src/components/MkUserSetupDialog.User.vue         |  1 -
 .../frontend/src/components/MkVisitorDashboard.vue    |  2 --
 packages/frontend/src/components/form/suspense.vue    |  1 -
 packages/frontend/src/components/global/MkA.vue       |  1 -
 .../src/components/global/MkAd.stories.impl.ts        |  3 ---
 .../src/components/global/MkUserName.stories.impl.ts  |  1 -
 packages/frontend/src/components/page/page.text.vue   |  1 -
 packages/frontend/src/components/page/page.vue        |  1 -
 packages/frontend/src/pages/admin-user.vue            |  4 ++--
 packages/frontend/src/pages/admin/branding.vue        |  3 ---
 packages/frontend/src/pages/admin/moderation.vue      |  2 --
 packages/frontend/src/pages/admin/modlog.ModLog.vue   |  2 --
 packages/frontend/src/pages/admin/roles.role.vue      |  2 +-
 packages/frontend/src/pages/ads.vue                   |  2 --
 packages/frontend/src/pages/avatar-decorations.vue    |  3 ---
 packages/frontend/src/pages/custom-emojis-manager.vue |  2 +-
 packages/frontend/src/pages/emoji-edit-dialog.vue     |  2 +-
 packages/frontend/src/pages/my-lists/list.vue         |  2 +-
 .../src/pages/page-editor/page-editor.container.vue   |  1 -
 packages/frontend/src/pages/search.note.vue           |  6 +-----
 packages/frontend/src/pages/search.user.vue           |  5 +----
 packages/frontend/src/pages/search.vue                |  3 +--
 packages/frontend/src/pages/settings/2fa.vue          |  2 +-
 .../src/pages/settings/avatar-decoration.dialog.vue   |  3 ---
 .../frontend/src/pages/settings/drive-cleaner.vue     |  1 -
 packages/frontend/src/pages/settings/navbar.vue       |  1 -
 .../frontend/src/pages/settings/notifications.vue     |  2 +-
 packages/frontend/src/pages/settings/profile.vue      |  2 --
 packages/frontend/src/pages/settings/roles.vue        | 11 +----------
 packages/frontend/src/pages/timeline.vue              |  1 -
 packages/frontend/src/pages/user-tag.vue              |  3 +--
 packages/frontend/src/pages/user/home.vue             |  1 -
 packages/frontend/src/pages/welcome.entrance.a.vue    |  6 ------
 packages/frontend/src/pages/welcome.timeline.vue      |  1 -
 packages/frontend/src/scripts/emoji-picker.ts         |  2 +-
 packages/frontend/src/ui/classic.sidebar.vue          |  2 +-
 packages/frontend/src/ui/classic.vue                  |  2 +-
 packages/frontend/src/ui/deck.vue                     |  1 -
 packages/frontend/src/ui/visitor.vue                  |  2 +-
 packages/frontend/src/widgets/WidgetActivity.vue      |  2 +-
 packages/frontend/src/widgets/WidgetAichan.vue        |  2 +-
 packages/frontend/src/widgets/WidgetAiscript.vue      |  2 +-
 packages/frontend/src/widgets/WidgetAiscriptApp.vue   |  2 +-
 packages/frontend/src/widgets/WidgetButton.vue        |  2 +-
 packages/frontend/src/widgets/WidgetCalendar.vue      |  2 +-
 packages/frontend/src/widgets/WidgetClicker.vue       |  2 +-
 packages/frontend/src/widgets/WidgetClock.vue         |  2 +-
 packages/frontend/src/widgets/WidgetDigitalClock.vue  |  2 +-
 packages/frontend/src/widgets/WidgetFederation.vue    |  2 +-
 packages/frontend/src/widgets/WidgetInstanceCloud.vue |  2 +-
 packages/frontend/src/widgets/WidgetInstanceInfo.vue  |  2 +-
 packages/frontend/src/widgets/WidgetJobQueue.vue      |  2 +-
 packages/frontend/src/widgets/WidgetMemo.vue          |  2 +-
 packages/frontend/src/widgets/WidgetNotifications.vue |  2 +-
 packages/frontend/src/widgets/WidgetOnlineUsers.vue   |  2 +-
 packages/frontend/src/widgets/WidgetPhotos.vue        |  2 +-
 packages/frontend/src/widgets/WidgetPostForm.vue      |  2 +-
 packages/frontend/src/widgets/WidgetProfile.vue       |  2 +-
 packages/frontend/src/widgets/WidgetRss.vue           |  2 +-
 packages/frontend/src/widgets/WidgetRssTicker.vue     |  2 +-
 packages/frontend/src/widgets/WidgetSlideshow.vue     |  2 +-
 packages/frontend/src/widgets/WidgetTimeline.vue      |  2 +-
 packages/frontend/src/widgets/WidgetTrends.vue        |  2 +-
 packages/frontend/src/widgets/WidgetUnixClock.vue     |  2 +-
 packages/frontend/src/widgets/WidgetUserList.vue      |  2 +-
 85 files changed, 64 insertions(+), 141 deletions(-)

diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index b0825ef11c..ef69eff764 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -3,16 +3,16 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent, App } from 'vue';
+import { computed, watch, version as vueVersion, App } from 'vue';
 import { compareVersions } from 'compare-versions';
 import widgets from '@/widgets/index.js';
 import directives from '@/directives/index.js';
 import components from '@/components/index.js';
-import { version, ui, lang, updateLocale, locale } from '@/config.js';
+import { version, lang, updateLocale, locale } from '@/config.js';
 import { applyTheme } from '@/scripts/theme.js';
 import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
-import { i18n, updateI18n } from '@/i18n.js';
-import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js';
+import { updateI18n } from '@/i18n.js';
+import { $i, refreshAccount, login } from '@/account.js';
 import { defaultStore, ColdDeviceStorage } from '@/store.js';
 import { fetchInstance, instance } from '@/instance.js';
 import { deviceKind } from '@/scripts/device-kind.js';
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index 8826413f45..0159d0c032 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -3,14 +3,14 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue';
+import { createApp, markRaw, defineAsyncComponent } from 'vue';
 import { common } from './common.js';
-import { version, ui, lang, updateLocale } from '@/config.js';
-import { i18n, updateI18n } from '@/i18n.js';
+import { ui } from '@/config.js';
+import { i18n } from '@/i18n.js';
 import { confirm, alert, post, popup, toast } from '@/os.js';
 import { useStream } from '@/stream.js';
 import * as sound from '@/scripts/sound.js';
-import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js';
+import { $i, updateAccount, signout } from '@/account.js';
 import { defaultStore, ColdDeviceStorage } from '@/store.js';
 import { makeHotkey } from '@/scripts/hotkey.js';
 import { reactionPicker } from '@/scripts/reaction-picker.js';
diff --git a/packages/frontend/src/boot/sub-boot.ts b/packages/frontend/src/boot/sub-boot.ts
index 9b4670e130..92ee074afb 100644
--- a/packages/frontend/src/boot/sub-boot.ts
+++ b/packages/frontend/src/boot/sub-boot.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue';
+import { createApp, defineAsyncComponent } from 'vue';
 import { common } from './common.js';
 
 export async function subBoot() {
diff --git a/packages/frontend/src/components/MkColorInput.vue b/packages/frontend/src/components/MkColorInput.vue
index 983a35103c..a7a3eff5af 100644
--- a/packages/frontend/src/components/MkColorInput.vue
+++ b/packages/frontend/src/components/MkColorInput.vue
@@ -24,8 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
-import { i18n } from '@/i18n.js';
+import { ref, shallowRef, toRefs } from 'vue';
 
 const props = defineProps<{
 	modelValue: string | null;
diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue
index 92b5388c34..3f8fef6632 100644
--- a/packages/frontend/src/components/MkMediaBanner.vue
+++ b/packages/frontend/src/components/MkMediaBanner.vue
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, shallowRef, watch, ref } from 'vue';
+import { shallowRef, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { i18n } from '@/i18n.js';
 
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index af0f1ec91b..8e4b86f1c7 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts">
-import { Ref, computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
+import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
 import { focusPrev, focusNext } from '@/scripts/focus.js';
 import MkSwitchButton from '@/components/MkSwitch.button.vue';
 import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu';
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index bb834a3845..66a5be22c3 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -151,7 +151,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, inject, onMounted, ref, shallowRef, Ref, defineAsyncComponent, watch, provide } from 'vue';
+import { computed, inject, onMounted, ref, shallowRef, Ref, watch, provide } from 'vue';
 import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import MkNoteSub from '@/components/MkNoteSub.vue';
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 48d90522c4..e88d33ed61 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -221,11 +221,10 @@ import { useNoteCapture } from '@/scripts/use-note-capture.js';
 import { deepClone } from '@/scripts/clone.js';
 import { useTooltip } from '@/scripts/use-tooltip.js';
 import { claimAchievement } from '@/scripts/achievements.js';
-import { MenuItem } from '@/types/menu.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 import MkButton from '@/components/MkButton.vue';
 
diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue
index 868f64a4b8..e7cb3f96f1 100644
--- a/packages/frontend/src/components/MkNoteSimple.vue
+++ b/packages/frontend/src/components/MkNoteSimple.vue
@@ -27,7 +27,6 @@ import * as Misskey from 'misskey-js';
 import MkNoteHeader from '@/components/MkNoteHeader.vue';
 import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
 import MkCwButton from '@/components/MkCwButton.vue';
-import { $i } from '@/account.js';
 
 const props = defineProps<{
 	note: Misskey.entities.Note;
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 5649ce1e6c..40362a955a 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -51,7 +51,6 @@ import { i18n } from '@/i18n.js';
 import { $i } from '@/account.js';
 import { userPage } from '@/filters/user.js';
 import { checkWordMute } from '@/scripts/check-word-mute.js';
-import { defaultStore } from '@/store.js';
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 2b9af26654..4d422c10ae 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -136,7 +136,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref, shallowRef } from 'vue';
+import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index cefef91285..bb8a5d2e72 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -24,13 +24,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onUnmounted, onDeactivated, onMounted, computed, shallowRef, onActivated, watch } from 'vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import { onUnmounted, onDeactivated, onMounted, computed, shallowRef, onActivated } from 'vue';
+import MkPagination from '@/components/MkPagination.vue';
 import XNotification from '@/components/MkNotification.vue';
 import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
 import MkNote from '@/components/MkNote.vue';
 import { useStream } from '@/stream.js';
-import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import { notificationTypes } from '@/const.js';
 import { infoImageUrl } from '@/instance.js';
diff --git a/packages/frontend/src/components/MkPullToRefresh.vue b/packages/frontend/src/components/MkPullToRefresh.vue
index 44555f2c13..54ef117d77 100644
--- a/packages/frontend/src/components/MkPullToRefresh.vue
+++ b/packages/frontend/src/components/MkPullToRefresh.vue
@@ -23,8 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, onUnmounted, watch, ref, shallowRef } from 'vue';
-import { deviceKind } from '@/scripts/device-kind.js';
+import { onMounted, onUnmounted, ref, shallowRef } from 'vue';
 import { i18n } from '@/i18n.js';
 import { getScrollContainer } from '@/scripts/scroll.js';
 
diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue
index 08e57fd8a6..dd05a44e04 100644
--- a/packages/frontend/src/components/MkSignupDialog.form.vue
+++ b/packages/frontend/src/components/MkSignupDialog.form.vue
@@ -80,7 +80,6 @@ import { ref, computed } from 'vue';
 import { toUnicode } from 'punycode/';
 import MkButton from './MkButton.vue';
 import MkInput from './MkInput.vue';
-import MkSwitch from './MkSwitch.vue';
 import MkCaptcha, { type Captcha } from '@/components/MkCaptcha.vue';
 import * as config from '@/config.js';
 import * as os from '@/os.js';
diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue
index 76163ab68b..8f9c1c93f8 100644
--- a/packages/frontend/src/components/MkSignupDialog.rules.vue
+++ b/packages/frontend/src/components/MkSignupDialog.rules.vue
@@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, onMounted, ref, watch } from 'vue';
+import { computed, ref } from 'vue';
 import { instance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import MkButton from '@/components/MkButton.vue';
diff --git a/packages/frontend/src/components/MkSignupDialog.vue b/packages/frontend/src/components/MkSignupDialog.vue
index 1467049e25..b4fba114a6 100644
--- a/packages/frontend/src/components/MkSignupDialog.vue
+++ b/packages/frontend/src/components/MkSignupDialog.vue
@@ -39,7 +39,6 @@ import XSignup from '@/components/MkSignupDialog.form.vue';
 import XServerRules from '@/components/MkSignupDialog.rules.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import { i18n } from '@/i18n.js';
-import { instance } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	autoSet?: boolean;
diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue
index 370894d4f4..438140649e 100644
--- a/packages/frontend/src/components/MkSubNoteContent.vue
+++ b/packages/frontend/src/components/MkSubNoteContent.vue
@@ -35,7 +35,6 @@ import * as Misskey from 'misskey-js';
 import MkMediaList from '@/components/MkMediaList.vue';
 import MkPoll from '@/components/MkPoll.vue';
 import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
 import { shouldCollapsed } from '@/scripts/collapsed.js';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
index 4ecca7334c..5f3f5b81dd 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
@@ -34,15 +34,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, ref, watch } from 'vue';
-import { instance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
-import MkButton from '@/components/MkButton.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import XUser from '@/components/MkUserSetupDialog.User.vue';
-import MkInfo from '@/components/MkInfo.vue';
-import * as os from '@/os.js';
-import { $i } from '@/account.js';
 import MkPagination from '@/components/MkPagination.vue';
 
 const pinnedUsers = { endpoint: 'pinned-users', noPaging: true };
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
index 4bca72511d..ecdfbb4969 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
@@ -44,14 +44,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, ref, watch } from 'vue';
-import { instance } from '@/instance.js';
+import { ref, watch } from 'vue';
 import { i18n } from '@/i18n.js';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import * as os from '@/os.js';
-import { $i } from '@/account.js';
 
 const isLocked = ref(false);
 const hideOnlineStatus = ref(false);
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
index 8de9bbdbb1..37aa677b44 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
@@ -30,8 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, ref, watch } from 'vue';
-import { instance } from '@/instance.js';
+import { ref, watch } from 'vue';
 import { i18n } from '@/i18n.js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue
index 4fbaf75454..49476c7364 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.User.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.User.vue
@@ -29,7 +29,6 @@ import * as Misskey from 'misskey-js';
 import { ref } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
 import * as os from '@/os.js';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 0678a7c09c..60068df842 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -53,7 +53,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import XTimeline from './welcome.timeline.vue';
 import XSigninDialog from '@/components/MkSigninDialog.vue';
 import XSignupDialog from '@/components/MkSignupDialog.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -63,7 +62,6 @@ import { instanceName } from '@/config.js';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
-import number from '@/filters/number.js';
 import MkNumber from '@/components/MkNumber.vue';
 import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
 
diff --git a/packages/frontend/src/components/form/suspense.vue b/packages/frontend/src/components/form/suspense.vue
index e3db639ff0..bfddac7523 100644
--- a/packages/frontend/src/components/form/suspense.vue
+++ b/packages/frontend/src/components/form/suspense.vue
@@ -21,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref, watch } from 'vue';
 import MkButton from '@/components/MkButton.vue';
-import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index 5552e96ee0..d34f47a68a 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -14,7 +14,6 @@ import { computed } from 'vue';
 import * as os from '@/os.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { url } from '@/config.js';
-import { popout as popout_ } from '@/scripts/popout.js';
 import { i18n } from '@/i18n.js';
 import { useRouter } from '@/router.js';
 
diff --git a/packages/frontend/src/components/global/MkAd.stories.impl.ts b/packages/frontend/src/components/global/MkAd.stories.impl.ts
index 360bc88b4a..5ae45ec58f 100644
--- a/packages/frontend/src/components/global/MkAd.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAd.stories.impl.ts
@@ -4,11 +4,8 @@
  */
 
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
-import { expect } from '@storybook/jest';
-import { userEvent, waitFor, within } from '@storybook/testing-library';
 import { StoryObj } from '@storybook/vue3';
 import MkAd from './MkAd.vue';
-import { i18n } from '@/i18n.js';
 
 let lock: Promise<undefined> | undefined;
 
diff --git a/packages/frontend/src/components/global/MkUserName.stories.impl.ts b/packages/frontend/src/components/global/MkUserName.stories.impl.ts
index 8c24a4819f..01455e492d 100644
--- a/packages/frontend/src/components/global/MkUserName.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkUserName.stories.impl.ts
@@ -5,7 +5,6 @@
 
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { expect } from '@storybook/jest';
-import { userEvent, within } from '@storybook/testing-library';
 import { StoryObj } from '@storybook/vue3';
 import { userDetailed } from '../../../.storybook/fakes';
 import MkUserName from './MkUserName.vue';
diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue
index e0f1a4af90..1ab2c0f3c3 100644
--- a/packages/frontend/src/components/page/page.text.vue
+++ b/packages/frontend/src/components/page/page.text.vue
@@ -16,7 +16,6 @@ import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { TextBlock } from './block.type';
 import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
-import { $i } from '@/account.js';
 
 const MkUrlPreview = defineAsyncComponent(() => import('@/components/MkUrlPreview.vue'));
 
diff --git a/packages/frontend/src/components/page/page.vue b/packages/frontend/src/components/page/page.vue
index ab37ca69ad..94ca7bdf04 100644
--- a/packages/frontend/src/components/page/page.vue
+++ b/packages/frontend/src/components/page/page.vue
@@ -10,7 +10,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, nextTick } from 'vue';
 import * as Misskey from 'misskey-js';
 import XBlock from './page.block.vue';
 
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 4ad8cc58c5..a614ba73d2 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -220,12 +220,12 @@ import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import * as os from '@/os.js';
 import { url } from '@/config.js';
-import { userPage, acct } from '@/filters/user.js';
+import { acct } from '@/filters/user.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 import { iAmAdmin, $i } from '@/account.js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 
 const props = withDefaults(defineProps<{
 	userId: string;
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index 28109cfd2d..e09f68f6e4 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -97,11 +97,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, computed } from 'vue';
 import JSON5 from 'json5';
 import XHeader from './_header_.vue';
-import MkSwitch from '@/components/MkSwitch.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
-import FormSection from '@/components/form/section.vue';
-import FormSplit from '@/components/form/split.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os.js';
 import { instance, fetchInstance } from '@/instance.js';
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index a64dad3164..f6c0b29403 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -64,8 +64,6 @@ import XHeader from './_header_.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
-import FormSection from '@/components/form/section.vue';
-import FormSplit from '@/components/form/split.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os.js';
 import { fetchInstance } from '@/instance.js';
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index fe825613fa..699b3c425a 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -123,9 +123,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 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';
 import MkFolder from '@/components/MkFolder.vue';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index c11cc24b4f..9aa7d8dd3c 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -73,7 +73,7 @@ import { useRouter } from '@/router.js';
 import MkButton from '@/components/MkButton.vue';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import { infoImageUrl } from '@/instance.js';
 
 const router = useRouter();
diff --git a/packages/frontend/src/pages/ads.vue b/packages/frontend/src/pages/ads.vue
index ee58049554..9e85e81f19 100644
--- a/packages/frontend/src/pages/ads.vue
+++ b/packages/frontend/src/pages/ads.vue
@@ -16,8 +16,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
-import * as os from '@/os.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue
index b9edb18d10..9dedbccedc 100644
--- a/packages/frontend/src/pages/avatar-decorations.vue
+++ b/packages/frontend/src/pages/avatar-decorations.vue
@@ -38,9 +38,6 @@ import { ref, computed } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
-import MkSwitch from '@/components/MkSwitch.vue';
-import MkRadios from '@/components/MkRadios.vue';
-import MkInfo from '@/components/MkInfo.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index fa92424fa0..8b2179c6eb 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -80,7 +80,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import FormSplit from '@/components/form/split.vue';
-import { selectFile, selectFiles } from '@/scripts/select-file.js';
+import { selectFile } from '@/scripts/select-file.js';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index 8119150df9..12928a25f8 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -85,7 +85,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { customEmojiCategories } from '@/custom-emojis.js';
 import MkSwitch from '@/components/MkSwitch.vue';
-import { selectFile, selectFiles } from '@/scripts/select-file.js';
+import { selectFile } from '@/scripts/select-file.js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index 3c6b0750de..cf9da02868 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -68,7 +68,7 @@ import MkInput from '@/components/MkInput.vue';
 import { userListsCache } from '@/cache.js';
 import { $i } from '@/account.js';
 import { defaultStore } from '@/store.js';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 
 const {
 	enableInfiniteScroll,
diff --git a/packages/frontend/src/pages/page-editor/page-editor.container.vue b/packages/frontend/src/pages/page-editor/page-editor.container.vue
index c1bc0c61c9..9b0dce820c 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.container.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.container.vue
@@ -29,7 +29,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	expanded?: boolean;
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index 3e74a6f591..38692c4de3 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -42,18 +42,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, onMounted, ref } from 'vue';
+import { ref } from 'vue';
 import MkNotes from '@/components/MkNotes.vue';
 import MkInput from '@/components/MkInput.vue';
-import MkRadios from '@/components/MkRadios.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import { $i } from '@/account.js';
-import { instance } from '@/instance.js';
-import MkInfo from '@/components/MkInfo.vue';
 import { useRouter } from '@/router.js';
 import MkFolder from '@/components/MkFolder.vue';
 
diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue
index 39707e634c..0d978e4107 100644
--- a/packages/frontend/src/pages/search.user.vue
+++ b/packages/frontend/src/pages/search.user.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
+import { ref } from 'vue';
 import MkUserList from '@/components/MkUserList.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkRadios from '@/components/MkRadios.vue';
@@ -33,9 +33,6 @@ import MkButton from '@/components/MkButton.vue';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import { $i } from '@/account.js';
-import { instance } from '@/instance.js';
-import MkInfo from '@/components/MkInfo.vue';
 import { useRouter } from '@/router.js';
 
 const router = useRouter();
diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue
index c47414e573..9d5e5697ce 100644
--- a/packages/frontend/src/pages/search.vue
+++ b/packages/frontend/src/pages/search.vue
@@ -23,10 +23,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
+import { computed, defineAsyncComponent, ref } from 'vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import * as os from '@/os.js';
 import { $i } from '@/account.js';
 import { instance } from '@/instance.js';
 import MkInfo from '@/components/MkInfo.vue';
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index d9a59cdc35..4c165ef4ee 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -72,7 +72,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref, defineAsyncComponent, computed } from 'vue';
+import { defineAsyncComponent, computed } from 'vue';
 import { supported as webAuthnSupported, create as webAuthnCreate, parseCreationOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
index 77e6b28fad..329ab4d47a 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
@@ -50,9 +50,6 @@ import MkButton from '@/components/MkButton.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import { i18n } from '@/i18n.js';
-import * as os from '@/os.js';
-import MkFolder from '@/components/MkFolder.vue';
-import MkInfo from '@/components/MkInfo.vue';
 import MkRange from '@/components/MkRange.vue';
 import { $i } from '@/account.js';
 
diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue
index 8da60ef504..4efcdb31da 100644
--- a/packages/frontend/src/pages/settings/drive-cleaner.vue
+++ b/packages/frontend/src/pages/settings/drive-cleaner.vue
@@ -55,7 +55,6 @@ import MkPagination from '@/components/MkPagination.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
 import { i18n } from '@/i18n.js';
 import bytes from '@/filters/bytes.js';
-import { dateString } from '@/filters/date.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkSelect from '@/components/MkSelect.vue';
 import { getDriveFileMenu } from '@/scripts/get-drive-file-menu.js';
diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue
index 66477a86ca..5fe7209a0a 100644
--- a/packages/frontend/src/pages/settings/navbar.vue
+++ b/packages/frontend/src/pages/settings/navbar.vue
@@ -57,7 +57,6 @@ import { defaultStore } from '@/store.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { deepClone } from '@/scripts/clone.js';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index def8fd3e69..98b82f7116 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, shallowRef, computed } from 'vue';
+import { shallowRef, computed } from 'vue';
 import XNotificationConfig from './notifications.notification-config.vue';
 import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index 2ee19b9671..d28c8284cf 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -110,8 +110,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, reactive, ref, watch, defineAsyncComponent } from 'vue';
-import Misskey from 'misskey-js';
-import XAvatarDecoration from './profile.avatar-decoration.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
diff --git a/packages/frontend/src/pages/settings/roles.vue b/packages/frontend/src/pages/settings/roles.vue
index 0f6c30dae9..40671f7132 100644
--- a/packages/frontend/src/pages/settings/roles.vue
+++ b/packages/frontend/src/pages/settings/roles.vue
@@ -23,21 +23,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, reactive, watch } from 'vue';
-import MkButton from '@/components/MkButton.vue';
-import MkInput from '@/components/MkInput.vue';
-import MkTextarea from '@/components/MkTextarea.vue';
-import MkSwitch from '@/components/MkSwitch.vue';
-import MkSelect from '@/components/MkSelect.vue';
-import FormSplit from '@/components/form/split.vue';
-import MkFolder from '@/components/MkFolder.vue';
-import FormSlot from '@/components/form/slot.vue';
+import { computed } from 'vue';
 import FormSection from '@/components/form/section.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { $i } from '@/account.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { defaultStore } from '@/store.js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
 
 function save() {
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 59c45a57ff..d976463db4 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -45,7 +45,6 @@ import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
 import { $i } from '@/account.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { miLocalStorage } from '@/local-storage.js';
 import { antennasCache, userListsCache } from '@/cache.js';
 import { deviceKind } from '@/scripts/device-kind.js';
 import { MenuItem } from '@/types/menu.js';
diff --git a/packages/frontend/src/pages/user-tag.vue b/packages/frontend/src/pages/user-tag.vue
index 06269ec9a9..5d83efc1a9 100644
--- a/packages/frontend/src/pages/user-tag.vue
+++ b/packages/frontend/src/pages/user-tag.vue
@@ -16,8 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { computed, watch } from 'vue';
-import * as os from '@/os.js';
+import { computed } from 'vue';
 import MkUserList from '@/components/MkUserList.vue';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index a9497f4fe0..2a9eb5f8e4 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -171,7 +171,6 @@ import { i18n } from '@/i18n.js';
 import { $i, iAmModerator } from '@/account.js';
 import { dateString } from '@/filters/date.js';
 import { confetti } from '@/scripts/confetti.js';
-import MkNotes from '@/components/MkNotes.vue';
 import { api } from '@/os.js';
 import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
 
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index 9c27eeec54..3ad34355f5 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -39,13 +39,7 @@ import XTimeline from './welcome.timeline.vue';
 import MarqueeText from '@/components/MkMarquee.vue';
 import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
 import misskeysvg from '/client-assets/misskey.svg';
-import MkInfo from '@/components/MkInfo.vue';
-import { instanceName } from '@/config.js';
 import * as os from '@/os.js';
-import { i18n } from '@/i18n.js';
-import { instance } from '@/instance.js';
-import number from '@/filters/number.js';
-import MkNumber from '@/components/MkNumber.vue';
 import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
 import { getProxiedImageUrl } from '@/scripts/media-proxy.js';
 
diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue
index 129131ce4a..92be80228a 100644
--- a/packages/frontend/src/pages/welcome.timeline.vue
+++ b/packages/frontend/src/pages/welcome.timeline.vue
@@ -34,7 +34,6 @@ import MkMediaList from '@/components/MkMediaList.vue';
 import MkPoll from '@/components/MkPoll.vue';
 import * as os from '@/os.js';
 import { getScrollContainer } from '@/scripts/scroll.js';
-import { $i } from '@/account.js';
 
 const notes = ref<Misskey.entities.Note[]>([]);
 const isScrolling = ref(false);
diff --git a/packages/frontend/src/scripts/emoji-picker.ts b/packages/frontend/src/scripts/emoji-picker.ts
index 3cf653ea1b..f87c3f6fb2 100644
--- a/packages/frontend/src/scripts/emoji-picker.ts
+++ b/packages/frontend/src/scripts/emoji-picker.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { defineAsyncComponent, Ref, ref, computed, ComputedRef } from 'vue';
+import { defineAsyncComponent, Ref, ref } from 'vue';
 import { popup } from '@/os.js';
 import { defaultStore } from '@/store.js';
 
diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue
index 402ab1efee..bc1527813c 100644
--- a/packages/frontend/src/ui/classic.sidebar.vue
+++ b/packages/frontend/src/ui/classic.sidebar.vue
@@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, onMounted, computed, watch, nextTick, ref, shallowRef } from 'vue';
+import { defineAsyncComponent, computed, watch, ref, shallowRef } from 'vue';
 import { openInstanceMenu } from './_common_/common.js';
 // import { host } from '@/config.js';
 import * as os from '@/os.js';
diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue
index b5381396cd..c8d15630ba 100644
--- a/packages/frontend/src/ui/classic.vue
+++ b/packages/frontend/src/ui/classic.vue
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { defineAsyncComponent, ComputedRef, onMounted, provide, ref, computed, shallowRef } from 'vue';
+import { defineAsyncComponent, onMounted, provide, ref, computed, shallowRef } from 'vue';
 import XSidebar from './classic.sidebar.vue';
 import XCommon from './_common_/common.vue';
 import { instanceName } from '@/config.js';
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index 1c459cbf3a..d184764b82 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -99,7 +99,6 @@ import { deckStore, addColumn as addColumnToStore, loadDeck, getProfiles, delete
 import XSidebar from '@/ui/_common_/navbar.vue';
 import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
 import MkButton from '@/components/MkButton.vue';
-import { getScrollContainer } from '@/scripts/scroll.js';
 import * as os from '@/os.js';
 import { navbarItemDef } from '@/navbar.js';
 import { $i } from '@/account.js';
diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue
index 8bf3a28d55..e0d54614b6 100644
--- a/packages/frontend/src/ui/visitor.vue
+++ b/packages/frontend/src/ui/visitor.vue
@@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ComputedRef, onMounted, provide, ref, computed } from 'vue';
 import XCommon from './_common_/common.vue';
-import { host, instanceName } from '@/config.js';
+import { instanceName } from '@/config.js';
 import * as os from '@/os.js';
 import { instance } from '@/instance.js';
 import XSigninDialog from '@/components/MkSigninDialog.vue';
diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue
index 6b890d41a8..db89265bff 100644
--- a/packages/frontend/src/widgets/WidgetActivity.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import XCalendar from './WidgetActivity.calendar.vue';
 import XChart from './WidgetActivity.chart.vue';
 import { GetFormResultType } from '@/scripts/form.js';
diff --git a/packages/frontend/src/widgets/WidgetAichan.vue b/packages/frontend/src/widgets/WidgetAichan.vue
index cf2012b74d..fef026244c 100644
--- a/packages/frontend/src/widgets/WidgetAichan.vue
+++ b/packages/frontend/src/widgets/WidgetAichan.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, onUnmounted, shallowRef } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 
 const name = 'ai';
diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue
index 1b8c8ad9bc..5968b54626 100644
--- a/packages/frontend/src/widgets/WidgetAiscript.vue
+++ b/packages/frontend/src/widgets/WidgetAiscript.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import { Interpreter, Parser, utils } from '@syuilo/aiscript';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
index 08037222d0..10248a840a 100644
--- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue
+++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, Ref, ref, watch } from 'vue';
 import { Interpreter, Parser } from '@syuilo/aiscript';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
 import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
diff --git a/packages/frontend/src/widgets/WidgetButton.vue b/packages/frontend/src/widgets/WidgetButton.vue
index a7bdd4c49c..11082c1e3f 100644
--- a/packages/frontend/src/widgets/WidgetButton.vue
+++ b/packages/frontend/src/widgets/WidgetButton.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { Interpreter, Parser } from '@syuilo/aiscript';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
 import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue
index 7fabd09a24..c78e291a2e 100644
--- a/packages/frontend/src/widgets/WidgetCalendar.vue
+++ b/packages/frontend/src/widgets/WidgetCalendar.vue
@@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import { i18n } from '@/i18n.js';
 import { useInterval } from '@/scripts/use-interval.js';
diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue
index 5e7464f3a4..988ec90369 100644
--- a/packages/frontend/src/widgets/WidgetClicker.vue
+++ b/packages/frontend/src/widgets/WidgetClicker.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkClickerGame from '@/components/MkClickerGame.vue';
diff --git a/packages/frontend/src/widgets/WidgetClock.vue b/packages/frontend/src/widgets/WidgetClock.vue
index ca115cfcf7..22f053db59 100644
--- a/packages/frontend/src/widgets/WidgetClock.vue
+++ b/packages/frontend/src/widgets/WidgetClock.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkAnalogClock from '@/components/MkAnalogClock.vue';
diff --git a/packages/frontend/src/widgets/WidgetDigitalClock.vue b/packages/frontend/src/widgets/WidgetDigitalClock.vue
index ba7b82aad5..a4b90c49d3 100644
--- a/packages/frontend/src/widgets/WidgetDigitalClock.vue
+++ b/packages/frontend/src/widgets/WidgetDigitalClock.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import { timezones } from '@/scripts/timezones.js';
 import MkDigitalClock from '@/components/MkDigitalClock.vue';
diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue
index 47f94402fb..d32a4e836b 100644
--- a/packages/frontend/src/widgets/WidgetFederation.vue
+++ b/packages/frontend/src/widgets/WidgetFederation.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkMiniChart from '@/components/MkMiniChart.vue';
diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
index 16e1a42da2..0fc96c0d35 100644
--- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { shallowRef } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkTagCloud from '@/components/MkTagCloud.vue';
diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue
index ff4a1b46c0..6904037532 100644
--- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import { host } from '@/config.js';
 import { instance } from '@/instance.js';
diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue
index cca368ec8f..10bc257e12 100644
--- a/packages/frontend/src/widgets/WidgetJobQueue.vue
+++ b/packages/frontend/src/widgets/WidgetJobQueue.vue
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onUnmounted, reactive, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import { useStream } from '@/stream.js';
 import number from '@/filters/number.js';
diff --git a/packages/frontend/src/widgets/WidgetMemo.vue b/packages/frontend/src/widgets/WidgetMemo.vue
index 1f5666b3ef..167014270a 100644
--- a/packages/frontend/src/widgets/WidgetMemo.vue
+++ b/packages/frontend/src/widgets/WidgetMemo.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import { defaultStore } from '@/store.js';
diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue
index 796578395f..506fc6b4d4 100644
--- a/packages/frontend/src/widgets/WidgetNotifications.vue
+++ b/packages/frontend/src/widgets/WidgetNotifications.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { defineAsyncComponent } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import XNotifications from '@/components/MkNotifications.vue';
diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
index 46fe991f37..0a6fec7f2e 100644
--- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue
+++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
 import { useInterval } from '@/scripts/use-interval.js';
diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue
index 9af4f80873..b1c62caf4d 100644
--- a/packages/frontend/src/widgets/WidgetPhotos.vue
+++ b/packages/frontend/src/widgets/WidgetPhotos.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onUnmounted, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import { useStream } from '@/stream.js';
 import { getStaticImageUrl } from '@/scripts/media-proxy.js';
diff --git a/packages/frontend/src/widgets/WidgetPostForm.vue b/packages/frontend/src/widgets/WidgetPostForm.vue
index 320b47a4ff..9979ae256e 100644
--- a/packages/frontend/src/widgets/WidgetPostForm.vue
+++ b/packages/frontend/src/widgets/WidgetPostForm.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkPostForm from '@/components/MkPostForm.vue';
 
diff --git a/packages/frontend/src/widgets/WidgetProfile.vue b/packages/frontend/src/widgets/WidgetProfile.vue
index fc54af2d71..3ff57bab86 100644
--- a/packages/frontend/src/widgets/WidgetProfile.vue
+++ b/packages/frontend/src/widgets/WidgetProfile.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import { $i } from '@/account.js';
 import { userPage } from '@/filters/user.js';
diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue
index be662e0ed1..78678920c7 100644
--- a/packages/frontend/src/widgets/WidgetRss.vue
+++ b/packages/frontend/src/widgets/WidgetRss.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, watch, computed } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import { url as base } from '@/config.js';
diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue
index 07f922bfec..34b4b8f884 100644
--- a/packages/frontend/src/widgets/WidgetRssTicker.vue
+++ b/packages/frontend/src/widgets/WidgetRssTicker.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, watch, computed } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import MarqueeText from '@/components/MkMarquee.vue';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue
index 82b6246add..eccb9a00bf 100644
--- a/packages/frontend/src/widgets/WidgetSlideshow.vue
+++ b/packages/frontend/src/widgets/WidgetSlideshow.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, ref, shallowRef } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
 import { useInterval } from '@/scripts/use-interval.js';
diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue
index a2d49f62af..4a7b06f1d9 100644
--- a/packages/frontend/src/widgets/WidgetTimeline.vue
+++ b/packages/frontend/src/widgets/WidgetTimeline.vue
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue
index 0d4df28a95..51de02d308 100644
--- a/packages/frontend/src/widgets/WidgetTrends.vue
+++ b/packages/frontend/src/widgets/WidgetTrends.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkMiniChart from '@/components/MkMiniChart.vue';
diff --git a/packages/frontend/src/widgets/WidgetUnixClock.vue b/packages/frontend/src/widgets/WidgetUnixClock.vue
index 33585cd721..35f29b5e21 100644
--- a/packages/frontend/src/widgets/WidgetUnixClock.vue
+++ b/packages/frontend/src/widgets/WidgetUnixClock.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onUnmounted, ref, watch } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 
 const name = 'unixClock';
diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue
index 4f3ce1c8c5..81b14fde3f 100644
--- a/packages/frontend/src/widgets/WidgetUserList.vue
+++ b/packages/frontend/src/widgets/WidgetUserList.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
+import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import * as os from '@/os.js';

From ffa0470cf820370b2bc2539be226a55f4cac1aff Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 21 Dec 2023 11:37:14 +0900
Subject: [PATCH 284/435] chore(deps): bump actions/setup-node from 4.0.0 to
 4.0.1 (#12713)

Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/v4.0.0...v4.0.1)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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/get-api-diff.yml    | 2 +-
 .github/workflows/lint.yml            | 6 +++---
 .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 +-
 7 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/.github/workflows/api-misskey-js.yml b/.github/workflows/api-misskey-js.yml
index 3be8f095f1..5cffbd81bc 100644
--- a/.github/workflows/api-misskey-js.yml
+++ b/.github/workflows/api-misskey-js.yml
@@ -14,7 +14,7 @@ jobs:
       - run: corepack enable
 
       - name: Setup Node.js
-        uses: actions/setup-node@v4.0.0
+        uses: actions/setup-node@v4.0.1
         with:
           node-version-file: '.node-version'
           cache: 'pnpm'
diff --git a/.github/workflows/get-api-diff.yml b/.github/workflows/get-api-diff.yml
index 9dc812061b..d604f9b16d 100644
--- a/.github/workflows/get-api-diff.yml
+++ b/.github/workflows/get-api-diff.yml
@@ -37,7 +37,7 @@ jobs:
         version: 8
         run_install: false
     - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.0
+      uses: actions/setup-node@v4.0.1
       with:
         node-version: ${{ matrix.node-version }}
         cache: 'pnpm'
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 5096e54af8..d6832278e8 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -19,7 +19,7 @@ jobs:
       with:
         version: 8
         run_install: false
-    - uses: actions/setup-node@v4.0.0
+    - uses: actions/setup-node@v4.0.1
       with:
         node-version-file: '.node-version'
         cache: 'pnpm'
@@ -46,7 +46,7 @@ jobs:
       with:
         version: 7
         run_install: false
-    - uses: actions/setup-node@v4.0.0
+    - uses: actions/setup-node@v4.0.1
       with:
         node-version-file: '.node-version'
         cache: 'pnpm'
@@ -72,7 +72,7 @@ jobs:
       with:
         version: 7
         run_install: false
-    - uses: actions/setup-node@v4.0.0
+    - uses: actions/setup-node@v4.0.1
       with:
         node-version-file: '.node-version'
         cache: 'pnpm'
diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml
index 1b0f22c8e9..9681cbec59 100644
--- a/.github/workflows/test-backend.yml
+++ b/.github/workflows/test-backend.yml
@@ -38,7 +38,7 @@ jobs:
         version: 8
         run_install: false
     - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.0
+      uses: actions/setup-node@v4.0.1
       with:
         node-version: ${{ matrix.node-version }}
         cache: 'pnpm'
diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml
index 18b2a8c202..83740bf156 100644
--- a/.github/workflows/test-frontend.yml
+++ b/.github/workflows/test-frontend.yml
@@ -25,7 +25,7 @@ jobs:
         version: 8
         run_install: false
     - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.0
+      uses: actions/setup-node@v4.0.1
       with:
         node-version: ${{ matrix.node-version }}
         cache: 'pnpm'
@@ -83,7 +83,7 @@ jobs:
         version: 7
         run_install: false
     - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.0
+      uses: actions/setup-node@v4.0.1
       with:
         node-version: ${{ matrix.node-version }}
         cache: 'pnpm'
diff --git a/.github/workflows/test-misskey-js.yml b/.github/workflows/test-misskey-js.yml
index 76e170b3e3..055152f321 100644
--- a/.github/workflows/test-misskey-js.yml
+++ b/.github/workflows/test-misskey-js.yml
@@ -26,7 +26,7 @@ jobs:
       - run: corepack enable
 
       - name: Setup Node.js ${{ matrix.node-version }}
-        uses: actions/setup-node@v4.0.0
+        uses: actions/setup-node@v4.0.1
         with:
           node-version: ${{ matrix.node-version }}
           cache: 'pnpm'
diff --git a/.github/workflows/test-production.yml b/.github/workflows/test-production.yml
index 694fa1a8f5..9e02c0d8f8 100644
--- a/.github/workflows/test-production.yml
+++ b/.github/workflows/test-production.yml
@@ -28,7 +28,7 @@ jobs:
         version: 8
         run_install: false
     - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v4.0.0
+      uses: actions/setup-node@v4.0.1
       with:
         node-version: ${{ matrix.node-version }}
         cache: 'pnpm'

From f48028765738bf1efea4369a2f2689baa0f81565 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 21 Dec 2023 11:37:26 +0900
Subject: [PATCH 285/435] New Crowdin updates (#12722)

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

* New translations ja-jp.yml (Korean)
---
 locales/ko-KR.yml | 7 +++++--
 locales/zh-TW.yml | 2 +-
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index ae612baecb..c8d69255f9 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -881,6 +881,8 @@ makeReactionsPublicDescription: "나의 리액션을 누구나 볼 수 있게 
 classic: "클래식"
 muteThread: "글타래 뮤트"
 unmuteThread: "글타래 뮤트 해제"
+followingVisibility: "팔로우의 공개 범위"
+followersVisibility: "팔로워의 공개 범위"
 continueThread: "글타래 더 보기"
 deleteAccountConfirm: "계정이 삭제되고 되돌릴 수 없게 됩니다. 계속하시겠습니까? "
 incorrectPassword: "비밀번호가 올바르지 않습니다."
@@ -1178,6 +1180,7 @@ reloadRequiredToApplySettings: "설정을 적용하려면 새로고침을 해야
 remainingN: "나머지: {n}"
 overwriteContentConfirm: "현재 내용을 덮어쓰기 합니다. 계속 진행하시겠습니까?"
 seasonalScreenEffect: "철에 맞는 화면으로 꾸미기"
+decorate: "장식하기"
 _announcement:
   forExistingUsers: "기존 유저에게만 알림"
   forExistingUsersDescription: "활성화하면 이 공지사항을 게시한 시점에서 이미 가입한 유저에게만 표시합니다. 비활성화하면 게시 후에 가입한 유저에게도 표시합니다."
@@ -1837,8 +1840,8 @@ _soundSettings:
   driveFileWarn: "드라이브에 있는 파일을 선택하세요."
   driveFileTypeWarn: "이 파일은 지원되지 않습니다."
   driveFileTypeWarnDescription: "오디오 파일을 선택하세요."
-  driveFileDurationWarn: "오디오가 너무 길어요."
-  driveFileDurationWarnDescription: "길은 오디오를 사용하시는 경우 미스키 사용에 지장이 갈 수도 있습니다. 그래도 괜찮습니까?"
+  driveFileDurationWarn: "오디오가 너무 깁니다"
+  driveFileDurationWarnDescription: "긴 오디오로 설정할 경우 미스키 사용에 지장이 갈 수도 있습니다. 그래도 괜찮습니까?"
 _ago:
   future: "미래"
   justNow: "방금 전"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 8fbf036385..419c063e27 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -1217,7 +1217,7 @@ _initialTutorial:
   skipAreYouSure: "結束教學模式?"
   _landing:
     title: "歡迎使用本教學課程"
-    description: "在這裡您可以查看Misskey的基本使用方法和功能。"
+    description: "在這裡您可以查看 Misskey 的基本使用方法和功能。"
   _note:
     title: "什麼是貼文?"
     description: "在Misskey上發布的內容稱為「貼文」。貼文在時間軸上按時間順序排列,並即時更新。"

From f88ed4dd1a6721ced856147bdc146ca69c27f760 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 21 Dec 2023 11:37:50 +0900
Subject: [PATCH 286/435] 2023.12.0-beta.6

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

diff --git a/package.json b/package.json
index 6120c07094..562c5ce407 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.12.0-beta.5",
+	"version": "2023.12.0-beta.6",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From 07d4632cd7839c6a7cdf2e9227a84aefe70ac7fd Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 21 Dec 2023 14:45:39 +0900
Subject: [PATCH 287/435] Update CHANGELOG.md

Co-authored-by: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f535aa3d9c..a7135d8745 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,7 @@
 
 ### Note
 - Node.js 20.10.0が最小要件になりました
+- 絵文字の追加辞書を既にインストールしている場合は、お手数ですが再インストールのほどお願いします
 - 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。  
 
 	**影響:**  

From 79ca93cefb8c892556b49fe4055d397e2a56adcf Mon Sep 17 00:00:00 2001
From: GrapeApple0 <84321396+GrapeApple0@users.noreply.github.com>
Date: Thu, 21 Dec 2023 16:57:05 +0900
Subject: [PATCH 288/435] =?UTF-8?q?enhance:=20api.json=E3=81=AE=E3=83=AC?=
 =?UTF-8?q?=E3=82=B9=E3=83=9D=E3=83=B3=E3=82=B9=E3=81=AE=E5=86=85=E5=AE=B9?=
 =?UTF-8?q?=E3=82=92=E5=AE=9F=E9=9A=9B=E3=81=AE=E5=86=85=E5=AE=B9=E3=81=AB?=
 =?UTF-8?q?=E5=90=88=E3=82=8F=E3=81=9B=E3=82=8B=20(#12723)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Create packedAdSchema

* admin/emoji/add

* admin/get-user-ips

* admin/roles/users

* admin/get-index-stats

* admin/accounts/find-by-email

* fix type of admin/ad/list

* federation/stats

* endpoints

* get-online-users-count

* i/2fa/register-key

* i/2fa/key-done

* i/2fa/register

* i/apps

* i/authorized-apps

* i/registry/get-all

* i/registry/get

* i/registry/get-detail

* i/registry/key-with-type

* i/registry/scopes-with-domain

* i/update-email

* i/move

* i/webhooks/create

* fix miss type

* i/webhooks/show

* i/webhooks/list

* flash/create

* roles/users

* server-info

* test

* users/lists/get-memberships

* users/achievements

* fetch-rss

* fetch-external-resources
---
 packages/backend/src/misc/json-schema.ts      |   2 +
 packages/backend/src/models/json-schema/ad.ts |  64 +++++++++
 .../endpoints/admin/accounts/find-by-email.ts |   5 +
 .../server/api/endpoints/admin/ad/create.ts   |  19 ++-
 .../src/server/api/endpoints/admin/ad/list.ts |  24 +++-
 .../server/api/endpoints/admin/emoji/add.ts   |   2 +
 .../api/endpoints/admin/get-index-stats.ts    |  10 ++
 .../api/endpoints/admin/get-user-ips.ts       |  19 +++
 .../server/api/endpoints/admin/roles/users.ts |  16 ++-
 .../src/server/api/endpoints/endpoint.ts      |  17 +++
 .../server/api/endpoints/federation/stats.ts  |  86 +++++++++++
 .../api/endpoints/fetch-external-resources.ts |  12 ++
 .../src/server/api/endpoints/fetch-rss.ts     |  12 ++
 .../src/server/api/endpoints/flash/create.ts  |   6 +
 .../api/endpoints/get-online-users-count.ts   |  10 ++
 .../server/api/endpoints/i/2fa/key-done.ts    |  10 ++
 .../api/endpoints/i/2fa/register-key.ts       | 134 ++++++++++++++++++
 .../server/api/endpoints/i/2fa/register.ts    |  13 ++
 .../src/server/api/endpoints/i/apps.ts        |  33 ++++-
 .../server/api/endpoints/i/authorized-apps.ts |  30 ++++
 .../src/server/api/endpoints/i/move.ts        |   4 +
 .../api/endpoints/i/registry/get-all.ts       |   4 +
 .../api/endpoints/i/registry/get-detail.ts    |   4 +
 .../server/api/endpoints/i/registry/get.ts    |   4 +
 .../endpoints/i/registry/keys-with-type.ts    |   4 +
 .../i/registry/scopes-with-domain.ts          |  22 +++
 .../server/api/endpoints/i/update-email.ts    |   5 +
 .../server/api/endpoints/i/webhooks/create.ts |  39 ++++-
 .../server/api/endpoints/i/webhooks/list.ts   |  45 +++++-
 .../server/api/endpoints/i/webhooks/show.ts   |  40 +++++-
 .../src/server/api/endpoints/roles/users.ts   |  19 +++
 .../src/server/api/endpoints/server-info.ts   |  47 ++++++
 .../backend/src/server/api/endpoints/test.ts  |  24 ++++
 .../api/endpoints/users/achievements.ts       |  15 ++
 .../endpoints/users/lists/get-memberships.ts  |  29 ++++
 35 files changed, 822 insertions(+), 7 deletions(-)
 create mode 100644 packages/backend/src/models/json-schema/ad.ts

diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index 49f35b9b74..176978d35f 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -38,6 +38,7 @@ import { packedFlashSchema } from '@/models/json-schema/flash.js';
 import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js';
 import { packedSigninSchema } from '@/models/json-schema/signin.js';
 import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js';
+import { packedAdSchema } from '@/models/json-schema/ad.js';
 
 export const refs = {
 	UserLite: packedUserLiteSchema,
@@ -49,6 +50,7 @@ export const refs = {
 	User: packedUserSchema,
 
 	UserList: packedUserListSchema,
+	Ad: packedAdSchema,
 	Announcement: packedAnnouncementSchema,
 	App: packedAppSchema,
 	Note: packedNoteSchema,
diff --git a/packages/backend/src/models/json-schema/ad.ts b/packages/backend/src/models/json-schema/ad.ts
new file mode 100644
index 0000000000..649ffcd4dc
--- /dev/null
+++ b/packages/backend/src/models/json-schema/ad.ts
@@ -0,0 +1,64 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export const packedAdSchema = {
+	type: 'object',
+	properties: {
+		id: {
+			type: 'string',
+			optional: false,
+			nullable: false,
+			format: 'id',
+			example: 'xxxxxxxxxx',
+		},
+		expiresAt: {
+			type: 'string',
+			optional: false,
+			nullable: false,
+			format: 'date-time',
+		},
+		startsAt: {
+			type: 'string',
+			optional: false,
+			nullable: false,
+			format: 'date-time',
+		},
+		place: {
+			type: 'string',
+			optional: false,
+			nullable: false,
+		},
+		priority: {
+			type: 'string',
+			optional: false,
+			nullable: false,
+		},
+		ratio: {
+			type: 'number',
+			optional: false,
+			nullable: false,
+		},
+		url: {
+			type: 'string',
+			optional: false,
+			nullable: false,
+		},
+		imageUrl: {
+			type: 'string',
+			optional: false,
+			nullable: false,
+		},
+		memo: {
+			type: 'string',
+			optional: false,
+			nullable: false,
+		},
+		dayOfWeek: {
+			type: 'integer',
+			optional: false,
+			nullable: false,
+		},
+	},
+} as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
index 7dc9ca830b..bc292fd53a 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
@@ -25,6 +25,11 @@ export const meta = {
 			id: 'cb865949-8af5-4062-a88c-ef55e8786d1d',
 		},
 	},
+	res: {
+		type: 'object',
+		optional: false, nullable: false,
+		ref: 'User',
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
index cbe9727c46..087ae4befc 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
@@ -17,6 +17,12 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
+	res: {
+		type: 'object',
+		optional: false,
+		nullable: false,
+		ref: 'Ad',
+	},
 } as const;
 
 export const paramDef = {
@@ -63,7 +69,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				ad: ad,
 			});
 
-			return ad;
+			return {
+				id: ad.id,
+				expiresAt: ad.expiresAt.toISOString(),
+				startsAt: ad.startsAt.toISOString(),
+				dayOfWeek: ad.dayOfWeek,
+				url: ad.url,
+				imageUrl: ad.imageUrl,
+				priority: ad.priority,
+				ratio: ad.ratio,
+				place: ad.place,
+				memo: ad.memo,
+			};
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
index 3bda9fcb02..12528917dc 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
@@ -16,6 +16,17 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
+	res: {
+		type: 'array',
+		optional: false,
+		nullable: false,
+		items: {
+			type: 'object',
+			optional: false,
+			nullable: false,
+			ref: 'Ad',
+		},
+	},
 } as const;
 
 export const paramDef = {
@@ -46,7 +57,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}
 			const ads = await query.limit(ps.limit).getMany();
 
-			return ads;
+			return ads.map(ad => ({
+				id: ad.id,
+				expiresAt: ad.expiresAt.toISOString(),
+				startsAt: ad.startsAt.toISOString(),
+				dayOfWeek: ad.dayOfWeek,
+				url: ad.url,
+				imageUrl: ad.imageUrl,
+				memo: ad.memo,
+				place: ad.place,
+				priority: ad.priority,
+				ratio: ad.ratio,
+			}));
 		});
 	}
 }
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 360926594a..76ff1c6b94 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
@@ -31,6 +31,8 @@ export const meta = {
 			id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975',
 		},
 	},
+
+	ref: 'EmojiDetailed',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts
index 2de85f655a..b81d9857d7 100644
--- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts
+++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts
@@ -15,6 +15,16 @@ export const meta = {
 	kind: 'read:admin',
 
 	tags: ['admin'],
+	res: {
+		type: 'array',
+		items: {
+			type: 'object',
+			properties: {
+				tablename: { type: 'string' },
+				indexname: { type: 'string' },
+			},
+		},
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
index 6a404c0c77..76c32f2a9f 100644
--- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
+++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
@@ -16,6 +16,25 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
+	res: {
+		type: 'array',
+		optional: false,
+		nullable: false,
+		items: {
+			type: 'object',
+			optional: false,
+			nullable: false,
+			properties: {
+				ip: { type: 'string' },
+				createdAt: {
+					type: 'string',
+					optional: false,
+					nullable: false,
+					format: 'date-time',
+				},
+			},
+		},
+	}
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
index 53145a32d6..6a0f7f9987 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
@@ -28,6 +28,20 @@ export const meta = {
 			id: '224eff5e-2488-4b18-b3e7-f50d94421648',
 		},
 	},
+
+	res: {
+		type: 'array',
+		items: {
+			type: 'object',
+			properties: {
+				id: { type: 'string', format: 'misskey:id' },
+				createdAt: { type: 'string', format: 'date-time' },
+				user: { ref: 'UserDetailed' },
+				expiresAt: { type: 'string', format: 'date-time', nullable: true },
+			},
+			required: ['id', 'createdAt', 'user'],
+		},
+	}
 } as const;
 
 export const paramDef = {
@@ -80,7 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				id: assign.id,
 				createdAt: this.idService.parse(assign.id).date.toISOString(),
 				user: await this.userEntityService.pack(assign.user!, me, { detail: true }),
-				expiresAt: assign.expiresAt,
+				expiresAt: assign.expiresAt?.toISOString() ?? null,
 			})));
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/endpoint.ts b/packages/backend/src/server/api/endpoints/endpoint.ts
index cecaded20a..66ac8f664f 100644
--- a/packages/backend/src/server/api/endpoints/endpoint.ts
+++ b/packages/backend/src/server/api/endpoints/endpoint.ts
@@ -11,6 +11,23 @@ export const meta = {
 	requireCredential: false,
 
 	tags: ['meta'],
+
+	res: {
+		type: 'object',
+		nullable: true,
+		properties: {
+			params: {
+				type: 'array',
+				items: {
+					type: 'object',
+					properties: {
+						name: { type: 'string' },
+						type: { type: 'string' },
+					},
+				},
+			},
+		},
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts
index e3ffea7b7e..6548142d41 100644
--- a/packages/backend/src/server/api/endpoints/federation/stats.ts
+++ b/packages/backend/src/server/api/endpoints/federation/stats.ts
@@ -18,6 +18,92 @@ export const meta = {
 
 	allowGet: true,
 	cacheSec: 60 * 60,
+
+	res: {
+		type: 'object',
+		optional: false,
+		nullable: false,
+		properties: {
+			topSubInstances: {
+				type: 'array',
+				optional: false,
+				nullable: false,
+				items: {
+					properties: {
+						id: { type: 'string' },
+						firstRetrievedAt: { type: 'string' },
+						host: { type: 'string' },
+						usersCount: { type: 'number' },
+						notesCount: { type: 'number' },
+						followingCount: { type: 'number' },
+						followersCount: { type: 'number' },
+						isNotResponding: { type: 'boolean' },
+						isSuspended: { type: 'boolean' },
+						isBlocked: { type: 'boolean' },
+						softwareName: { type: 'string' },
+						softwareVersion: { type: 'string' },
+						openRegistrations: { type: 'boolean' },
+						name: { type: 'string' },
+						description: { type: 'string' },
+						maintainerName: { type: 'string' },
+						maintainerEmail: { type: 'string' },
+						isSilenced: { type: 'boolean' },
+						iconUrl: { type: 'string' },
+						faviconUrl: { type: 'string' },
+						themeColor: { type: 'string' },
+						infoUpdatedAt: {
+							type: 'string',
+							nullable: true,
+						},
+						latestRequestReceivedAt: {
+							type: 'string',
+							nullable: true,
+						},
+					}
+				},
+			},
+			otherFollowersCount: { type: 'number' },
+			topPubInstances: {
+				type: 'array',
+				optional: false,
+				nullable: false,
+				items: {
+					properties: {
+						id: { type: 'string' },
+						firstRetrievedAt: { type: 'string' },
+						host: { type: 'string' },
+						usersCount: { type: 'number' },
+						notesCount: { type: 'number' },
+						followingCount: { type: 'number' },
+						followersCount: { type: 'number' },
+						isNotResponding: { type: 'boolean' },
+						isSuspended: { type: 'boolean' },
+						isBlocked: { type: 'boolean' },
+						softwareName: { type: 'string' },
+						softwareVersion: { type: 'string' },
+						openRegistrations: { type: 'boolean' },
+						name: { type: 'string' },
+						description: { type: 'string' },
+						maintainerName: { type: 'string' },
+						maintainerEmail: { type: 'string' },
+						isSilenced: { type: 'boolean' },
+						iconUrl: { type: 'string' },
+						faviconUrl: { type: 'string' },
+						themeColor: { type: 'string' },
+						infoUpdatedAt: {
+							type: 'string',
+							nullable: true,
+						},
+						latestRequestReceivedAt: {
+							type: 'string',
+							nullable: true,
+						},
+					}
+				},
+			},
+			otherFollowingCount: { type: 'number' },
+		},
+	}
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/fetch-external-resources.ts b/packages/backend/src/server/api/endpoints/fetch-external-resources.ts
index d7b46cc666..6391a2f580 100644
--- a/packages/backend/src/server/api/endpoints/fetch-external-resources.ts
+++ b/packages/backend/src/server/api/endpoints/fetch-external-resources.ts
@@ -32,6 +32,18 @@ export const meta = {
 			id: '693ba8ba-b486-40df-a174-72f8279b56a4',
 		},
 	},
+
+	res: {
+		type: 'object',
+		properties: {
+			type: {
+				type: 'string',
+			},
+			data: {
+				type: 'string',
+			},
+		},
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/fetch-rss.ts b/packages/backend/src/server/api/endpoints/fetch-rss.ts
index 37859d8330..b2dee83fe9 100644
--- a/packages/backend/src/server/api/endpoints/fetch-rss.ts
+++ b/packages/backend/src/server/api/endpoints/fetch-rss.ts
@@ -16,6 +16,18 @@ export const meta = {
 	requireCredential: false,
 	allowGet: true,
 	cacheSec: 60 * 3,
+
+	res: {
+		type: 'object',
+		properties: {
+			items: {
+				type: 'array',
+				items: {
+					type: 'object',
+				},
+			}
+		}
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/flash/create.ts b/packages/backend/src/server/api/endpoints/flash/create.ts
index 4fa65ac9aa..674f323734 100644
--- a/packages/backend/src/server/api/endpoints/flash/create.ts
+++ b/packages/backend/src/server/api/endpoints/flash/create.ts
@@ -27,6 +27,12 @@ export const meta = {
 
 	errors: {
 	},
+
+	res: {
+		type: 'object',
+		optional: false, nullable: false,
+		ref: 'Flash',
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts
index 8a61168f25..737d637b7e 100644
--- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts
+++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts
@@ -16,6 +16,16 @@ export const meta = {
 	requireCredential: false,
 	allowGet: true,
 	cacheSec: 60 * 1,
+	res: {
+		type: 'object',
+		optional: false, nullable: false,
+		properties: {
+			count: {
+				type: 'number',
+				nullable: false,
+			},
+		},
+	},
 } as const;
 
 export const paramDef = {
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 6d530aba3b..a7be47fd0f 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
@@ -32,6 +32,16 @@ export const meta = {
 			id: '798d6847-b1ed-4f9c-b1f9-163c42655995',
 		},
 	},
+
+	res: {
+		type: 'object',
+		nullable: false,
+		optional: false,
+		properties: {
+			id: { type: 'string' },
+			name: { type: 'string' },
+		},
+	},
 } as const;
 
 export const paramDef = {
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 c39005f2dd..0fac96d58f 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
@@ -36,6 +36,140 @@ export const meta = {
 			id: 'bf32b864-449b-47b8-974e-f9a5468546f1',
 		},
 	},
+
+	res: {
+		type: 'object',
+		nullable: false,
+		optional: false,
+		properties: {
+			rp: {
+				type: 'object',
+				properties: {
+					id: {
+						type: 'string',
+						nullable: true,
+					},
+				},
+			},
+			user: {
+				type: 'object',
+				properties: {
+					id: {
+						type: 'string',
+					},
+					name: {
+						type: 'string',
+					},
+					displayName: {
+						type: 'string',
+					},
+				},
+			},
+			challenge: {
+				type: 'string',
+			},
+			pubKeyCredParams: {
+				type: 'array',
+				items: {
+					type: 'object',
+					properties: {
+						type: {
+							type: 'string',
+						},
+						alg: {
+							type: 'number',
+						},
+					},
+				},
+			},
+			timeout: {
+				type: 'number',
+				nullable: true,
+			},
+			excludeCredentials: {
+				type: 'array',
+				nullable: true,
+				items: {
+					type: 'object',
+					properties: {
+						id: {
+							type: 'string',
+						},
+						type: {
+							type: 'string',
+						},
+						transports: {
+							type: 'array',
+							items: {
+								type: 'string',
+								enum: [
+									"ble",
+									"cable",
+									"hybrid",
+									"internal",
+									"nfc",
+									"smart-card",
+									"usb",
+								],
+							},
+						},
+					},
+				},
+			},
+			authenticatorSelection: {
+				type: 'object',
+				nullable: true,
+				properties: {
+					authenticatorAttachment: {
+						type: 'string',
+						enum: [
+							"cross-platform",
+							"platform",
+						],
+					},
+					requireResidentKey: {
+						type: 'boolean',
+					},
+					userVerification: {
+						type: 'string',
+						enum: [
+							"discouraged",
+							"preferred",
+							"required",
+						],
+					},
+				},
+			},
+			attestation: {
+				type: 'string',
+				nullable: true,
+				enum: [
+					"direct",
+					"enterprise",
+					"indirect",
+					"none",
+				],
+			},
+			extensions: {
+				type: 'object',
+				nullable: true,
+				properties: {
+					appid: {
+						type: 'string',
+						nullable: true,
+					},
+					credProps: {
+						type: 'boolean',
+						nullable: true,
+					},
+					hmacCreateSecret: {
+						type: 'boolean',
+						nullable: true,
+					},
+				},
+			},
+		},
+	},
 } as const;
 
 export const paramDef = {
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 b358c812ee..cc083cbf7b 100644
--- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts
+++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts
@@ -26,6 +26,19 @@ export const meta = {
 			id: '78d6c839-20c9-4c66-b90a-fc0542168b48',
 		},
 	},
+
+	res: {
+		type: 'object',
+		nullable: false,
+		optional: false,
+		properties: {
+			qr: { type: 'string' },
+			url: { type: 'string' },
+			secret: { type: 'string' },
+			label: { type: 'string' },
+			issuer: { type: 'string' },
+		},
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts
index 09f6540a77..ef89f93181 100644
--- a/packages/backend/src/server/api/endpoints/i/apps.ts
+++ b/packages/backend/src/server/api/endpoints/i/apps.ts
@@ -13,6 +13,37 @@ export const meta = {
 	requireCredential: true,
 
 	secure: true,
+
+	res: {
+		type: 'array',
+		items: {
+			type: 'object',
+			properties: {
+				id: {
+					type: 'string',
+					format: 'misskey:id',
+				},
+				name: {
+					type: 'string',
+				},
+				createdAt: {
+					type: 'string',
+					format: 'date-time',
+				},
+				lastUsedAt: {
+					type: 'string',
+					format: 'date-time',
+				},
+				permission: {
+					type: 'array',
+					uniqueItems: true,
+					items: {
+						type: 'string'
+					},
+				}
+			},
+		},
+	},
 } as const;
 
 export const paramDef = {
@@ -50,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				id: token.id,
 				name: token.name ?? token.app?.name,
 				createdAt: this.idService.parse(token.id).date.toISOString(),
-				lastUsedAt: token.lastUsedAt,
+				lastUsedAt: token.lastUsedAt?.toISOString(),
 				permission: token.permission,
 			})));
 		});
diff --git a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts
index 32061c2aa4..a0ed371fb8 100644
--- a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts
+++ b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts
@@ -14,6 +14,36 @@ export const meta = {
 	requireCredential: true,
 
 	secure: true,
+
+	res: {
+		type: 'array',
+		items: {
+			type: 'object',
+			properties: {
+				id: {
+					type: 'string',
+					format: 'misskey:id',
+				},
+				name: {
+					type: 'string',
+				},
+				callbackUrl: {
+					type: 'string',
+					nullable: true,
+				},
+				permission: {
+					type: 'array',
+					uniqueItems: true,
+					items: {
+						type: 'string'
+					},
+				},
+				isAuthorized: {
+					type: 'boolean',
+				},
+			},
+		},
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/move.ts b/packages/backend/src/server/api/endpoints/i/move.ts
index 86b726e054..f3ba720c2b 100644
--- a/packages/backend/src/server/api/endpoints/i/move.ts
+++ b/packages/backend/src/server/api/endpoints/i/move.ts
@@ -64,6 +64,10 @@ export const meta = {
 			id: 'b234a14e-9ebe-4581-8000-074b3c215962',
 		},
 	},
+
+	res: {
+		type: 'object',
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts
index 29fa0a29cc..bd6e85a074 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts
@@ -9,6 +9,10 @@ import { RegistryApiService } from '@/core/RegistryApiService.js';
 
 export const meta = {
 	requireCredential: true,
+
+	res: {
+		type: 'object',
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts
index 5b460b45d6..2352beb130 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts
@@ -18,6 +18,10 @@ export const meta = {
 			id: '97a1e8e7-c0f7-47d2-957a-92e61256e01a',
 		},
 	},
+
+	res: {
+		type: 'object',
+	}
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts
index e8c28298ef..4155a43e0d 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/get.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts
@@ -18,6 +18,10 @@ export const meta = {
 			id: 'ac3ed68a-62f0-422b-a7bc-d5e09e8f6a6a',
 		},
 	},
+
+	res: {
+		type: 'object',
+	}
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts
index 8953ee5d3d..b411cdd3d9 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts
@@ -9,6 +9,10 @@ import { RegistryApiService } from '@/core/RegistryApiService.js';
 
 export const meta = {
 	requireCredential: true,
+
+	res: {
+		type: 'object',
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts
index 1ff994b82c..0aca2a26fe 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/scopes-with-domain.ts
@@ -10,6 +10,28 @@ import { RegistryApiService } from '@/core/RegistryApiService.js';
 export const meta = {
 	requireCredential: true,
 	secure: true,
+
+	res: {
+		type: 'array',
+		items: {
+			type: 'object',
+			properties: {
+				scopes: {
+					type: 'array',
+					items: {
+						type: 'array',
+						items: {
+							type: 'string',
+						}
+					}
+				},
+				domain: {
+					type: 'string',
+					nullable: true,
+				},
+			},
+		},
+	}
 } as const;
 
 export const paramDef = {
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 a36b3a732b..52977f5a07 100644
--- a/packages/backend/src/server/api/endpoints/i/update-email.ts
+++ b/packages/backend/src/server/api/endpoints/i/update-email.ts
@@ -40,6 +40,11 @@ export const meta = {
 			id: 'a2defefb-f220-8849-0af6-17f816099323',
 		},
 	},
+
+	res: {
+		type: 'object',
+		ref: 'UserDetailed',
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
index f00dba4a85..bdc9f9ea8b 100644
--- a/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
+++ b/packages/backend/src/server/api/endpoints/i/webhooks/create.ts
@@ -27,6 +27,33 @@ export const meta = {
 			id: '87a9bb19-111e-4e37-81d3-a3e7426453b0',
 		},
 	},
+
+	res: {
+		type: 'object',
+		properties: {
+			id: {
+				type: 'string',
+				format: 'misskey:id'
+			},
+			userId: {
+				type: 'string',
+				format: 'misskey:id',
+			},
+			name: { type: 'string' },
+			on: {
+				type: 'array',
+				items: {
+					type: 'string',
+					enum: webhookEventTypes,
+				}
+			},
+			url: { type: 'string' },
+			secret: { type: 'string' },
+			active: { type: 'boolean' },
+			latestSentAt: { type: 'string', format: 'date-time', nullable: true },
+			latestStatus: { type: 'integer', nullable: true },
+		},
+	},
 } as const;
 
 export const paramDef = {
@@ -73,7 +100,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 
 			this.globalEventService.publishInternalEvent('webhookCreated', webhook);
 
-			return webhook;
+			return {
+				id: webhook.id,
+				userId: webhook.userId,
+				name: webhook.name,
+				on: webhook.on,
+				url: webhook.url,
+				secret: webhook.secret,
+				active: webhook.active,
+				latestSentAt: webhook.latestSentAt?.toISOString(),
+				latestStatus: webhook.latestStatus,
+			};
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts
index aa8921fe24..afb2d0509e 100644
--- a/packages/backend/src/server/api/endpoints/i/webhooks/list.ts
+++ b/packages/backend/src/server/api/endpoints/i/webhooks/list.ts
@@ -5,6 +5,7 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
+import { webhookEventTypes } from '@/models/Webhook.js';
 import type { WebhooksRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 
@@ -14,6 +15,36 @@ export const meta = {
 	requireCredential: true,
 
 	kind: 'read:account',
+
+	res: {
+		type: 'array',
+		items: {
+			type: 'object',
+			properties: {
+				id: {
+					type: 'string',
+					format: 'misskey:id'
+				},
+				userId: {
+					type: 'string',
+					format: 'misskey:id',
+				},
+				name: { type: 'string' },
+				on: {
+					type: 'array',
+					items: {
+						type: 'string',
+						enum: webhookEventTypes,
+					}
+				},
+				url: { type: 'string' },
+				secret: { type: 'string' },
+				active: { type: 'boolean' },
+				latestSentAt: { type: 'string', format: 'date-time', nullable: true },
+				latestStatus: { type: 'integer', nullable: true },
+			},
+		}
+	}
 } as const;
 
 export const paramDef = {
@@ -33,7 +64,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				userId: me.id,
 			});
 
-			return webhooks;
+			return webhooks.map(webhook => (
+				{
+					id: webhook.id,
+					userId: webhook.userId,
+					name: webhook.name,
+					on: webhook.on,
+					url: webhook.url,
+					secret: webhook.secret,
+					active: webhook.active,
+					latestSentAt: webhook.latestSentAt?.toISOString(),
+					latestStatus: webhook.latestStatus,
+				}
+			));
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts
index f1294bb5c8..5c6dd908b4 100644
--- a/packages/backend/src/server/api/endpoints/i/webhooks/show.ts
+++ b/packages/backend/src/server/api/endpoints/i/webhooks/show.ts
@@ -5,6 +5,7 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
+import { webhookEventTypes } from '@/models/Webhook.js';
 import type { WebhooksRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 import { ApiError } from '../../../error.js';
@@ -23,6 +24,33 @@ export const meta = {
 			id: '50f614d9-3047-4f7e-90d8-ad6b2d5fb098',
 		},
 	},
+
+	res: {
+		type: 'object',
+		properties: {
+			id: {
+				type: 'string',
+				format: 'misskey:id'
+			},
+			userId: {
+				type: 'string',
+				format: 'misskey:id',
+			},
+			name: { type: 'string' },
+			on: {
+				type: 'array',
+				items: {
+					type: 'string',
+					enum: webhookEventTypes,
+				}
+			},
+			url: { type: 'string' },
+			secret: { type: 'string' },
+			active: { type: 'boolean' },
+			latestSentAt: { type: 'string', format: 'date-time', nullable: true },
+			latestStatus: { type: 'integer', nullable: true },
+		},
+	},
 } as const;
 
 export const paramDef = {
@@ -49,7 +77,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				throw new ApiError(meta.errors.noSuchWebhook);
 			}
 
-			return webhook;
+			return {
+				id: webhook.id,
+				userId: webhook.userId,
+				name: webhook.name,
+				on: webhook.on,
+				url: webhook.url,
+				secret: webhook.secret,
+				active: webhook.active,
+				latestSentAt: webhook.latestSentAt?.toISOString(),
+				latestStatus: webhook.latestStatus,
+			};
 		});
 	}
 }
diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts
index caaa3735e9..d304d075b2 100644
--- a/packages/backend/src/server/api/endpoints/roles/users.ts
+++ b/packages/backend/src/server/api/endpoints/roles/users.ts
@@ -24,6 +24,25 @@ export const meta = {
 			id: '30aaaee3-4792-48dc-ab0d-cf501a575ac5',
 		},
 	},
+
+	res: {
+		type: 'array',
+		items: {
+			type: 'object',
+			nullable: false,
+			properties: {
+				id: {
+					type: 'string',
+					format: 'misskey:id'
+				},
+				user: {
+					type: 'object',
+					ref: 'User'
+				},
+			},
+			required: ['id', 'user'],
+		},
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts
index c8cb63e6b3..079f2d7f1d 100644
--- a/packages/backend/src/server/api/endpoints/server-info.ts
+++ b/packages/backend/src/server/api/endpoints/server-info.ts
@@ -15,6 +15,53 @@ export const meta = {
 	cacheSec: 60 * 1,
 
 	tags: ['meta'],
+	res: {
+		type: 'object',
+		optional: false, nullable: false,
+		properties: {
+			machine: {
+				type: 'string',
+				nullable: false,
+			},
+			cpu: {
+				type: 'object',
+				nullable: false,
+				properties: {
+					model: {
+						type: 'string',
+						nullable: false,
+					},
+					cores: {
+						type: 'number',
+						nullable: false,
+					},
+				},
+			},
+			mem: {
+				type: 'object',
+				properties: {
+					total: {
+						type: 'number',
+						nullable: false,
+					},
+				},
+			},
+			fs: {
+				type: 'object',
+				nullable: false,
+				properties: {
+					total: {
+						type: 'number',
+						nullable: false,
+					},
+					used: {
+						type: 'number',
+						nullable: false,
+					},
+				},
+			},
+		},
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts
index 6d6d44f752..949867c572 100644
--- a/packages/backend/src/server/api/endpoints/test.ts
+++ b/packages/backend/src/server/api/endpoints/test.ts
@@ -12,6 +12,30 @@ export const meta = {
 	description: 'Endpoint for testing input validation.',
 
 	requireCredential: false,
+
+	res: {
+		type: 'object',
+		properties: {
+			id: {
+				type: 'string',
+				format: 'misskey:id'
+			},
+			required: {
+				type: 'boolean',
+			},
+			string: {
+				type: 'string',
+			},
+			default: {
+				type: 'string',
+			},
+			nullableDefault: {
+				type: 'string',
+				default: 'hello',
+				nullable: true,
+			},
+		}
+	}
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/users/achievements.ts b/packages/backend/src/server/api/endpoints/users/achievements.ts
index e4845d57bf..d6ad718dfa 100644
--- a/packages/backend/src/server/api/endpoints/users/achievements.ts
+++ b/packages/backend/src/server/api/endpoints/users/achievements.ts
@@ -10,6 +10,21 @@ import { DI } from '@/di-symbols.js';
 
 export const meta = {
 	requireCredential: true,
+
+	res: {
+		type: 'array',
+		items: {
+			type: 'object',
+			properties: {
+				name: {
+					type: 'string',
+				},
+				unlockedAt: {
+					type: 'number',
+				},
+			},
+		},
+	}
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts b/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts
index ae8b4e9b81..985141515e 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/get-memberships.ts
@@ -25,6 +25,35 @@ export const meta = {
 			id: '7bc05c21-1d7a-41ae-88f1-66820f4dc686',
 		},
 	},
+
+	res: {
+		type: 'array',
+		items: {
+			type: 'object',
+			nullable: false,
+			properties: {
+				id: {
+					type: 'string',
+					format: 'misskey:id',
+				},
+				createdAt: {
+					type: 'string',
+					format: 'date-time',
+				},
+				userId: {
+					type: 'string',
+					format: 'misskey:id',
+				},
+				user: {
+					type: 'object',
+					ref: 'User',
+				},
+				withReplies: {
+					type: 'boolean',
+				},
+			},
+		},
+	},
 } as const;
 
 export const paramDef = {

From 03dc86853660bc3b195c5d332e73009cf1e299be Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Thu, 21 Dec 2023 21:18:13 +0100
Subject: [PATCH 289/435] chore: update text and urls in about

Closes #243
---
 packages/frontend/src/pages/about-sharkey.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/pages/about-sharkey.vue b/packages/frontend/src/pages/about-sharkey.vue
index 67401cfff4..e66aab2098 100644
--- a/packages/frontend/src/pages/about-sharkey.vue
+++ b/packages/frontend/src/pages/about-sharkey.vue
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<FormLink to="https://git.joinsharkey.org/Sharkey/Sharkey" external>
 							<template #icon><i class="ph-code ph-bold ph-lg"></i></template>
 							{{ i18n.ts._aboutMisskey.source }}
-							<template #suffix>GitHub</template>
+							<template #suffix>Forgejo</template>
 						</FormLink>
 						<FormLink to="https://ko-fi.com/transfem" external>
 							<template #icon><i class="ph-piggy-bank ph-bold ph-lg"></i></template>
@@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<span :class="$style.contributorUsername">@Amelia</span>
 						</a>
 					</div>
-					<template #caption><MkLink url="https://github.com/transfem-org/sharkey/graphs/contributors">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template>
+					<template #caption><MkLink url="https://git.joinsharkey.org/Sharkey/Sharkey/graph">{{ i18n.ts._aboutMisskey.allContributors }}</MkLink></template>
 				</FormSection>
 				<FormSection>
 					<template #label>Misskey Contributors</template>

From 7a83e46e6a16323f9143479b6e1570472946aef9 Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Thu, 21 Dec 2023 21:31:28 +0100
Subject: [PATCH 290/435] upd: replace default dark theme

Closes #244
---
 packages/frontend/src/scripts/theme.ts        |  1 -
 packages/frontend/src/store.ts                |  2 +-
 .../src/themes/d-transfem-cherry.json5        | 20 -------------------
 3 files changed, 1 insertion(+), 22 deletions(-)
 delete mode 100644 packages/frontend/src/themes/d-transfem-cherry.json5

diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index 8bfb03f0dd..22b8a5df37 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -42,7 +42,6 @@ export const getBuiltinThemes = () => Promise.all(
 		'd-green-lime',
 		'd-green-orange',
 		'd-cherry',
-		'd-transfem-cherry',
 		'd-ice',
 		'd-u0',
 	].map(name => import(`../themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index a53336b5f7..1f0aae3a3a 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -477,7 +477,7 @@ interface Watcher {
  * 常にメモリにロードしておく必要がないような設定情報を保管するストレージ(非リアクティブ)
  */
 import lightTheme from '@/themes/l-cherry.json5';
-import darkTheme from '@/themes/d-transfem-cherry.json5';
+import darkTheme from '@/themes/d-ice.json5';
 
 export class ColdDeviceStorage {
 	public static default = {
diff --git a/packages/frontend/src/themes/d-transfem-cherry.json5 b/packages/frontend/src/themes/d-transfem-cherry.json5
deleted file mode 100644
index a6fdcec77b..0000000000
--- a/packages/frontend/src/themes/d-transfem-cherry.json5
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-	id: '679b3b87-a4e9-4289-8693-b56c15cc33b5',
-
-	name: 'Transfem Mi Cherry Dark',
-	author: 'Amelia and syuilo',
-
-	base: 'dark',
-    
-	props: {
-		accent: 'rgb(245, 169, 184)',
-		bg: 'rgb(28, 28, 37)',
-		fg: 'rgb(236, 239, 244)',
-		panel: 'rgb(35, 35, 47)',
-		renote: '@accent',
-		link: '@accent',
-		mention: '@accent',
-		hashtag: '@accent',
-		divider: 'rgb(63, 63, 80)',
-	},
-}

From beded1c7ce1f9ee1a932f5305188a7a0c3e4b9dc Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Thu, 21 Dec 2023 22:04:58 +0100
Subject: [PATCH 291/435] upd: show reload dialog for note design

Closes #242
---
 packages/frontend/src/pages/settings/general.vue | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 419ea13713..32dda149b8 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -319,6 +319,12 @@ watch(useSystemFont, () => {
 	}
 });
 
+watch(noteDesign, async (newval) => {
+	if (noteDesign.value === newval) {
+		await reloadAsk();
+	}
+});
+
 watch([
 	lang,
 	fontSize,

From 90f8d8e575fadd60792514151610305bfb52a77b Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Thu, 21 Dec 2023 22:24:23 +0100
Subject: [PATCH 292/435] upd: fix tag view not respecting blocks and
 suspensions

Closes #234
---
 .../api/endpoints/notes/search-by-tag.ts      | 25 ++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
index bc33d6948c..89e05fd57e 100644
--- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
+++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts
@@ -13,6 +13,8 @@ import { QueryService } from '@/core/QueryService.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { DI } from '@/di-symbols.js';
 import { MetaService } from '@/core/MetaService.js';
+import { CacheService } from '@/core/CacheService.js';
+import { UtilityService } from '@/core/UtilityService.js';
 
 export const meta = {
 	tags: ['notes', 'hashtags'],
@@ -73,23 +75,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private noteEntityService: NoteEntityService,
 		private queryService: QueryService,
 		private metaService: MetaService,
+		private cacheService: CacheService,
+		private utilityService: UtilityService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			const meta = await this.metaService.fetch(true);
+
 			const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId)
+				.andWhere('note.visibility = \'public\'')
 				.innerJoinAndSelect('note.user', 'user')
 				.leftJoinAndSelect('note.reply', 'reply')
 				.leftJoinAndSelect('note.renote', 'renote')
 				.leftJoinAndSelect('reply.user', 'replyUser')
 				.leftJoinAndSelect('renote.user', 'renoteUser');
 
-			const meta = await this.metaService.fetch(true);
-
 			if (!meta.enableBotTrending) query.andWhere('user.isBot = FALSE');
 
 			this.queryService.generateVisibilityQuery(query, me);
 			if (me) this.queryService.generateMutedUserQuery(query, me);
 			if (me) this.queryService.generateBlockedUserQuery(query, me);
 
+			const [
+				followings,
+			] = me ? await Promise.all([
+				this.cacheService.userFollowingsCache.fetch(me.id),
+			]) : [undefined];
+
 			try {
 				if (ps.tag) {
 					if (!safeForSql(normalizeForSearch(ps.tag))) throw new Error('Injection');
@@ -140,7 +151,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 			}
 
 			// Search notes
-			const notes = await query.limit(ps.limit).getMany();
+			let notes = await query.limit(ps.limit).getMany();
+
+			notes = notes.filter(note => {
+				if (note.user?.isSilenced && me && followings && note.userId !== me.id && !followings[note.userId]) return false;
+				if (note.user?.isSuspended) return false;
+				if (this.utilityService.isBlockedHost(meta.blockedHosts, note.userHost)) return false;
+				if (this.utilityService.isSilencedHost(meta.silencedHosts, note.userHost)) return false;
+				return true;
+			});
 
 			return await this.noteEntityService.packMany(notes, me);
 		});

From 013ea8ae656d91c98ba5e3bbfb085a9b359d34c7 Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Thu, 21 Dec 2023 22:30:38 +0100
Subject: [PATCH 293/435] upd: remove show more button for user description

Closes #232
---
 packages/frontend/src/pages/user/home.vue | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 2ed55b1215..2e7fc68748 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -74,10 +74,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 						/>
 					</div>
 					<div class="description">
-						<MkOmit>
-							<Mfm v-if="user.description" :text="user.description" :isNote="false" :author="user"/>
-							<p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p>
-						</MkOmit>
+						<Mfm v-if="user.description" :text="user.description" :isNote="false" :author="user"/>
+						<p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p>
 					</div>
 					<div class="fields system">
 						<dl v-if="user.location" class="field">
@@ -170,7 +168,6 @@ import MkFollowButton from '@/components/MkFollowButton.vue';
 import MkAccountMoved from '@/components/MkAccountMoved.vue';
 import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
-import MkOmit from '@/components/MkOmit.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkButton from '@/components/MkButton.vue';
 import { getScrollPosition } from '@/scripts/scroll.js';

From fd9ba530ba30e5462f38daa531319f40a100c66e Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Thu, 21 Dec 2023 22:34:10 +0100
Subject: [PATCH 294/435] fix: clicking on reaction icon not triggering toggle

Closes #239
---
 packages/frontend/src/components/MkReactionsViewer.reaction.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index bab592317d..42b5243e94 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]"
 	@click="toggleReaction()"
 >
-	<MkReactionIcon :class="$style.icon" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/>
+	<MkReactionIcon :class="$style.icon" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]" @click="toggleReaction()"/>
 	<span :class="$style.count">{{ count }}</span>
 </button>
 </template>

From 9c6a7aed98a1d37f4f1fca1898f8ca9a2a6b0ced Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Thu, 21 Dec 2023 22:42:06 +0100
Subject: [PATCH 295/435] fix: websocket for timelines not checking following
 for muted instance users

Closes #233
---
 .../backend/src/server/api/stream/channels/bubble-timeline.ts   | 2 +-
 .../backend/src/server/api/stream/channels/global-timeline.ts   | 2 +-
 .../backend/src/server/api/stream/channels/home-timeline.ts     | 2 +-
 .../backend/src/server/api/stream/channels/hybrid-timeline.ts   | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/backend/src/server/api/stream/channels/bubble-timeline.ts b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts
index 74d5c3ea4e..1a3fcede62 100644
--- a/packages/backend/src/server/api/stream/channels/bubble-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts
@@ -71,7 +71,7 @@ class BubbleTimelineChannel extends Channel {
 		if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
 
 		// Ignore notes from instances the user has muted
-		if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
+		if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? [])) && !this.following[note.userId]) return;
 
 		// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
 		if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts
index fa0493854b..f64a13bcc5 100644
--- a/packages/backend/src/server/api/stream/channels/global-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts
@@ -67,7 +67,7 @@ class GlobalTimelineChannel extends Channel {
 		if (note.renote && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !this.withRenotes) return;
 
 		// Ignore notes from instances the user has muted
-		if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
+		if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? [])) && !this.following[note.userId]) return;
 
 		// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
 		if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts
index 32bb9fd984..534973f834 100644
--- a/packages/backend/src/server/api/stream/channels/home-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts
@@ -51,7 +51,7 @@ class HomeTimelineChannel extends Channel {
 		}
 
 		// Ignore notes from instances the user has muted
-		if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances))) return;
+		if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances)) && !this.following[note.userId]) return;
 
 		if (note.visibility === 'followers') {
 			if (!isMe && !Object.hasOwn(this.following, note.userId)) return;
diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
index cf904b475a..746c661d31 100644
--- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
@@ -74,7 +74,7 @@ class HybridTimelineChannel extends Channel {
 		}
 
 		// Ignore notes from instances the user has muted
-		if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances))) return;
+		if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances)) && !this.following[note.userId]) return;
 
 		if (note.reply) {
 			const reply = note.reply;

From 4ca3c054879e8da3a2d65c772099b8bbd4c371b6 Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Thu, 21 Dec 2023 23:15:03 +0100
Subject: [PATCH 296/435] upd: make new posts be marked as NSFW if instance is
 marked as NSFW

Closes #226
---
 packages/backend/src/core/NoteCreateService.ts | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 2055a0d210..939662707f 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -368,6 +368,14 @@ export class NoteCreateService implements OnApplicationShutdown {
 			}
 		}
 
+		if (user.host && !data.cw) {
+			await this.federatedInstanceService.fetch(user.host).then(async i => {
+				if (i.isNSFW) {
+					data.cw = 'Instance is marked as NSFW';
+				}
+			});
+		}
+
 		const note = await this.insertNote(user, data, tags, emojis, mentionedUsers);
 
 		setImmediate('post created', { signal: this.#shutdownController.signal }).then(

From 043f26c8951c1a980dd51cc1f31ab6c1ee69947d Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Thu, 21 Dec 2023 23:15:49 +0100
Subject: [PATCH 297/435] upd: improve performance for checking if instance is
 marked as NSFW on images

---
 .../core/activitypub/models/ApImageService.ts | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/packages/backend/src/core/activitypub/models/ApImageService.ts b/packages/backend/src/core/activitypub/models/ApImageService.ts
index b7b8acd661..2eff7c64e0 100644
--- a/packages/backend/src/core/activitypub/models/ApImageService.ts
+++ b/packages/backend/src/core/activitypub/models/ApImageService.ts
@@ -5,7 +5,7 @@
 
 import { Inject, Injectable } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
-import type { DriveFilesRepository, InstancesRepository } from '@/models/_.js';
+import type { DriveFilesRepository } from '@/models/_.js';
 import type { MiRemoteUser } from '@/models/User.js';
 import type { MiDriveFile } from '@/models/DriveFile.js';
 import { MetaService } from '@/core/MetaService.js';
@@ -15,10 +15,10 @@ import { DriveService } from '@/core/DriveService.js';
 import type Logger from '@/logger.js';
 import { bindThis } from '@/decorators.js';
 import { checkHttps } from '@/misc/check-https.js';
+import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
 import { ApResolverService } from '../ApResolverService.js';
 import { ApLoggerService } from '../ApLoggerService.js';
 import type { IObject } from '../type.js';
-import { UtilityService } from '@/core/UtilityService.js';
 
 @Injectable()
 export class ApImageService {
@@ -28,14 +28,11 @@ export class ApImageService {
 		@Inject(DI.driveFilesRepository)
 		private driveFilesRepository: DriveFilesRepository,
 
-		@Inject(DI.instancesRepository)
-		private instancesRepository: InstancesRepository,
-
 		private metaService: MetaService,
 		private apResolverService: ApResolverService,
 		private driveService: DriveService,
 		private apLoggerService: ApLoggerService,
-		private utilityService: UtilityService,
+		private federatedInstanceService: FederatedInstanceService,
 	) {
 		this.logger = this.apLoggerService.logger;
 	}
@@ -73,11 +70,11 @@ export class ApImageService {
 		// 2. or the image is not sensitive
 		const shouldBeCached = instance.cacheRemoteFiles && (instance.cacheRemoteSensitiveFiles || !image.sensitive);
 
-		const shouldBeSensitive = await this.instancesRepository.findOneBy({ host: this.utilityService.toPuny(actor.host), isNSFW: true });
-
-		if (shouldBeSensitive) {
-			image.sensitive = true;
-		}
+		await this.federatedInstanceService.fetch(actor.host).then(async i => {
+			if (i.isNSFW) {
+				image.sensitive = true;
+			}
+		});
 
 		const file = await this.driveService.uploadFromUrl({
 			url: image.url,

From 433d46e57f127ae7b3dc3715e9363790425eeabf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Fri, 22 Dec 2023 09:06:13 +0900
Subject: [PATCH 298/435] =?UTF-8?q?fix(backend):=20=E3=83=86=E3=82=B9?=
 =?UTF-8?q?=E3=83=88=E3=81=8C=E6=AD=BB=E3=82=93=E3=81=A7=E3=81=84=E3=82=8B?=
 =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12738)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix test

* fix test

* fix test

* fix test

* fix test
---
 packages/backend/src/core/UserListService.ts | 13 ++++++++++---
 packages/backend/test/e2e/timelines.ts       | 15 +++++++--------
 packages/backend/test/unit/RoleService.ts    | 20 +++++++++++++++++---
 3 files changed, 34 insertions(+), 14 deletions(-)

diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts
index 832b715d97..b6e4e1e884 100644
--- a/packages/backend/src/core/UserListService.ts
+++ b/packages/backend/src/core/UserListService.ts
@@ -3,8 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
+import { Inject, Injectable, OnApplicationShutdown, OnModuleInit } from '@nestjs/common';
 import * as Redis from 'ioredis';
+import { ModuleRef } from '@nestjs/core';
 import type { UserListMembershipsRepository } from '@/models/_.js';
 import type { MiUser } from '@/models/User.js';
 import type { MiUserList } from '@/models/UserList.js';
@@ -21,12 +22,15 @@ import { RedisKVCache } from '@/misc/cache.js';
 import { RoleService } from '@/core/RoleService.js';
 
 @Injectable()
-export class UserListService implements OnApplicationShutdown {
+export class UserListService implements OnApplicationShutdown, OnModuleInit {
 	public static TooManyUsersError = class extends Error {};
 
 	public membersCache: RedisKVCache<Set<string>>;
+	private roleService: RoleService;
 
 	constructor(
+		private moduleRef: ModuleRef,
+
 		@Inject(DI.redis)
 		private redisClient: Redis.Redis,
 
@@ -38,7 +42,6 @@ export class UserListService implements OnApplicationShutdown {
 
 		private userEntityService: UserEntityService,
 		private idService: IdService,
-		private roleService: RoleService,
 		private globalEventService: GlobalEventService,
 		private proxyAccountService: ProxyAccountService,
 		private queueService: QueueService,
@@ -54,6 +57,10 @@ export class UserListService implements OnApplicationShutdown {
 		this.redisForSub.on('message', this.onMessage);
 	}
 
+	async onModuleInit() {
+		this.roleService = this.moduleRef.get(RoleService.name);
+	}
+
 	@bindThis
 	private async onMessage(_: string, data: string): Promise<void> {
 		const obj = JSON.parse(data);
diff --git a/packages/backend/test/e2e/timelines.ts b/packages/backend/test/e2e/timelines.ts
index 73c446444b..cb9558b416 100644
--- a/packages/backend/test/e2e/timelines.ts
+++ b/packages/backend/test/e2e/timelines.ts
@@ -10,9 +10,8 @@ process.env.NODE_ENV = 'test';
 process.env.FORCE_FOLLOW_REMOTE_USER_FOR_TESTING = 'true';
 
 import * as assert from 'assert';
-import { signup, api, post, react, startServer, waitFire, sleep, uploadUrl, randomString } from '../utils.js';
+import { api, post, randomString, signup, sleep, startServer, uploadUrl } from '../utils.js';
 import type { INestApplicationContext } from '@nestjs/common';
-import type * as misskey from 'misskey-js';
 
 function genHost() {
 	return randomString() + '.example.com';
@@ -366,8 +365,8 @@ describe('Timelines', () => {
 			await api('/following/create', { userId: bob.id }, alice);
 			await sleep(1000);
 			const [bobFile, carolFile] = await Promise.all([
-				uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png'),
-				uploadUrl(carol, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png'),
+				uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
+				uploadUrl(carol, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
 			]);
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { fileIds: [bobFile.id] });
@@ -666,7 +665,7 @@ describe('Timelines', () => {
 		test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png');
+			const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { fileIds: [file.id] });
 
@@ -804,7 +803,7 @@ describe('Timelines', () => {
 		test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png');
+			const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { fileIds: [file.id] });
 
@@ -999,7 +998,7 @@ describe('Timelines', () => {
 
 			const list = await api('/users/lists/create', { name: 'list' }, alice).then(res => res.body);
 			await api('/users/lists/push', { listId: list.id, userId: bob.id }, alice);
-			const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png');
+			const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { fileIds: [file.id] });
 
@@ -1158,7 +1157,7 @@ describe('Timelines', () => {
 		test.concurrent('[withFiles: true] ファイル付きノートのみ含まれる', async () => {
 			const [alice, bob] = await Promise.all([signup(), signup()]);
 
-			const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/icon.png');
+			const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png');
 			const bobNote1 = await post(bob, { text: 'hi' });
 			const bobNote2 = await post(bob, { fileIds: [file.id] });
 
diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts
index 99c6912116..9879eb8e3e 100644
--- a/packages/backend/test/unit/RoleService.ts
+++ b/packages/backend/test/unit/RoleService.ts
@@ -73,13 +73,21 @@ describe('RoleService', () => {
 				CacheService,
 				IdService,
 				GlobalEventService,
+				{
+					provide: NotificationService,
+					useFactory: () => ({
+						createNotification: jest.fn(),
+					}),
+				},
+				{
+					provide: NotificationService.name,
+					useExisting: NotificationService,
+				},
 			],
 		})
 			.useMocker((token) => {
 				if (token === MetaService) {
 					return { fetch: jest.fn() };
-				} else if (token === NotificationService) {
-					return { createNotification: jest.fn() };
 				}
 				if (typeof token === 'function') {
 					const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
@@ -98,6 +106,8 @@ describe('RoleService', () => {
 
 		metaService = app.get<MetaService>(MetaService) as jest.Mocked<MetaService>;
 		notificationService = app.get<NotificationService>(NotificationService) as jest.Mocked<NotificationService>;
+
+		await roleService.onModuleInit();
 	});
 
 	afterEach(async () => {
@@ -284,10 +294,12 @@ describe('RoleService', () => {
 			const user = await createUser();
 			const role = await createRole({
 				isPublic: true,
+				name: 'a',
 			});
 
 			await roleService.assign(user.id, role.id);
 
+			clock.uninstall();
 			await sleep(100);
 
 			const assignments = await roleAssignmentsRepository.find({
@@ -301,7 +313,7 @@ describe('RoleService', () => {
 			expect(notificationService.createNotification).toHaveBeenCalled();
 			expect(notificationService.createNotification.mock.lastCall![0]).toBe(user.id);
 			expect(notificationService.createNotification.mock.lastCall![1]).toBe('roleAssigned');
-			expect(notificationService.createNotification.mock.lastCall![2]).toBe({
+			expect(notificationService.createNotification.mock.lastCall![2]).toEqual({
 				roleId: role.id,
 			});
 		});
@@ -310,10 +322,12 @@ describe('RoleService', () => {
 			const user = await createUser();
 			const role = await createRole({
 				isPublic: false,
+				name: 'a',
 			});
 
 			await roleService.assign(user.id, role.id);
 
+			clock.uninstall();
 			await sleep(100);
 
 			const assignments = await roleAssignmentsRepository.find({

From 5e7846491807c2cb57118703e4965f60f955d5d3 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Fri, 22 Dec 2023 01:25:30 +0100
Subject: [PATCH 299/435] upd(ci): stable docker builds with version and latest
 tag

---
 .forgejo/workflows/docker.yml | 12 ++++--------
 1 file changed, 4 insertions(+), 8 deletions(-)

diff --git a/.forgejo/workflows/docker.yml b/.forgejo/workflows/docker.yml
index b9dce0a155..d9076611e3 100644
--- a/.forgejo/workflows/docker.yml
+++ b/.forgejo/workflows/docker.yml
@@ -1,12 +1,8 @@
 name: Publish Docker image
 
 on:
-  push:
-    branches:
-      - stable
-    paths:
-      - packages/**
-      - locales/**
+  release:
+    types: [published]
   workflow_dispatch:
 
 env:
@@ -61,5 +57,5 @@ jobs:
           push: true
           platforms: ${{ steps.buildx.outputs.platforms }}
           provenance: false
-          tags: ${{ env.REGISTRY }}/sharkey/sharkey:stable
-          labels: stable
\ No newline at end of file
+          tags: ${{ steps.meta.outputs.tags }}
+          labels: ${{ steps.meta.outputs.labels }}
\ No newline at end of file

From 52b94dbc4ab60aa6efba927ef8df509a3bb0d046 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Fri, 22 Dec 2023 14:03:39 +0900
Subject: [PATCH 300/435] =?UTF-8?q?fix:=20=E5=BC=95=E7=94=A8RN=E3=81=8Cpur?=
 =?UTF-8?q?e=20RN=E3=81=A8=E3=81=97=E3=81=A6=E9=80=A3=E5=90=88=E3=81=95?=
 =?UTF-8?q?=E3=82=8C=E3=80=81pure=20RN=E3=81=8C=E5=BC=95=E7=94=A8RN?=
 =?UTF-8?q?=E3=81=A8=E3=81=97=E3=81=A6=E9=80=A3=E5=90=88=E3=81=95=E3=82=8C?=
 =?UTF-8?q?=E3=82=8B=20(#12744)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: quote notes are rendered as pure renote

* fix: filesが指定されてて空配列のときにQuote扱いされる

* chore: isQuoteの仕様をmisc/is-quote.tsと揃える

* docs: is-quote.tsの方にNoteCreateService.isQuoteのことを書いて更新忘れを防ぐ
---
 packages/backend/src/core/NoteCreateService.ts | 9 +++++----
 packages/backend/src/misc/is-quote.ts          | 1 +
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 9fe965b139..54493612b8 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -293,7 +293,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 		}
 
 		// Check blocking
-		if (data.renote && this.isQuote(data)) {
+		if (this.isQuote(data)) {
 			if (data.renote.userHost === null) {
 				if (data.renote.userId !== user.id) {
 					const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id);
@@ -730,8 +730,9 @@ export class NoteCreateService implements OnApplicationShutdown {
 	}
 
 	@bindThis
-	private isQuote(note: Option): boolean {
-		return !!note.text || !!note.cw || !!note.files || !!note.poll;
+	private isQuote(note: Option): note is Option & { renote: MiNote } {
+		// sync with misc/is-quote.ts
+		return !!note.renote && (!!note.text || !!note.cw || (!!note.files && !!note.files.length) || !!note.poll);
 	}
 
 	@bindThis
@@ -799,7 +800,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 	private async renderNoteOrRenoteActivity(data: Option, note: MiNote) {
 		if (data.localOnly) return null;
 
-		const content = data.renote && this.isQuote(data)
+		const content = data.renote && !this.isQuote(data)
 			? this.apRendererService.renderAnnounce(data.renote.uri ? data.renote.uri : `${this.config.url}/notes/${data.renote.id}`, note)
 			: this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note);
 
diff --git a/packages/backend/src/misc/is-quote.ts b/packages/backend/src/misc/is-quote.ts
index 059f6a4b5f..db72d1d57a 100644
--- a/packages/backend/src/misc/is-quote.ts
+++ b/packages/backend/src/misc/is-quote.ts
@@ -7,5 +7,6 @@ import type { MiNote } from '@/models/Note.js';
 
 // eslint-disable-next-line import/no-default-export
 export default function(note: MiNote): boolean {
+	// sync with NoteCreateService.isQuote
 	return note.renoteId != null && (note.text != null || note.hasPoll || (note.fileIds != null && note.fileIds.length > 0));
 }

From 5979a3f8d90e1b1b66c684f6199ed87395223a3a Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Fri, 22 Dec 2023 06:22:40 +0100
Subject: [PATCH 301/435] upd(ci): add tag for stable

---
 .forgejo/workflows/docker.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.forgejo/workflows/docker.yml b/.forgejo/workflows/docker.yml
index d9076611e3..155c5f7deb 100644
--- a/.forgejo/workflows/docker.yml
+++ b/.forgejo/workflows/docker.yml
@@ -42,6 +42,7 @@ jobs:
             type=semver,pattern={{version}}
             type=semver,pattern={{major}}.{{minor}}
             type=semver,pattern={{major}}
+            type=raw,value=stable
       - name: Log in to GHCR
         uses: https://github.com/docker/login-action@v3
         with:

From d68214bd463d96aadd88e412f8106301811423bf Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Fri, 22 Dec 2023 15:38:27 +0900
Subject: [PATCH 302/435] =?UTF-8?q?fix(i18n):=20=E3=83=AD=E3=83=BC?=
 =?UTF-8?q?=E3=83=AB=E3=81=8C=E4=BB=98=E4=B8=8E=E3=81=95=E3=82=8C=E3=81=9F?=
 =?UTF-8?q?=E9=9A=9B=E3=81=AE=E9=80=9A=E7=9F=A5=E3=81=AE=E3=83=AD=E3=83=BC?=
 =?UTF-8?q?=E3=82=AB=E3=83=A9=E3=82=A4=E3=82=BC=E3=83=BC=E3=82=B7=E3=83=A7?=
 =?UTF-8?q?=E3=83=B3=E3=81=8C=E4=B8=80=E9=83=A8=E6=AC=A0=E3=81=91=E3=81=A6?=
 =?UTF-8?q?=E3=81=84=E3=82=8B=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1274?=
 =?UTF-8?q?5)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 locales/index.d.ts | 1 +
 locales/ja-JP.yml  | 1 +
 2 files changed, 2 insertions(+)

diff --git a/locales/index.d.ts b/locales/index.d.ts
index f22b7f1c4a..fd96fd7625 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2347,6 +2347,7 @@ export interface Locale {
             "pollEnded": string;
             "receiveFollowRequest": string;
             "followRequestAccepted": string;
+            "roleAssigned": string;
             "achievementEarned": string;
             "app": string;
         };
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 2185183c98..2c29bd20da 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2250,6 +2250,7 @@ _notification:
     pollEnded: "アンケートが終了"
     receiveFollowRequest: "フォロー申請を受け取った"
     followRequestAccepted: "フォローが受理された"
+    roleAssigned: "ロールが付与された"
     achievementEarned: "実績の獲得"
     app: "連携アプリからの通知"
 

From 6d4aa316ac886870ad98be09ccd4c5d03cf7a9c4 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Dec 2023 18:05:41 +0900
Subject: [PATCH 303/435] New Crowdin updates (#12732)

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

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (Catalan)

* New translations ja-jp.yml (English)
---
 locales/ca-ES.yml | 158 ++++++++++++++++++++++++++++++++++++++++++++++
 locales/en-US.yml |   7 +-
 locales/fr-FR.yml |   1 +
 locales/ko-KR.yml |   1 +
 locales/zh-TW.yml |   1 +
 5 files changed, 166 insertions(+), 2 deletions(-)

diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index b4fa799ada..727e473cf3 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -121,6 +121,12 @@ sensitive: "NSFW"
 add: "Afegir"
 reaction: "Reaccions"
 reactions: "Reaccions"
+emojiPicker: "Selecció d'emojis"
+pinnedEmojisForReactionSettingDescription: "Selecciona l'emoji amb el qual reaccionar"
+pinnedEmojisSettingDescription: "Selecciona l'emoji amb el qual reaccionar"
+emojiPickerDisplay: "Visualitza el selector d'emojis"
+overwriteFromPinnedEmojisForReaction: "Reemplaça els emojis de la reacció"
+overwriteFromPinnedEmojis: "Sobreescriu des dels emojis fixats"
 reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem \"+\" per afegir."
 rememberNoteVisibility: "Recorda la configuració de visibilitat de les notes"
 attachCancel: "Eliminar el fitxer adjunt"
@@ -213,6 +219,9 @@ clearQueueConfirmText: "Les notes no lliurades que quedin a la cua no es federar
 clearCachedFiles: "Esborra la memòria cau"
 clearCachedFilesConfirm: "Segur que voleu eliminar tots els fitxers de la memòria cau?"
 blockedInstances: "Instàncies bloquejades"
+blockedInstancesDescription: "Llista els enllaços d'amfitrió de les instàncies que vols bloquejar separades per un salt de pàgina. Les instàncies llistades no podran comunicar-se amb aquesta instància."
+silencedInstances: "Instàncies silenciades"
+silencedInstancesDescription: "Llista els enllaços d'amfitrió de les instàncies que vols silenciar. Tots els comptes de les instàncies llistades s'establiran com silenciades i només podran fer sol·licitacions de seguiment, i no podran mencionar als comptes locals si no els segueixen. Això no afectarà les instàncies bloquejades."
 muteAndBlock: "Silencia i bloca"
 mutedUsers: "Usuaris silenciats"
 blockedUsers: "Usuaris bloquejats"
@@ -227,9 +236,12 @@ preview: "Vista prèvia"
 default: "Per defecte"
 defaultValueIs: "Per defecte: {value}"
 noCustomEmojis: "Cap emoji personalitzat"
+noJobs: "No hi ha feines"
 federating: "Federant"
 blocked: "Bloquejat"
 suspended: "Suspés"
+all: "tot"
+subscribing: "Subscrit a"
 publishing: "S'està publicant"
 notResponding: "Sense resposta"
 instanceFollowing: "Seguits del servidor"
@@ -254,11 +266,31 @@ removed: "Eliminat"
 removeAreYouSure: "Segur que voleu retirar «{x}»?"
 deleteAreYouSure: "Segur que voleu retirar «{x}»?"
 resetAreYouSure: "Segur que voleu restablir-ho?"
+areYouSure: "Està segur?"
 saved: "S'ha desat"
 messaging: "Xat"
 upload: "Puja"
+keepOriginalUploading: "Guarda la imatge original"
+keepOriginalUploadingDescription: "Guarda la imatge pujada com hi és. Si està apagat, una versió per a la visualització a la xarxa serà generada quan sigui pujada."
+fromDrive: "Des de la unitat"
+fromUrl: "Des d'un enllaç"
+uploadFromUrl: "Carrega des d'un enllaç"
+uploadFromUrlDescription: "Enllaç del fitxer que vols carregar"
+uploadFromUrlRequested: "Càrrega sol·licitada"
+uploadFromUrlMayTakeTime: "La càrrega des de l'enllaç pot prendre un temps"
+explore: "Explora"
+messageRead: "Vist"
+noMoreHistory: "No hi resta més per veure"
+startMessaging: "Començar a xatejar"
+nUsersRead: "Vist per {n}"
+agreeTo: "Accepto que {0}"
+agree: "Hi estic d'acord"
+agreeBelow: "Hi estic d'acord amb el següent"
+basicNotesBeforeCreateAccount: "Notes importants"
+termsOfService: "Condicions d'ús"
 start: "Comença"
 home: "Inici"
+remoteUserCaution: "Ja que aquest usuari resideix a una instància remota, la informació mostrada es podria trobar incompleta."
 activity: "Activitat"
 images: "Imatges"
 image: "Imatges"
@@ -274,16 +306,34 @@ dark: "Fosc"
 lightThemes: "Temes clars"
 darkThemes: "Temes foscos"
 syncDeviceDarkMode: "Sincronitza el mode fosc amb la configuració del dispositiu"
+drive: "Unitat"
+fileName: "Nom del Fitxer"
+selectFile: "Selecciona fitxers"
+selectFiles: "Selecciona fitxers"
+selectFolder: "Selecció de carpeta"
+selectFolders: "Selecció de carpeta"
 renameFile: "Canvia el nom del fitxer"
 folderName: "Nom de la carpeta"
 createFolder: "Crea una carpeta"
 renameFolder: "Canvia el nom de la carpeta"
 deleteFolder: "Elimina la carpeta"
+folder: "Carpeta "
 addFile: "Afegeix un fitxer"
+emptyDrive: "La teva unitat és buida"
 emptyFolder: "La carpeta està buida"
 unableToDelete: "No es pot eliminar"
+inputNewFileName: "Introduïu el nom de fitxer nou"
+inputNewDescription: "Inserta una nova llegenda"
+inputNewFolderName: "Introduïu el nom de la carpeta nova"
+circularReferenceFolder: "La carpeta destinatària és una subcarpeta de la carpeta a la qual la desitges moure"
+hasChildFilesOrFolders: "No és possible esborrar aquesta carpeta ja que no és buida"
 copyUrl: "Copia l'URL"
 rename: "Canvia el nom"
+avatar: "Icona"
+banner: "Bàner"
+displayOfSensitiveMedia: "Visualització de contingut sensible"
+whenServerDisconnected: "Quan es perdi la connexió al servidor"
+disconnectedFromServer: "Desconnectat pel servidor"
 reload: "Actualitza"
 doNothing: "Ignora"
 accept: "Accepta"
@@ -353,33 +403,132 @@ notFound: "No s'ha trobat"
 markAsReadAllUnreadNotes: "Marca-ho tot com a llegit"
 help: "Ajuda"
 invites: "Convida"
+title: "Títol"
+text: "Text"
+enable: "Habilita"
 next: "Següent"
+retype: "Torneu a introduir-la"
 noteOf: "Publicació de: {user}"
+quoteAttached: "Frase adjunta"
+quoteQuestion: "Vols annexar-la com a cita?"
+noMessagesYet: "Encara no hi ha missatges"
+newMessageExists: "Has rebut un nou missatge"
+onlyOneFileCanBeAttached: "Només pots adjuntar un fitxer a un missatge"
+signinRequired: "Si us plau, Registra't o inicia la sessió abans de continuar"
 invitations: "Convida"
+invitationCode: "Codi d'invitació"
+checking: "Comprovació en curs..."
+available: "Disponible"
+unavailable: "No és disponible"
+usernameInvalidFormat: "Pots fer servir lletres (majúscules i minúscules), números i barres baixes (\"_\")"
+tooShort: "Massa curt"
+tooLong: "Massa llarg"
+weakPassword: "Contrasenya insegura"
+normalPassword: "Bona contrasenya"
+strongPassword: "Contrasenya segura"
+passwordMatched: "Correcte!"
+passwordNotMatched: "No coincideix"
+signinWith: "Inicia sessió amb amb {x}"
+signinFailed: "Autenticació sense èxit. Intenta-ho un altre cop utilitzant la contrasenya i el nom correctes."
+or: "O"
+language: "Idioma"
+uiLanguage: "Idioma de l'interfície"
+aboutX: "Respecte a {x}"
+emojiStyle: "Estil d'emoji"
+native: "Nadiu"
+disableDrawer: "No mostrar els menús en calaixos"
+showNoteActionsOnlyHover: "Només mostra accions de la nota en passar amb el cursor"
+noHistory: "No hi ha un registre previ"
+signinHistory: "Historial d'autenticacions"
+enableAdvancedMfm: "Habilitar l'MFM avançat"
+enableAnimatedMfm: "Habilitar l'MFM amb moviment"
+doing: "Processant..."
+category: "Categoria"
 tags: "Etiquetes"
 docSource: "Font del document"
 createAccount: "Crea un compte"
 existingAccount: "Compte existent"
 regenerate: "Regenera"
 fontSize: "Mida del text"
+mediaListWithOneImageAppearance: "Altura de la llista de fitxers amb una única imatge"
+limitTo: "Limita a {x}"
 noFollowRequests: "No tens sol·licituds de seguiment"
+openImageInNewTab: "Obre imatges a una nova pestanya"
 dashboard: "Panell de control"
 local: "Local"
 remote: "Remot"
 total: "Total"
+weekOverWeekChanges: "Canvis l'última setmana"
+dayOverDayChanges: "Canvis ahir"
 appearance: "Aparença"
 clientSettings: "Configuració del client"
 accountSettings: "Configuració del compte"
+promotion: "Promocionat"
+promote: "Promoure"
+numberOfDays: "Nombre de dies"
 hideThisNote: "Amaga la publicació"
 showFeaturedNotesInTimeline: "Mostra publicacions destacades en la línia de temps"
+objectStorage: "Emmagatzematge d'objectes\n"
+useObjectStorage: "Utilitzar l'emmagatzematge d'objectes"
+objectStorageBaseUrl: "Base d'enllaç"
+objectStorageBaseUrlDesc: "Prefix d'enllaç utilitzat per a fer referencia als fitxers. Especifica l'enllaç del teu CDN o Proxy si n'estàs utilitzant qualsevol, en cas contrari, especifica l'enllaç al que es pot accedir públicament segons la guia de servei que vosté utilitza.\nPer l'ús d'S3 utilitza 'https://<bucket>.s3.amazonaws.com' I per a GCS o serveis equivalents utilitza 'https://storage.googleapis.com/<bucket>'."
 newNoteRecived: "Hi ha publicacions noves"
 installedDate: "Data d'instal·lació"
 state: "Estat"
 sort: "Ordena"
 ascendingOrder: "Ascendent"
 descendingOrder: "Descendent"
+removeAllFollowing: "Deixar de seguir tots els usuaris seguits"
+removeAllFollowingDescription: "El fet d'executar això, et farà deixar de seguir a tots els usuaris de {host}. Si us plau, executa això si l'amfitrió, per exemple, ja no existeix."
+userSuspended: "Aquest usuari ha sigut suspès"
+userSilenced: "Aquest usuari està sent silenciat"
+yourAccountSuspendedTitle: "Aquest compte és suspès"
+yourAccountSuspendedDescription: "Aquest compte ha sigut suspès a causa de la violació de les condicions d'ús o similars. Contacta l'administrador si en vol saber més. Si us plau, no en faci un altre compte."
+tokenRevoked: "Codi de seguretat no vàlid"
+tokenRevokedDescription: "La petició més recent ha estat denegada perquè contenia un codi de seguretat no vàlid. Actualitza la pàgina i torna-ho a provar."
+accountDeleted: "Compte eliminat amb èxit"
+accountDeletedDescription: "Aquest compte ha sigut eliminat"
+menu: "Menú"
+divider: "Divisor"
+addItem: "Afegir element"
+rearrange: "Torna a ordenar"
+relays: "Relés"
+addRelay: "Afegeix relés"
+inboxUrl: "Enllaç de la safata d'entrada"
+addedRelays: "Relés afegits"
+serviceworkerInfo: "És obligatòria l'activació per a obtenir notificacions push"
 deletedNote: "Publicacions eliminades"
 invisibleNote: "Publicacions amagades"
+enableInfiniteScroll: "Carrega més automàticament\n"
+visibility: "Visibilitat"
+poll: "Enquesta"
+useCw: "Amaga el contingut"
+enablePlayer: "Obre el reproductor de vídeo"
+disablePlayer: "Tanca el reproductor de vídeo"
+expandTweet: "Expandir post"
+themeEditor: "Editor de temes"
+description: "Descripció"
+describeFile: "Afegir subtitulació"
+enterFileDescription: "Afegeix un títol"
+author: "Autor"
+leaveConfirm: "Hi ha canvis sense guardar. Els vols descartar?"
+manage: "Administració"
+plugins: "Extensions"
+preferencesBackups: "Configuracions de les Còpies de seguretat"
+deck: "Escriptori"
+undeck: "Tanca l'escriptori"
+useBlurEffectForModal: "Utilitzar l'efecte de difuminació a modals"
+useFullReactionPicker: "Utilitza el cercador de reaccions d'escala sencera"
+width: "Amplada"
+height: "Alçària"
+large: "Gran"
+medium: "Mitjà"
+small: "Petit"
+generateAccessToken: "Genera codi d'accés"
+permission: "Permisos"
+enableAll: "Habilita tot"
+disableAll: "Deshabilita tot"
+tokenRequested: "Donar accés al compte"
 smtpHost: "Amfitrió"
 smtpUser: "Nom d'usuari"
 smtpPass: "Contrasenya"
@@ -389,12 +538,17 @@ clearCache: "Esborra la memòria cau"
 showingPastTimeline: "Estàs veient una línia de temps antiga"
 info: "Informació"
 user: "Usuaris"
+administration: "Administració"
+middle: "Mitjà"
 global: "Global"
 searchByGoogle: "Cercar"
 file: "Fitxers"
+icon: "Icona"
 replies: "Respondre"
 renotes: "Impulsa"
 _role:
+  _priority:
+    middle: "Mitjà"
   _options:
     antennaMax: "Nombre màxim d'antenes"
 _email:
@@ -403,9 +557,11 @@ _email:
 _instanceMute:
   instanceMuteDescription: "Silencia tots els impulsos dels servidors seleccionats, també els usuaris que responen a altres d'un servidor silenciat."
 _theme:
+  description: "Descripció"
   keys:
     mention: "Menció"
     renote: "Renotar"
+    divider: "Divisor"
 _sfx:
   note: "Notes"
   notification: "Notificacions"
@@ -447,6 +603,8 @@ _timelines:
   local: "Local"
   social: "Social"
   global: "Global"
+_play:
+  summary: "Descripció"
 _pages:
   contents: "Contingut"
   blocks:
diff --git a/locales/en-US.yml b/locales/en-US.yml
index 65fe07b6d0..0701bc3710 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -543,7 +543,7 @@ showInPage: "Show in page"
 popout: "Pop-out"
 volume: "Volume"
 masterVolume: "Master volume"
-notUseSound: "No sounds output."
+notUseSound: "Disable sound"
 useSoundOnlyWhenActive: "Output sounds only if Misskey is active."
 details: "Details"
 chooseEmoji: "Select an emoji"
@@ -1167,6 +1167,7 @@ cwNotationRequired: "If \"Hide content\" is enabled, a description must be provi
 doReaction: "Add reaction"
 code: "Code"
 reloadRequiredToApplySettings: "Reloading is required to apply the settings."
+decorate: "Decorate"
 _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."
@@ -1256,7 +1257,7 @@ _initialTutorial:
     sensitiveSucceeded: "When attaching files, please set sensitivities in accordance with the server guidelines."
     doItToContinue: "Mark the attachment file as sensitive to proceed."
   _done:
-    title: "The tutorial is complete! 🎉"
+    title: "You've completed the tutorial! 🎉"
     description: "The functions introduced here are just a small part. For a more detailed understanding of using Misskey, please refer to {link}."
 _timelineDescription:
   home: "In the Home timeline, you can see notes from accounts you follow."
@@ -2154,6 +2155,7 @@ _notification:
   pollEnded: "Poll results have become available"
   newNote: "New note"
   unreadAntennaNote: "Antenna {name}"
+  roleAssigned: "Role given"
   emptyPushNotificationMessage: "Push notifications have been updated"
   achievementEarned: "Achievement unlocked"
   testNotification: "Test notification"
@@ -2175,6 +2177,7 @@ _notification:
     pollEnded: "Polls ending"
     receiveFollowRequest: "Received follow requests"
     followRequestAccepted: "Accepted follow requests"
+    roleAssigned: "Role given"
     achievementEarned: "Achievement unlocked"
     app: "Notifications from linked apps"
   _actions:
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 8acbc7d7a6..e12b508617 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -1899,6 +1899,7 @@ _notification:
   yourFollowRequestAccepted: "Votre demande d’abonnement a été accepté"
   pollEnded: "Les résultats du sondage sont disponibles"
   unreadAntennaNote: "Antenne {name}"
+  roleAssigned: "Rôle attribué"
   emptyPushNotificationMessage: "Les notifications push ont été mises à jour"
   achievementEarned: "Accomplissement"
   testNotification: "Tester la notification"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index c8d69255f9..d8efa7f04e 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -2171,6 +2171,7 @@ _notification:
   pollEnded: "투표 결과가 발표되었습니다"
   newNote: "새 게시물"
   unreadAntennaNote: "안테나 {name}"
+  roleAssigned: "역할이 부여 되었습니다."
   emptyPushNotificationMessage: "푸시 알림이 갱신되었습니다"
   achievementEarned: "도전 과제를 달성했습니다"
   testNotification: "알림 테스트"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 419c063e27..d05691d42e 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -2171,6 +2171,7 @@ _notification:
   pollEnded: "問卷調查已產生結果"
   newNote: "新的貼文"
   unreadAntennaNote: "天線 {name}"
+  roleAssigned: "已授予角色"
   emptyPushNotificationMessage: "推送通知已更新"
   achievementEarned: "獲得成就"
   testNotification: "通知測試"

From b71e5cf2fb23e0aa260bad025a605b47c7109109 Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Fri, 22 Dec 2023 11:51:43 +0100
Subject: [PATCH 304/435] fix: avatar decoration role not working

Closes #150
---
 packages/frontend/src/router.ts             | 4 ++++
 packages/frontend/src/ui/_common_/common.ts | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts
index d06b6019b3..b148325c71 100644
--- a/packages/frontend/src/router.ts
+++ b/packages/frontend/src/router.ts
@@ -307,6 +307,10 @@ export const routes = [{
 }, {
 	path: '/channels',
 	component: page(() => import('./pages/channels.vue')),
+}, {
+	path: '/avatar-decorations',
+	name: 'avatarDecorations',
+	component: page(() => import('./pages/avatar-decorations.vue')),
 }, {
 	path: '/custom-emojis-manager',
 	component: page(() => import('./pages/custom-emojis-manager.vue')),
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index 7a3f643561..d153f776dd 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -34,7 +34,7 @@ function toolsMenuItems(): MenuItem[] {
 		icon: 'ph-smiley ph-bold ph-lg',
 	} : undefined, ($i && ($i.isAdmin || $i.policies.canManageAvatarDecorations)) ? {
 		type: 'link',
-		to: '/admin/avatar-decorations',
+		to: '/avatar-decorations',
 		text: i18n.ts.manageAvatarDecorations,
 		icon: 'ph-sparkle ph-bold ph-lg',
 	} : undefined];

From 179cb1d8139ac9ffdc3b5e527e0008f9b7422067 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Dec 2023 20:14:20 +0900
Subject: [PATCH 305/435] fix type

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

diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 301e691fa0..8624aebdcf 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -53,7 +53,7 @@ import { PageHeaderItem } from '@/types/page-header.js';
 const props = withDefaults(defineProps<{
 	tabs?: Tab[];
 	tab?: string;
-	actions?: PageHeaderItem[];
+	actions?: PageHeaderItem[] | null;
 	thin?: boolean;
 	displayMyAvatar?: boolean;
 }>(), {

From 3d4af183274353fb7b1ea0ab0c9f8819d6e329be 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 Dec 2023 20:16:31 +0900
Subject: [PATCH 306/435] =?UTF-8?q?[Hub=20Next]=20Misskey=20Hub=E3=81=AE?=
 =?UTF-8?q?=E3=83=AA=E3=83=B3=E3=82=AF=E3=82=92=E5=A4=89=E6=9B=B4=20(#1269?=
 =?UTF-8?q?9)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* misskey hub のリンクを変更

* misskey-hub.net -> assets.misskey-hub.net
Related to misskey-dev/misskey-hub-next#57
---
 CHANGELOG.md                                  |  4 +-
 README.md                                     |  6 +--
 .../frontend/src/components/MkDonation.vue    |  2 +-
 .../src/components/MkSignupDialog.rules.vue   |  2 +-
 .../components/MkTutorialDialog.Timeline.vue  |  2 +-
 .../src/components/MkTutorialDialog.vue       |  2 +-
 .../frontend/src/components/MkUpdated.vue     |  2 +-
 .../src/components/MkVisitorDashboard.vue     |  4 +-
 packages/frontend/src/pages/about-misskey.vue | 48 +++++++++----------
 packages/frontend/src/pages/share.vue         |  2 +-
 packages/frontend/src/ui/_common_/common.ts   |  2 +-
 11 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7135d8745..95f435b7d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -152,7 +152,7 @@
 ### General
 - Feat: アイコンデコレーション機能
 	- サーバーで用意された画像をアイコンに重ねることができます
-	- 画像のテンプレートはこちらです: https://misskey-hub.net/avatar-decoration-template.png
+	- 画像のテンプレートはこちらです: https://misskey-hub.net/brand-assets/
 		- 最大でも黄色いエリア内にデコレーションを収めることを推奨します。
 		- 画像は512x512pxを推奨します。
 - Feat: チャンネル設定にリノート/引用リノートの可否を設定できる項目を追加
@@ -169,7 +169,7 @@
 ### Client
 - Feat: プラグイン・テーマを外部サイトから直接インストールできるようになりました
 	- 外部サイトでの実装が必要です。詳細は Misskey Hub をご覧ください
-	  https://misskey-hub.net/docs/advanced/publish-on-your-website.html
+	  https://misskey-hub.net/docs/for-developers/publish-on-your-website/
 - Feat: 通知をグルーピングして表示するオプション(オプトアウト)
 - Feat: Misskeyの基本的なチュートリアルを実装
 - Feat: スワイプしてタイムラインを再読込できるように
diff --git a/README.md b/README.md
index ab4388c2eb..6fa804f1fa 100644
--- a/README.md
+++ b/README.md
@@ -7,10 +7,10 @@
 
 ---
 
-<a href="https://misskey-hub.net/instances.html">
+<a href="https://misskey-hub.net/servers/">
 		<img src="https://custom-icon-badges.herokuapp.com/badge/find_an-instance-acea31?logoColor=acea31&style=for-the-badge&logo=misskey&labelColor=363B40" alt="find an instance"/></a>
 
-<a href="https://misskey-hub.net/docs/install.html">
+<a href="https://misskey-hub.net/docs/for-admin/install/guides/">
 		<img src="https://custom-icon-badges.herokuapp.com/badge/create_an-instance-FBD53C?logoColor=FBD53C&style=for-the-badge&logo=server&labelColor=363B40" alt="create an instance"/></a>
 
 <a href="./CONTRIBUTING.md">
@@ -51,7 +51,7 @@ With Misskey's built in drive, you get cloud storage right in your social media,
 
 ## Documentation
 
-Misskey Documentation can be found at [Misskey Hub](https://misskey-hub.net/), some of the links and graphics above also lead to specific portions of it.
+Misskey Documentation can be found at [Misskey Hub](https://misskey-hub.net/docs/), some of the links and graphics above also lead to specific portions of it.
 
 ## Sponsors
 
diff --git a/packages/frontend/src/components/MkDonation.vue b/packages/frontend/src/components/MkDonation.vue
index a2a0b6023b..3a1bab5f98 100644
--- a/packages/frontend/src/components/MkDonation.vue
+++ b/packages/frontend/src/components/MkDonation.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</template>
 			</I18n>
 			<div style="margin-top: 0.2em;">
-				<MkLink target="_blank" url="https://misskey-hub.net/docs/donate.html">{{ i18n.ts.learnMore }}</MkLink>
+				<MkLink target="_blank" url="https://misskey-hub.net/docs/for-users/resources/donate/">{{ i18n.ts.learnMore }}</MkLink>
 			</div>
 		</div>
 		<div class="_buttons">
diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue
index 8f9c1c93f8..8cf7ce92ad 100644
--- a/packages/frontend/src/components/MkSignupDialog.rules.vue
+++ b/packages/frontend/src/components/MkSignupDialog.rules.vue
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<template #label>{{ i18n.ts.basicNotesBeforeCreateAccount }}</template>
 				<template #suffix><i v-if="agreeNote" class="ti ti-check" style="color: var(--success)"></i></template>
 
-				<a href="https://misskey-hub.net/docs/notes.html" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }} <i class="ti ti-external-link"></i></a>
+				<a href="https://misskey-hub.net/docs/for-users/onboarding/warning/" class="_link" target="_blank">{{ i18n.ts.basicNotesBeforeCreateAccount }} <i class="ti ti-external-link"></i></a>
 
 				<MkSwitch :modelValue="agreeNote" style="margin-top: 16px;" data-cy-signup-rules-notes-agree @update:modelValue="updateAgreeNote">{{ i18n.ts.agree }}</MkSwitch>
 			</MkFolder>
diff --git a/packages/frontend/src/components/MkTutorialDialog.Timeline.vue b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
index 75b917f33c..93181cf2b1 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Timeline.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<div :class="$style.divider"></div>
 	<I18n :src="i18n.ts._initialTutorial._timeline.description3" tag="div" style="padding: 0 16px;">
 		<template #link>
-			<a href="https://misskey-hub.net/docs/features/timeline.html" target="_blank" class="_link">{{ i18n.ts.help }}</a>
+			<a href="https://misskey-hub.net/docs/for-users/features/timeline/" target="_blank" class="_link">{{ i18n.ts.help }}</a>
 		</template>
 	</I18n>
 
diff --git a/packages/frontend/src/components/MkTutorialDialog.vue b/packages/frontend/src/components/MkTutorialDialog.vue
index e28838425f..963e78a1ff 100644
--- a/packages/frontend/src/components/MkTutorialDialog.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.vue
@@ -130,7 +130,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<div style="font-size: 120%;">{{ i18n.ts._initialTutorial._done.title }}</div>
 							<I18n :src="i18n.ts._initialTutorial._done.description" tag="div" style="padding: 0 16px;">
 								<template #link>
-									<a href="https://misskey-hub.net/help.html" target="_blank" class="_link">{{ i18n.ts.help }}</a>
+									<a href="https://misskey-hub.net/docs/for-users/" target="_blank" class="_link">{{ i18n.ts.help }}</a>
 								</template>
 							</I18n>
 							<div>{{ i18n.t('_initialAccountSetting.haveFun', { name: instance.name ?? host }) }}</div>
diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue
index 699d7af33e..391733931a 100644
--- a/packages/frontend/src/components/MkUpdated.vue
+++ b/packages/frontend/src/components/MkUpdated.vue
@@ -27,7 +27,7 @@ const modal = shallowRef<InstanceType<typeof MkModal>>();
 
 const whatIsNew = () => {
 	modal.value.close();
-	window.open(`https://misskey-hub.net/docs/releases.html#_${version.replace(/\./g, '-')}`, '_blank');
+	window.open(`https://misskey-hub.net/docs/releases/#_${version.replace(/\./g, '')}`, '_blank');
 };
 
 onMounted(() => {
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 60068df842..9ed08ee372 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -123,13 +123,13 @@ function showMenu(ev) {
 		text: i18n.ts.help,
 		icon: 'ti ti-help-circle',
 		action: () => {
-			window.open('https://misskey-hub.net/help.md', '_blank', 'noopener');
+			window.open('https://misskey-hub.net/docs/for-users/', '_blank', 'noopener');
 		},
 	}], ev.currentTarget ?? ev.target);
 }
 
 function exploreOtherServers() {
-	window.open('https://join.misskey.page/instances', '_blank', 'noopener');
+	window.open('https://misskey-hub.net/servers/', '_blank', 'noopener');
 }
 </script>
 
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index c245b9b6cb..20c65f4541 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<button v-if="thereIsTreasure" class="_button treasure" @click="getTreasure"><img src="/fluent-emoji/1f3c6.png" class="treasureImg"></button>
 				</div>
 				<div style="text-align: center;">
-					{{ i18n.ts._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/misskey.html" target="_blank" class="_link">{{ i18n.ts.learnMore }}</a>
+					{{ i18n.ts._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/about-misskey/" target="_blank" class="_link">{{ i18n.ts.learnMore }}</a>
 				</div>
 				<div v-if="$i != null" style="text-align: center;">
 					<MkButton primary rounded inline @click="iLoveMisskey">I <Mfm text="$[jelly ❤]"/> #Misskey</MkButton>
@@ -139,73 +139,73 @@ import { $i } from '@/account.js';
 
 const patronsWithIcon = [{
 	name: 'カイヤン',
-	icon: 'https://misskey-hub.net/patrons/a2820716883e408cb87773e377ce7c8d.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/a2820716883e408cb87773e377ce7c8d.jpg',
 }, {
 	name: 'だれかさん',
-	icon: 'https://misskey-hub.net/patrons/f7409b5e5a88477a9b9d740c408de125.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/f7409b5e5a88477a9b9d740c408de125.jpg',
 }, {
 	name: 'narazaka',
-	icon: 'https://misskey-hub.net/patrons/e3affff31ffb4877b1196c7360abc3e5.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/e3affff31ffb4877b1196c7360abc3e5.jpg',
 }, {
 	name: 'ひとぅ',
-	icon: 'https://misskey-hub.net/patrons/8cc0d0a0a6d84c88bca1aedabf6ed5ab.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/8cc0d0a0a6d84c88bca1aedabf6ed5ab.jpg',
 }, {
 	name: 'ぱーこ',
-	icon: 'https://misskey-hub.net/patrons/79c6602ffade489e8df2fcf2c2bc5d9d.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/79c6602ffade489e8df2fcf2c2bc5d9d.jpg',
 }, {
 	name: 'わっほー☆',
-	icon: 'https://misskey-hub.net/patrons/d31d5d13924443a082f3da7966318a0a.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/d31d5d13924443a082f3da7966318a0a.jpg',
 }, {
 	name: 'mollinaca',
-	icon: 'https://misskey-hub.net/patrons/ceb36b8f66e549bdadb3b90d5da62314.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/ceb36b8f66e549bdadb3b90d5da62314.jpg',
 }, {
 	name: '坂本龍',
-	icon: 'https://misskey-hub.net/patrons/a631cf8b490145cf8dbbe4e7508cfbc2.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/a631cf8b490145cf8dbbe4e7508cfbc2.jpg',
 }, {
 	name: 'takke',
-	icon: 'https://misskey-hub.net/patrons/6c3327e626c046f2914fbcd9f7557935.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/6c3327e626c046f2914fbcd9f7557935.jpg',
 }, {
 	name: 'ぺんぎん',
-	icon: 'https://misskey-hub.net/patrons/6a652e0534ff4cb1836e7ce4968d76a7.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/6a652e0534ff4cb1836e7ce4968d76a7.jpg',
 }, {
 	name: 'かみらえっと',
-	icon: 'https://misskey-hub.net/patrons/be1326bda7d940a482f3758ffd9ffaf6.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/be1326bda7d940a482f3758ffd9ffaf6.jpg',
 }, {
 	name: 'へてて',
-	icon: 'https://misskey-hub.net/patrons/0431eacd7c6843d09de8ea9984307e86.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/0431eacd7c6843d09de8ea9984307e86.jpg',
 }, {
 	name: 'spinlock',
-	icon: 'https://misskey-hub.net/patrons/6a1cebc819d540a78bf20e9e3115baa8.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/6a1cebc819d540a78bf20e9e3115baa8.jpg',
 }, {
 	name: 'じゅくま',
-	icon: 'https://misskey-hub.net/patrons/3e56bdac69dd42f7a06e0f12cf2fc895.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/3e56bdac69dd42f7a06e0f12cf2fc895.jpg',
 }, {
 	name: '清遊あみ',
-	icon: 'https://misskey-hub.net/patrons/de25195b88e940a388388bea2e7637d8.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/de25195b88e940a388388bea2e7637d8.jpg',
 }, {
 	name: 'Nagi8410',
-	icon: 'https://misskey-hub.net/patrons/31b102ab4fc540ed806b0461575d38be.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/31b102ab4fc540ed806b0461575d38be.jpg',
 }, {
 	name: '山岡士郎',
-	icon: 'https://misskey-hub.net/patrons/84b9056341684266bb1eda3e680d094d.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/84b9056341684266bb1eda3e680d094d.jpg',
 }, {
 	name: 'よもやまたろう',
-	icon: 'https://misskey-hub.net/patrons/4273c9cce50d445f8f7d0f16113d6d7f.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/4273c9cce50d445f8f7d0f16113d6d7f.jpg',
 }, {
 	name: '花咲ももか',
-	icon: 'https://misskey-hub.net/patrons/8c9b2b9128cb4fee99f04bb4f86f2efa.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/8c9b2b9128cb4fee99f04bb4f86f2efa.jpg',
 }, {
 	name: 'カガミ',
-	icon: 'https://misskey-hub.net/patrons/226ea3a4617749548580ec2d9a263e24.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/226ea3a4617749548580ec2d9a263e24.jpg',
 }, {
 	name: 'フランギ・シュウ',
-	icon: 'https://misskey-hub.net/patrons/3016d37e35f3430b90420176c912d304.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/3016d37e35f3430b90420176c912d304.jpg',
 }, {
 	name: '百日紅',
-	icon: 'https://misskey-hub.net/patrons/302dce2898dd457ba03c3f7dc037900b.jpg',
+	icon: 'https://assets.misskey-hub.net/patrons/302dce2898dd457ba03c3f7dc037900b.jpg',
 }, {
 	name: 'taichan',
-	icon: 'https://misskey-hub.net/patrons/f981ab0159fb4e2c998e05f7263e1cd9.png',
+	icon: 'https://assets.misskey-hub.net/patrons/f981ab0159fb4e2c998e05f7263e1cd9.png',
 }];
 
 const patrons = [
diff --git a/packages/frontend/src/pages/share.vue b/packages/frontend/src/pages/share.vue
index 3e9cac9858..cb5acf3afa 100644
--- a/packages/frontend/src/pages/share.vue
+++ b/packages/frontend/src/pages/share.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-// SPECIFICATION: https://misskey-hub.net/docs/features/share-form.html
+// SPECIFICATION: https://misskey-hub.net/docs/for-users/features/share-form/
 
 import { ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index bfafe3dd96..b970ff1df4 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -101,7 +101,7 @@ export function openInstanceMenu(ev: MouseEvent) {
 		text: i18n.ts.help,
 		icon: 'ti ti-help-circle',
 		action: () => {
-			window.open('https://misskey-hub.net/help.html', '_blank', 'noopener');
+			window.open('https://misskey-hub.net/docs/for-users/', '_blank', 'noopener');
 		},
 	}, ($i) ? {
 		text: i18n.ts._initialTutorial.launchTutorial,

From 8536e22cfe076449c513fc250dc7a9ce0acd3cbd Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Fri, 22 Dec 2023 12:19:39 +0100
Subject: [PATCH 307/435] fix: change way url example gets displayed on page
 editor

Closes #212
---
 packages/frontend/src/pages/page-editor/page-editor.vue | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue
index 69a4c7dac4..f455f8904e 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</MkInput>
 
 				<MkInput v-model="name">
-					<template #prefix>{{ url }}/@{{ author.username }}/pages/</template>
+					<template #prefix>@{{ author.username }}/pages/</template>
 					<template #label>{{ i18n.ts._pages.url }}</template>
 				</MkInput>
 
@@ -68,7 +68,6 @@ import MkButton from '@/components/MkButton.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkInput from '@/components/MkInput.vue';
-import { url } from '@/config.js';
 import * as os from '@/os.js';
 import { selectFile } from '@/scripts/select-file.js';
 import { mainRouter } from '@/router.js';

From 5eb944ecdeb0d65ec82b89522dfdf37d980bdb51 Mon Sep 17 00:00:00 2001
From: ikasoba <57828948+ikasoba@users.noreply.github.com>
Date: Fri, 22 Dec 2023 20:41:42 +0900
Subject: [PATCH 308/435] =?UTF-8?q?enhance:=20=E3=83=81=E3=83=A3=E3=83=B3?=
 =?UTF-8?q?=E3=83=8D=E3=83=AB=E3=81=AB=E6=96=B0=E8=A6=8F=E3=81=AE=E6=8A=95?=
 =?UTF-8?q?=E7=A8=BF=E3=81=8C=E3=81=82=E3=82=8B=E5=A0=B4=E5=90=88=E3=81=AB?=
 =?UTF-8?q?=E3=83=90=E3=83=83=E3=82=B8=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=95?=
 =?UTF-8?q?=E3=81=9B=E3=82=8B=20(#12690)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 多分できたかも

* 不要なpropsを削除

* 不要なimportを削除

* 縁を付けた

* 枠線の位置を端に寄せた

* やっぱり内側へ寄せることにした

* できたかも

* 修正

* 修正

* クラスにまとめた

* 微調整

* 直せたかも

* importを付け足し

* 多分できたかも

* Update channel.vue

* Update MkMenu.vue

* Update channel.vue

* Update CHANGELOG.md

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                  |  1 +
 .../src/components/MkChannelPreview.vue       | 98 +++++++++++++------
 packages/frontend/src/components/MkMenu.vue   | 64 ++++++++----
 packages/frontend/src/local-storage.ts        |  3 +-
 packages/frontend/src/pages/channel.vue       | 40 ++++++--
 packages/frontend/src/pages/timeline.vue      | 18 ++--
 6 files changed, 159 insertions(+), 65 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 95f435b7d7..d5ce885b9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -69,6 +69,7 @@
 	- 逆に、MFMでコードハイライトを利用したい際は言語を明示的に指定する必要があります  
 	(例: ` ```js ` → Javascript, ` ```ais ` → AiScript)
 -	Enhance: 絵文字などのオートコンプリートでShift+Tabを押すと前の候補を選択できるように
+- Enhance: チャンネルに新規の投稿がある場合にバッジを表示させる
 - Fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
diff --git a/packages/frontend/src/components/MkChannelPreview.vue b/packages/frontend/src/components/MkChannelPreview.vue
index 4512f2dd60..bf6504d6bf 100644
--- a/packages/frontend/src/components/MkChannelPreview.vue
+++ b/packages/frontend/src/components/MkChannelPreview.vue
@@ -4,49 +4,70 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1">
-	<div class="banner" :style="bannerStyle">
-		<div class="fade"></div>
-		<div class="name"><i class="ti ti-device-tv"></i> {{ channel.name }}</div>
-		<div v-if="channel.isSensitive" class="sensitiveIndicator">{{ i18n.ts.sensitive }}</div>
-		<div class="status">
-			<div>
-				<i class="ti ti-users ti-fw"></i>
-				<I18n :src="i18n.ts._channel.usersCount" tag="span" style="margin-left: 4px;">
-					<template #n>
-						<b>{{ channel.usersCount }}</b>
-					</template>
-				</I18n>
-			</div>
-			<div>
-				<i class="ti ti-pencil ti-fw"></i>
-				<I18n :src="i18n.ts._channel.notesCount" tag="span" style="margin-left: 4px;">
-					<template #n>
-						<b>{{ channel.notesCount }}</b>
-					</template>
-				</I18n>
+<div style="position: relative;">
+	<MkA :to="`/channels/${channel.id}`" class="eftoefju _panel" tabindex="-1" @click="updateLastReadedAt">
+		<div class="banner" :style="bannerStyle">
+			<div class="fade"></div>
+			<div class="name"><i class="ti ti-device-tv"></i> {{ channel.name }}</div>
+			<div v-if="channel.isSensitive" class="sensitiveIndicator">{{ i18n.ts.sensitive }}</div>
+			<div class="status">
+				<div>
+					<i class="ti ti-users ti-fw"></i>
+					<I18n :src="i18n.ts._channel.usersCount" tag="span" style="margin-left: 4px;">
+						<template #n>
+							<b>{{ channel.usersCount }}</b>
+						</template>
+					</I18n>
+				</div>
+				<div>
+					<i class="ti ti-pencil ti-fw"></i>
+					<I18n :src="i18n.ts._channel.notesCount" tag="span" style="margin-left: 4px;">
+						<template #n>
+							<b>{{ channel.notesCount }}</b>
+						</template>
+					</I18n>
+				</div>
 			</div>
 		</div>
-	</div>
-	<article v-if="channel.description">
-		<p :title="channel.description">{{ channel.description.length > 85 ? channel.description.slice(0, 85) + '…' : channel.description }}</p>
-	</article>
-	<footer>
-		<span v-if="channel.lastNotedAt">
-			{{ i18n.ts.updatedAt }}: <MkTime :time="channel.lastNotedAt"/>
-		</span>
-	</footer>
-</MkA>
+		<article v-if="channel.description">
+			<p :title="channel.description">{{ channel.description.length > 85 ? channel.description.slice(0, 85) + '…' : channel.description }}</p>
+		</article>
+		<footer>
+			<span v-if="channel.lastNotedAt">
+				{{ i18n.ts.updatedAt }}: <MkTime :time="channel.lastNotedAt"/>
+			</span>
+		</footer>
+	</MkA>
+	<div
+		v-if="channel.lastNotedAt && (channel.isFavorited || channel.isFollowing) && (!lastReadedAt || Date.parse(channel.lastNotedAt) > lastReadedAt)"
+		class="indicator"
+	></div>
+</div>
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue';
+import { computed, ref, watch } from 'vue';
 import { i18n } from '@/i18n.js';
+import { miLocalStorage } from '@/local-storage.js';
 
 const props = defineProps<{
 	channel: Record<string, any>;
 }>();
 
+const getLastReadedAt = (): number | null => {
+	return miLocalStorage.getItemAsJson(`channelLastReadedAt:${props.channel.id}`) ?? null;
+};
+
+const lastReadedAt = ref(getLastReadedAt());
+
+watch(() => props.channel.id, () => {
+	lastReadedAt.value = getLastReadedAt();
+});
+
+const updateLastReadedAt = () => {
+	lastReadedAt.value = props.channel.lastNotedAt ? Date.parse(props.channel.lastNotedAt) : Date.now();
+};
+
 const bannerStyle = computed(() => {
 	if (props.channel.bannerUrl) {
 		return { backgroundImage: `url(${props.channel.bannerUrl})` };
@@ -170,4 +191,17 @@ const bannerStyle = computed(() => {
 	}
 }
 
+.indicator {
+	position: absolute;
+	top: 0;
+	right: 0;
+	transform: translate(25%, -25%);
+	background-color: var(--accent);
+	border: solid var(--bg) 4px;
+	border-radius: 100%;
+	width: 1.5rem;
+	height: 1.5rem;
+	aspect-ratio: 1 / 1;
+}
+
 </style>
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 8e4b86f1c7..0a97cf3dba 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<template v-for="(item, i) in items2">
 			<div v-if="item.type === 'divider'" role="separator" :class="$style.divider"></div>
 			<span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item]">
-				<span>{{ item.text }}</span>
+				<span style="opacity: 0.7;">{{ item.text }}</span>
 			</span>
 			<span v-else-if="item.type === 'pending'" role="menuitem" :tabindex="i" :class="[$style.pending, $style.item]">
 				<span><MkEllipsis/></span>
@@ -23,32 +23,44 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<MkA v-else-if="item.type === 'link'" role="menuitem" :to="item.to" :tabindex="i" class="_button" :class="$style.item" @click.passive="close(true)" @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"/>
-				<span>{{ item.text }}</span>
-				<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+				<div :class="$style.item_content">
+					<span :class="$style.item_content_text">{{ item.text }}</span>
+					<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+				</div>
 			</MkA>
 			<a v-else-if="item.type === 'a'" role="menuitem" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="$style.item" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
 				<i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i>
-				<span>{{ item.text }}</span>
-				<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+				<div :class="$style.item_content">
+					<span :class="$style.item_content_text">{{ item.text }}</span>
+					<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+				</div>
 			</a>
 			<button v-else-if="item.type === 'user'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)">
 				<MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/>
-				<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+				<div v-if="item.indicate" :class="$style.item_content">
+					<span :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+				</div>
 			</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)"/>
-				<span :class="$style.switchText">{{ item.text }}</span>
+				<div :class="$style.item_content">
+					<span :class="[$style.item_content_text, $style.switchText]">{{ item.text }}</span>
+				</div>
 			</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]" 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>
+				<div :class="$style.item_content">
+					<span :class="$style.item_content_text" 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>
+				</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"/>
-				<span>{{ item.text }}</span>
-				<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+				<div :class="$style.item_content">
+					<span :class="$style.item_content_text">{{ item.text }}</span>
+					<span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span>
+				</div>
 			</button>
 		</template>
 		<span v-if="items2.length === 0" :class="[$style.none, $style.item]">
@@ -228,6 +240,7 @@ onBeforeUnmount(() => {
 .root {
 	padding: 8px 0;
 	box-sizing: border-box;
+	max-width: 100vw;
 	min-width: 200px;
 	overflow: auto;
 	overscroll-behavior: contain;
@@ -267,7 +280,8 @@ onBeforeUnmount(() => {
 }
 
 .item {
-	display: block;
+	display: flex;
+	align-items: center;
 	position: relative;
 	padding: 5px 16px;
 	width: 100%;
@@ -340,10 +354,6 @@ onBeforeUnmount(() => {
 		pointer-events: none;
 		font-size: 0.7em;
 		padding-bottom: 4px;
-
-		> span {
-			opacity: 0.7;
-		}
 	}
 
 	&.pending {
@@ -373,6 +383,22 @@ onBeforeUnmount(() => {
 	}
 }
 
+.item_content {
+	width: 100%;
+	max-width: 100vw;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	gap: 8px;
+	text-overflow: ellipsis;
+}
+
+.item_content_text {
+	max-width: calc(100vw - 4rem);
+	text-overflow: ellipsis;
+	overflow: hidden;
+}
+
 .switch {
 	position: relative;
 	display: flex;
@@ -406,6 +432,7 @@ onBeforeUnmount(() => {
 
 .icon {
 	margin-right: 8px;
+	line-height: 1;
 }
 
 .caret {
@@ -419,9 +446,8 @@ onBeforeUnmount(() => {
 }
 
 .indicator {
-	position: absolute;
-	top: 5px;
-	left: 13px;
+	display: flex;
+	align-items: center;
 	color: var(--indicator);
 	font-size: 12px;
 	animation: blink 1s infinite;
diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts
index 0d73885b68..1ef115978e 100644
--- a/packages/frontend/src/local-storage.ts
+++ b/packages/frontend/src/local-storage.ts
@@ -35,7 +35,8 @@ type Keys =
 	`themes:${string}` |
 	`aiscript:${string}` |
 	'lastEmojisFetchedAt' | // DEPRECATED, stored in indexeddb (13.9.0~)
-	'emojis' // DEPRECATED, stored in indexeddb (13.9.0~);
+	'emojis' | // DEPRECATED, stored in indexeddb (13.9.0~);
+	`channelLastReadedAt:${string}`
 
 export const miLocalStorage = {
 	getItem: (key: Keys): string | null => window.localStorage.getItem(key),
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index 698f7fa383..421895ea6c 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<XChannelFollowButton :channel="channel" :full="true" :class="$style.subscribe"/>
 				<MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike class="button" rounded primary :class="$style.favorite" @click="unfavorite()"><i class="ti ti-star"></i></MkButton>
 				<MkButton v-else v-tooltip="i18n.ts.favorite" asLike class="button" rounded :class="$style.favorite" @click="favorite()"><i class="ti ti-star"></i></MkButton>
-				<div :style="{ backgroundImage: channel.bannerUrl ? `url(${channel.bannerUrl})` : null }" :class="$style.banner">
+				<div :style="{ backgroundImage: channel.bannerUrl ? `url(${channel.bannerUrl})` : undefined }" :class="$style.banner">
 					<div :class="$style.bannerStatus">
 						<div><i class="ti ti-users ti-fw"></i><I18n :src="i18n.ts._channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div>
 						<div><i class="ti ti-pencil ti-fw"></i><I18n :src="i18n.ts._channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></I18n></div>
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 			<MkFoldableSection>
 				<template #header><i class="ti ti-pin ti-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedNotes }}</template>
-				<div v-if="channel.pinnedNotes.length > 0" class="_gaps">
+				<div v-if="channel.pinnedNotes && channel.pinnedNotes.length > 0" class="_gaps">
 					<MkNote v-for="note in channel.pinnedNotes" :key="note.id" class="_panel" :note="note"/>
 				</div>
 			</MkFoldableSection>
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<!-- スマホ・タブレットの場合、キーボードが表示されると投稿が見づらくなるので、デスクトップ場合のみ自動でフォーカスを当てる -->
 			<MkPostForm v-if="$i && defaultStore.reactiveState.showFixedPostFormInChannel.value" :channel="channel" class="post-form _panel" fixed :autofocus="deviceKind === 'desktop'"/>
 
-			<MkTimeline :key="channelId" src="channel" :channel="channelId" @before="before" @after="after"/>
+			<MkTimeline :key="channelId" src="channel" :channel="channelId" @before="before" @after="after" @note="miLocalStorage.setItemAsJson(`channelLastReadedAt:${channel.id}`, Date.now())"/>
 		</div>
 		<div v-else-if="tab === 'featured'">
 			<MkNotes :pagination="featuredPagination"/>
@@ -69,6 +69,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, watch, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkPostForm from '@/components/MkPostForm.vue';
 import MkTimeline from '@/components/MkTimeline.vue';
 import XChannelFollowButton from '@/components/MkChannelFollowButton.vue';
@@ -89,6 +90,7 @@ import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import { PageHeaderItem } from '@/types/page-header.js';
 import { isSupportShare } from '@/scripts/navigator.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { miLocalStorage } from '@/local-storage.js';
 
 const router = useRouter();
 
@@ -97,7 +99,7 @@ const props = defineProps<{
 }>();
 
 const tab = ref('overview');
-const channel = ref(null);
+const channel = ref<Misskey.entities.Channel | null>(null);
 const favorited = ref(false);
 const searchQuery = ref('');
 const searchPagination = ref();
@@ -114,14 +116,23 @@ watch(() => props.channelId, async () => {
 	channel.value = await os.api('channels/show', {
 		channelId: props.channelId,
 	});
-	favorited.value = channel.value.isFavorited;
+	favorited.value = channel.value.isFavorited ?? false;
 	if (favorited.value || channel.value.isFollowing) {
 		tab.value = 'timeline';
 	}
+
+	if ((favorited.value || channel.value.isFollowing) && channel.value.lastNotedAt) {
+		const lastReadedAt: number = miLocalStorage.getItemAsJson(`channelLastReadedAt:${channel.value.id}`) ?? 0;
+		const lastNotedAt = Date.parse(channel.value.lastNotedAt);
+
+		if (lastNotedAt > lastReadedAt) {
+			miLocalStorage.setItemAsJson(`channelLastReadedAt:${channel.value.id}`, lastNotedAt);
+		}
+	}
 }, { immediate: true });
 
 function edit() {
-	router.push(`/channels/${channel.value.id}/edit`);
+	router.push(`/channels/${channel.value?.id}/edit`);
 }
 
 function openPostForm() {
@@ -131,6 +142,8 @@ function openPostForm() {
 }
 
 function favorite() {
+	if (!channel.value) return;
+
 	os.apiWithDialog('channels/favorite', {
 		channelId: channel.value.id,
 	}).then(() => {
@@ -139,6 +152,8 @@ function favorite() {
 }
 
 async function unfavorite() {
+	if (!channel.value) return;
+
 	const confirm = await os.confirm({
 		type: 'warning',
 		text: i18n.ts.unfavoriteConfirm,
@@ -152,6 +167,8 @@ async function unfavorite() {
 }
 
 async function search() {
+	if (!channel.value) return;
+
 	const query = searchQuery.value.toString().trim();
 
 	if (query == null) return;
@@ -176,6 +193,10 @@ const headerActions = computed(() => {
 			icon: 'ti ti-link',
 			text: i18n.ts.copyUrl,
 			handler: async (): Promise<void> => {
+				if (!channel.value) {
+					console.warn('failed to copy channel URL. channel.value is null.');
+					return;
+				}
 				copyToClipboard(`${url}/channels/${channel.value.id}`);
 				os.success();
 			},
@@ -186,9 +207,14 @@ const headerActions = computed(() => {
 				icon: 'ti ti-share',
 				text: i18n.ts.share,
 				handler: async (): Promise<void> => {
+					if (!channel.value) {
+						console.warn('failed to share channel. channel.value is null.');
+						return;
+					}
+
 					navigator.share({
 						title: channel.value.name,
-						text: channel.value.description,
+						text: channel.value.description ?? undefined,
 						url: `${url}/channels/${channel.value.id}`,
 					});
 				},
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index d976463db4..1b24f98bdb 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -48,6 +48,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { antennasCache, userListsCache } from '@/cache.js';
 import { deviceKind } from '@/scripts/device-kind.js';
 import { MenuItem } from '@/types/menu.js';
+import { miLocalStorage } from '@/local-storage.js';
 
 provide('shouldOmitHeaderTitle', true);
 
@@ -125,12 +126,17 @@ async function chooseChannel(ev: MouseEvent): Promise<void> {
 		limit: 100,
 	});
 	const items: MenuItem[] = [
-		...channels.map(channel => ({
-			type: 'link' as const,
-			text: channel.name,
-			indicate: channel.hasUnreadNote,
-			to: `/channels/${channel.id}`,
-		})),
+		...channels.map(channel => {
+			const lastReadedAt = miLocalStorage.getItemAsJson(`channelLastReadedAt:${channel.id}`) ?? null;
+			const hasUnreadNote = (lastReadedAt && channel.lastNotedAt) ? Date.parse(channel.lastNotedAt) > lastReadedAt : !!(!lastReadedAt && channel.lastNotedAt);
+
+			return {
+				type: 'link' as const,
+				text: channel.name,
+				indicate: hasUnreadNote,
+				to: `/channels/${channel.id}`,
+			};
+		}),
 		(channels.length === 0 ? undefined : { type: 'divider' }),
 		{
 			type: 'link' as const,

From 6b7a810b8e2b6efabada73b2969b310177e195db Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Dec 2023 20:57:59 +0900
Subject: [PATCH 309/435] Update CHANGELOG.md

---
 CHANGELOG.md | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d5ce885b9b..a2a444269c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -63,6 +63,7 @@
 - Enhance: ユーザー名、プロフィール、お知らせ、ページの編集画面でMFMや絵文字のオートコンプリートが使用できるように
 - Enhance: プロフィール、お知らせの編集画面でMFMのプレビューを表示できるように
 - Enhance: 絵文字の詳細ページに記載される情報を追加
+- Enhance: リアクションの表示幅制限を設定可能に
 - Enhance: Unicode 15.0のサポート
 - Enhance: コードブロックのハイライト機能を利用するには言語を明示的に指定させるように
 	- MFMでコードブロックを利用する際に意図しないハイライトが起こらないようになりました
@@ -70,6 +71,8 @@
 	(例: ` ```js ` → Javascript, ` ```ais ` → AiScript)
 -	Enhance: 絵文字などのオートコンプリートでShift+Tabを押すと前の候補を選択できるように
 - Enhance: チャンネルに新規の投稿がある場合にバッジを表示させる
+- Enhance: サウンド設定に「サウンドを出力しない」と「Misskeyがアクティブな時のみサウンドを出力する」を追加
+- Enhance: 設定したタグをトレンドに表示させないようにする項目を管理画面で設定できるように
 - Fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
@@ -85,10 +88,14 @@
 - Fix: 投票のみ/画像のみの引用RNが、通知欄でただのRNとして判定されるバグを修正
 - Fix: CWをつけて引用RNしても、普通のRNとして扱われてしまうバグを修正しました。
 - Fix: 「画像が1枚のみのメディアリストの高さ」を「デフォルト」以外に設定していると、CWの中などに添付された画像が見られないバグを修正
+- Fix: DeepL TranslationのPro accountトグルスイッチが表示されていなかったのを修正
+- Fix: twitterの埋め込みカード内リンクからリンク先を開けない問題を修正
+- Fix: WebKitブラウザー上でも「デバイスの画面を常にオンにする」機能が効くように
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
 - Enhance: Meilisearchを有効にした検索で、ユーザーのミュートやブロックを考慮するように
+- Enhance: カスタム絵文字のインポート時の動作を改善
 - Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303
 - Fix: ロールタイムラインが保存されない問題を修正
 - Fix: api.jsonの生成ロジックを改善 #12402
@@ -125,7 +132,6 @@
 	- 例: `$[unixtime 1701356400]`
 - Enhance: プラグインでエラーが発生した場合のハンドリングを強化
 - Enhance: 細かなUIのブラッシュアップ
-- Enhance: サウンド設定に「サウンドを出力しない」と「Misskeyがアクティブな時のみサウンドを出力する」を追加
 - Fix: 効果音が再生されるとデバイスで再生している動画や音声が停止する問題を修正 #12339
 - Fix: デッキに表示されたチャンネルの表示先チャンネルを切り替えた際、即座に反映されない問題を修正 #12236
 - Fix: プラグインでノートの表示を書き換えられない問題を修正

From 9c0474935972aedc5151ae24a7b92b20768030ca Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Dec 2023 20:59:28 +0900
Subject: [PATCH 310/435] 2023.12.0

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a2a444269c..ddebf7d267 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,7 +13,7 @@
 
 -->
 
-## 2023.x.x (unreleased)
+## 2023.12.0
 
 ### Note
 - Node.js 20.10.0が最小要件になりました
diff --git a/package.json b/package.json
index 562c5ce407..d39b800a18 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "2023.12.0-beta.6",
+	"version": "2023.12.0",
 	"codename": "nasubi",
 	"repository": {
 		"type": "git",

From e0155cffae64e5a4d57676c802bcf97c509adf34 Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Fri, 22 Dec 2023 13:10:21 +0100
Subject: [PATCH 311/435] add: Custom MOTDs

This works almost exactly like FF's custom MOTDs with the only difference being that they get defined in the config file for performance reasons.

Closes #86
---
 .config/docker_example.yml                       |  2 ++
 .config/example.yml                              |  2 ++
 packages/backend/src/config.ts                   |  4 ++++
 .../src/server/web/ClientServerService.ts        |  1 +
 packages/backend/src/server/web/style.css        | 16 +++++++++++++++-
 packages/backend/src/server/web/views/base.pug   |  3 +++
 6 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/.config/docker_example.yml b/.config/docker_example.yml
index 0a21f20320..b16a44f6b0 100644
--- a/.config/docker_example.yml
+++ b/.config/docker_example.yml
@@ -199,5 +199,7 @@ signToActivityPubGet: true
 #  '127.0.0.1/32'
 #]
 
+#customMOTD: ['Hello World', 'The sharks rule all', 'Shonks']
+
 # Upload or download file size limits (bytes)
 #maxFileSize: 262144000
diff --git a/.config/example.yml b/.config/example.yml
index 7cd710f250..219688aa12 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -214,6 +214,8 @@ signToActivityPubGet: true
 #  '127.0.0.1/32'
 #]
 
+#customMOTD: ['Hello World', 'The sharks rule all', 'Shonks']
+
 # Upload or download file size limits (bytes)
 #maxFileSize: 262144000
 
diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index b25554b229..dceeac4691 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -85,6 +85,8 @@ type Source = {
 	proxyRemoteFiles?: boolean;
 	videoThumbnailGenerator?: string;
 
+	customMOTD?: string[];
+
 	signToActivityPubGet?: boolean;
 
 	perChannelMaxNoteCacheCount?: number;
@@ -142,6 +144,7 @@ export type Config = {
 	deliverJobMaxAttempts: number | undefined;
 	inboxJobMaxAttempts: number | undefined;
 	proxyRemoteFiles: boolean | undefined;
+	customMOTD: string[] | undefined;
 	signToActivityPubGet: boolean | undefined;
 
 	version: string;
@@ -248,6 +251,7 @@ export function loadConfig(): Config {
 		deliverJobMaxAttempts: config.deliverJobMaxAttempts,
 		inboxJobMaxAttempts: config.inboxJobMaxAttempts,
 		proxyRemoteFiles: config.proxyRemoteFiles,
+		customMOTD: config.customMOTD,
 		signToActivityPubGet: config.signToActivityPubGet,
 		mediaProxy: externalMediaProxy ?? internalMediaProxy,
 		externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy,
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index ea7bfd70b8..aa696046ea 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -178,6 +178,7 @@ export class ClientServerService {
 			infoImageUrl: meta.infoImageUrl ?? 'https://launcher.moe/nothinghere.png',
 			notFoundImageUrl: meta.notFoundImageUrl ?? 'https://launcher.moe/missingpage.webp',
 			instanceUrl: this.config.url,
+			randomMOTD: this.config.customMOTD ? this.config.customMOTD[Math.floor(Math.random() * this.config.customMOTD.length)] : undefined,
 		};
 	}
 
diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css
index 952be9bf0b..171827a523 100644
--- a/packages/backend/src/server/web/style.css
+++ b/packages/backend/src/server/web/style.css
@@ -44,7 +44,7 @@ html {
 	display: inline-block;
 	width: 28px;
 	height: 28px;
-	transform: translateY(70px);
+	transform: translateY(80px);
 	color: var(--accent);
 }
 #splashSpinner > .spinner {
@@ -74,3 +74,17 @@ html {
 		transform: rotate(360deg);
 	}
 }
+
+#splashText {
+	position: absolute;
+	top: 0;
+	right: 0;
+	bottom: 0;
+	left: 0;
+	margin: auto;
+	display: inline-block;
+	width: 70%;
+	height: 0;
+	text-align: center;
+	transform: translateY(40px);
+}
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 5e6f6cedd6..c15e123a15 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -85,6 +85,9 @@ html
 			| Please turn on your JavaScript
 		div#splash
 			img#splashIcon(src= icon || '/static-assets/splash.png')
+			span#splashText
+				block randomMOTD
+					= randomMOTD
 			div#splashSpinner
 				<svg class="spinner bg" viewBox="0 0 152 152" xmlns="http://www.w3.org/2000/svg">
 					<g transform="matrix(1,0,0,1,12,12)">

From 6254954957f74238e060f4d02ab5b391925ddbfe Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 22 Dec 2023 21:50:40 +0900
Subject: [PATCH 312/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ddebf7d267..2c21684e77 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,8 +5,7 @@
 -
 
 ### Client
-- Fix: ページ一覧ページの表示がモバイル環境において崩れているのを修正
-- Fix: MFMでルビの中のテキストがnyaizeされない問題を修正
+- 
 
 ### Server
 -
@@ -91,6 +90,8 @@
 - Fix: DeepL TranslationのPro accountトグルスイッチが表示されていなかったのを修正
 - Fix: twitterの埋め込みカード内リンクからリンク先を開けない問題を修正
 - Fix: WebKitブラウザー上でも「デバイスの画面を常にオンにする」機能が効くように
+- Fix: ページ一覧ページの表示がモバイル環境において崩れているのを修正
+- Fix: MFMでルビの中のテキストがnyaizeされない問題を修正
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように

From 220344c5529b7448bb944e87c955d1d582a947c4 Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Sat, 23 Dec 2023 02:15:12 +0100
Subject: [PATCH 313/435] fix: email validation

---
 packages/backend/src/core/EmailService.ts        | 8 ++++----
 packages/backend/src/server/FileServerService.ts | 4 +---
 2 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts
index f31cec2b3a..05d120a8db 100644
--- a/packages/backend/src/core/EmailService.ts
+++ b/packages/backend/src/core/EmailService.ts
@@ -167,18 +167,18 @@ export class EmailService {
 		const verifymailApi = meta.enableVerifymailApi && meta.verifymailAuthKey != null;
 		let validated;
 
-		if (meta.enableActiveEmailValidation && meta.verifymailAuthKey) {
-			if (verifymailApi) {
+		if (meta.enableActiveEmailValidation) {
+			if (verifymailApi && meta.verifymailAuthKey) {
 				validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey);
 			} else {
-				validated = meta.enableActiveEmailValidation ? await validateEmail({
+				validated = await validateEmail({
 					email: emailAddress,
 					validateRegex: true,
 					validateMx: true,
 					validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので
 					validateDisposable: true, // 捨てアドかどうかチェック
 					validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので
-				}) : { valid: true, reason: null };
+				});
 			}
 		} else {
 			validated = { valid: true, reason: null };
diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts
index 0c7fc8cefe..e82ef64dc4 100644
--- a/packages/backend/src/server/FileServerService.ts
+++ b/packages/backend/src/server/FileServerService.ts
@@ -61,9 +61,7 @@ export class FileServerService {
 	public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
 		fastify.addHook('onRequest', (request, reply, done) => {
 			reply.header('Content-Security-Policy', 'default-src \'none\'; img-src \'self\'; media-src \'self\'; style-src \'unsafe-inline\'');
-			if (process.env.NODE_ENV === 'development') {
-				reply.header('Access-Control-Allow-Origin', '*');
-			}
+			reply.header('Access-Control-Allow-Origin', '*');
 			done();
 		});
 

From b3c4f7eddc4d97e15077f1e9041c5abecc184afb Mon Sep 17 00:00:00 2001
From: Nya Candy <dev@candinya.com>
Date: Sat, 23 Dec 2023 10:00:14 +0800
Subject: [PATCH 314/435] fix: email verify enable logic (#12743)

---
 packages/backend/src/core/EmailService.ts | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts
index f31cec2b3a..d4508d5313 100644
--- a/packages/backend/src/core/EmailService.ts
+++ b/packages/backend/src/core/EmailService.ts
@@ -167,18 +167,18 @@ export class EmailService {
 		const verifymailApi = meta.enableVerifymailApi && meta.verifymailAuthKey != null;
 		let validated;
 
-		if (meta.enableActiveEmailValidation && meta.verifymailAuthKey) {
+		if (meta.enableActiveEmailValidation) {
 			if (verifymailApi) {
 				validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey);
 			} else {
-				validated = meta.enableActiveEmailValidation ? await validateEmail({
+				validated = await validateEmail({
 					email: emailAddress,
 					validateRegex: true,
 					validateMx: true,
 					validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので
 					validateDisposable: true, // 捨てアドかどうかチェック
 					validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので
-				}) : { valid: true, reason: null };
+				});
 			}
 		} else {
 			validated = { valid: true, reason: null };

From 5b5a537f567a7a2cbce008ac19aaaea372dd4695 Mon Sep 17 00:00:00 2001
From: GrapeApple0 <84321396+GrapeApple0@users.noreply.github.com>
Date: Sat, 23 Dec 2023 12:06:22 +0900
Subject: [PATCH 315/435] =?UTF-8?q?feat:=20=E7=99=BB=E9=8C=B2=E3=82=92?=
 =?UTF-8?q?=E6=8B=92=E5=90=A6=E3=81=99=E3=82=8B=E3=83=A1=E3=83=BC=E3=83=AB?=
 =?UTF-8?q?=E3=82=A2=E3=83=89=E3=83=AC=E3=82=B9=E3=81=AE=E3=83=89=E3=83=A1?=
 =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=92=E6=89=8B=E5=8B=95=E3=81=A7=E8=A8=AD?=
 =?UTF-8?q?=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?=
 =?UTF-8?q?=20(#12740)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* feat: 使い捨てアドレスのドメインを手動で設定できるように

* Update CHANGELOG.md

* disposableEmailDomains -> bannedEmailDomains

* isBlockedHostを使うように
---
 CHANGELOG.md                                   |  1 +
 locales/index.d.ts                             |  1 +
 locales/ja-JP.yml                              |  1 +
 .../1703209889304-bannedEmailDomains.js        | 18 ++++++++++++++++++
 packages/backend/src/core/EmailService.ts      | 10 ++++++++--
 packages/backend/src/models/Meta.ts            |  7 +++++++
 .../src/server/api/endpoints/admin/meta.ts     |  9 +++++++++
 .../server/api/endpoints/admin/update-meta.ts  |  5 +++++
 .../src/components/MkSignupDialog.form.vue     |  4 +++-
 packages/frontend/src/pages/admin/security.vue | 15 +++++++++++++++
 10 files changed, 68 insertions(+), 3 deletions(-)
 create mode 100644 packages/backend/migration/1703209889304-bannedEmailDomains.js

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2c21684e77..71a90620e2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -94,6 +94,7 @@
 - Fix: MFMでルビの中のテキストがnyaizeされない問題を修正
 
 ### Server
+- Feat: 使い捨てメールのドメインを手動で設定できるように
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
 - Enhance: Meilisearchを有効にした検索で、ユーザーのミュートやブロックを考慮するように
 - Enhance: カスタム絵文字のインポート時の動作を改善
diff --git a/locales/index.d.ts b/locales/index.d.ts
index fd96fd7625..b3589082e1 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1745,6 +1745,7 @@ export interface Locale {
         "disposable": string;
         "mx": string;
         "smtp": string;
+        "banned": string;
     };
     "_ffVisibility": {
         "public": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 2c29bd20da..b59fb6e749 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1652,6 +1652,7 @@ _emailUnavailable:
   disposable: "恒久的に使用可能なアドレスではありません"
   mx: "正しいメールサーバーではありません"
   smtp: "メールサーバーが応答しません"
+  banned: "このメールアドレスでは登録できません"
 
 _ffVisibility:
   public: "公開"
diff --git a/packages/backend/migration/1703209889304-bannedEmailDomains.js b/packages/backend/migration/1703209889304-bannedEmailDomains.js
new file mode 100644
index 0000000000..5dc99c138f
--- /dev/null
+++ b/packages/backend/migration/1703209889304-bannedEmailDomains.js
@@ -0,0 +1,18 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class bannedEmailDomains1703209889304 {
+		constructor() {
+				this.name = 'bannedEmailDomains1703209889304';
+		}
+
+		async up(queryRunner) {
+				await queryRunner.query(`ALTER TABLE "meta" ADD "bannedEmailDomains" character varying(1024) array NOT NULL DEFAULT '{}'`);
+		}
+
+		async down(queryRunner) {
+				await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "bannedEmailDomains"`);
+		}
+}
diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts
index d4508d5313..6107b9601c 100644
--- a/packages/backend/src/core/EmailService.ts
+++ b/packages/backend/src/core/EmailService.ts
@@ -9,6 +9,7 @@ import { Inject, Injectable } from '@nestjs/common';
 import { validate as validateEmail } from 'deep-email-validator';
 import { SubOutputFormat } from 'deep-email-validator/dist/output/output.js';
 import { MetaService } from '@/core/MetaService.js';
+import { UtilityService } from '@/core/UtilityService.js';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import type Logger from '@/logger.js';
@@ -30,6 +31,7 @@ export class EmailService {
 
 		private metaService: MetaService,
 		private loggerService: LoggerService,
+		private utilityService: UtilityService,
 		private httpRequestService: HttpRequestService,
 	) {
 		this.logger = this.loggerService.getLogger('email');
@@ -155,7 +157,7 @@ export class EmailService {
 	@bindThis
 	public async validateEmailForAccount(emailAddress: string): Promise<{
 		available: boolean;
-		reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp';
+		reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | 'banned';
 	}> {
 		const meta = await this.metaService.fetch();
 
@@ -184,12 +186,16 @@ export class EmailService {
 			validated = { valid: true, reason: null };
 		}
 
-		const available = exist === 0 && validated.valid;
+		const emailDomain: string = emailAddress.split('@')[1];
+		const isBanned = this.utilityService.isBlockedHost(meta.bannedEmailDomains, emailDomain);
+
+		const available = exist === 0 && validated.valid && !isBanned;
 
 		return {
 			available,
 			reason: available ? null :
 			exist !== 0 ? 'used' :
+			isBanned ? 'banned' :
 			validated.reason === 'regex' ? 'format' :
 			validated.reason === 'disposable' ? 'disposable' :
 			validated.reason === 'mx' ? 'mx' :
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 83e8962f5d..84ca762492 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -495,6 +495,13 @@ export class MiMeta {
 	})
 	public manifestJsonOverride: string;
 
+	@Column('varchar', {
+		length: 1024,
+		array: true,
+		default: '{}',
+	})
+	public bannedEmailDomains: string[];
+
 	@Column('varchar', {
 		length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }',
 	})
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 07912154bd..6f8494d1d0 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -145,6 +145,14 @@ export const meta = {
 					type: 'string',
 				},
 			},
+			bannedEmailDomains: {
+				type: 'array',
+				optional: true, nullable: false,
+				items: {
+					type: 'string',
+					optional: false, nullable: false,
+				},
+			},
 			preservedUsernames: {
 				type: 'array',
 				optional: false, nullable: false,
@@ -513,6 +521,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				enableChartsForFederatedInstances: instance.enableChartsForFederatedInstances,
 				enableServerMachineStats: instance.enableServerMachineStats,
 				enableIdenticonGeneration: instance.enableIdenticonGeneration,
+				bannedEmailDomains: instance.bannedEmailDomains,
 				policies: { ...DEFAULT_POLICIES, ...instance.policies },
 				manifestJsonOverride: instance.manifestJsonOverride,
 				enableFanoutTimeline: instance.enableFanoutTimeline,
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 293a95a9a4..5f9de0523e 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -122,6 +122,7 @@ export const paramDef = {
 		enableServerMachineStats: { type: 'boolean' },
 		enableIdenticonGeneration: { type: 'boolean' },
 		serverRules: { type: 'array', items: { type: 'string' } },
+		bannedEmailDomains: { type: 'array', items: { type: 'string' } },
 		preservedUsernames: { type: 'array', items: { type: 'string' } },
 		manifestJsonOverride: { type: 'string' },
 		enableFanoutTimeline: { type: 'boolean' },
@@ -526,6 +527,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				set.notesPerOneAd = ps.notesPerOneAd;
 			}
 
+			if (ps.bannedEmailDomains !== undefined) {
+				set.bannedEmailDomains = ps.bannedEmailDomains;
+			}
+
 			const before = await this.metaService.fetch(true);
 
 			await this.metaService.update(set);
diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue
index dd05a44e04..f171e449c8 100644
--- a/packages/frontend/src/components/MkSignupDialog.form.vue
+++ b/packages/frontend/src/components/MkSignupDialog.form.vue
@@ -38,6 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					<span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span>
 					<span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span>
 					<span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span>
+					<span v-else-if="emailState === 'unavailable:banned'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.banned }}</span>
 					<span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span>
 					<span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span>
 					<span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span>
@@ -110,7 +111,7 @@ const retypedPassword = ref<string>('');
 const invitationCode = ref<string>('');
 const email = ref('');
 const usernameState = ref<null | 'wait' | 'ok' | 'unavailable' | 'error' | 'invalid-format' | 'min-range' | 'max-range'>(null);
-const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error'>(null);
+const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:format' | 'unavailable:disposable' | 'unavailable:banned' | 'unavailable:mx' | 'unavailable:smtp' | 'unavailable' | 'error'>(null);
 const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>('');
 const passwordRetypeState = ref<null | 'match' | 'not-match'>(null);
 const submitting = ref<boolean>(false);
@@ -209,6 +210,7 @@ function onChangeEmail(): void {
 			result.reason === 'used' ? 'unavailable:used' :
 			result.reason === 'format' ? 'unavailable:format' :
 			result.reason === 'disposable' ? 'unavailable:disposable' :
+			result.reason === 'banned' ? 'unavailable:banned' :
 			result.reason === 'mx' ? 'unavailable:mx' :
 			result.reason === 'smtp' ? 'unavailable:smtp' :
 			'unavailable';
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index 9835591fa8..bda29cee58 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -83,6 +83,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 					</div>
 				</MkFolder>
 
+				<MkFolder>
+					<template #label>Banned Email Domains</template>
+
+					<div class="_gaps_m">
+						<MkTextarea v-model="bannedEmailDomains">
+							<template #label>Banned Email Domains List</template>
+						</MkTextarea>
+						<MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
+					</div>
+				</MkFolder>
+
 				<MkFolder>
 					<template #label>Log IP address</template>
 					<template v-if="enableIpLogging" #suffix>Enabled</template>
@@ -124,6 +135,7 @@ import FormSuspense from '@/components/form/suspense.vue';
 import MkRange from '@/components/MkRange.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
 import * as os from '@/os.js';
 import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
@@ -141,6 +153,7 @@ const enableIpLogging = ref<boolean>(false);
 const enableActiveEmailValidation = ref<boolean>(false);
 const enableVerifymailApi = ref<boolean>(false);
 const verifymailAuthKey = ref<string | null>(null);
+const bannedEmailDomains = ref<string>('');
 
 async function init() {
 	const meta = await os.api('admin/meta');
@@ -161,6 +174,7 @@ async function init() {
 	enableActiveEmailValidation.value = meta.enableActiveEmailValidation;
 	enableVerifymailApi.value = meta.enableVerifymailApi;
 	verifymailAuthKey.value = meta.verifymailAuthKey;
+	bannedEmailDomains.value = meta.bannedEmailDomains.join('\n');
 }
 
 function save() {
@@ -180,6 +194,7 @@ function save() {
 		enableActiveEmailValidation: enableActiveEmailValidation.value,
 		enableVerifymailApi: enableVerifymailApi.value,
 		verifymailAuthKey: verifymailAuthKey.value,
+		bannedEmailDomains: bannedEmailDomains.value.split('\n'),
 	}).then(() => {
 		fetchInstance();
 	});

From 2c7d07bca6a6b6a3390674c5fcc3b618092b2507 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Dec 2023 12:15:10 +0900
Subject: [PATCH 316/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 71a90620e2..d131077bcb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,7 @@
 - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)
 - Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
 - Feat: TL上からノートが見えなくなるワードミュートであるハードミュートを追加
+- Enhance: 指定したドメインのメールアドレスの登録を弾くことができるように
 - Enhance: 公開ロールにアサインされたときに通知が作成されるように
 - Enhance: アイコンデコレーションを複数設定できるように
 - Enhance: アイコンデコレーションの位置を微調整できるように
@@ -94,7 +95,6 @@
 - Fix: MFMでルビの中のテキストがnyaizeされない問題を修正
 
 ### Server
-- Feat: 使い捨てメールのドメインを手動で設定できるように
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
 - Enhance: Meilisearchを有効にした検索で、ユーザーのミュートやブロックを考慮するように
 - Enhance: カスタム絵文字のインポート時の動作を改善

From 6c8f994e6f61f2fe75b1fc8b315e338f1bde0eac Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Sat, 23 Dec 2023 04:22:47 +0100
Subject: [PATCH 317/435] chore: update locale

Closes #250
---
 locales/en-US.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 84121f2d0b..888e26adbc 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -2207,7 +2207,7 @@ _notification:
   sendTestNotification: "Send test notification"
   notificationWillBeDisplayedLikeThis: "Notifications look like this"
   reactedBySomeUsers: "{n} users reacted"
-  renotedBySomeUsers: "Renote from {n} users"
+  renotedBySomeUsers: "Boosted by {n} users"
   followedBySomeUsers: "Followed by {n} users"
   _types:
     all: "All"

From 166a940f024c7a6de28c21de914df68ca3417626 Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Sat, 23 Dec 2023 04:26:23 +0100
Subject: [PATCH 318/435] chore: lint

---
 packages/frontend/src/components/MkSignupDialog.vue | 2 +-
 packages/frontend/src/pages/instance-info.vue       | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/src/components/MkSignupDialog.vue b/packages/frontend/src/components/MkSignupDialog.vue
index c8020c6636..6efdced69f 100644
--- a/packages/frontend/src/components/MkSignupDialog.vue
+++ b/packages/frontend/src/components/MkSignupDialog.vue
@@ -65,7 +65,7 @@ function onSignupEmailPending() {
 }
 
 function onApprovalPending() {
-	dialog.close();
+	dialog.value.close();
 }
 </script>
 
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 3e1bc31df7..683a31c36d 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -206,10 +206,10 @@ async function toggleSuspend(): Promise<void> {
 }
 
 async function toggleNSFW(): Promise<void> {
-	if (!instance) throw new Error('No instance?');
+	if (!instance.value) throw new Error('No instance?');
 	await os.api('admin/federation/update-instance', {
-		host: instance.host,
-		isNSFW: isNSFW,
+		host: instance.value.host,
+		isNSFW: isNSFW.value,
 	});
 }
 

From 98734af9a7d9365608a8e84995eb34fdfdfb1dfe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 23 Dec 2023 14:30:39 +0900
Subject: [PATCH 319/435] =?UTF-8?q?fix:=202023.12.0=E3=81=AENote=E3=81=AE?=
 =?UTF-8?q?=E4=B8=80=E9=83=A8=E6=96=87=E8=A8=80=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(#12754)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d131077bcb..0f864acfee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,12 +20,12 @@
 - 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。  
 
 	**影響:**  
-	それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された投稿用のピン留め絵文字が使われるため)。   
+	それにより、投稿フォームから表示される絵文字ピッカーのピン留め絵文字がリセットされたように感じるかもしれません(新設された"ピン留め(全般)"の設定が使われるため)。   
 	投稿用のピン留め絵文字をアップデート前の状態にするには、以下の手順で操作します。
 
 	1. 「設定」メニューに移動し、「絵文字ピッカー」タブを選択します。
 	2. 「ピン留 (全般)」のタブを選択します。
-	3. 「リアクション設定からコピーする」ボタンを押すことで、アップデート前の状態に戻すことができます。
+	3. 「リアクション設定から上書きする」ボタンを押すことで、アップデート前の状態に戻すことができます。
 
 ### General
 - Feat: メールアドレスの認証にverifymail.ioを使えるように (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/971ba07a44550f68d2ba31c62066db2d43a0caed)

From 6e4894c1656d283906b679866923fddab2b146bf Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Dec 2023 14:39:01 +0900
Subject: [PATCH 320/435] lint

---
 packages/backend/src/core/EmailService.ts | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts
index 6107b9601c..3a61e353f1 100644
--- a/packages/backend/src/core/EmailService.ts
+++ b/packages/backend/src/core/EmailService.ts
@@ -166,11 +166,10 @@ export class EmailService {
 			email: emailAddress,
 		});
 
-		const verifymailApi = meta.enableVerifymailApi && meta.verifymailAuthKey != null;
 		let validated;
 
 		if (meta.enableActiveEmailValidation) {
-			if (verifymailApi) {
+			if (meta.enableVerifymailApi && meta.verifymailAuthKey != null) {
 				validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey);
 			} else {
 				validated = await validateEmail({

From 1716c6562c86c20aaba734b427913fc6a6abd67c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sat, 23 Dec 2023 15:32:31 +0900
Subject: [PATCH 321/435] =?UTF-8?q?fix:=20.npmrc=E3=81=AB=E3=82=88?=
 =?UTF-8?q?=E3=82=8Apackage.json=E8=A8=98=E8=BC=89=E3=81=AEnode=E3=83=90?=
 =?UTF-8?q?=E3=83=BC=E3=82=B8=E3=83=A7=E3=83=B3=E3=81=AB=E6=BA=80=E3=81=9F?=
 =?UTF-8?q?=E3=81=AA=E3=81=84=E5=A0=B4=E5=90=88=E3=81=AF=E3=83=93=E3=83=AB?=
 =?UTF-8?q?=E3=83=89=E3=81=AB=E5=A4=B1=E6=95=97=E3=81=99=E3=82=8B=E3=82=88?=
 =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#12755)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .npmrc | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 .npmrc

diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000000..c42da845b4
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+engine-strict = true

From 30cf5c3ab09717829a2e49a6afe14fe6478140dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=9A=90=E6=9C=88=E3=81=AA=E3=81=B5=20=28Nafu=20Satsuki?=
 =?UTF-8?q?=29?= <satsuki@nafusoft.dev>
Date: Sat, 23 Dec 2023 15:32:53 +0900
Subject: [PATCH 322/435] =?UTF-8?q?chore(frontend):=20API=E8=A8=AD?=
 =?UTF-8?q?=E5=AE=9A=E9=A0=85=E7=9B=AE=E3=81=AE=E5=90=8D=E5=89=8D=E3=82=92?=
 =?UTF-8?q?=E3=81=8D=E3=81=A1=E3=82=93=E3=81=A8=E3=82=B5=E3=83=BC=E3=83=93?=
 =?UTF-8?q?=E3=82=B9=E3=81=AE=E5=90=8D=E5=89=8D=E3=81=A7=E8=A1=A8=E8=A8=98?=
 =?UTF-8?q?=E3=81=99=E3=82=8B=20(#12753)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/frontend/src/pages/admin/security.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index bda29cee58..7070157ca9 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -74,11 +74,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<template #label>Enable</template>
 						</MkSwitch>
 						<MkSwitch v-model="enableVerifymailApi" @update:modelValue="save">
-							<template #label>Use Verifymail API</template>
+							<template #label>Use Verifymail.io API</template>
 						</MkSwitch>
 						<MkInput v-model="verifymailAuthKey" @update:modelValue="save">
 							<template #prefix><i class="ti ti-key"></i></template>
-							<template #label>Verifymail API Auth Key</template>
+							<template #label>Verifymail.io API Auth Key</template>
 						</MkInput>
 					</div>
 				</MkFolder>

From 59b47b862340f772a51e5d8e8cbaab5f1c4e53f5 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Dec 2023 16:40:31 +0900
Subject: [PATCH 323/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f864acfee..c7afb101db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,7 +15,7 @@
 ## 2023.12.0
 
 ### Note
-- Node.js 20.10.0が最小要件になりました
+- 依存関係の更新に伴い、Node.js 20.10.0が最小要件になりました
 - 絵文字の追加辞書を既にインストールしている場合は、お手数ですが再インストールのほどお願いします
 - 絵文字ピッカーにピン留め表示する絵文字設定が「リアクション用」と「絵文字入力用」に分かれました。以前の設定は「リアクション用」として使用されます。  
 

From 8caf2b0a4ac771d4568f1549bdce850de2af7777 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Dec 2023 16:43:06 +0900
Subject: [PATCH 324/435] New Crowdin updates (#12748)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Chinese Traditional)
---
 locales/fr-FR.yml | 1 +
 locales/ko-KR.yml | 1 +
 locales/zh-TW.yml | 1 +
 3 files changed, 3 insertions(+)

diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index e12b508617..43cc1d45b6 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -1917,6 +1917,7 @@ _notification:
     pollEnded: "Sondages se cloturant"
     receiveFollowRequest: "Demande d'abonnement reçue"
     followRequestAccepted: "Demande d'abonnement acceptée"
+    roleAssigned: "Rôle reçu"
     achievementEarned: "Accomplissement"
     app: "Notifications provenant des apps"
   _actions:
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index d8efa7f04e..6cdcc2c246 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -2193,6 +2193,7 @@ _notification:
     pollEnded: "투표가 종료됨"
     receiveFollowRequest: "팔로우 요청을 받았을 때"
     followRequestAccepted: "팔로우 요청이 승인되었을 때"
+    roleAssigned: "역할이 부여 됨"
     achievementEarned: "도전 과제 획득"
     app: "연동된 앱을 통한 알림"
   _actions:
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index d05691d42e..51ba42e66c 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -2193,6 +2193,7 @@ _notification:
     pollEnded: "問卷調查結束"
     receiveFollowRequest: "已收到追隨請求"
     followRequestAccepted: "追隨請求已接受"
+    roleAssigned: "已授予角色"
     achievementEarned: "獲得成就"
     app: "應用程式通知"
   _actions:

From f43599552fb5764aa3121b083e441d3946c72cd8 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Dec 2023 17:54:29 +0900
Subject: [PATCH 325/435] =?UTF-8?q?fix(backend):=20renote=E5=88=A4?=
 =?UTF-8?q?=E5=AE=9A=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/backend/src/core/NoteCreateService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 54493612b8..c4fc51847b 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -293,7 +293,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 		}
 
 		// Check blocking
-		if (this.isQuote(data)) {
+		if (data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0)) {
 			if (data.renote.userHost === null) {
 				if (data.renote.userId !== user.id) {
 					const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id);

From e852f4b60d48edc5b28e6db104ca6a88dd678740 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Dec 2023 17:55:27 +0900
Subject: [PATCH 326/435] =?UTF-8?q?Revert=20"fix(backend):=20renote?=
 =?UTF-8?q?=E5=88=A4=E5=AE=9A=E3=81=8C=E3=81=8A=E3=81=8B=E3=81=97=E3=81=84?=
 =?UTF-8?q?"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This reverts commit f43599552fb5764aa3121b083e441d3946c72cd8.
---
 packages/backend/src/core/NoteCreateService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index c4fc51847b..54493612b8 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -293,7 +293,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 		}
 
 		// Check blocking
-		if (data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0)) {
+		if (this.isQuote(data)) {
 			if (data.renote.userHost === null) {
 				if (data.renote.userId !== user.id) {
 					const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id);

From 2f425aa03f705bc886711bc3b61f6fdd5f014f0b Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sat, 23 Dec 2023 17:55:34 +0900
Subject: [PATCH 327/435] =?UTF-8?q?fix:=20=E3=83=96=E3=83=AD=E3=83=83?=
 =?UTF-8?q?=E3=82=AF=E3=81=95=E3=82=8C=E3=81=A6=E3=81=A6=E3=82=82pure=20RN?=
 =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=20(#12758)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

なぜかわからないけど元々Quoteはできるようなのでそれに戻しました
---
 packages/backend/src/core/NoteCreateService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 54493612b8..2bdff872ad 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -293,7 +293,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 		}
 
 		// Check blocking
-		if (this.isQuote(data)) {
+		if (data.renote && !this.isQuote(data)) {
 			if (data.renote.userHost === null) {
 				if (data.renote.userId !== user.id) {
 					const blocked = await this.userBlockingService.checkBlocked(data.renote.userId, user.id);

From 471c8ec0509741cd1c813535aa0e751f85b64a7c Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 23 Dec 2023 19:59:27 +0900
Subject: [PATCH 328/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c7afb101db..ac31bc0d28 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -73,6 +73,7 @@
 - Enhance: チャンネルに新規の投稿がある場合にバッジを表示させる
 - Enhance: サウンド設定に「サウンドを出力しない」と「Misskeyがアクティブな時のみサウンドを出力する」を追加
 - Enhance: 設定したタグをトレンドに表示させないようにする項目を管理画面で設定できるように
+- Enhance: 絵文字ピッカーのカテゴリに「/」を入れることでフォルダ分け表示できるように
 - Fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 - Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 - Fix: コードエディタが正しく表示されない問題を修正
@@ -243,7 +244,6 @@
 ### Client
 - Enhance: TLの返信表示オプションを記憶するように
 - Enhance: 投稿されてから時間が経過しているノートであることを視覚的に分かりやすく
-- Feat: 絵文字ピッカーのカテゴリに「/」を入れることでフォルダ分け表示できるように
 
 ### Server
 - Enhance: タイムライン取得時のパフォーマンスを向上

From 683b4aafb2928f2d8dfa626352584542e3df8e3a Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Tue, 19 Dec 2023 09:07:32 +0000
Subject: [PATCH 329/435] real-time updates on note detail view

`useNoteCapture` already subscribes to all updates for a note, so
we can tell it when a note gets replied to, too

Since I'm not actually adding any extra subscription in the client,
just an extra callback, there should be no overhead when replies are
not coming in.

Also, all the timelines already call `useNoteCapture` for each note
displayed, so we know the whole `GlobalEventService` thing works fine.

Many thanks to VueJS for taking care of all the DOM complications
---
 packages/backend/src/core/GlobalEventService.ts     |  3 +++
 packages/backend/src/core/NoteCreateService.ts      |  3 +++
 packages/frontend/src/components/MkNoteDetailed.vue |  5 +++++
 packages/frontend/src/components/MkNoteSub.vue      |  9 +++++++--
 packages/frontend/src/components/SkNoteDetailed.vue |  5 +++++
 packages/frontend/src/components/SkNoteSub.vue      |  9 +++++++--
 packages/frontend/src/scripts/use-note-capture.ts   | 12 ++++++++++++
 7 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts
index d175f21f2f..95a4eba742 100644
--- a/packages/backend/src/core/GlobalEventService.ts
+++ b/packages/backend/src/core/GlobalEventService.ts
@@ -130,6 +130,9 @@ export interface NoteEventTypes {
 		reaction: string;
 		userId: MiUser['id'];
 	};
+	replied: {
+		id: MiNote['id'];
+	};
 }
 type NoteStreamEventTypes = {
 	[key in keyof NoteEventTypes]: {
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 0b06931213..6406bc4c50 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -780,6 +780,9 @@ export class NoteCreateService implements OnApplicationShutdown {
 
 			// If has in reply to note
 			if (data.reply) {
+				this.globalEventService.publishNoteStream(data.reply.id, 'replied', {
+					id: note.id,
+				});
 				// 通知
 				if (data.reply.userHost === null) {
 					const isThreadMuted = await this.noteThreadMutingsRepository.exist({
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index f29b9db6ae..ae9d8a0d6b 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -372,11 +372,16 @@ const reactionsPagination = computed(() => ({
 	},
 }));
 
+async function addReplyTo(note, replyNote: Misskey.entities.Note) {
+		replies.value.unshift(replyNote);
+}
+
 useNoteCapture({
 	rootEl: el,
 	note: appearNote,
 	pureNote: note,
 	isDeletedRef: isDeleted,
+	onReplyCallback: addReplyTo,
 });
 
 useTooltip(renoteButton, async (showing) => {
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 8d394c0c15..3c840cf598 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -132,6 +132,7 @@ const likeButton = shallowRef<HTMLElement>();
 
 let appearNote = computed(() => isRenote ? props.note.renote as Misskey.entities.Note : props.note);
 const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
+const replies = ref<Misskey.entities.Note[]>([]);
 
 const isRenote = (
 	props.note.renote != null &&
@@ -140,10 +141,16 @@ const isRenote = (
 	props.note.poll == null
 );
 
+async function addReplyTo(note, replyNote: Misskey.entities.Note) {
+		replies.value.unshift(replyNote);
+}
+
 useNoteCapture({
 	rootEl: el,
 	note: appearNote,
 	isDeletedRef: isDeleted,
+	// only update replies if we are, in fact, showing replies
+	onReplyCallback: props.detail && props.depth < numberOfReplies.value ? addReplyTo : undefined,
 });
 
 if ($i) {
@@ -250,8 +257,6 @@ watch(() => props.expandAllCws, (expandAllCws) => {
 	if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
 });
 
-let replies = ref<Misskey.entities.Note[]>([]);
-
 function boostVisibility() {
 	os.popupMenu([
 		{
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index 8bf9e244e0..ff2058d79f 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -380,11 +380,16 @@ const reactionsPagination = computed(() => ({
 	},
 }));
 
+async function addReplyTo(note, replyNote: Misskey.entities.Note) {
+		replies.value.unshift(replyNote);
+}
+
 useNoteCapture({
 	rootEl: el,
 	note: appearNote,
 	pureNote: note,
 	isDeletedRef: isDeleted,
+	onReplyCallback: addReplyTo,
 });
 
 useTooltip(renoteButton, async (showing) => {
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index fc30dc87aa..f4279fe8a1 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -141,6 +141,7 @@ const likeButton = shallowRef<HTMLElement>();
 
 let appearNote = computed(() => isRenote ? props.note.renote as Misskey.entities.Note : props.note);
 const defaultLike = computed(() => defaultStore.state.like ? defaultStore.state.like : null);
+const replies = ref<Misskey.entities.Note[]>([]);
 
 const isRenote = (
 	props.note.renote != null &&
@@ -149,10 +150,16 @@ const isRenote = (
 	props.note.poll == null
 );
 
+async function addReplyTo(note, replyNote: Misskey.entities.Note) {
+		replies.value.unshift(replyNote);
+}
+
 useNoteCapture({
 	rootEl: el,
 	note: appearNote,
 	isDeletedRef: isDeleted,
+	// only update replies if we are, in fact, showing replies
+	onReplyCallback: props.detail && props.depth < numberOfReplies.value ? addReplyTo : undefined,
 });
 
 if ($i) {
@@ -259,8 +266,6 @@ watch(() => props.expandAllCws, (expandAllCws) => {
 	if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
 });
 
-let replies = ref<Misskey.entities.Note[]>([]);
-
 function boostVisibility() {
 	os.popupMenu([
 		{
diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts
index ab232598cd..8692d056b0 100644
--- a/packages/frontend/src/scripts/use-note-capture.ts
+++ b/packages/frontend/src/scripts/use-note-capture.ts
@@ -14,6 +14,7 @@ export function useNoteCapture(props: {
 	note: Ref<Misskey.entities.Note>;
 	pureNote: Ref<Misskey.entities.Note>;
 	isDeletedRef: Ref<boolean>;
+	onReplyCallback: (note, replyNote: Misskey.entities.Note) => void | undefined;
 }) {
 	const note = props.note;
 	const pureNote = props.pureNote !== undefined ? props.pureNote : props.note;
@@ -25,6 +26,17 @@ export function useNoteCapture(props: {
 		if ((id !== note.value.id) && (id !== pureNote.value.id)) return;
 
 		switch (type) {
+			case 'replied': {
+				if (!props.onReplyCallback) break;
+
+				const replyNote = await os.api("notes/show", {
+					noteId: body.id,
+				});
+
+				await props.onReplyCallback(pureNote, replyNote);
+				break;
+			}
+
 			case 'reacted': {
 				const reaction = body.reaction;
 

From d06939bd25db133995f1eced8b5420abfd3cbcf0 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Tue, 19 Dec 2023 09:07:38 +0000
Subject: [PATCH 330/435] real-time update: hide deleted replies

---
 packages/frontend/src/components/MkNoteSub.vue | 2 +-
 packages/frontend/src/components/SkNoteSub.vue | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 3c840cf598..fd8904f3c2 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div v-if="!muted" ref="el" :class="[$style.root, { [$style.children]: depth > 1 }]">
+<div v-show="!isDeleted" v-if="!muted" ref="el" :class="[$style.root, { [$style.children]: depth > 1 }]">
 	<div :class="$style.main">
 		<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
 		<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index f4279fe8a1..b6e0cd4b35 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<div v-if="!muted" ref="el" :class="[$style.root, { [$style.children]: depth > 1 }]">
+<div v-show="!isDeleted" v-if="!muted" ref="el" :class="[$style.root, { [$style.children]: depth > 1 }]">
 	<div v-if="!hideLine" :class="$style.line"></div>
 	<div :class="$style.main">
 		<div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>

From 576a87118c18c9354f35ee3cb207d5e6bccdba91 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Thu, 21 Dec 2023 16:00:59 +0000
Subject: [PATCH 331/435] real-time update: adjust replyCount up/down

this also fixes the connecting lines in the Sk-style view

thanks @ShittyKopper for reporting the bug!

NOTE: at this point, the `isDeletedRef` boolean is pretty much
useless, because we're directly removing deleted notes from the
`replies` array and therefore from the DOM (we were just hiding them,
before); I'm intentionally not touching `isDeletedRef` to simplify
merges from upstream
---
 .../frontend/src/components/MkNoteDetailed.vue    | 13 +++++++++++--
 packages/frontend/src/components/MkNoteSub.vue    | 15 +++++++++++++--
 .../frontend/src/components/SkNoteDetailed.vue    | 13 +++++++++++--
 packages/frontend/src/components/SkNoteSub.vue    | 15 +++++++++++++--
 packages/frontend/src/scripts/use-note-capture.ts |  7 +++++--
 5 files changed, 53 insertions(+), 10 deletions(-)

diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index ae9d8a0d6b..1ff05fd318 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -170,7 +170,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div v-if="!repliesLoaded" style="padding: 16px">
 				<MkButton style="margin: 0 auto;" primary rounded @click="loadReplies">{{ i18n.ts.loadReplies }}</MkButton>
 			</div>
-			<MkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws"/>
+			<MkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply" />
 		</div>
 		<div v-else-if="tab === 'renotes'" :class="$style.tab_renotes">
 			<MkPagination :pagination="renotesPagination" :disableAutoLoad="true">
@@ -372,8 +372,17 @@ const reactionsPagination = computed(() => ({
 	},
 }));
 
-async function addReplyTo(note, replyNote: Misskey.entities.Note) {
+async function addReplyTo(replyNote: Misskey.entities.Note) {
 		replies.value.unshift(replyNote);
+		appearNote.repliesCount += 1;
+}
+
+async function removeReply(id: Misskey.entities.Note['id']) {
+		const replyIdx = replies.value.findIndex(note => note.id === id);
+		if (replyIdx >= 0) {
+			replies.value.splice(replyIdx, 1);
+			appearNote.repliesCount -= 1;
+		}
 }
 
 useNoteCapture({
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index fd8904f3c2..d1e45b30bb 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</div>
 	</div>
 	<template v-if="depth < numberOfReplies">
-		<MkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="$style.reply" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws"/>
+		<MkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="$style.reply" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply"/>
 	</template>
 	<div v-else :class="$style.more">
 		<MkA class="_link" :to="notePage(note)">{{ i18n.ts.continueThread }} <i class="ph-caret-double-right ph-bold ph-lg"></i></MkA>
@@ -110,6 +110,7 @@ const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
 	detail?: boolean;
 	expandAllCws?: boolean;
+	onDeleteCallback?: (id: Misskey.entities.Note['id']) => void;
 
 	// how many notes are in between this one and the note being viewed in detail
 	depth?: number;
@@ -141,8 +142,17 @@ const isRenote = (
 	props.note.poll == null
 );
 
-async function addReplyTo(note, replyNote: Misskey.entities.Note) {
+async function addReplyTo(replyNote: Misskey.entities.Note) {
 		replies.value.unshift(replyNote);
+		appearNote.repliesCount += 1;
+}
+
+async function removeReply(id: Misskey.entities.Note['id']) {
+		const replyIdx = replies.value.findIndex(note => note.id === id);
+		if (replyIdx >= 0) {
+			replies.value.splice(replyIdx, 1);
+			appearNote.repliesCount -= 1;
+		}
 }
 
 useNoteCapture({
@@ -151,6 +161,7 @@ useNoteCapture({
 	isDeletedRef: isDeleted,
 	// only update replies if we are, in fact, showing replies
 	onReplyCallback: props.detail && props.depth < numberOfReplies.value ? addReplyTo : undefined,
+	onDeleteCallback: props.detail && props.depth < numberOfReplies.value ? props.onDeleteCallback : undefined,
 });
 
 if ($i) {
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index ff2058d79f..06783f7fff 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -178,7 +178,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<div v-if="!repliesLoaded" style="padding: 16px">
 				<MkButton style="margin: 0 auto;" primary rounded @click="loadReplies">{{ i18n.ts.loadReplies }}</MkButton>
 			</div>
-			<SkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws"/>
+			<SkNoteSub v-for="note in replies" :key="note.id" :note="note" :class="$style.reply" :detail="true" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply" />
 		</div>
 		<div v-else-if="tab === 'renotes'" :class="$style.tab_renotes">
 			<MkPagination :pagination="renotesPagination" :disableAutoLoad="true">
@@ -380,8 +380,17 @@ const reactionsPagination = computed(() => ({
 	},
 }));
 
-async function addReplyTo(note, replyNote: Misskey.entities.Note) {
+async function addReplyTo(replyNote: Misskey.entities.Note) {
 		replies.value.unshift(replyNote);
+		appearNote.repliesCount += 1;
+}
+
+async function removeReply(id: Misskey.entities.Note['id']) {
+		const replyIdx = replies.value.findIndex(note => note.id === id);
+		if (replyIdx >= 0) {
+			replies.value.splice(replyIdx, 1);
+			appearNote.repliesCount -= 1;
+		}
 }
 
 useNoteCapture({
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index b6e0cd4b35..bee4074bd0 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -73,7 +73,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</div>
 	</div>
 	<template v-if="depth < numberOfReplies">
-		<SkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="[$style.reply, { [$style.single]: replies.length === 1 }]" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws"/>
+		<SkNoteSub v-for="reply in replies" :key="reply.id" :note="reply" :class="[$style.reply, { [$style.single]: replies.length === 1 }]" :detail="true" :depth="depth + 1" :expandAllCws="props.expandAllCws" :onDeleteCallback="removeReply"/>
 	</template>
 	<div v-else :class="$style.more">
 		<MkA class="_link" :to="notePage(note)">{{ i18n.ts.continueThread }} <i class="ph-caret-double-right ph-bold ph-lg"></i></MkA>
@@ -119,6 +119,7 @@ const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
 	detail?: boolean;
 	expandAllCws?: boolean;
+	onDeleteCallback?: (id: Misskey.entities.Note['id']) => void;
 
 	// how many notes are in between this one and the note being viewed in detail
 	depth?: number;
@@ -150,8 +151,17 @@ const isRenote = (
 	props.note.poll == null
 );
 
-async function addReplyTo(note, replyNote: Misskey.entities.Note) {
+async function addReplyTo(replyNote: Misskey.entities.Note) {
 		replies.value.unshift(replyNote);
+		appearNote.repliesCount += 1;
+}
+
+async function removeReply(id: Misskey.entities.Note['id']) {
+		const replyIdx = replies.value.findIndex(note => note.id === id);
+		if (replyIdx >= 0) {
+			replies.value.splice(replyIdx, 1);
+			appearNote.repliesCount -= 1;
+		}
 }
 
 useNoteCapture({
@@ -160,6 +170,7 @@ useNoteCapture({
 	isDeletedRef: isDeleted,
 	// only update replies if we are, in fact, showing replies
 	onReplyCallback: props.detail && props.depth < numberOfReplies.value ? addReplyTo : undefined,
+	onDeleteCallback: props.detail && props.depth < numberOfReplies.value ? props.onDeleteCallback : undefined,
 });
 
 if ($i) {
diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts
index 8692d056b0..427bc6ff36 100644
--- a/packages/frontend/src/scripts/use-note-capture.ts
+++ b/packages/frontend/src/scripts/use-note-capture.ts
@@ -14,7 +14,8 @@ export function useNoteCapture(props: {
 	note: Ref<Misskey.entities.Note>;
 	pureNote: Ref<Misskey.entities.Note>;
 	isDeletedRef: Ref<boolean>;
-	onReplyCallback: (note, replyNote: Misskey.entities.Note) => void | undefined;
+	onReplyCallback: (replyNote: Misskey.entities.Note) => void | undefined;
+	onDeleteCallback: (id: Misskey.entities.Note['id']) => void | undefined;
 }) {
 	const note = props.note;
 	const pureNote = props.pureNote !== undefined ? props.pureNote : props.note;
@@ -33,7 +34,7 @@ export function useNoteCapture(props: {
 					noteId: body.id,
 				});
 
-				await props.onReplyCallback(pureNote, replyNote);
+				await props.onReplyCallback(replyNote);
 				break;
 			}
 
@@ -88,6 +89,8 @@ export function useNoteCapture(props: {
 
 			case 'deleted': {
 				props.isDeletedRef.value = true;
+
+				if (props.onDeleteCallback) await props.onDeleteCallback(id);
 				break;
 			}
 

From fea6428245591ca710f718eaff5000194d5ed233 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Sat, 23 Dec 2023 14:47:14 +0000
Subject: [PATCH 332/435] add missing `.value`

---
 packages/frontend/src/components/MkNoteDetailed.vue | 4 ++--
 packages/frontend/src/components/MkNoteSub.vue      | 4 ++--
 packages/frontend/src/components/SkNoteDetailed.vue | 4 ++--
 packages/frontend/src/components/SkNoteSub.vue      | 4 ++--
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 1ff05fd318..a793a85ff9 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -374,14 +374,14 @@ const reactionsPagination = computed(() => ({
 
 async function addReplyTo(replyNote: Misskey.entities.Note) {
 		replies.value.unshift(replyNote);
-		appearNote.repliesCount += 1;
+		appearNote.value.repliesCount += 1;
 }
 
 async function removeReply(id: Misskey.entities.Note['id']) {
 		const replyIdx = replies.value.findIndex(note => note.id === id);
 		if (replyIdx >= 0) {
 			replies.value.splice(replyIdx, 1);
-			appearNote.repliesCount -= 1;
+			appearNote.value.repliesCount -= 1;
 		}
 }
 
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index d1e45b30bb..887a9b5c45 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -144,14 +144,14 @@ const isRenote = (
 
 async function addReplyTo(replyNote: Misskey.entities.Note) {
 		replies.value.unshift(replyNote);
-		appearNote.repliesCount += 1;
+		appearNote.value.repliesCount += 1;
 }
 
 async function removeReply(id: Misskey.entities.Note['id']) {
 		const replyIdx = replies.value.findIndex(note => note.id === id);
 		if (replyIdx >= 0) {
 			replies.value.splice(replyIdx, 1);
-			appearNote.repliesCount -= 1;
+			appearNote.value.repliesCount -= 1;
 		}
 }
 
diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index 06783f7fff..df0259a2c7 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -382,14 +382,14 @@ const reactionsPagination = computed(() => ({
 
 async function addReplyTo(replyNote: Misskey.entities.Note) {
 		replies.value.unshift(replyNote);
-		appearNote.repliesCount += 1;
+		appearNote.value.repliesCount += 1;
 }
 
 async function removeReply(id: Misskey.entities.Note['id']) {
 		const replyIdx = replies.value.findIndex(note => note.id === id);
 		if (replyIdx >= 0) {
 			replies.value.splice(replyIdx, 1);
-			appearNote.repliesCount -= 1;
+			appearNote.value.repliesCount -= 1;
 		}
 }
 
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index bee4074bd0..5928b2676c 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -153,14 +153,14 @@ const isRenote = (
 
 async function addReplyTo(replyNote: Misskey.entities.Note) {
 		replies.value.unshift(replyNote);
-		appearNote.repliesCount += 1;
+		appearNote.value.repliesCount += 1;
 }
 
 async function removeReply(id: Misskey.entities.Note['id']) {
 		const replyIdx = replies.value.findIndex(note => note.id === id);
 		if (replyIdx >= 0) {
 			replies.value.splice(replyIdx, 1);
-			appearNote.repliesCount -= 1;
+			appearNote.value.repliesCount -= 1;
 		}
 }
 

From 6526968f2d91ce9a22dfae8d43690031469f2e14 Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Sat, 23 Dec 2023 16:08:04 +0100
Subject: [PATCH 333/435] fix: check

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

diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts
index 4fce64606f..3a61e353f1 100644
--- a/packages/backend/src/core/EmailService.ts
+++ b/packages/backend/src/core/EmailService.ts
@@ -169,7 +169,7 @@ export class EmailService {
 		let validated;
 
 		if (meta.enableActiveEmailValidation) {
-			if (verifymailApi && meta.verifymailAuthKey) {
+			if (meta.enableVerifymailApi && meta.verifymailAuthKey != null) {
 				validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey);
 			} else {
 				validated = await validateEmail({

From e5ea882ed74b3cdd0e86f4dda5a8ce724f30d2ef Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Wed, 20 Dec 2023 12:17:59 +0000
Subject: [PATCH 334/435] authorized fetch #217

the implementation is copied from the other places we already check
HTTP signatures, and cross-checked with Firefish's implementation
---
 .config/example.yml                           |   2 +
 chart/files/default.yml                       |   2 +
 packages/backend/src/config.ts                |   3 +
 .../src/server/ActivityPubServerService.ts    | 129 ++++++++++++++++++
 4 files changed, 136 insertions(+)

diff --git a/.config/example.yml b/.config/example.yml
index 4b0d07ae85..28fe5b359d 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -212,6 +212,8 @@ proxyRemoteFiles: true
 
 # Sign to ActivityPub GET request (default: true)
 signToActivityPubGet: true
+# check that inbound ActivityPub GET requests are signed ("authorized fetch")
+checkActivityPubGetSignature: false
 
 # For security reasons, uploading attachments from the intranet is prohibited,
 # but exceptions can be made from the following settings. Default value is "undefined". 
diff --git a/chart/files/default.yml b/chart/files/default.yml
index 4cc291e80a..9c81964736 100644
--- a/chart/files/default.yml
+++ b/chart/files/default.yml
@@ -194,6 +194,8 @@ id: "aidx"
 
 # Sign to ActivityPub GET request (default: true)
 signToActivityPubGet: true
+# check that inbound ActivityPub GET requests are signed ("authorized fetch")
+checkActivityPubGetSignature: false
 
 #allowedPrivateNetworks: [
 #  '127.0.0.1/32'
diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index dceeac4691..4a4d7e8311 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -88,6 +88,7 @@ type Source = {
 	customMOTD?: string[];
 
 	signToActivityPubGet?: boolean;
+	checkActivityPubGetSignature?: boolean;
 
 	perChannelMaxNoteCacheCount?: number;
 	perUserNotificationsMaxCount?: number;
@@ -146,6 +147,7 @@ export type Config = {
 	proxyRemoteFiles: boolean | undefined;
 	customMOTD: string[] | undefined;
 	signToActivityPubGet: boolean | undefined;
+	checkActivityPubGetSignature: boolean | undefined;
 
 	version: string;
 	host: string;
@@ -253,6 +255,7 @@ export function loadConfig(): Config {
 		proxyRemoteFiles: config.proxyRemoteFiles,
 		customMOTD: config.customMOTD,
 		signToActivityPubGet: config.signToActivityPubGet,
+		checkActivityPubGetSignature: config.checkActivityPubGetSignature,
 		mediaProxy: externalMediaProxy ?? internalMediaProxy,
 		externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy,
 		videoThumbnailGenerator: config.videoThumbnailGenerator ?
diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 68e426b5bc..a0ad4a134f 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -5,6 +5,7 @@
 
 import * as crypto from 'node:crypto';
 import { IncomingMessage } from 'node:http';
+import { format as formatURL } from 'node:url';
 import { Inject, Injectable } from '@nestjs/common';
 import fastifyAccepts from '@fastify/accepts';
 import httpSignature from '@peertube/http-signature';
@@ -17,9 +18,13 @@ import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReact
 import * as url from '@/misc/prelude/url.js';
 import type { Config } from '@/config.js';
 import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
+import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js';
 import { QueueService } from '@/core/QueueService.js';
 import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js';
+import { MetaService } from '@/core/MetaService.js';
 import { UserKeypairService } from '@/core/UserKeypairService.js';
+import { InstanceActorService } from '@/core/InstanceActorService.js';
+import type { MiUserPublickey } from '@/models/UserPublickey.js';
 import type { MiFollowing } from '@/models/Following.js';
 import { countIf } from '@/misc/prelude/array.js';
 import type { MiNote } from '@/models/Note.js';
@@ -65,9 +70,12 @@ export class ActivityPubServerService {
 		@Inject(DI.followRequestsRepository)
 		private followRequestsRepository: FollowRequestsRepository,
 
+		private metaService: MetaService,
 		private utilityService: UtilityService,
 		private userEntityService: UserEntityService,
+		private instanceActorService: InstanceActorService,
 		private apRendererService: ApRendererService,
+		private apDbResolverService: ApDbResolverService,
 		private queueService: QueueService,
 		private userKeypairService: UserKeypairService,
 		private queryService: QueryService,
@@ -99,6 +107,101 @@ export class ActivityPubServerService {
 		return this.apRendererService.renderCreate(await this.apRendererService.renderNote(note, false), note);
 	}
 
+	@bindThis
+	private async shouldRefuseGetRequest(request: FastifyRequest, reply: FastifyReply, userId: string | undefined = undefined): Promise<boolean> {
+		if (!this.config.checkActivityPubGetSignature) return false;
+
+		/* this code is inspired from the `inbox` function below, and
+			 `queue/processors/InboxProcessorService`
+
+			 those pieces of code also check `digest`, and various bits from the
+			 request body, but that only makes sense for requests with a body:
+			 here we're validating GET requests
+
+			 this is also inspired by FireFish's `checkFetch`
+		*/
+
+		/* we always allow requests about our instance actor, because when
+			 a remote instance needs to check our signature on a request we
+			 sent, it will need to fetch information about the user that
+			 signed it (which is our instance actor), and if we try to check
+			 their signature on *that* request, we'll fetch *their* instance
+			 actor... leading to an infinite recursion */
+		if (userId) {
+			const instanceActor = await this.instanceActorService.getInstanceActor();
+
+			if (userId === instanceActor.id) return false;
+		}
+
+		let signature;
+
+		try {
+			signature = httpSignature.parseRequest(request.raw, { 'headers': [] });
+		} catch (e) {
+			// not signed, or malformed signature: refuse
+			reply.code(401);
+			return true;
+		}
+
+		if (signature.params.headers.indexOf('host') === -1
+			|| request.headers.host !== this.config.host) {
+			// no destination host, or not us: refuse
+			reply.code(401);
+			return true;
+		}
+
+		const keyId = new URL(signature.keyId);
+		const keyHost = this.utilityService.toPuny(keyId.hostname);
+
+		const meta = await this.metaService.fetch();
+		if (this.utilityService.isBlockedHost(meta.blockedHosts, keyHost)) {
+			/* blocked instance: refuse (we don't care if the signature is
+				 good, if they even pretend to be from a blocked instance,
+				 they're out) */
+			reply.code(401);
+			return true;
+		}
+
+		// do we know the signer already?
+		let authUser: {
+			user: MiRemoteUser;
+			key: MiUserPublickey | null;
+		} | null = await this.apDbResolverService.getAuthUserFromKeyId(signature.keyId);
+
+		if (authUser == null) {
+			/* keyId is often in the shape `${user.uri}#${keyname}`, try
+				 fetching information about the remote user */
+			const candidate = formatURL(keyId, { fragment: false });
+			authUser = await this.apDbResolverService.getAuthUserFromApId(candidate);
+		}
+
+		if (authUser?.key == null) {
+			// we can't figure out who the signer is, or we can't get their key: refuse
+			reply.code(401);
+			return true;
+		}
+
+		let httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
+
+		if (!httpSignatureValidated) {
+			// maybe they changed their key? refetch it
+			authUser.key = await this.apDbResolverService.refetchPublicKeyForApId(authUser.user);
+
+			if (authUser.key != null) {
+				httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
+			}
+		}
+
+		if (!httpSignatureValidated) {
+			// bad signature: refuse
+			reply.code(401);
+			return true;
+		}
+
+		// all good, don't refuse
+		return false;
+	}
+
 	@bindThis
 	private inbox(request: FastifyRequest, reply: FastifyReply) {
 		let signature;
@@ -172,6 +275,8 @@ export class ActivityPubServerService {
 		request: FastifyRequest<{ Params: { user: string; }; Querystring: { cursor?: string; page?: string; }; }>,
 		reply: FastifyReply,
 	) {
+		if (await this.shouldRefuseGetRequest(request, reply, request.params.user)) return;
+
 		const userId = request.params.user;
 
 		const cursor = request.query.cursor;
@@ -264,6 +369,8 @@ export class ActivityPubServerService {
 		request: FastifyRequest<{ Params: { user: string; }; Querystring: { cursor?: string; page?: string; }; }>,
 		reply: FastifyReply,
 	) {
+		if (await this.shouldRefuseGetRequest(request, reply, request.params.user)) return;
+
 		const userId = request.params.user;
 
 		const cursor = request.query.cursor;
@@ -353,6 +460,8 @@ export class ActivityPubServerService {
 
 	@bindThis
 	private async featured(request: FastifyRequest<{ Params: { user: string; }; }>, reply: FastifyReply) {
+		if (await this.shouldRefuseGetRequest(request, reply, request.params.user)) return;
+
 		const userId = request.params.user;
 
 		const user = await this.usersRepository.findOneBy({
@@ -397,6 +506,8 @@ export class ActivityPubServerService {
 		}>,
 		reply: FastifyReply,
 	) {
+		if (await this.shouldRefuseGetRequest(request, reply, request.params.user)) return;
+
 		const userId = request.params.user;
 
 		const sinceId = request.query.since_id;
@@ -551,6 +662,8 @@ export class ActivityPubServerService {
 
 		// note
 		fastify.get<{ Params: { note: string; } }>('/notes/:note', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
+			if (await this.shouldRefuseGetRequest(request, reply)) return;
+
 			vary(reply.raw, 'Accept');
 
 			const note = await this.notesRepository.findOneBy({
@@ -581,6 +694,8 @@ export class ActivityPubServerService {
 
 		// note activity
 		fastify.get<{ Params: { note: string; } }>('/notes/:note/activity', async (request, reply) => {
+			if (await this.shouldRefuseGetRequest(request, reply)) return;
+
 			vary(reply.raw, 'Accept');
 
 			const note = await this.notesRepository.findOneBy({
@@ -623,6 +738,8 @@ export class ActivityPubServerService {
 
 		// publickey
 		fastify.get<{ Params: { user: string; } }>('/users/:user/publickey', async (request, reply) => {
+			if (await this.shouldRefuseGetRequest(request, reply, request.params.user)) return;
+
 			const userId = request.params.user;
 
 			const user = await this.usersRepository.findOneBy({
@@ -648,6 +765,8 @@ export class ActivityPubServerService {
 		});
 
 		fastify.get<{ Params: { user: string; } }>('/users/:user', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
+			if (await this.shouldRefuseGetRequest(request, reply, request.params.user)) return;
+
 			const userId = request.params.user;
 
 			const user = await this.usersRepository.findOneBy({
@@ -660,6 +779,8 @@ export class ActivityPubServerService {
 		});
 
 		fastify.get<{ Params: { user: string; } }>('/@:user', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
+			if (await this.shouldRefuseGetRequest(request, reply, request.params.user)) return;
+
 			const user = await this.usersRepository.findOneBy({
 				usernameLower: request.params.user.toLowerCase(),
 				host: IsNull(),
@@ -672,6 +793,8 @@ export class ActivityPubServerService {
 
 		// emoji
 		fastify.get<{ Params: { emoji: string; } }>('/emojis/:emoji', async (request, reply) => {
+			if (await this.shouldRefuseGetRequest(request, reply)) return;
+
 			const emoji = await this.emojisRepository.findOneBy({
 				host: IsNull(),
 				name: request.params.emoji,
@@ -689,6 +812,8 @@ export class ActivityPubServerService {
 
 		// like
 		fastify.get<{ Params: { like: string; } }>('/likes/:like', async (request, reply) => {
+			if (await this.shouldRefuseGetRequest(request, reply)) return;
+
 			const reaction = await this.noteReactionsRepository.findOneBy({ id: request.params.like });
 
 			if (reaction == null) {
@@ -710,6 +835,8 @@ export class ActivityPubServerService {
 
 		// follow
 		fastify.get<{ Params: { follower: string; followee: string; } }>('/follows/:follower/:followee', async (request, reply) => {
+			if (await this.shouldRefuseGetRequest(request, reply)) return;
+
 			// This may be used before the follow is completed, so we do not
 			// check if the following exists.
 
@@ -736,6 +863,8 @@ export class ActivityPubServerService {
 
 		// follow
 		fastify.get<{ Params: { followRequestId: string ; } }>('/follows/:followRequestId', async (request, reply) => {
+			if (await this.shouldRefuseGetRequest(request, reply)) return;
+
 			// This may be used before the follow is completed, so we do not
 			// check if the following exists and only check if the follow request exists.
 

From 1984416e3efeb987c94cfefb44d800bc0521e415 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Thu, 21 Dec 2023 18:17:19 +0000
Subject: [PATCH 335/435] authorized fetch: let /@instance.actor through

this is probably never actually used, but it still looks like a good
idea (also, FireFish does it)

thanks @ShittyKoper for noticing!
---
 packages/backend/src/server/ActivityPubServerService.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index a0ad4a134f..967a69cb77 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -131,6 +131,7 @@ export class ActivityPubServerService {
 			const instanceActor = await this.instanceActorService.getInstanceActor();
 
 			if (userId === instanceActor.id) return false;
+			if (userId === instanceActor.username) return false;
 		}
 
 		let signature;

From 477cda0b635ff293760f91eceb7246b8f9d456e5 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Sat, 23 Dec 2023 15:26:21 +0000
Subject: [PATCH 336/435] authorized fetch: log when things go wrong

---
 .../src/server/ActivityPubServerService.ts    | 23 +++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 967a69cb77..a87796e267 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -36,12 +36,17 @@ import { IActivity } from '@/core/activitypub/type.js';
 import { isPureRenote } from '@/misc/is-pure-renote.js';
 import type { FastifyInstance, FastifyRequest, FastifyReply, FastifyPluginOptions, FastifyBodyParser } from 'fastify';
 import type { FindOptionsWhere } from 'typeorm';
+import type Logger from '@/logger.js';
+import { LoggerService } from '@/core/LoggerService.js';
 
 const ACTIVITY_JSON = 'application/activity+json; charset=utf-8';
 const LD_JSON = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"; charset=utf-8';
 
 @Injectable()
 export class ActivityPubServerService {
+	private logger: Logger;
+	private authlogger: Logger;
+
 	constructor(
 		@Inject(DI.config)
 		private config: Config,
@@ -79,8 +84,11 @@ export class ActivityPubServerService {
 		private queueService: QueueService,
 		private userKeypairService: UserKeypairService,
 		private queryService: QueryService,
+		private loggerService: LoggerService,
 	) {
 		//this.createServer = this.createServer.bind(this);
+		this.logger = this.loggerService.getLogger('apserv', 'pink');
+		this.authlogger = this.logger.createSubLogger('sigcheck');
 	}
 
 	@bindThis
@@ -130,8 +138,10 @@ export class ActivityPubServerService {
 		if (userId) {
 			const instanceActor = await this.instanceActorService.getInstanceActor();
 
-			if (userId === instanceActor.id) return false;
-			if (userId === instanceActor.username) return false;
+			if (userId === instanceActor.id || userId === instanceActor.username) {
+				this.authlogger.debug(`${request.id} ${request.url} request to instance.actor, letting through`);
+				return false;
+			}
 		}
 
 		let signature;
@@ -140,6 +150,7 @@ export class ActivityPubServerService {
 			signature = httpSignature.parseRequest(request.raw, { 'headers': [] });
 		} catch (e) {
 			// not signed, or malformed signature: refuse
+			this.authlogger.warn(`${request.id} ${request.url} not signed, or malformed signature: refuse`);
 			reply.code(401);
 			return true;
 		}
@@ -147,6 +158,7 @@ export class ActivityPubServerService {
 		if (signature.params.headers.indexOf('host') === -1
 			|| request.headers.host !== this.config.host) {
 			// no destination host, or not us: refuse
+			this.authlogger.warn(`${request.id} ${request.url} no destination host, or not us: refuse`);
 			reply.code(401);
 			return true;
 		}
@@ -159,6 +171,7 @@ export class ActivityPubServerService {
 			/* blocked instance: refuse (we don't care if the signature is
 				 good, if they even pretend to be from a blocked instance,
 				 they're out) */
+			this.authlogger.warn(`${request.id} ${request.url} instance ${keyHost} is blocked: refuse`);
 			reply.code(401);
 			return true;
 		}
@@ -173,11 +186,13 @@ export class ActivityPubServerService {
 			/* keyId is often in the shape `${user.uri}#${keyname}`, try
 				 fetching information about the remote user */
 			const candidate = formatURL(keyId, { fragment: false });
+			this.authlogger.info(`${request.id} ${request.url} we don't know the user for keyId ${keyID}, trying to fetch via ${candidate}`);
 			authUser = await this.apDbResolverService.getAuthUserFromApId(candidate);
 		}
 
 		if (authUser?.key == null) {
 			// we can't figure out who the signer is, or we can't get their key: refuse
+			this.authlogger.warn(`${request.id} ${request.url} we can't figure out who the signer is, or we can't get their key: refuse`);
 			reply.code(401);
 			return true;
 		}
@@ -185,16 +200,20 @@ export class ActivityPubServerService {
 		let httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
 
 		if (!httpSignatureValidated) {
+			this.authlogger.info(`${request.id} ${request.url} failed to validate signature, re-fetching the key for ${authUser.user}`);
 			// maybe they changed their key? refetch it
 			authUser.key = await this.apDbResolverService.refetchPublicKeyForApId(authUser.user);
 
 			if (authUser.key != null) {
 				httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
+			} else {
+				this.authlogger.warn(`${request.id} ${request.url} failed to re-fetch key for ${authUser.user}`);
 			}
 		}
 
 		if (!httpSignatureValidated) {
 			// bad signature: refuse
+			this.authlogger.info(`${request.id} ${request.url} failed to validate signature: refuse`);
 			reply.code(401);
 			return true;
 		}

From e14a06cd1607479e184a43ffacbbcd774b802907 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Sat, 23 Dec 2023 14:25:41 +0000
Subject: [PATCH 337/435] always use black shadow in instance ticker #238

we know this looks decent because it's what the MkInstanceTicker
already does
---
 packages/frontend/src/components/SkInstanceTicker.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/SkInstanceTicker.vue b/packages/frontend/src/components/SkInstanceTicker.vue
index fa7b2a444d..d69e5fecec 100644
--- a/packages/frontend/src/components/SkInstanceTicker.vue
+++ b/packages/frontend/src/components/SkInstanceTicker.vue
@@ -50,7 +50,7 @@ const bg = {
 	padding: 4px;
 	overflow: clip;
 	color: #fff;
-	text-shadow: -1px -1px 0 var(--bg),1px -1px 0 var(--bg),-1px 1px 0 var(--bg),1px 1px 0 var(--bg)
+	text-shadow: -1px -1px 0 #000,1px -1px 0 #000,-1px 1px 0 #000,1px 1px 0 #000;
 }
 
 .icon {

From eb036d558ff5758bd1f3333e359e77111b861aaf Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Sat, 23 Dec 2023 19:21:37 +0100
Subject: [PATCH 338/435] fix: note footer not properly resizing

---
 packages/frontend/src/components/MkNote.vue    | 3 ++-
 packages/frontend/src/components/MkNoteSub.vue | 3 ++-
 packages/frontend/src/components/SkNote.vue    | 3 ++-
 packages/frontend/src/components/SkNoteSub.vue | 3 ++-
 4 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 9ecf21071d..cdd2cccb2e 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -829,7 +829,8 @@ function emitUpdReaction(emoji: string, delta: number) {
 		z-index: 1;
 		margin-top: 0.4em;
 		width: max-content;
-		min-width: max-content;
+		min-width: min-content;
+		max-width: fit-content;
 	}
 
 	&:hover > .article > .main > .footer > .footerButton {
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 8d394c0c15..c61f0836bd 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -415,7 +415,8 @@ if (props.detail) {
 		z-index: 1;
 		margin-top: 0.4em;
 		width: max-content;
-		min-width: max-content;
+		min-width: min-content;
+		max-width: fit-content;
 }
 
 .main {
diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue
index cb37861330..83909654c7 100644
--- a/packages/frontend/src/components/SkNote.vue
+++ b/packages/frontend/src/components/SkNote.vue
@@ -830,7 +830,8 @@ function emitUpdReaction(emoji: string, delta: number) {
 		z-index: 1;
 		margin-top: 0.4em;
 		width: max-content;
-		min-width: max-content;
+		min-width: min-content;
+		max-width: fit-content;
 	}
 
 	&:hover > .article > .main > .footer > .footerButton {
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index fc30dc87aa..f0653d6ec2 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -432,7 +432,8 @@ if (props.detail) {
 	z-index: 1;
 	margin-top: 0.4em;
 	width: max-content;
-	min-width: max-content;
+	min-width: min-content;
+	max-width: fit-content;
 }
 
 .main {

From e6c02909c751c70b4f825616a4143f0e956dee85 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Sat, 23 Dec 2023 20:11:53 +0000
Subject: [PATCH 339/435] fix typo

thanks @Marie
---
 packages/backend/src/server/ActivityPubServerService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index a87796e267..26580e21ed 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -186,7 +186,7 @@ export class ActivityPubServerService {
 			/* keyId is often in the shape `${user.uri}#${keyname}`, try
 				 fetching information about the remote user */
 			const candidate = formatURL(keyId, { fragment: false });
-			this.authlogger.info(`${request.id} ${request.url} we don't know the user for keyId ${keyID}, trying to fetch via ${candidate}`);
+			this.authlogger.info(`${request.id} ${request.url} we don't know the user for keyId ${keyId}, trying to fetch via ${candidate}`);
 			authUser = await this.apDbResolverService.getAuthUserFromApId(candidate);
 		}
 

From a3dd61dec4ba26a90a8cae6ade7a615f6d459da3 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Sat, 23 Dec 2023 21:27:48 +0000
Subject: [PATCH 340/435] fix logging

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

diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 26580e21ed..68de738238 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -200,7 +200,7 @@ export class ActivityPubServerService {
 		let httpSignatureValidated = httpSignature.verifySignature(signature, authUser.key.keyPem);
 
 		if (!httpSignatureValidated) {
-			this.authlogger.info(`${request.id} ${request.url} failed to validate signature, re-fetching the key for ${authUser.user}`);
+			this.authlogger.info(`${request.id} ${request.url} failed to validate signature, re-fetching the key for ${authUser.user.uri}`);
 			// maybe they changed their key? refetch it
 			authUser.key = await this.apDbResolverService.refetchPublicKeyForApId(authUser.user);
 

From 62a0f43c8468efcde83290dbf3f76ffa1510b2c7 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Mon, 25 Dec 2023 17:31:21 +0000
Subject: [PATCH 341/435] add package

---
 packages/frontend/package.json |  1 +
 pnpm-lock.yaml                 | 10 ++++++++++
 2 files changed, 11 insertions(+)

diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 275a6ad025..b46663aca1 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -51,6 +51,7 @@
 		"insert-text-at-cursor": "0.3.0",
 		"is-file-animated": "1.0.2",
 		"json5": "2.2.3",
+		"katex": "^0.16.9",
 		"matter-js": "0.19.0",
 		"misskey-js": "workspace:*",
 		"photoswipe": "5.4.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ec05ad3485..c4c51bca2a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -756,6 +756,9 @@ importers:
       json5:
         specifier: 2.2.3
         version: 2.2.3
+      katex:
+        specifier: ^0.16.9
+        version: 0.16.9
       matter-js:
         specifier: 0.19.0
         version: 0.19.0
@@ -14554,6 +14557,13 @@ packages:
       safe-buffer: 5.2.1
     dev: false
 
+  /katex@0.16.9:
+    resolution: {integrity: sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==}
+    hasBin: true
+    dependencies:
+      commander: 9.5.0
+    dev: false
+
   /keyv@4.5.4:
     resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
     dependencies:

From 6f63ff62fcfbba5dbb48e4761c5353c97e361a7e Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Mon, 25 Dec 2023 18:40:49 +0100
Subject: [PATCH 342/435] fix: autocomplete on users being janky

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

diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts
index 6f3d3ba8e1..2a9a42ace5 100644
--- a/packages/frontend/src/scripts/autocomplete.ts
+++ b/packages/frontend/src/scripts/autocomplete.ts
@@ -101,7 +101,7 @@ export class Autocomplete {
 
 		if (isMention && this.onlyType.includes('user')) {
 			const username = text.substring(mentionIndex + 1);
-			if (username !== '' && username.match(/^[a-zA-Z0-9_.]+$/)) {
+			if (username !== '' && username.match(/^[a-zA-Z0-9_]+$/)) {
 				this.open('user', username);
 				opened = true;
 			} else if (username === '') {

From 8d291ef03982037b391f7529d5a4c7feb1972823 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Mon, 25 Dec 2023 18:12:55 +0000
Subject: [PATCH 343/435] formula component based on katex

thanks a lot to MoshiBar for the initial implementation!
---
 .../frontend/src/components/MkFormula.vue     | 33 +++++++++++++++++++
 .../global/MkMisskeyFlavoredMarkdown.ts       | 14 ++++++--
 2 files changed, 44 insertions(+), 3 deletions(-)
 create mode 100644 packages/frontend/src/components/MkFormula.vue

diff --git a/packages/frontend/src/components/MkFormula.vue b/packages/frontend/src/components/MkFormula.vue
new file mode 100644
index 0000000000..64d0cc0b4e
--- /dev/null
+++ b/packages/frontend/src/components/MkFormula.vue
@@ -0,0 +1,33 @@
+<!--
+		 SPDX-FileCopyrightText: dakkar, MoshiBar, and other Sharkey contributors
+		 SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+	<div v-if="block" :class="$style.block" v-html="renderedFormula"></div>
+	<span v-else v-html="renderedFormula"></span>
+</template>
+
+<script lang="ts" setup>
+	import { computed } from 'vue';
+	import katex from 'katex';
+	import 'katex/dist/katex.min.css';
+
+	const props = defineProps<{
+		formula: string;
+		block: boolean;
+	}>();
+
+	const renderedFormula = computed(() =>
+		katex.renderToString(props.formula, {
+			throwOnError: false,
+			trust: false,
+			displayMode: props.block,
+		} as any));
+</script>
+
+<style lang="scss" module>
+	.block {
+		text-align: center;
+	}
+</style>
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index 60d12fdcde..83b7ef2e78 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { VNode, h } from 'vue';
+import { VNode, h, defineAsyncComponent } from 'vue';
 import * as mfm from '@sharkey/sfm-js';
 import * as Misskey from 'misskey-js';
 import MkUrl from '@/components/global/MkUrl.vue';
@@ -61,6 +61,8 @@ export default function(props: MfmProps) {
 
 	const useAnim = defaultStore.state.advancedMfm && defaultStore.state.animatedMfm ? true : props.isAnim ? true : false;
 
+	const MkFormula = defineAsyncComponent(() => import('@/components/MkFormula.vue'));
+
 	/**
 	 * Gen Vue Elements from MFM AST
 	 * @param ast MFM AST
@@ -410,11 +412,17 @@ export default function(props: MfmProps) {
 			}
 
 			case 'mathInline': {
-				return [h('code', token.props.formula)];
+				return [h(MkFormula, {
+					formula: token.props.formula,
+					block: false,
+				})];
 			}
 
 			case 'mathBlock': {
-				return [h('code', token.props.formula)];
+				return [h(MkFormula, {
+					formula: token.props.formula,
+					block: true,
+				})];
 			}
 
 			case 'search': {

From 8fd72fa12cd7878c40571294cc37be30c9ca9090 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Mon, 25 Dec 2023 19:51:41 +0000
Subject: [PATCH 344/435] remove spurious indent

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

diff --git a/packages/frontend/src/components/MkFormula.vue b/packages/frontend/src/components/MkFormula.vue
index 64d0cc0b4e..039cef8da8 100644
--- a/packages/frontend/src/components/MkFormula.vue
+++ b/packages/frontend/src/components/MkFormula.vue
@@ -1,6 +1,6 @@
 <!--
-		 SPDX-FileCopyrightText: dakkar, MoshiBar, and other Sharkey contributors
-		 SPDX-License-Identifier: AGPL-3.0-only
+SPDX-FileCopyrightText: dakkar, MoshiBar, and other Sharkey contributors
+SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>

From 5bc036180f6b82b1ed26ffa24803aa43d46d608c Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Mon, 25 Dec 2023 21:37:13 +0100
Subject: [PATCH 345/435] upd: module versions

---
 packages/backend/package.json  |   2 +-
 packages/frontend/package.json |   4 +-
 pnpm-lock.yaml                 | 115 +++++++--------------------------
 3 files changed, 26 insertions(+), 95 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index aaf30bef12..14243b9ba2 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -76,7 +76,7 @@
 		"@nestjs/core": "10.2.10",
 		"@nestjs/testing": "10.2.10",
 		"@peertube/http-signature": "1.7.0",
-		"@sharkey/sfm-js": "0.24.0",
+		"@sharkey/sfm-js": "0.24.1",
 		"@simplewebauthn/server": "8.3.5",
 		"@sinonjs/fake-timers": "11.2.2",
 		"@smithy/node-http-handler": "2.1.10",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index b46663aca1..496a083aa9 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -22,7 +22,7 @@
 		"@rollup/plugin-json": "6.1.0",
 		"@rollup/plugin-replace": "5.0.5",
 		"@rollup/pluginutils": "5.1.0",
-		"@sharkey/sfm-js": "0.24.0",
+		"@sharkey/sfm-js": "0.24.1",
 		"@syuilo/aiscript": "0.16.0",
 		"@phosphor-icons/web": "^2.0.3",
 		"@twemoji/parser": "15.0.0",
@@ -51,7 +51,7 @@
 		"insert-text-at-cursor": "0.3.0",
 		"is-file-animated": "1.0.2",
 		"json5": "2.2.3",
-		"katex": "^0.16.9",
+		"katex": "0.16.9",
 		"matter-js": "0.19.0",
 		"misskey-js": "workspace:*",
 		"photoswipe": "5.4.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c4c51bca2a..5f77a0081c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -110,8 +110,8 @@ importers:
         specifier: 1.7.0
         version: 1.7.0
       '@sharkey/sfm-js':
-        specifier: 0.24.0
-        version: 0.24.0
+        specifier: 0.24.1
+        version: 0.24.1
       '@simplewebauthn/server':
         specifier: 8.3.5
         version: 8.3.5
@@ -673,8 +673,8 @@ importers:
         specifier: 5.1.0
         version: 5.1.0(rollup@4.9.1)
       '@sharkey/sfm-js':
-        specifier: 0.24.0
-        version: 0.24.0
+        specifier: 0.24.1
+        version: 0.24.1
       '@syuilo/aiscript':
         specifier: 0.16.0
         version: 0.16.0
@@ -757,7 +757,7 @@ importers:
         specifier: 2.2.3
         version: 2.2.3
       katex:
-        specifier: ^0.16.9
+        specifier: 0.16.9
         version: 0.16.9
       matter-js:
         specifier: 0.19.0
@@ -873,10 +873,10 @@ importers:
         version: 7.6.5
       '@storybook/vue3':
         specifier: 7.6.5
-        version: 7.6.5(@vue/compiler-core@3.3.8)(vue@3.3.12)
+        version: 7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12)
       '@storybook/vue3-vite':
         specifier: 7.6.5
-        version: 7.6.5(@vue/compiler-core@3.3.8)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12)
+        version: 7.6.5(@vue/compiler-core@3.3.12)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12)
       '@testing-library/vue':
         specifier: 8.0.1
         version: 8.0.1(@vue/compiler-sfc@3.3.12)(vue@3.3.12)
@@ -981,7 +981,7 @@ importers:
         version: 7.6.5
       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.6.5)(@storybook/components@7.5.3)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0)
+        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.6.5)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0)
       summaly:
         specifier: github:misskey-dev/summaly
         version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8
@@ -5853,8 +5853,8 @@ packages:
       string-argv: 0.3.1
     dev: true
 
-  /@sharkey/sfm-js@0.24.0:
-    resolution: {integrity: sha512-OkrStlAj6MK0KHylLCc6I9IE1D9UjaV32bWo731o05Mny2R1JmkdoFTof92XTkJ4CKmvSk2oXlLBAt3jQ4Ghlg==, tarball: https://git.joinsharkey.org/api/packages/Sharkey/npm/%40sharkey%2Fsfm-js/-/0.24.0/sfm-js-0.24.0.tgz}
+  /@sharkey/sfm-js@0.24.1:
+    resolution: {integrity: sha512-STBMI34OEXjS94+/uUk9MtJLoKzF6TqZbS6BZRZ8bo4NEq2rTH330R6Q90xSJI1FY6RIV7kxepIG8cjUumY4kA==, tarball: https://git.joinsharkey.org/api/packages/Sharkey/npm/%40sharkey%2Fsfm-js/-/0.24.1/sfm-js-0.24.1.tgz}
     dependencies:
       '@twemoji/parser': 15.0.0
     dev: false
@@ -6694,17 +6694,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/channels@7.5.3:
-    resolution: {integrity: sha512-dhWuV2o2lmxH0RKuzND8jxYzvSQTSmpE13P0IT/k8+I1up/rSNYOBQJT6SalakcNWXFAMXguo/8E7ApmnKKcEw==}
-    dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/core-events': 7.5.3
-      '@storybook/global': 5.0.0
-      qs: 6.11.1
-      telejson: 7.2.0
-      tiny-invariant: 1.3.1
-    dev: true
-
   /@storybook/channels@7.6.5:
     resolution: {integrity: sha512-FIlNkyfQy9uHoJfAFL2/wO3ASGJELFvBzURBE2rcEF/TS7GcUiqWnBfiDxAbwSEjSOm2F0eEq3UXhaZEjpJHDw==}
     dependencies:
@@ -6768,12 +6757,6 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/client-logger@7.5.3:
-    resolution: {integrity: sha512-vUFYALypjix5FoJ5M/XUP6KmyTnQJNW1poHdW7WXUVSg+lBM6E5eAtjTm0hdxNNDH8KSrdy24nCLra5h0X0BWg==}
-    dependencies:
-      '@storybook/global': 5.0.0
-    dev: true
-
   /@storybook/client-logger@7.6.5:
     resolution: {integrity: sha512-S5aROWgssqg7tcs9lgW5wmCAz4SxMAtioiyVj5oFecmPCbQtFVIAREYzeoxE4GfJL+plrfRkum4BzziANn8EhQ==}
     dependencies:
@@ -6801,29 +6784,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/components@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-M3+cjvEsDGLUx8RvK5wyF6/13LNlUnKbMgiDE8Sxk/v/WPpyhOAIh/B8VmrU1psahS61Jd4MTkFmLf1cWau1vw==}
-    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.5.3
-      '@storybook/csf': 0.1.0
-      '@storybook/global': 5.0.0
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.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.6.5(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-w4ZucbBBZ+NKMWlJKVj2I/bMBBq7gzDp9lzc4+8QaQ3vUPXKqc1ilIPYo/7UR5oxwDVMZocmMSgl9L8lvf7+Mw==}
     peerDependencies:
@@ -6885,12 +6845,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/core-events@7.5.3:
-    resolution: {integrity: sha512-DFOpyQ22JD5C1oeOFzL8wlqSWZzrqgDfDbUGP8xdO4wJu+FVTxnnWN6ZYLdTPB1u27DOhd7TzjQMfLDHLu7kbQ==}
-    dependencies:
-      ts-dedent: 2.2.0
-    dev: true
-
   /@storybook/core-events@7.6.5:
     resolution: {integrity: sha512-zk2q/qicYXAzHA4oV3GDbIql+Kd4TOHUgDE8e4jPCOPp856z2ScqEKUAbiJizs6eEJOH4nW9Db1kuzgrBVEykQ==}
     dependencies:
@@ -6973,12 +6927,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/csf@0.1.0:
-    resolution: {integrity: sha512-uk+jMXCZ8t38jSTHk2o5btI+aV2Ksbvl6DoOv3r6VaCM1KZqeuMwtwywIQdflkA8/6q/dKT8z8L+g8hC4GC3VQ==}
-    dependencies:
-      type-fest: 2.19.0
-    dev: true
-
   /@storybook/csf@0.1.2:
     resolution: {integrity: sha512-ePrvE/pS1vsKR9Xr+o+YwdqNgHUyXvg+1Xjx0h9LrVx7Zq4zNe06pd63F5EvzTbCbJsHj7GHr9tkiaqm7U8WRA==}
     dependencies:
@@ -7208,20 +7156,6 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/theming@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-Cjmthe1MAk0z4RKCZ7m72gAD8YD0zTAH97z5ryM1Qv84QXjiCQ143fGOmYz1xEQdNFpOThPcwW6FEccLHTkVcg==}
-    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.5.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.6.5(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-RpcWT0YEgiobO41McVPDfQQHHFnjyr1sJnNTPJIvOUgSfURdgSj17mQVxtD5xcXcPWUdle5UhIOrCixHbL/NNw==}
     peerDependencies:
@@ -7236,15 +7170,6 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/types@7.5.3:
-    resolution: {integrity: sha512-iu5W0Kdd6nysN5CPkY4GRl+0BpxRTdSfBIJak7mb6xCIHSB5t1tw4BOuqMQ5EgpikRY3MWJ4gY647QkWBX3MNQ==}
-    dependencies:
-      '@storybook/channels': 7.5.3
-      '@types/babel__core': 7.20.5
-      '@types/express': 4.17.17
-      file-system-cache: 2.3.0
-    dev: true
-
   /@storybook/types@7.6.5:
     resolution: {integrity: sha512-Q757v+fYZZSaEpks/zDL5YgXRozxkgKakXFc+BoQHK5q5sVhJ+0jvpLJiAQAniIIaMIkqY/G24Kd6Uo6UdKBCg==}
     dependencies:
@@ -7254,7 +7179,7 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.3.8)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12):
+  /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.3.12)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12):
     resolution: {integrity: sha512-7wUCq2Lrjlekftd5ha3hG0GSGbbzuc370cKkBqSmwFuOfI38z5+VeYt7nDtAlncxcpVSH7DejTGRuKTlC7NyYg==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
@@ -7262,7 +7187,7 @@ packages:
     dependencies:
       '@storybook/builder-vite': 7.6.5(typescript@5.3.3)(vite@5.0.10)
       '@storybook/core-server': 7.6.5
-      '@storybook/vue3': 7.6.5(@vue/compiler-core@3.3.8)(vue@3.3.12)
+      '@storybook/vue3': 7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12)
       '@vitejs/plugin-vue': 4.5.2(vite@5.0.10)(vue@3.3.12)
       magic-string: 0.30.5
       vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
@@ -7279,7 +7204,7 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.6.5(@vue/compiler-core@3.3.8)(vue@3.3.12):
+  /@storybook/vue3@7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12):
     resolution: {integrity: sha512-tv/9rVc3XXDOJu5hfZtKhrhM8x4GTLKon62Rmaxlq06weqkGlfBi/V/g1EZ7OE71Pi+woKS/TX7p9qbRrvgahg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
@@ -7291,7 +7216,7 @@ packages:
       '@storybook/global': 5.0.0
       '@storybook/preview-api': 7.6.5
       '@storybook/types': 7.6.5
-      '@vue/compiler-core': 3.3.8
+      '@vue/compiler-core': 3.3.12
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
@@ -10410,6 +10335,11 @@ packages:
     engines: {node: '>= 10'}
     dev: false
 
+  /commander@8.3.0:
+    resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
+    engines: {node: '>= 12'}
+    dev: false
+
   /commander@9.5.0:
     resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
     engines: {node: ^12.20.0 || >=14}
@@ -14561,7 +14491,7 @@ packages:
     resolution: {integrity: sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==}
     hasBin: true
     dependencies:
-      commander: 9.5.0
+      commander: 8.3.0
     dev: false
 
   /keyv@4.5.4:
@@ -18898,6 +18828,7 @@ packages:
   /ts-case-convert@2.0.2:
     resolution: {integrity: sha512-vdKfx1VAdpvEBOBv5OpVu5ZFqRg9HdTI4sYt6qqMeICBeNyXvitrarCnFWNDAki51IKwCyx+ZssY46Q9jH5otA==}
     dev: true
+    bundledDependencies: []
 
   /ts-dedent@2.2.0:
     resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
@@ -20291,7 +20222,7 @@ packages:
       sharp: 0.31.3
     dev: false
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.5.3)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0):
+  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.6.5)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(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
@@ -20313,7 +20244,7 @@ packages:
         optional: true
     dependencies:
       '@storybook/blocks': 7.6.5(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/components': 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/core-events': 7.6.5
       '@storybook/manager-api': 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/preview-api': 7.6.5

From 2f5c51c1ca5e9726a590211416796a772beb9511 Mon Sep 17 00:00:00 2001
From: ShittyKopper <shittykopper@w.on-t.work>
Date: Mon, 27 Nov 2023 22:12:20 +0300
Subject: [PATCH 346/435] fix: don't load text of empty draft

this fixes a small ux quirk where blanking a reply and cancelling it
results in an empty reply next time you try to reply to that note, accidentally
un-tagging everyone
---
 packages/frontend/src/components/MkPostForm.vue | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index c9784fc40f..a209128d55 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -923,12 +923,19 @@ onMounted(() => {
 		if (!props.instant && !props.mention && !props.specified && !props.mock) {
 			const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey.value];
 			if (draft) {
-				text.value = draft.data.text;
+				if (typeof draft.data.text === 'string' && draft.data.text.trim()) {
+					text.value = draft.data.text;
+				}
+
+				if (typeof draft.data.cw === 'string' && draft.data.cw.trim()) {
+					cw.value = draft.data.cw;
+				}
+
 				useCw.value = draft.data.useCw;
-				cw.value = draft.data.cw;
 				visibility.value = draft.data.visibility;
 				localOnly.value = draft.data.localOnly;
 				files.value = (draft.data.files || []).filter(draftFile => draftFile);
+
 				if (draft.data.poll) {
 					poll.value = draft.data.poll;
 				}

From 62a5793e7104b1806a7b7ebe270632af1a4cd9c0 Mon Sep 17 00:00:00 2001
From: ShittyKopper <shittykopper@w.on-t.work>
Date: Mon, 27 Nov 2023 23:55:43 +0300
Subject: [PATCH 347/435] fix: don't show filename as alt text

---
 packages/frontend/src/components/MkMediaBanner.vue | 2 +-
 packages/frontend/src/components/MkMediaImage.vue  | 8 ++++----
 packages/frontend/src/components/MkMediaList.vue   | 4 ++--
 packages/frontend/src/components/MkMediaVideo.vue  | 2 +-
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue
index 4594c8a1db..7b0387cefe 100644
--- a/packages/frontend/src/components/MkMediaBanner.vue
+++ b/packages/frontend/src/components/MkMediaBanner.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<audio
 			ref="audioEl"
 			:src="media.url"
-			:title="media.name"
+			:title="media.comment ?? undefined"
 			controls
 			preload="metadata"
 		/>
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index 0040f00dc2..ef57cea32a 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -8,10 +8,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<component
 		:is="disableImageLink ? 'div' : 'a'"
 		v-bind="disableImageLink ? {
-			title: image.name,
+			title: image.comment,
 			class: $style.imageContainer,
 		} : {
-			title: image.name,
+			title: image.comment,
 			class: $style.imageContainer,
 			href: image.url,
 			style: 'cursor: zoom-in;'
@@ -22,8 +22,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 			:src="(defaultStore.state.dataSaver.media && hide) ? null : url"
 			:forceBlurhash="hide"
 			:cover="hide || cover"
-			:alt="image.comment || image.name"
-			:title="image.comment || image.name"
+			:alt="image.comment"
+			:title="image.comment"
 			:width="image.properties.width"
 			:height="image.properties.height"
 			:style="hide ? 'filter: brightness(0.7);' : null"
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index 46e32ef2d8..ff227b0ef9 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -156,8 +156,8 @@ onMounted(() => {
 			[itemData.w, itemData.h] = [itemData.h, itemData.w];
 		}
 		itemData.msrc = file.thumbnailUrl;
-		itemData.alt = file.comment ?? file.name;
-		itemData.comment = file.comment ?? file.name;
+		itemData.alt = file.comment ?? undefined;
+		itemData.comment = file.comment;
 		itemData.thumbCropped = true;
 	});
 
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 4f8560f0f1..a1950b110a 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		ref="videoEl"
 		:class="$style.video"
 		:poster="video.thumbnailUrl"
-		:title="video.comment"
+		:title="video.comment ?? undefined"
 		:alt="video.comment"
 		preload="none"
 		controls

From 32318bbbfa1e67913100c3d03a3ee64393831d50 Mon Sep 17 00:00:00 2001
From: ShittyKopper <shittykopper@w.on-t.work>
Date: Sat, 23 Dec 2023 15:20:51 +0300
Subject: [PATCH 348/435] fix: un-hardcode more border radius values

all of these are blind find and replace, no clue if they end up breaking
anything
---
 packages/frontend/src/components/MkChannelPreview.vue         | 2 +-
 packages/frontend/src/components/MkCode.core.vue              | 2 +-
 packages/frontend/src/components/MkCode.vue                   | 4 ++--
 packages/frontend/src/components/MkCodeEditor.vue             | 4 ++--
 packages/frontend/src/components/MkEmojiPicker.section.vue    | 4 ++--
 packages/frontend/src/components/MkPostForm.vue               | 2 +-
 .../src/components/global/MkMisskeyFlavoredMarkdown.ts        | 2 +-
 .../src/pages/settings/avatar-decoration.decoration.vue       | 2 +-
 packages/frontend/src/pages/settings/emoji-picker.vue         | 4 ++--
 packages/frontend/src/pages/user/raw.vue                      | 2 +-
 10 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/packages/frontend/src/components/MkChannelPreview.vue b/packages/frontend/src/components/MkChannelPreview.vue
index 96590a469b..f870b0eef1 100644
--- a/packages/frontend/src/components/MkChannelPreview.vue
+++ b/packages/frontend/src/components/MkChannelPreview.vue
@@ -198,7 +198,7 @@ const bannerStyle = computed(() => {
 	transform: translate(25%, -25%);
 	background-color: var(--accent);
 	border: solid var(--bg) 4px;
-	border-radius: 100%;
+	border-radius: var(--radius-full);
 	width: 1.5rem;
 	height: 1.5rem;
 	aspect-ratio: 1 / 1;
diff --git a/packages/frontend/src/components/MkCode.core.vue b/packages/frontend/src/components/MkCode.core.vue
index 19418cd4da..579c72b186 100644
--- a/packages/frontend/src/components/MkCode.core.vue
+++ b/packages/frontend/src/components/MkCode.core.vue
@@ -62,7 +62,7 @@ watch(() => props.lang, (to) => {
 	padding: 1em;
 	margin: .5em 0;
 	overflow: auto;
-	border-radius: 8px;
+	border-radius: var(--radius-sm);
 
 	& pre,
 	& code {
diff --git a/packages/frontend/src/components/MkCode.vue b/packages/frontend/src/components/MkCode.vue
index 2c016e4d7c..7346d5782b 100644
--- a/packages/frontend/src/components/MkCode.vue
+++ b/packages/frontend/src/components/MkCode.vue
@@ -56,7 +56,7 @@ const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue'))
 	padding: 1em;
 	margin: .5em 0;
 	overflow: auto;
-	border-radius: 8px;
+	border-radius: var(--radius-sm);
 }
 
 .codeBlockFallbackCode {
@@ -74,7 +74,7 @@ const XCode = defineAsyncComponent(() => import('@/components/MkCode.core.vue'))
 	cursor: pointer;
 
 	box-sizing: border-box;
-	border-radius: 8px;
+	border-radius: var(--radius-sm);
 	padding: 24px;
 	margin-top: 4px;
 	color: #D4D4D4;
diff --git a/packages/frontend/src/components/MkCodeEditor.vue b/packages/frontend/src/components/MkCodeEditor.vue
index c9bcc71196..c1aaa7f1ff 100644
--- a/packages/frontend/src/components/MkCodeEditor.vue
+++ b/packages/frontend/src/components/MkCodeEditor.vue
@@ -161,7 +161,7 @@ watch(v, newValue => {
 	overflow-y: hidden;
 	box-sizing: border-box;
 	margin: 0;
-	border-radius: 6px;
+	border-radius: var(--radius-sm);
 	padding: 0;
 	color: var(--fg);
 	border: solid 1px var(--panel);
@@ -202,7 +202,7 @@ watch(v, newValue => {
 	caret-color: rgb(225, 228, 232);
 	background-color: transparent;
 	border: 0;
-	border-radius: 6px;
+	border-radius: var(--radius-sm);
 	outline: 0;
 	min-width: calc(100% - 24px);
 	height: 100%;
diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index 49c146b68d..ea9d7a0d26 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと -->
 <!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) -->
-<section v-if="!hasChildSection" v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);">
+<section v-if="!hasChildSection" v-panel style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--divider);">
 	<header class="_acrylic" @click="shown = !shown">
 		<i class="toggle ti-fw" :class="shown ? 'ph-caret-down ph-bold ph-lg' : 'ph-caret-up ph-bold ph-lg'"></i> <slot></slot> (<i class="ph-bold ph-lg"></i>:{{ emojis.length }})
 	</header>
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	</div>
 </section>
 <!-- フォルダの中にはカスタム絵文字やフォルダがある -->
-<section v-else v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);">
+<section v-else v-panel style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--divider);">
 	<header class="_acrylic" @click="shown = !shown">
 		<i class="toggle ti-fw" :class="shown ? 'ph-caret-down ph-bold ph-lg' : 'ph-caret-up ph-bold ph-lg'"></i> <slot></slot> (<i class="ph-folder ph-bold ph-lg"></i>:{{ customEmojiTree.length }} <i class="ti ti-icons ti-fw"></i>:{{ emojis.length }})
 	</header>
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index a209128d55..b26ce2932a 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -1059,7 +1059,7 @@ defineExpose({
 	left: 12px;
 	width: 5px;
 	height: 100% ;
-	border-radius: 999px;
+	border-radius: var(--radius-ellipse);
 	pointer-events: none;
 }
 
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index 83b7ef2e78..4c6034c901 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -277,7 +277,7 @@ export default function(props: MfmProps) {
 						const child = token.children[0];
 						const unixtime = parseInt(child.type === 'text' ? child.props.text : '');
 						return h('span', {
-							style: 'display: inline-block; font-size: 90%; border: solid 1px var(--divider); border-radius: 999px; padding: 4px 10px 4px 6px;',
+							style: 'display: inline-block; font-size: 90%; border: solid 1px var(--divider); border-radius: var(--radius-ellipse); padding: 4px 10px 4px 6px;',
 						}, [
 							h('i', {
 								class: 'ph-clock ph-bold ph-lg',
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
index 9c95b5547e..fcd74002f2 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
@@ -42,7 +42,7 @@ const emit = defineEmits<{
 	cursor: pointer;
 	padding: 16px 16px 28px 16px;
 	border: solid 2px var(--divider);
-	border-radius: 8px;
+	border-radius: var(--radius-sm);
 	text-align: center;
 	font-size: 90%;
 	overflow: clip;
diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue
index 61f3332122..1a07790e75 100644
--- a/packages/frontend/src/pages/settings/emoji-picker.vue
+++ b/packages/frontend/src/pages/settings/emoji-picker.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 		<div class="_gaps">
 			<div>
-				<div v-panel style="border-radius: 6px;">
+				<div v-panel style="border-radius: var(--radius-sm);">
 					<Sortable
 						v-model="pinnedEmojisForReaction"
 						:class="$style.emojis"
@@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 		<div class="_gaps">
 			<div>
-				<div v-panel style="border-radius: 6px;">
+				<div v-panel style="border-radius: var(--radius-sm);">
 					<Sortable
 						v-model="pinnedEmojis"
 						:class="$style.emojis"
diff --git a/packages/frontend/src/pages/user/raw.vue b/packages/frontend/src/pages/user/raw.vue
index 0c0bfc29ca..ebe40d5860 100644
--- a/packages/frontend/src/pages/user/raw.vue
+++ b/packages/frontend/src/pages/user/raw.vue
@@ -107,7 +107,7 @@ const suspended = computed(() => props.user.isSuspended ?? false);
 	> .moderator {
 		display: inline-block;
 		border: solid 1px;
-		border-radius: 6px;
+		border-radius: var(--radius-sm);
 		padding: 2px 6px;
 		font-size: 85%;
 	}

From aafdcccd247fd5fdef5b14fae033e87bf058aec5 Mon Sep 17 00:00:00 2001
From: ShittyKopper <shittykopper@w.on-t.work>
Date: Tue, 26 Dec 2023 21:56:34 +0300
Subject: [PATCH 349/435] fix: note history not working

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

diff --git a/packages/frontend/src/components/SkOldNoteWindow.vue b/packages/frontend/src/components/SkOldNoteWindow.vue
index 237032c9d5..f8de28e346 100644
--- a/packages/frontend/src/components/SkOldNoteWindow.vue
+++ b/packages/frontend/src/components/SkOldNoteWindow.vue
@@ -132,7 +132,7 @@ const isRenote = (
 );
 
 const el = shallowRef<HTMLElement>();
-let appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note);
+let appearNote = computed(() => isRenote ? note.value.renote as Misskey.entities.Note : note.value);
 const renoteUrl = appearNote.value.renote ? appearNote.value.renote.url : null;
 const renoteUri = appearNote.value.renote ? appearNote.value.renote.uri : null;
 

From 6242f371114ad691041676613845754d110143a0 Mon Sep 17 00:00:00 2001
From: ShittyKopper <shittykopper@w.on-t.work>
Date: Tue, 26 Dec 2023 21:40:49 +0300
Subject: [PATCH 350/435] Partial Revert "fix: fix: notification count
 position"

Firefox now implements :has. There is no reason for this workaround

This partially reverts commit 4f0bc185af86096c1337d35119e6444253b69682.
---
 packages/frontend/src/ui/_common_/navbar.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index 184d32edd7..20d4564770 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 					v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"
 				>
 					<i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span>
-					<span v-if="navbarItemDef[item].indicated" :class="[$style.itemIndicator, { [$style.hasItemIndicateValueIcon]: navbarItemDef[item].indicateValue }]">
+					<span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator">
 						<span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span>
 						<i v-else class="_indicatorCircle"></i>
 					</span>
@@ -315,7 +315,7 @@ function more(ev: MouseEvent) {
 		font-size: 8px;
 		animation: blink 1s infinite;
 
-		&.hasItemIndicateValueIcon {
+		&:has(.itemIndicateValueIcon) {
 			animation: none;
 			left: auto;
 			right: 40px;

From 45b99a476db8789898f09125ee3bf79288c38d10 Mon Sep 17 00:00:00 2001
From: Marie <Marie@kaifa.ch>
Date: Wed, 27 Dec 2023 09:39:59 +0100
Subject: [PATCH 351/435] upd: swap `api` out with `apiWithDialog` for word
 mute saving

Closes #259
---
 packages/frontend/src/pages/settings/mute-block.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index 6419d23a7a..966bf0d443 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -216,11 +216,11 @@ async function toggleBlockItem(item) {
 }
 
 async function saveMutedWords(mutedWords: (string | string[])[]) {
-	await os.api('i/update', { mutedWords });
+	await os.apiWithDialog('i/update', { mutedWords });
 }
 
 async function saveHardMutedWords(hardMutedWords: (string | string[])[]) {
-	await os.api('i/update', { hardMutedWords });
+	await os.apiWithDialog('i/update', { hardMutedWords });
 }
 
 const headerActions = computed(() => []);

From dd22d7b6830403858ced87d2f871bff6bd3801e0 Mon Sep 17 00:00:00 2001
From: Amelia Yukii <admin@transfem.org>
Date: Wed, 27 Dec 2023 10:16:22 +0100
Subject: [PATCH 352/435] chore: update hard word mute icon

---
 packages/frontend/src/pages/settings/mute-block.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index 966bf0d443..a996a03cce 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	</MkFolder>
 
 	<MkFolder>
-		<template #icon><i class="ti ti-message-off"></i></template>
+		<template #icon><i class="ph-x-square ph-bold ph-lg"></i></template>
 		<template #label>{{ i18n.ts.hardWordMute }}</template>
 
 		<XWordMute :muted="$i!.hardMutedWords" @save="saveHardMutedWords"/>

From 5d746d4ee5fca16392ef8b61d0277bc16af540b4 Mon Sep 17 00:00:00 2001
From: ShittyKopper <shittykopper@w.on-t.work>
Date: Wed, 27 Dec 2023 13:19:29 +0300
Subject: [PATCH 353/435] fix: hide photoswipe alt text when its empty

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

diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index ff227b0ef9..8f73018734 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -172,6 +172,12 @@ onMounted(() => {
 				el.appendChild(textBox);
 
 				pwsp.on('change', (a) => {
+					if (pwsp.currSlide?.data.comment) {
+						textBox.style.display = '';
+					} else {
+						textBox.style.display = 'none';
+					}
+
 					textBox.textContent = pwsp.currSlide.data.comment;
 				});
 			},

From 64d34f595cb5dcef3f6880cba3c39147a48539c6 Mon Sep 17 00:00:00 2001
From: trivernis <trivernis@protonmail.com>
Date: Sun, 24 Dec 2023 17:16:24 +0100
Subject: [PATCH 354/435] Change loadConfig to load all yaml files in the
 config directory

---
 package.json                   |   6 +-
 packages/backend/package.json  |   3 +-
 packages/backend/src/config.ts |  24 +++++---
 pnpm-lock.yaml                 | 109 +++++----------------------------
 4 files changed, 38 insertions(+), 104 deletions(-)

diff --git a/package.json b/package.json
index 6fad6f8bf8..f267db967c 100644
--- a/package.json
+++ b/package.json
@@ -45,8 +45,8 @@
 		"lodash": "4.17.21"
 	},
 	"dependencies": {
-		"execa": "8.0.1",
 		"cssnano": "6.0.2",
+		"execa": "8.0.1",
 		"js-yaml": "4.1.0",
 		"postcss": "8.4.32",
 		"terser": "5.26.0",
@@ -58,7 +58,7 @@
 		"cross-env": "7.0.3",
 		"cypress": "13.6.1",
 		"eslint": "8.56.0",
-		"start-server-and-test": "2.0.3",
-		"ncp": "2.0.0"
+		"ncp": "2.0.0",
+		"start-server-and-test": "2.0.3"
 	}
 }
diff --git a/packages/backend/package.json b/packages/backend/package.json
index aaf30bef12..cda2da911c 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -109,6 +109,7 @@
 		"file-type": "18.7.0",
 		"fluent-ffmpeg": "2.1.2",
 		"form-data": "4.0.0",
+		"glob": "^10.3.10",
 		"got": "14.0.0",
 		"happy-dom": "10.0.3",
 		"hpagent": "1.2.0",
@@ -122,8 +123,8 @@
 		"json5": "2.2.3",
 		"jsonld": "8.3.2",
 		"jsrsasign": "10.9.0",
-		"meilisearch": "0.36.0",
 		"megalodon": "workspace:*",
+		"meilisearch": "0.36.0",
 		"microformats-parser": "2.0.2",
 		"mime-types": "2.1.35",
 		"misskey-js": "workspace:*",
diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index 4a4d7e8311..9b6c2afde5 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'node:url';
 import { dirname, resolve } from 'node:path';
 import * as yaml from 'js-yaml';
 import type { RedisOptions } from 'ioredis';
+import { globSync } from 'glob';
 
 type RedisOptionsSource = Partial<RedisOptions> & {
 	host: string;
@@ -185,19 +186,26 @@ const dir = `${_dirname}/../../../.config`;
 /**
  * Path of configuration file
  */
-const path = process.env.MISSKEY_CONFIG_YML
-	? resolve(dir, process.env.MISSKEY_CONFIG_YML)
+const cfgDir = process.env.MISSKEY_CONFIG_DIR
+	? resolve(dir, process.env.MISSKEY_CONFIG_DIR)
 	: process.env.NODE_ENV === 'test'
-		? resolve(dir, 'test.yml')
-		: resolve(dir, 'default.yml');
+		? resolve(dir, './test/')
+		: dir;
 
 export function loadConfig(): Config {
 	const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8'));
-	const clientManifestExists = fs.existsSync(_dirname + '/../../../built/_vite_/manifest.json');
-	const clientManifest = clientManifestExists ?
-		JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_vite_/manifest.json`, 'utf-8'))
+	const clientManifestExists = fs.existsSync(`${_dirname}/../../../built/_vite_/manifest.json`);
+	const clientManifest = clientManifestExists
+		? JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_vite_/manifest.json`, 'utf-8'))
 		: { 'src/_boot_.ts': { file: 'src/_boot_.ts' } };
-	const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
+
+	const config = globSync(`${cfgDir}/*.{yaml,yml}`)
+		.map(path => fs.readFileSync(path, 'utf-8'))
+		.map(contents => yaml.load(contents) as Source)
+		.reduce(
+			(acc: Source, cur: Source) => Object.assign(acc, cur),
+			{} as Source,
+		) as Source;
 
 	const url = tryCreateUrl(config.url);
 	const version = meta.version;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ec05ad3485..193d294f2b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -208,6 +208,9 @@ importers:
       form-data:
         specifier: 4.0.0
         version: 4.0.0
+      glob:
+        specifier: ^10.3.10
+        version: 10.3.10
       got:
         specifier: 14.0.0
         version: 14.0.0
@@ -870,10 +873,10 @@ importers:
         version: 7.6.5
       '@storybook/vue3':
         specifier: 7.6.5
-        version: 7.6.5(@vue/compiler-core@3.3.8)(vue@3.3.12)
+        version: 7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12)
       '@storybook/vue3-vite':
         specifier: 7.6.5
-        version: 7.6.5(@vue/compiler-core@3.3.8)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12)
+        version: 7.6.5(@vue/compiler-core@3.3.12)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12)
       '@testing-library/vue':
         specifier: 8.0.1
         version: 8.0.1(@vue/compiler-sfc@3.3.12)(vue@3.3.12)
@@ -978,7 +981,7 @@ importers:
         version: 7.6.5
       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.6.5)(@storybook/components@7.5.3)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0)
+        version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.6.5)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0)
       summaly:
         specifier: github:misskey-dev/summaly
         version: github.com/misskey-dev/summaly/d2a3e07205c3c9769bc5a7b42031c8884b5a25c8
@@ -6691,17 +6694,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/channels@7.5.3:
-    resolution: {integrity: sha512-dhWuV2o2lmxH0RKuzND8jxYzvSQTSmpE13P0IT/k8+I1up/rSNYOBQJT6SalakcNWXFAMXguo/8E7ApmnKKcEw==}
-    dependencies:
-      '@storybook/client-logger': 7.5.3
-      '@storybook/core-events': 7.5.3
-      '@storybook/global': 5.0.0
-      qs: 6.11.1
-      telejson: 7.2.0
-      tiny-invariant: 1.3.1
-    dev: true
-
   /@storybook/channels@7.6.5:
     resolution: {integrity: sha512-FIlNkyfQy9uHoJfAFL2/wO3ASGJELFvBzURBE2rcEF/TS7GcUiqWnBfiDxAbwSEjSOm2F0eEq3UXhaZEjpJHDw==}
     dependencies:
@@ -6765,12 +6757,6 @@ packages:
       - utf-8-validate
     dev: true
 
-  /@storybook/client-logger@7.5.3:
-    resolution: {integrity: sha512-vUFYALypjix5FoJ5M/XUP6KmyTnQJNW1poHdW7WXUVSg+lBM6E5eAtjTm0hdxNNDH8KSrdy24nCLra5h0X0BWg==}
-    dependencies:
-      '@storybook/global': 5.0.0
-    dev: true
-
   /@storybook/client-logger@7.6.5:
     resolution: {integrity: sha512-S5aROWgssqg7tcs9lgW5wmCAz4SxMAtioiyVj5oFecmPCbQtFVIAREYzeoxE4GfJL+plrfRkum4BzziANn8EhQ==}
     dependencies:
@@ -6798,29 +6784,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/components@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-M3+cjvEsDGLUx8RvK5wyF6/13LNlUnKbMgiDE8Sxk/v/WPpyhOAIh/B8VmrU1psahS61Jd4MTkFmLf1cWau1vw==}
-    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.5.3
-      '@storybook/csf': 0.1.0
-      '@storybook/global': 5.0.0
-      '@storybook/theming': 7.5.3(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/types': 7.5.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.6.5(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-w4ZucbBBZ+NKMWlJKVj2I/bMBBq7gzDp9lzc4+8QaQ3vUPXKqc1ilIPYo/7UR5oxwDVMZocmMSgl9L8lvf7+Mw==}
     peerDependencies:
@@ -6882,12 +6845,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/core-events@7.5.3:
-    resolution: {integrity: sha512-DFOpyQ22JD5C1oeOFzL8wlqSWZzrqgDfDbUGP8xdO4wJu+FVTxnnWN6ZYLdTPB1u27DOhd7TzjQMfLDHLu7kbQ==}
-    dependencies:
-      ts-dedent: 2.2.0
-    dev: true
-
   /@storybook/core-events@7.6.5:
     resolution: {integrity: sha512-zk2q/qicYXAzHA4oV3GDbIql+Kd4TOHUgDE8e4jPCOPp856z2ScqEKUAbiJizs6eEJOH4nW9Db1kuzgrBVEykQ==}
     dependencies:
@@ -6970,12 +6927,6 @@ packages:
       - supports-color
     dev: true
 
-  /@storybook/csf@0.1.0:
-    resolution: {integrity: sha512-uk+jMXCZ8t38jSTHk2o5btI+aV2Ksbvl6DoOv3r6VaCM1KZqeuMwtwywIQdflkA8/6q/dKT8z8L+g8hC4GC3VQ==}
-    dependencies:
-      type-fest: 2.19.0
-    dev: true
-
   /@storybook/csf@0.1.2:
     resolution: {integrity: sha512-ePrvE/pS1vsKR9Xr+o+YwdqNgHUyXvg+1Xjx0h9LrVx7Zq4zNe06pd63F5EvzTbCbJsHj7GHr9tkiaqm7U8WRA==}
     dependencies:
@@ -7205,20 +7156,6 @@ packages:
       ts-dedent: 2.2.0
     dev: true
 
-  /@storybook/theming@7.5.3(react-dom@18.2.0)(react@18.2.0):
-    resolution: {integrity: sha512-Cjmthe1MAk0z4RKCZ7m72gAD8YD0zTAH97z5ryM1Qv84QXjiCQ143fGOmYz1xEQdNFpOThPcwW6FEccLHTkVcg==}
-    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.5.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.6.5(react-dom@18.2.0)(react@18.2.0):
     resolution: {integrity: sha512-RpcWT0YEgiobO41McVPDfQQHHFnjyr1sJnNTPJIvOUgSfURdgSj17mQVxtD5xcXcPWUdle5UhIOrCixHbL/NNw==}
     peerDependencies:
@@ -7233,15 +7170,6 @@ packages:
       react-dom: 18.2.0(react@18.2.0)
     dev: true
 
-  /@storybook/types@7.5.3:
-    resolution: {integrity: sha512-iu5W0Kdd6nysN5CPkY4GRl+0BpxRTdSfBIJak7mb6xCIHSB5t1tw4BOuqMQ5EgpikRY3MWJ4gY647QkWBX3MNQ==}
-    dependencies:
-      '@storybook/channels': 7.5.3
-      '@types/babel__core': 7.20.5
-      '@types/express': 4.17.17
-      file-system-cache: 2.3.0
-    dev: true
-
   /@storybook/types@7.6.5:
     resolution: {integrity: sha512-Q757v+fYZZSaEpks/zDL5YgXRozxkgKakXFc+BoQHK5q5sVhJ+0jvpLJiAQAniIIaMIkqY/G24Kd6Uo6UdKBCg==}
     dependencies:
@@ -7251,7 +7179,7 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.3.8)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12):
+  /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.3.12)(typescript@5.3.3)(vite@5.0.10)(vue@3.3.12):
     resolution: {integrity: sha512-7wUCq2Lrjlekftd5ha3hG0GSGbbzuc370cKkBqSmwFuOfI38z5+VeYt7nDtAlncxcpVSH7DejTGRuKTlC7NyYg==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
@@ -7259,7 +7187,7 @@ packages:
     dependencies:
       '@storybook/builder-vite': 7.6.5(typescript@5.3.3)(vite@5.0.10)
       '@storybook/core-server': 7.6.5
-      '@storybook/vue3': 7.6.5(@vue/compiler-core@3.3.8)(vue@3.3.12)
+      '@storybook/vue3': 7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12)
       '@vitejs/plugin-vue': 4.5.2(vite@5.0.10)(vue@3.3.12)
       magic-string: 0.30.5
       vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
@@ -7276,7 +7204,7 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.6.5(@vue/compiler-core@3.3.8)(vue@3.3.12):
+  /@storybook/vue3@7.6.5(@vue/compiler-core@3.3.12)(vue@3.3.12):
     resolution: {integrity: sha512-tv/9rVc3XXDOJu5hfZtKhrhM8x4GTLKon62Rmaxlq06weqkGlfBi/V/g1EZ7OE71Pi+woKS/TX7p9qbRrvgahg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
@@ -7288,7 +7216,7 @@ packages:
       '@storybook/global': 5.0.0
       '@storybook/preview-api': 7.6.5
       '@storybook/types': 7.6.5
-      '@vue/compiler-core': 3.3.8
+      '@vue/compiler-core': 3.3.12
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
@@ -8048,6 +7976,7 @@ packages:
 
   /@types/node@20.10.5:
     resolution: {integrity: sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==}
+    requiresBuild: true
     dependencies:
       undici-types: 5.26.5
 
@@ -12799,7 +12728,7 @@ packages:
       fs.realpath: 1.0.0
       inflight: 1.0.6
       inherits: 2.0.4
-      minimatch: 5.1.2
+      minimatch: 5.1.6
       once: 1.4.0
 
   /global-dirs@3.0.1:
@@ -15060,12 +14989,6 @@ packages:
     dependencies:
       brace-expansion: 1.1.11
 
-  /minimatch@5.1.2:
-    resolution: {integrity: sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==}
-    engines: {node: '>=10'}
-    dependencies:
-      brace-expansion: 2.0.1
-
   /minimatch@5.1.6:
     resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
     engines: {node: '>=10'}
@@ -17304,7 +17227,7 @@ packages:
   /readdir-glob@1.1.2:
     resolution: {integrity: sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==}
     dependencies:
-      minimatch: 5.1.2
+      minimatch: 5.1.6
     dev: false
 
   /readdirp@3.6.0:
@@ -18888,6 +18811,7 @@ packages:
   /ts-case-convert@2.0.2:
     resolution: {integrity: sha512-vdKfx1VAdpvEBOBv5OpVu5ZFqRg9HdTI4sYt6qqMeICBeNyXvitrarCnFWNDAki51IKwCyx+ZssY46Q9jH5otA==}
     dev: true
+    bundledDependencies: []
 
   /ts-dedent@2.2.0:
     resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
@@ -19256,6 +19180,7 @@ packages:
 
   /undici-types@5.26.5:
     resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+    requiresBuild: true
 
   /undici@5.22.1:
     resolution: {integrity: sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==}
@@ -20281,7 +20206,7 @@ packages:
       sharp: 0.31.3
     dev: false
 
-  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.5.3)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(react-dom@18.2.0)(react@18.2.0):
+  github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.6.5)(@storybook/components@7.6.5)(@storybook/core-events@7.6.5)(@storybook/manager-api@7.6.5)(@storybook/preview-api@7.6.5)(@storybook/theming@7.6.5)(@storybook/types@7.6.5)(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
@@ -20303,7 +20228,7 @@ packages:
         optional: true
     dependencies:
       '@storybook/blocks': 7.6.5(react-dom@18.2.0)(react@18.2.0)
-      '@storybook/components': 7.5.3(react-dom@18.2.0)(react@18.2.0)
+      '@storybook/components': 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/core-events': 7.6.5
       '@storybook/manager-api': 7.6.5(react-dom@18.2.0)(react@18.2.0)
       '@storybook/preview-api': 7.6.5

From df7f4aa3ecd7f883cd6d71a31711f46bab77d28e Mon Sep 17 00:00:00 2001
From: trivernis <trivernis@protonmail.com>
Date: Wed, 27 Dec 2023 16:08:37 +0100
Subject: [PATCH 355/435] Add support for glob syntax to config file env
 variables

This change allows loading config files using glob syntax, for
exakple `production-*.yml` to load all config files prefixed with
*production*. With this change the config file can also be configured
using two additional env variables `SHARKEY_CONFIG_YML`
and `SHARKEY_CONFIG_FILE`.
---
 packages/backend/src/config.ts | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index 9b6c2afde5..99a3ed0f76 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -183,14 +183,27 @@ const _dirname = dirname(_filename);
  */
 const dir = `${_dirname}/../../../.config`;
 
+function buildPath() {
+	const envVars = ['MISSKEY_CONFIG_YML', 'SHARKEY_CONFIG_YML', 'SHARKEY_CONFIG_FILE'];
+	const envCfgFile = envVars
+		.map(v => process.env[v])
+		.map(v => v ? resolve(dir, v) : undefined)
+		.find(v => !!v);
+
+	if (envCfgFile) {
+		return envCfgFile;
+	}
+	if (process.env.NODE_ENV === 'test') {
+		return resolve(dir, 'test.yml');
+	}
+
+	return resolve(dir, 'default.yml');
+}
+
 /**
- * Path of configuration file
+ * Path of configuration file. Supports loading multiple files using glob syntax
  */
-const cfgDir = process.env.MISSKEY_CONFIG_DIR
-	? resolve(dir, process.env.MISSKEY_CONFIG_DIR)
-	: process.env.NODE_ENV === 'test'
-		? resolve(dir, './test/')
-		: dir;
+const path = buildPath();
 
 export function loadConfig(): Config {
 	const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8'));
@@ -199,7 +212,7 @@ export function loadConfig(): Config {
 		? JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_vite_/manifest.json`, 'utf-8'))
 		: { 'src/_boot_.ts': { file: 'src/_boot_.ts' } };
 
-	const config = globSync(`${cfgDir}/*.{yaml,yml}`)
+	const config = globSync(path)
 		.map(path => fs.readFileSync(path, 'utf-8'))
 		.map(contents => yaml.load(contents) as Source)
 		.reduce(

From 93094bcb7206f79c67bd69536a2ce51aeda0ddc8 Mon Sep 17 00:00:00 2001
From: trivernis <trivernis@protonmail.com>
Date: Wed, 27 Dec 2023 16:35:14 +0100
Subject: [PATCH 356/435] Keep only the changes to loading the config files
 using glob patterns

---
 packages/backend/src/config.ts | 29 ++++++++---------------------
 1 file changed, 8 insertions(+), 21 deletions(-)

diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index 99a3ed0f76..24a0296aa9 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -183,33 +183,20 @@ const _dirname = dirname(_filename);
  */
 const dir = `${_dirname}/../../../.config`;
 
-function buildPath() {
-	const envVars = ['MISSKEY_CONFIG_YML', 'SHARKEY_CONFIG_YML', 'SHARKEY_CONFIG_FILE'];
-	const envCfgFile = envVars
-		.map(v => process.env[v])
-		.map(v => v ? resolve(dir, v) : undefined)
-		.find(v => !!v);
-
-	if (envCfgFile) {
-		return envCfgFile;
-	}
-	if (process.env.NODE_ENV === 'test') {
-		return resolve(dir, 'test.yml');
-	}
-
-	return resolve(dir, 'default.yml');
-}
-
 /**
- * Path of configuration file. Supports loading multiple files using glob syntax
+ * Path of configuration file
  */
-const path = buildPath();
+const path = process.env.MISSKEY_CONFIG_YML
+	? resolve(dir, process.env.MISSKEY_CONFIG_YML)
+	: process.env.NODE_ENV === 'test'
+		? resolve(dir, 'test.yml')
+		: resolve(dir, 'default.yml');
 
 export function loadConfig(): Config {
 	const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8'));
 	const clientManifestExists = fs.existsSync(`${_dirname}/../../../built/_vite_/manifest.json`);
-	const clientManifest = clientManifestExists
-		? JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_vite_/manifest.json`, 'utf-8'))
+	const clientManifest = clientManifestExists ?
+		JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_vite_/manifest.json`, 'utf-8'))
 		: { 'src/_boot_.ts': { file: 'src/_boot_.ts' } };
 
 	const config = globSync(path)

From 06d110a7739b3dff34916e5d443d930333b5bcce Mon Sep 17 00:00:00 2001
From: trivernis <trivernis@protonmail.com>
Date: Wed, 27 Dec 2023 20:56:13 +0100
Subject: [PATCH 357/435] Lock glob to exact package version 10.3.10

---
 packages/backend/package.json | 2 +-
 pnpm-lock.yaml                | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 0321936165..37a54039d2 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -109,7 +109,7 @@
 		"file-type": "18.7.0",
 		"fluent-ffmpeg": "2.1.2",
 		"form-data": "4.0.0",
-		"glob": "^10.3.10",
+		"glob": "10.3.10",
 		"got": "14.0.0",
 		"happy-dom": "10.0.3",
 		"hpagent": "1.2.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4a9777ea2b..39e943e564 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -209,7 +209,7 @@ importers:
         specifier: 4.0.0
         version: 4.0.0
       glob:
-        specifier: ^10.3.10
+        specifier: 10.3.10
         version: 10.3.10
       got:
         specifier: 14.0.0
@@ -7224,7 +7224,7 @@ packages:
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.3.12(typescript@5.3.3)
-      vue-component-type-helpers: 1.8.26
+      vue-component-type-helpers: 1.8.27
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -19627,8 +19627,8 @@ packages:
   /vscode-textmate@8.0.0:
     resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
 
-  /vue-component-type-helpers@1.8.26:
-    resolution: {integrity: sha512-CIwb7s8cqUuPpHDk+0DY8EJ/x8tzdzqw8ycX8hhw1GnbngTgSsIceHAqrrLjmv8zXi+j5XaiqYRQMw8sKyyjkw==}
+  /vue-component-type-helpers@1.8.27:
+    resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==}
     dev: true
 
   /vue-component-type-helpers@1.8.4:

From 3f6fbbaf05e42853871826c8d2bd0c3debef1f85 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Wed, 27 Dec 2023 23:30:25 +0100
Subject: [PATCH 358/435] upd: improve indication of multiple poll

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

diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue
index 641b17d066..6ee0c44658 100644
--- a/packages/frontend/src/components/MkPoll.vue
+++ b/packages/frontend/src/components/MkPoll.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<p v-if="!readOnly" :class="$style.info">
 		<span>{{ i18n.t('_poll.totalVotes', { n: total }) }}</span>
 		<span v-if="note.poll.multiple"> · </span>
-		<span v-if="note.poll.multiple">{{ i18n.ts._poll.multiple }}</span>
+		<span v-if="note.poll.multiple" style="color: var(--accent); font-weight: bolder;">{{ i18n.ts._poll.multiple }}</span>
 		<span> · </span>
 		<a v-if="!closed && !isVoted" style="color: inherit;" @click="showResult = !showResult">{{ showResult ? i18n.ts._poll.vote : i18n.ts._poll.showResult }}</a>
 		<span v-if="isVoted">{{ i18n.ts._poll.voted }}</span>
@@ -103,8 +103,8 @@ const vote = async (id) => {
 
 async function refresh() {
 	if (!props.note.uri) return;
-	const obj = await os.apiWithDialog("ap/show", { uri: props.note.uri });
-	if (obj.type === "Note" && obj.object.poll) {
+	const obj = await os.apiWithDialog('ap/show', { uri: props.note.uri });
+	if (obj.type === 'Note' && obj.object.poll) {
 		props.note.poll = obj.object.poll; // eslint-disable-line vue/no-mutating-props
 	}
 }

From 1f5256b99c5d87786d532757c924e5742fe27259 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Wed, 27 Dec 2023 23:53:37 +0100
Subject: [PATCH 359/435] upd: mute reaction notifications if thread is muted

Closes #263
---
 packages/backend/src/core/ReactionService.ts | 22 +++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts
index 4233b8d4c3..82ee9ae4ed 100644
--- a/packages/backend/src/core/ReactionService.ts
+++ b/packages/backend/src/core/ReactionService.ts
@@ -6,7 +6,7 @@
 import { Inject, Injectable } from '@nestjs/common';
 import * as Redis from 'ioredis';
 import { DI } from '@/di-symbols.js';
-import type { EmojisRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/_.js';
+import type { EmojisRepository, NoteReactionsRepository, UsersRepository, NotesRepository, NoteThreadMutingsRepository } from '@/models/_.js';
 import { IdentifiableError } from '@/misc/identifiable-error.js';
 import type { MiRemoteUser, MiUser } from '@/models/User.js';
 import type { MiNote } from '@/models/Note.js';
@@ -81,6 +81,9 @@ export class ReactionService {
 		@Inject(DI.noteReactionsRepository)
 		private noteReactionsRepository: NoteReactionsRepository,
 
+		@Inject(DI.noteThreadMutingsRepository)
+		private noteThreadMutingsRepository: NoteThreadMutingsRepository,
+
 		@Inject(DI.emojisRepository)
 		private emojisRepository: EmojisRepository,
 
@@ -244,10 +247,19 @@ export class ReactionService {
 
 		// リアクションされたユーザーがローカルユーザーなら通知を作成
 		if (note.userHost === null) {
-			this.notificationService.createNotification(note.userId, 'reaction', {
-				noteId: note.id,
-				reaction: reaction,
-			}, user.id);
+			const isThreadMuted = await this.noteThreadMutingsRepository.exist({
+				where: {
+					userId: note.userId,
+					threadId: note.threadId ?? note.id,
+				},
+			});
+
+			if (!isThreadMuted) {
+				this.notificationService.createNotification(note.userId, 'reaction', {
+					noteId: note.id,
+					reaction: reaction,
+				}, user.id);
+			}
 		}
 
 		//#region 配信

From 42cc909c5b94bd804e1974e64a75c16ca111dfb1 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Dec 2023 15:23:56 +0900
Subject: [PATCH 360/435] =?UTF-8?q?enhance(backend):=20=E3=82=BB=E3=83=B3?=
 =?UTF-8?q?=E3=82=B7=E3=83=86=E3=82=A3=E3=83=96=E3=83=AF=E3=83=BC=E3=83=89?=
 =?UTF-8?q?=E3=81=AE=E8=A8=AD=E5=AE=9A=E3=81=8C=E3=83=8F=E3=83=83=E3=82=B7?=
 =?UTF-8?q?=E3=83=A5=E3=82=BF=E3=82=B0=E3=83=88=E3=83=AC=E3=83=B3=E3=83=89?=
 =?UTF-8?q?=E3=81=AB=E3=82=82=E9=81=A9=E7=94=A8=E3=81=95=E3=82=8C=E3=82=8B?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                  | 11 ++++++++
 packages/backend/src/core/HashtagService.ts   |  3 ++
 .../backend/src/core/NoteCreateService.ts     | 27 +-----------------
 packages/backend/src/core/UtilityService.ts   | 28 +++++++++++++++++++
 4 files changed, 43 insertions(+), 26 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ac31bc0d28..af2aea7996 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,17 @@
 
 -->
 
+## 2023.12.1
+
+### General
+-
+
+### Client
+- 
+
+### Server
+- Enhance: センシティブワードの設定がハッシュタグトレンドにも適用されるようになりました
+
 ## 2023.12.0
 
 ### Note
diff --git a/packages/backend/src/core/HashtagService.ts b/packages/backend/src/core/HashtagService.ts
index d378999907..5a2417c9cd 100644
--- a/packages/backend/src/core/HashtagService.ts
+++ b/packages/backend/src/core/HashtagService.ts
@@ -15,6 +15,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { FeaturedService } from '@/core/FeaturedService.js';
 import { MetaService } from '@/core/MetaService.js';
+import { UtilityService } from '@/core/UtilityService.js';
 
 @Injectable()
 export class HashtagService {
@@ -29,6 +30,7 @@ export class HashtagService {
 		private featuredService: FeaturedService,
 		private idService: IdService,
 		private metaService: MetaService,
+		private utilityService: UtilityService,
 	) {
 	}
 
@@ -161,6 +163,7 @@ export class HashtagService {
 		const instance = await this.metaService.fetch();
 		const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t));
 		if (hiddenTags.includes(hashtag)) return;
+		if (this.utilityService.isSensitiveWordIncluded(hashtag, instance.sensitiveWords)) return;
 
 		// YYYYMMDDHHmm (10分間隔)
 		const now = new Date();
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 6406bc4c50..583fa97aff 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -422,7 +422,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 
 		if (data.visibility === 'public' && data.channel == null) {
 			const sensitiveWords = meta.sensitiveWords;
-			if (this.isSensitive(data, sensitiveWords)) {
+			if (this.utilityService.isSensitiveWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
 				data.visibility = 'home';
 			} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
 				data.visibility = 'home';
@@ -989,31 +989,6 @@ export class NoteCreateService implements OnApplicationShutdown {
 		if (!user.noindex) this.index(note);
 	}
 
-	@bindThis
-	private isSensitive(note: Option, sensitiveWord: string[]): boolean {
-		if (sensitiveWord.length > 0) {
-			const text = note.cw ?? note.text ?? '';
-			if (text === '') return false;
-			const matched = sensitiveWord.some(filter => {
-				// represents RegExp
-				const regexp = filter.match(/^\/(.+)\/(.*)$/);
-				// This should never happen due to input sanitisation.
-				if (!regexp) {
-					const words = filter.split(' ');
-					return words.every(keyword => text.includes(keyword));
-				}
-				try {
-					return new RE2(regexp[1], regexp[2]).test(text);
-				} catch (err) {
-					// This should never happen due to input sanitisation.
-					return false;
-				}
-			});
-			if (matched) return true;
-		}
-		return false;
-	}
-
 	@bindThis
 	private isQuote(note: Option): note is Option & { renote: MiNote } {
 		// sync with misc/is-quote.ts
diff --git a/packages/backend/src/core/UtilityService.ts b/packages/backend/src/core/UtilityService.ts
index b95e41167b..5dec36c89e 100644
--- a/packages/backend/src/core/UtilityService.ts
+++ b/packages/backend/src/core/UtilityService.ts
@@ -6,6 +6,7 @@
 import { URL } from 'node:url';
 import { toASCII } from 'punycode';
 import { Inject, Injectable } from '@nestjs/common';
+import RE2 from 're2';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import { bindThis } from '@/decorators.js';
@@ -41,6 +42,33 @@ export class UtilityService {
 		return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
 	}
 
+	@bindThis
+	public isSensitiveWordIncluded(text: string, sensitiveWords: string[]): boolean {
+		if (sensitiveWords.length === 0) return false;
+		if (text === '') return false;
+
+		const regexpregexp = /^\/(.+)\/(.*)$/;
+
+		const matched = sensitiveWords.some(filter => {
+			// represents RegExp
+			const regexp = filter.match(regexpregexp);
+			// This should never happen due to input sanitisation.
+			if (!regexp) {
+				const words = filter.split(' ');
+				return words.every(keyword => text.includes(keyword));
+			}
+			try {
+				// TODO: RE2インスタンスをキャッシュ
+				return new RE2(regexp[1], regexp[2]).test(text);
+			} catch (err) {
+				// This should never happen due to input sanitisation.
+				return false;
+			}
+		});
+
+		return matched;
+	}
+
 	@bindThis
 	public extractDbHost(uri: string): string {
 		const url = new URL(uri);

From f743cba26bd37f88e150ff1ed55a374a37aa5692 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=8A=E3=81=95=E3=82=80=E3=81=AE=E3=81=B2=E3=81=A8?=
 <46447427+samunohito@users.noreply.github.com>
Date: Sun, 24 Dec 2023 15:24:26 +0900
Subject: [PATCH 361/435] =?UTF-8?q?fix(backend):=201702718871541-ffVisibil?=
 =?UTF-8?q?ity.js=E3=81=AEdown=E3=81=8C=E5=A3=8A=E3=82=8C=E3=81=A6?=
 =?UTF-8?q?=E3=81=84=E3=82=8B=20(#12767)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/backend/migration/1702718871541-ffVisibility.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/packages/backend/migration/1702718871541-ffVisibility.js b/packages/backend/migration/1702718871541-ffVisibility.js
index 24b1873134..e9e820c897 100644
--- a/packages/backend/migration/1702718871541-ffVisibility.js
+++ b/packages/backend/migration/1702718871541-ffVisibility.js
@@ -24,9 +24,11 @@ export class ffVisibility1702718871541 {
 	async down(queryRunner) {
 		await queryRunner.query(`CREATE TYPE "public"."user_profile_ffvisibility_enum" AS ENUM('public', 'followers', 'private')`);
 		await queryRunner.query(`ALTER TABLE "user_profile" ADD "ffVisibility" "public"."user_profile_ffvisibility_enum" NOT NULL DEFAULT 'public'`);
+
 		await queryRunner.query(`CREATE CAST ("public"."user_profile_followingvisibility_enum" AS "public"."user_profile_ffvisibility_enum") WITH INOUT AS ASSIGNMENT`);
-		await queryRunner.query(`UPDATE "user_profile" SET ffVisibility = "user_profile"."followingVisibility"`);
+		await queryRunner.query(`UPDATE "user_profile" SET "ffVisibility" = "followingVisibility"`);
 		await queryRunner.query(`DROP CAST ("public"."user_profile_followingvisibility_enum" AS "public"."user_profile_ffvisibility_enum")`);
+
 		await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "followersVisibility"`);
 		await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "followingVisibility"`);
 		await queryRunner.query(`DROP TYPE "public"."user_profile_followersVisibility_enum"`);

From 6f65091cef9bfc1837e5d604d1818c717a2af6b4 Mon Sep 17 00:00:00 2001
From: Nya Candy <dev@candinya.com>
Date: Sun, 24 Dec 2023 14:24:51 +0800
Subject: [PATCH 362/435] fix: lint (#12761)

---
 packages/backend/src/core/EmailService.ts | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts
index 3a61e353f1..7fc7800783 100644
--- a/packages/backend/src/core/EmailService.ts
+++ b/packages/backend/src/core/EmailService.ts
@@ -7,7 +7,6 @@ import { URLSearchParams } from 'node:url';
 import * as nodemailer from 'nodemailer';
 import { Inject, Injectable } from '@nestjs/common';
 import { validate as validateEmail } from 'deep-email-validator';
-import { SubOutputFormat } from 'deep-email-validator/dist/output/output.js';
 import { MetaService } from '@/core/MetaService.js';
 import { UtilityService } from '@/core/UtilityService.js';
 import { DI } from '@/di-symbols.js';
@@ -166,7 +165,10 @@ export class EmailService {
 			email: emailAddress,
 		});
 
-		let validated;
+		let validated: {
+			valid: boolean,
+			reason?: string | null,
+		};
 
 		if (meta.enableActiveEmailValidation) {
 			if (meta.enableVerifymailApi && meta.verifymailAuthKey != null) {

From e90919ca756b55787548e4fb4eb830691c26d049 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Dec 2023 15:25:13 +0900
Subject: [PATCH 363/435] New Crowdin updates (#12759)

* New translations ja-jp.yml (Spanish)

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

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean)
---
 locales/es-ES.yml | 78 +++++++++++++++++++++++++++++++++++++++++++++++
 locales/ko-KR.yml |  5 +--
 locales/zh-TW.yml | 15 ++++-----
 3 files changed, 89 insertions(+), 9 deletions(-)

diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index a9830db35a..25b31ce428 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -121,6 +121,12 @@ sensitive: "Marcado como sensible"
 add: "Agregar"
 reaction: "Reacción"
 reactions: "Reacción"
+emojiPicker: "Selector de emojis"
+pinnedEmojisForReactionSettingDescription: "Puedes seleccionar reacciones para fijarlos en el selector"
+pinnedEmojisSettingDescription: "Puedes seleccionar emojis para fijarlos en el selector"
+emojiPickerDisplay: "Mostrar el selector de emojis"
+overwriteFromPinnedEmojisForReaction: "Sobreescribir las reacciones fijadas"
+overwriteFromPinnedEmojis: "Sobreescribir los emojis fijados"
 reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir."
 rememberNoteVisibility: "Recordar visibilidad"
 attachCancel: "Quitar adjunto"
@@ -260,6 +266,7 @@ removed: "Borrado"
 removeAreYouSure: "¿Desea borrar \"{x}\"?"
 deleteAreYouSure: "¿Desea borrar \"{x}\"?"
 resetAreYouSure: "¿Desea reestablecer?"
+areYouSure: "¿Estás conforme?"
 saved: "Guardado"
 messaging: "Chat"
 upload: "Subir"
@@ -640,6 +647,7 @@ smtpSecure: "Usar SSL/TLS implícito en la conexión SMTP"
 smtpSecureInfo: "Apagar cuando se use STARTTLS"
 testEmail: "Prueba de envío"
 wordMute: "Silenciar palabras"
+hardWordMute: "Filtro de palabra fuerte"
 regexpError: "Error de la expresión regular"
 regexpErrorDescription: "Ocurrió un error en la expresión regular en la linea {line} de las palabras muteadas {tab}"
 instanceMute: "Instancias silenciadas"
@@ -873,6 +881,8 @@ makeReactionsPublicDescription: "Todas las reacciones que hayas hecho serán pú
 classic: "Clásico"
 muteThread: "Silenciar hilo"
 unmuteThread: "Mostrar hilo"
+followingVisibility: "Visibilidad de seguidos"
+followersVisibility: "Visibilidad de seguidores"
 continueThread: "Ver la continuación del hilo"
 deleteAccountConfirm: "La cuenta será borrada. ¿Está seguro?"
 incorrectPassword: "La contraseña es incorrecta"
@@ -1024,6 +1034,7 @@ sensitiveWords: "Palabras sensibles"
 sensitiveWordsDescription: "La visibilidad de todas las notas que contienen cualquiera de las palabras configuradas serán puestas en \"Inicio\" automáticamente. Puedes enumerás varias separándolas con saltos de línea"
 sensitiveWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares."
 hiddenTags: "Hashtags ocultos"
+hiddenTagsDescription: "Selecciona las etiquetas que no se mostrarán en tendencias. Una etiqueta por línea."
 notesSearchNotAvailable: "No se puede buscar una nota"
 license: "Licencia"
 unfavoriteConfirm: "¿Desea quitar de favoritos?"
@@ -1152,6 +1163,7 @@ tosAndPrivacyPolicy: "Condiciones de Uso y Política de Privacidad"
 avatarDecorations: "Decoraciones de avatar"
 attach: "Acoplar"
 detach: "Quitar"
+detachAll: "Quitar todo"
 angle: "Ángulo"
 flip: "Echar de un capirotazo"
 showAvatarDecorations: "Mostrar decoraciones de avatar"
@@ -1165,6 +1177,10 @@ cwNotationRequired: "Si se ha activado \"ocultar contenido\", es necesario propo
 doReaction: "Añadir reacción"
 code: "Código"
 reloadRequiredToApplySettings: "Es necesario recargar para que se aplique la configuración."
+remainingN: "Faltan: {n}"
+overwriteContentConfirm: "¿Quieres sustituir todo el contenido actual?"
+seasonalScreenEffect: "Efectos de pantalla asociados a estaciones"
+decorate: "Decorar"
 _announcement:
   forExistingUsers: "Solo para usuarios registrados"
   forExistingUsersDescription: "Este anuncio solo se mostrará a aquellos usuarios registrados en el momento de su publicación. Si se deshabilita esta opción, aquellos usuarios que se registren tras su publicación también lo verán."
@@ -1222,6 +1238,45 @@ _initialTutorial:
     home: "Puedes ver los posts de las cuentas que sigues."
     local: "Puedes ver los posts de todos los usuarios de este servidor."
     social: "Se ven los posts de la línea de tiempo de inicio junto con los de la línea de tiempo local."
+    global: "Puedes ver notas de todos los servidores conectados."
+    description2: "Puedes cambiar la línea de tiempo en la parte superior de la pantalla cuando quieras."
+    description3: "Además, hay listas de líneas de tiempo y listas de canales. Para más detalle, por favor visita este enlace: {link}"
+  _postNote:
+    title: "Ajustes de publicación de nota"
+    description1: "Cuando publicas una nota en Misskey, hay varias opciones disponibles. El formulario tiene este aspecto."
+    _visibility:
+      description: "Puedes limitar quién puede ver tu nota."
+      public: "Tu nota será visible para todos los usuarios."
+      home: "Publicar solo en la línea de tiempo de Inicio. La nota se verá en tu perfil, la verán tus seguidores y también cuando sea renotada."
+      followers: "Visible solo para seguidores. Sólo tus seguidores podrán ver la nota, y no podrá ser renotada por otras personas."
+      direct: "Visible sólo para usuarios específicos, y el destinatario será notificado. Puede usarse como alternativa a la mensajería directa."
+      doNotSendConfidencialOnDirect1: "¡Ten cuidado cuando vayas a enviar información sensible!"
+      doNotSendConfidencialOnDirect2: "Los administradores del servidor pueden leer lo que escribes. Ten cuidado cuando envíes información sensible en notas directas en servidores no confiables."
+      localOnly: "Publicando con esta opción seleccionada, la nota no se federará hacia otros servidores. Los usuarios de otros servidores no podrán ver estas notas directamente, sin importar los ajustes seleccionados más arriba."
+    _cw:
+      title: "Alerta de contenido (CW)"
+      description: "En lugar de mostrarse el contenido de la nota, se mostrará lo que escribas en el campo \"comentarios\". Pulsando en \"leer más\" desplegará el contenido de la nota."
+      _exampleNote:
+        cw: "¡Esto te hará tener hambre!"
+        note: "Acabo de comerme un donut de chocolate glaseado 🍩😋"
+      useCases: "Esto se usa cuando las normas del servidor lo requieren, o para ocultar spoilers o contenido sensible."
+  _howToMakeAttachmentsSensitive:
+    title: "¿Cómo puedo marcar adjuntos como contenido sensible?"
+    description: "Cuando las normas del servidor lo requieran, o el contenido lo requiera, marca la opción de \"contenido sensible\" para el adjunto."
+    tryThisFile: "¡Prueba a marcar la imagen adjunta como contenido sensible!"
+    _exampleNote:
+      note: "Ups, la he liado al abrir la tapa del natto..."
+    method: "Para marcar un adjunto como sensible, haz clic en la miniatura, abre el menú, y haz clic en \"Marcar como sensible\"."
+    sensitiveSucceeded: "Cuando adjuntes archivos, por favor, ten en cuenta las normas del servidor para marcarlos como contenido sensible."
+    doItToContinue: "Marca el archivo adjunto como sensible para continuar."
+  _done:
+    title: "¡Has completado el tutorial! 🎉"
+    description: "Las funciones que mostramos aquí son sólo una pequeña parte. Para más detalles sobre el funcionamiento de Misskey, pulsa en este enlace: {link}"
+_timelineDescription:
+  home: "En la línea de tiempo de Inicio puedes ver las notas de las cuentas a las que sigues."
+  local: "En la línea de tiempo Local puedes ver las notas de todos los usuarios del servidor."
+  social: "En la línea de tiempo Social verás las notas de Inicio y Local a la vez."
+  global: "En la línea de tiempo Global verás las notas de todos los servidores conectados."
 _serverRules:
   description: "Un conjunto de reglas que serán mostradas antes del registro. Configurar un sumario de términos de servicio es recomendado."
 _serverSettings:
@@ -1233,6 +1288,7 @@ _serverSettings:
   manifestJsonOverride: "Sobreescribir manifest.json"
   shortName: "Nombre corto"
   shortNameDescription: "Forma corta del nombre de la instancia que puede mostrarse si el nombre completo es demasiado largo."
+  fanoutTimelineDescription: "Incrementa el rendimiento de forma significativa cuando se obtienen las líneas de tiempo y reduce la carga en la base de datos. A cambio, el uso de la memoria en Redis incrementará. Considera desactivar esta opción en caso de que tu servidor tenga poca memoria o detectes inestabilidad."
 _accountMigration:
   moveFrom: "Trasladar de otra cuenta a ésta"
   moveFromSub: "Crear un alias para otra cuenta."
@@ -1490,6 +1546,9 @@ _achievements:
     _smashTestNotificationButton:
       title: "Sobrecarga de pruebas"
       description: "Envía muchas notificaciones de prueba en un corto espacio de tiempo"
+    _tutorialCompleted:
+      title: "Diploma del Curso Básico de Misskey"
+      description: "Tutorial completado"
 _role:
   new: "Crear rol"
   edit: "Editar rol"
@@ -1500,7 +1559,9 @@ _role:
   assignTarget: "Asignar objetivo"
   descriptionOfAssignTarget: "<b>Manual</b> Para cambiar manualmente lo que se incluye en este rol.\n<b>Condicional</b> configura una condición, y los usuarios que cumplan la condición serán incluídos automáticamente."
   manual: "manual"
+  manualRoles: "Roles manuales"
   conditional: "condicional"
+  conditionalRoles: "Roles condicionales"
   condition: "condición"
   isConditionalRole: "Esto es un rol condicional"
   isPublic: "Publicar rol"
@@ -1549,6 +1610,7 @@ _role:
     canHideAds: "Puede ocultar anuncios"
     canSearchNotes: "Uso de la búsqueda de notas"
     canUseTranslator: "Uso de traductor"
+    avatarDecorationLimit: "Número máximo de decoraciones de avatar"
   _condition:
     isLocal: "Usuario local"
     isRemote: "Usuario remoto"
@@ -1577,6 +1639,7 @@ _emailUnavailable:
   disposable: "No es un correo reutilizable"
   mx: "Servidor de correo inválido"
   smtp: "Servidor de correo no disponible"
+  banned: "Email no disponible"
 _ffVisibility:
   public: "Publicar"
   followers: "Visible solo para seguidores"
@@ -1653,6 +1716,7 @@ _aboutMisskey:
   donate: "Donar a Misskey"
   morePatrons: "Muchas más personas nos apoyan. Muchas gracias🥰"
   patrons: "Patrocinadores"
+  projectMembers: "Miembros del proyecto"
 _displayOfSensitiveMedia:
   respect: "Esconder medios marcados como sensibles"
   ignore: "Mostrar medios marcados como sensibles"
@@ -1677,6 +1741,7 @@ _channel:
   notesCount: "{n} notas"
   nameAndDescription: "Nombre y descripción"
   nameOnly: "Sólo nombre"
+  allowRenoteToExternal: "Permitir renotas y menciones fuera del canal"
 _menuDisplay:
   sideFull: "Horizontal"
   sideIcon: "Horizontal (ícono)"
@@ -1780,6 +1845,12 @@ _ago:
   yearsAgo: "Hace {n} años"
   invalid: "No hay nada que ver aqui"
 _timeIn:
+  seconds: "En {n} segundos"
+  minutes: "En {n}m"
+  hours: "En {n}h"
+  days: "En {n}d"
+  weeks: "En {n}sem."
+  months: "En {n}M"
   years: "En {n} años"
 _time:
   second: "Segundos"
@@ -1906,6 +1977,7 @@ _widgets:
   _userList:
     chooseList: "Seleccione una lista"
   clicker: "Cliqueador"
+  birthdayFollowings: "Hoy cumplen años"
 _cw:
   hide: "Ocultar"
   show: "Ver más"
@@ -1969,6 +2041,7 @@ _profile:
   changeAvatar: "Cambiar avatar"
   changeBanner: "Cambiar banner"
   verifiedLinkDescription: "Introduciendo una URL que contiene un enlace a tu perfil, se puede mostrar un icono de verificación de propiedad al lado del campo."
+  avatarDecorationMax: "Puedes añadir un máximo de {max} decoraciones de avatar."
 _exportOrImport:
   allNotes: "Todas las notas"
   favoritedNotes: "Notas favoritas"
@@ -2090,6 +2163,7 @@ _notification:
   pollEnded: "Estan disponibles los resultados de la encuesta"
   newNote: "Nueva nota"
   unreadAntennaNote: "Antena {name}"
+  roleAssigned: "Rol asignado"
   emptyPushNotificationMessage: "Se han actualizado las notificaciones push"
   achievementEarned: "Logro desbloqueado"
   testNotification: "Notificación de prueba"
@@ -2111,6 +2185,7 @@ _notification:
     pollEnded: "La encuesta terminó"
     receiveFollowRequest: "Recibió una solicitud de seguimiento"
     followRequestAccepted: "El seguimiento fue aceptado"
+    roleAssigned: "Rol asignado"
     achievementEarned: "Logro desbloqueado"
     app: "Notificaciones desde aplicaciones"
   _actions:
@@ -2256,3 +2331,6 @@ _externalResourceInstaller:
     _themeInstallFailed:
       title: "Instalación de tema fallida"
       description: "Ha ocurrido un problema al instalar el tema. Por favor, inténtalo de nuevo. Se pueden ver más detalles del error en la consola de Javascript."
+_dataSaver:
+  _media:
+    title: "Cargando Multimedia"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 6cdcc2c246..63d0812e93 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -114,7 +114,7 @@ quote: "인용"
 inChannelRenote: "채널 내 리노트"
 inChannelQuote: "채널 내 인용"
 pinnedNote: "고정된 노트"
-pinned: "프로필에 고정"
+pinned: "고정하기"
 you: "나"
 clickToShow: "클릭하여 보기"
 sensitive: "열람 주의"
@@ -1179,7 +1179,7 @@ code: "문자열"
 reloadRequiredToApplySettings: "설정을 적용하려면 새로고침을 해야 합니다."
 remainingN: "나머지: {n}"
 overwriteContentConfirm: "현재 내용을 덮어쓰기 합니다. 계속 진행하시겠습니까?"
-seasonalScreenEffect: "철에 맞는 화면으로 꾸미기"
+seasonalScreenEffect: "계절에 따른 효과 보이기"
 decorate: "장식하기"
 _announcement:
   forExistingUsers: "기존 유저에게만 알림"
@@ -1641,6 +1641,7 @@ _emailUnavailable:
   disposable: "임시 이메일 주소는 사용할 수 없습니다"
   mx: "메일 서버가 올바르지 않습니다"
   smtp: "메일 서버가 응답하지 않습니다"
+  banned: "이 메일 주소는 사용할 수 없습니다"
 _ffVisibility:
   public: "공개"
   followers: "팔로워에게만 공개"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 51ba42e66c..782f871b1e 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -632,11 +632,11 @@ tokenRequested: "允許存取帳戶"
 pluginTokenRequestedDescription: "此外掛將擁有在此設定的權限。"
 notificationType: "通知形式"
 edit: "編輯"
-emailServer: "電郵伺服器"
-enableEmail: "啟用發送電郵功能"
-emailConfigInfo: "用於確認電郵地址及密碼重置"
+emailServer: "電子郵件伺服器"
+enableEmail: "啟用發送電子郵件功能"
+emailConfigInfo: "用於確認電子郵件地址及密碼重置"
 email: "電子郵件"
-emailAddress: "電郵地址"
+emailAddress: "電子郵件位址"
 smtpConfig: "SMTP 伺服器設定"
 smtpHost: "主機"
 smtpPort: "埠"
@@ -731,7 +731,7 @@ disableShowingAnimatedImages: "不播放動態圖檔"
 highlightSensitiveMedia: "強調敏感標記"
 verificationEmailSent: "已發送驗證電子郵件。請點擊進入電子郵件中的鏈接完成驗證。"
 notSet: "未設定"
-emailVerified: "已成功驗證您的電郵"
+emailVerified: "已成功驗證您的電子郵件地址"
 noteFavoritesCount: "我的最愛貼文的數目"
 pageLikesCount: "頁面被按讚次數"
 pageLikedCount: "頁面被按讚次數"
@@ -783,7 +783,7 @@ capacity: "容量"
 inUse: "已使用"
 editCode: "編輯代碼"
 apply: "套用"
-receiveAnnouncementFromInstance: "接收由本實例發出的電郵通知"
+receiveAnnouncementFromInstance: "接收來自伺服器的通知"
 emailNotification: "郵件通知"
 publish: "發布"
 inChannelSearch: "頻道内搜尋"
@@ -955,7 +955,7 @@ cannotUploadBecauseExceedsFileSizeLimit: "由於超過了檔案大小的限制
 beta: "測試版"
 enableAutoSensitive: "自動 NSFW 判定"
 enableAutoSensitiveDescription: "如果可用,它將使用機器學習技術判斷檔案是否需要標記為敏感。即使關閉此功能,也可能會依實例規則而自動啟用。"
-activeEmailValidationDescription: "積極驗證使用者的電郵地址,以判斷它是否可以通訊。關閉此選項代表只會檢查地址是否符合格式。"
+activeEmailValidationDescription: "主動地驗證使用者的電子郵件地址,以確定是否是一次性地址以及是否可以真正與其進行通訊。關閉時,僅檢查格式是否正確。"
 navbar: "導覽列"
 shuffle: "隨機"
 account: "帳戶"
@@ -1641,6 +1641,7 @@ _emailUnavailable:
   disposable: "不是永久可用的地址"
   mx: "郵件伺服器不正確"
   smtp: "郵件伺服器沒有應答"
+  banned: "無法使用此電子郵件地址註冊"
 _ffVisibility:
   public: "公開"
   followers: "只有關注您的使用者能看到"

From 20a52ceb07417ee741049511f2179bfc3548641e Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Dec 2023 15:31:48 +0900
Subject: [PATCH 364/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index af2aea7996..4751fff654 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -110,6 +110,7 @@
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
 - Enhance: Meilisearchを有効にした検索で、ユーザーのミュートやブロックを考慮するように
 - Enhance: カスタム絵文字のインポート時の動作を改善
+- Enhance: json-schema(OpenAPIの戻り値として使用されるスキーマ定義)を出来る限り最新化 #12311
 - Fix: 時間経過により無効化されたアンテナを再有効化したとき、サーバ再起動までその状況が反映されないのを修正 #12303
 - Fix: ロールタイムラインが保存されない問題を修正
 - Fix: api.jsonの生成ロジックを改善 #12402
@@ -137,7 +138,6 @@
 - Feat: 管理者がコントロールパネルからメールアドレスの照会を行えるようになりました
 - Enhance: ローカリゼーションの更新
 - Enhance: 依存関係の更新
-- Enhance: json-schema(OpenAPIの戻り値として使用されるスキーマ定義)を出来る限り最新化 #12311
 
 ### Client
 - Enhance: MFMでルビを振れるように

From 4b36ed728d2e6cef2e169c7446c64494932596b8 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sun, 24 Dec 2023 15:38:03 +0900
Subject: [PATCH 365/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4751fff654..432b39afb6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,13 +15,14 @@
 ## 2023.12.1
 
 ### General
--
+- Enhance: ローカリゼーションの更新
 
 ### Client
 - 
 
 ### Server
 - Enhance: センシティブワードの設定がハッシュタグトレンドにも適用されるようになりました
+- Fix: 1702718871541-ffVisibility.jsのdownが壊れている
 
 ## 2023.12.0
 

From ed3e30581f949cb7d75e6cc7c9fa634d785e70e6 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Sun, 24 Dec 2023 16:16:58 +0900
Subject: [PATCH 366/435] =?UTF-8?q?refactor(frontend):=20import=E5=AE=A3?=
 =?UTF-8?q?=E8=A8=80=E5=91=A8=E3=82=8A=E3=81=AE=E3=82=A8=E3=83=A9=E3=83=BC?=
 =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12773)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 ...lugin-unwind-css-module-class-name.test.ts |  2 +-
 .../components/MkAbuseReport.stories.impl.ts  |  4 ++--
 .../MkAbuseReportWindow.stories.impl.ts       |  4 ++--
 .../components/MkAccountMoved.stories.impl.ts |  2 +-
 .../components/MkAchievements.stories.impl.ts |  4 ++--
 .../components/MkAutocomplete.stories.impl.ts |  4 ++--
 .../src/components/MkAvatars.stories.impl.ts  |  4 ++--
 .../frontend/src/components/MkContextMenu.vue |  2 +-
 .../src/components/MkDateSeparatedList.vue    |  4 ++--
 .../MkGalleryPostPreview.stories.impl.ts      |  2 +-
 .../components/MkInviteCode.stories.impl.ts   |  4 ++--
 packages/frontend/src/components/MkMenu.vue   |  2 +-
 .../frontend/src/components/MkPageWindow.vue  |  2 +-
 .../frontend/src/components/MkPagination.vue  |  2 +-
 .../MkUserSetupDialog.Follow.stories.impl.ts  |  4 ++--
 .../MkUserSetupDialog.User.stories.impl.ts    |  2 +-
 .../MkUserSetupDialog.stories.impl.ts         |  4 ++--
 .../frontend/src/components/MkWidgets.vue     |  2 +-
 .../components/global/MkAcct.stories.impl.ts  |  2 +-
 .../global/MkAvatar.stories.impl.ts           |  2 +-
 .../MkMisskeyFlavoredMarkdown.stories.impl.ts |  2 +-
 .../components/global/MkStickyContainer.vue   |  2 +-
 .../components/global/MkUrl.stories.impl.ts   |  2 +-
 .../global/MkUserName.stories.impl.ts         |  2 +-
 .../src/components/global/RouterView.vue      |  2 +-
 .../src/components/page/page.block.vue        |  2 +-
 .../src/components/page/page.image.vue        |  2 +-
 .../src/components/page/page.note.vue         |  2 +-
 .../src/components/page/page.section.vue      |  2 +-
 .../src/components/page/page.text.vue         |  2 +-
 packages/frontend/src/directives/hotkey.ts    |  2 +-
 packages/frontend/src/directives/index.ts     | 22 +++++++++----------
 .../frontend/src/pages/admin/_header_.vue     |  2 +-
 .../frontend/src/pages/admin/roles.edit.vue   |  2 +-
 packages/frontend/src/pages/admin/roles.vue   |  2 +-
 packages/frontend/src/pages/clip.vue          |  2 +-
 .../frontend/src/pages/my-antennas/edit.vue   |  2 +-
 .../frontend/src/pages/my-antennas/index.vue  |  2 +-
 .../frontend/src/pages/my-clips/index.vue     |  2 +-
 .../frontend/src/pages/my-lists/index.vue     |  2 +-
 .../frontend/src/pages/settings/navbar.vue    |  2 +-
 .../src/pages/settings/theme.manage.vue       |  2 +-
 .../frontend/src/pages/settings/theme.vue     |  2 +-
 packages/frontend/src/pages/theme-editor.vue  |  2 +-
 .../src/pages/user/home.stories.impl.ts       |  4 ++--
 .../src/scripts/upload/compress-config.ts     |  2 +-
 packages/frontend/src/ui/_common_/common.vue  |  4 ++--
 .../src/ui/_common_/navbar-for-mobile.vue     |  4 ++--
 packages/frontend/src/ui/classic.header.vue   |  4 ++--
 packages/frontend/src/ui/deck/column.vue      |  2 +-
 packages/frontend/src/ui/deck/list-column.vue |  2 +-
 packages/frontend/src/ui/deck/main-column.vue |  2 +-
 .../src/widgets/server-metric/index.vue       |  2 +-
 packages/frontend/test/init.ts                |  4 ++--
 packages/frontend/vite.config.local-dev.ts    |  2 +-
 packages/frontend/vite.config.ts              |  7 +++---
 56 files changed, 83 insertions(+), 82 deletions(-)

diff --git a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts
index fab80eae2f..d78f054395 100644
--- a/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts
+++ b/packages/frontend/lib/rollup-plugin-unwind-css-module-class-name.test.ts
@@ -6,7 +6,7 @@
 import { parse } from 'acorn';
 import { generate } from 'astring';
 import { describe, expect, it } from 'vitest';
-import { normalizeClass, unwindCssModuleClassName } from './rollup-plugin-unwind-css-module-class-name';
+import { normalizeClass, unwindCssModuleClassName } from './rollup-plugin-unwind-css-module-class-name.js';
 import type * as estree from 'estree';
 
 function parseExpression(code: string): estree.Expression {
diff --git a/packages/frontend/src/components/MkAbuseReport.stories.impl.ts b/packages/frontend/src/components/MkAbuseReport.stories.impl.ts
index 3b64529620..77e7c84d5c 100644
--- a/packages/frontend/src/components/MkAbuseReport.stories.impl.ts
+++ b/packages/frontend/src/components/MkAbuseReport.stories.impl.ts
@@ -7,8 +7,8 @@
 import { action } from '@storybook/addon-actions';
 import { StoryObj } from '@storybook/vue3';
 import { rest } from 'msw';
-import { abuseUserReport } from '../../.storybook/fakes';
-import { commonHandlers } from '../../.storybook/mocks';
+import { abuseUserReport } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
 import MkAbuseReport from './MkAbuseReport.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts
index b45d54679b..dc842b3d1b 100644
--- a/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts
+++ b/packages/frontend/src/components/MkAbuseReportWindow.stories.impl.ts
@@ -7,8 +7,8 @@
 import { action } from '@storybook/addon-actions';
 import { StoryObj } from '@storybook/vue3';
 import { rest } from 'msw';
-import { userDetailed } from '../../.storybook/fakes';
-import { commonHandlers } from '../../.storybook/mocks';
+import { userDetailed } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
 import MkAbuseReportWindow from './MkAbuseReportWindow.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/components/MkAccountMoved.stories.impl.ts b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts
index a6d4d18c1b..33c6c24631 100644
--- a/packages/frontend/src/components/MkAccountMoved.stories.impl.ts
+++ b/packages/frontend/src/components/MkAccountMoved.stories.impl.ts
@@ -5,7 +5,7 @@
 
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { StoryObj } from '@storybook/vue3';
-import { userDetailed } from '../../.storybook/fakes';
+import { userDetailed } from '../../.storybook/fakes.js';
 import MkAccountMoved from './MkAccountMoved.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/components/MkAchievements.stories.impl.ts b/packages/frontend/src/components/MkAchievements.stories.impl.ts
index a67e1def13..6d972467b1 100644
--- a/packages/frontend/src/components/MkAchievements.stories.impl.ts
+++ b/packages/frontend/src/components/MkAchievements.stories.impl.ts
@@ -6,8 +6,8 @@
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { StoryObj } from '@storybook/vue3';
 import { rest } from 'msw';
-import { userDetailed } from '../../.storybook/fakes';
-import { commonHandlers } from '../../.storybook/mocks';
+import { userDetailed } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
 import MkAchievements from './MkAchievements.vue';
 import { ACHIEVEMENT_TYPES } from '@/scripts/achievements.js';
 export const Empty = {
diff --git a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
index 8232759ba0..969519386f 100644
--- a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
+++ b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
@@ -9,8 +9,8 @@ import { expect } from '@storybook/jest';
 import { userEvent, waitFor, within } from '@storybook/testing-library';
 import { StoryObj } from '@storybook/vue3';
 import { rest } from 'msw';
-import { userDetailed } from '../../.storybook/fakes';
-import { commonHandlers } from '../../.storybook/mocks';
+import { userDetailed } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
 import MkAutocomplete from './MkAutocomplete.vue';
 import MkInput from './MkInput.vue';
 import { tick } from '@/scripts/test-utils.js';
diff --git a/packages/frontend/src/components/MkAvatars.stories.impl.ts b/packages/frontend/src/components/MkAvatars.stories.impl.ts
index 659c0eebdf..d41b64695f 100644
--- a/packages/frontend/src/components/MkAvatars.stories.impl.ts
+++ b/packages/frontend/src/components/MkAvatars.stories.impl.ts
@@ -6,8 +6,8 @@
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { StoryObj } from '@storybook/vue3';
 import { rest } from 'msw';
-import { userDetailed } from '../../.storybook/fakes';
-import { commonHandlers } from '../../.storybook/mocks';
+import { userDetailed } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
 import MkAvatars from './MkAvatars.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue
index b78252be89..e29cf472f7 100644
--- a/packages/frontend/src/components/MkContextMenu.vue
+++ b/packages/frontend/src/components/MkContextMenu.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, onBeforeUnmount, shallowRef, ref } from 'vue';
 import MkMenu from './MkMenu.vue';
-import { MenuItem } from './types/menu.vue';
+import { MenuItem } from '@/types/menu.js';
 import contains from '@/scripts/contains.js';
 import { defaultStore } from '@/store.js';
 import * as os from '@/os.js';
diff --git a/packages/frontend/src/components/MkDateSeparatedList.vue b/packages/frontend/src/components/MkDateSeparatedList.vue
index b5508af62e..b45aef45ff 100644
--- a/packages/frontend/src/components/MkDateSeparatedList.vue
+++ b/packages/frontend/src/components/MkDateSeparatedList.vue
@@ -6,11 +6,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts">
 import { defineComponent, h, PropType, TransitionGroup, useCssModule } from 'vue';
 import MkAd from '@/components/global/MkAd.vue';
-import { isDebuggerEnabled, stackTraceInstances } from '@/debug';
+import { isDebuggerEnabled, stackTraceInstances } from '@/debug.js';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
 import { defaultStore } from '@/store.js';
-import { MisskeyEntity } from '@/types/date-separated-list';
+import { MisskeyEntity } from '@/types/date-separated-list.js';
 
 export default defineComponent({
 	props: {
diff --git a/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts b/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts
index 29e27e1373..035b727a35 100644
--- a/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts
+++ b/packages/frontend/src/components/MkGalleryPostPreview.stories.impl.ts
@@ -7,7 +7,7 @@
 import { expect } from '@storybook/jest';
 import { userEvent, waitFor, within } from '@storybook/testing-library';
 import { StoryObj } from '@storybook/vue3';
-import { galleryPost } from '../../.storybook/fakes';
+import { galleryPost } from '../../.storybook/fakes.js';
 import MkGalleryPostPreview from './MkGalleryPostPreview.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/components/MkInviteCode.stories.impl.ts b/packages/frontend/src/components/MkInviteCode.stories.impl.ts
index 0127ce061b..2ea32dd3b6 100644
--- a/packages/frontend/src/components/MkInviteCode.stories.impl.ts
+++ b/packages/frontend/src/components/MkInviteCode.stories.impl.ts
@@ -6,8 +6,8 @@
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { StoryObj } from '@storybook/vue3';
 import { rest } from 'msw';
-import { userDetailed, inviteCode } from '../../.storybook/fakes';
-import { commonHandlers } from '../../.storybook/mocks';
+import { userDetailed, inviteCode } from '../../.storybook/fakes.js';
+import { commonHandlers } from '../../.storybook/mocks.js';
 import MkInviteCode from './MkInviteCode.vue';
 
 export const Default = {
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index b0f997a1b9..5f48f43bfb 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -77,7 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, shallowRef, watch } from 'vue';
 import { focusPrev, focusNext } from '@/scripts/focus.js';
 import MkSwitchButton from '@/components/MkSwitch.button.vue';
-import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu';
+import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu.js';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { isTouchUsing } from '@/scripts/touch.js';
diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue
index d1d4c2106c..13a703e9f6 100644
--- a/packages/frontend/src/components/MkPageWindow.vue
+++ b/packages/frontend/src/components/MkPageWindow.vue
@@ -37,7 +37,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { url } from '@/config.js';
 import { mainRouter, routes, page } from '@/router.js';
 import { $i } from '@/account.js';
-import { Router, useScrollPositionManager } from '@/nirax';
+import { Router, useScrollPositionManager } from '@/nirax.js';
 import { i18n } from '@/i18n.js';
 import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
 import { openingWindowsCount } from '@/os.js';
diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue
index 07347eda29..bdd96238d3 100644
--- a/packages/frontend/src/components/MkPagination.vue
+++ b/packages/frontend/src/components/MkPagination.vue
@@ -49,7 +49,7 @@ import * as os from '@/os.js';
 import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js';
 import { useDocumentVisibility } from '@/scripts/use-document-visibility.js';
 import { defaultStore } from '@/store.js';
-import { MisskeyEntity } from '@/types/date-separated-list';
+import { MisskeyEntity } from '@/types/date-separated-list.js';
 import { i18n } from '@/i18n.js';
 
 const SECOND_FETCH_LIMIT = 30;
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts
index 9122bb8983..45c7da40ce 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts
+++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.stories.impl.ts
@@ -6,8 +6,8 @@
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { StoryObj } from '@storybook/vue3';
 import { rest } from 'msw';
-import { commonHandlers } from '../../.storybook/mocks';
-import { userDetailed } from '../../.storybook/fakes';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import { userDetailed } from '../../.storybook/fakes.js';
 import MkUserSetupDialog_Follow from './MkUserSetupDialog.Follow.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts
index 3324c0186c..31176c0832 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts
+++ b/packages/frontend/src/components/MkUserSetupDialog.User.stories.impl.ts
@@ -5,7 +5,7 @@
 
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { StoryObj } from '@storybook/vue3';
-import { userDetailed } from '../../.storybook/fakes';
+import { userDetailed } from '../../.storybook/fakes.js';
 import MkUserSetupDialog_User from './MkUserSetupDialog.User.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts b/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts
index 2795bcb2fa..5182db12b2 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts
+++ b/packages/frontend/src/components/MkUserSetupDialog.stories.impl.ts
@@ -6,8 +6,8 @@
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { StoryObj } from '@storybook/vue3';
 import { rest } from 'msw';
-import { commonHandlers } from '../../.storybook/mocks';
-import { userDetailed } from '../../.storybook/fakes';
+import { commonHandlers } from '../../.storybook/mocks.js';
+import { userDetailed } from '../../.storybook/fakes.js';
 import MkUserSetupDialog from './MkUserSetupDialog.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue
index 35300a3bf3..a5d5ff733f 100644
--- a/packages/frontend/src/components/MkWidgets.vue
+++ b/packages/frontend/src/components/MkWidgets.vue
@@ -54,7 +54,7 @@ import { defineAsyncComponent, ref } from 'vue';
 import { v4 as uuid } from 'uuid';
 import MkSelect from '@/components/MkSelect.vue';
 import MkButton from '@/components/MkButton.vue';
-import { widgets as widgetDefs } from '@/widgets';
+import { widgets as widgetDefs } from '@/widgets/index.js';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 
diff --git a/packages/frontend/src/components/global/MkAcct.stories.impl.ts b/packages/frontend/src/components/global/MkAcct.stories.impl.ts
index 00c1d94330..49ec61211c 100644
--- a/packages/frontend/src/components/global/MkAcct.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAcct.stories.impl.ts
@@ -5,7 +5,7 @@
 
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { StoryObj } from '@storybook/vue3';
-import { userDetailed } from '../../../.storybook/fakes';
+import { userDetailed } from '../../../.storybook/fakes.js';
 import MkAcct from './MkAcct.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/components/global/MkAvatar.stories.impl.ts b/packages/frontend/src/components/global/MkAvatar.stories.impl.ts
index 877511f8fc..515d7eab18 100644
--- a/packages/frontend/src/components/global/MkAvatar.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAvatar.stories.impl.ts
@@ -5,7 +5,7 @@
 
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { StoryObj } from '@storybook/vue3';
-import { userDetailed } from '../../../.storybook/fakes';
+import { userDetailed } from '../../../.storybook/fakes.js';
 import MkAvatar from './MkAvatar.vue';
 const common = {
 	render(args) {
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts
index 9d9febf693..9cdb490e4b 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.stories.impl.ts
@@ -7,7 +7,7 @@
 import { StoryObj } from '@storybook/vue3';
 import { within } from '@storybook/testing-library';
 import { expect } from '@storybook/jest';
-import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.ts';
+import MkMisskeyFlavoredMarkdown from './MkMisskeyFlavoredMarkdown.js';
 export const Default = {
 	render(args) {
 		return {
diff --git a/packages/frontend/src/components/global/MkStickyContainer.vue b/packages/frontend/src/components/global/MkStickyContainer.vue
index 1d707af2d1..70cc68b14c 100644
--- a/packages/frontend/src/components/global/MkStickyContainer.vue
+++ b/packages/frontend/src/components/global/MkStickyContainer.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, onUnmounted, provide, inject, Ref, ref, watch, shallowRef } from 'vue';
 
-import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const';
+import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@/const.js';
 
 const rootEl = shallowRef<HTMLElement>();
 const headerEl = shallowRef<HTMLElement>();
diff --git a/packages/frontend/src/components/global/MkUrl.stories.impl.ts b/packages/frontend/src/components/global/MkUrl.stories.impl.ts
index 84ac13f95a..b35b6114fd 100644
--- a/packages/frontend/src/components/global/MkUrl.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkUrl.stories.impl.ts
@@ -8,7 +8,7 @@ import { expect } from '@storybook/jest';
 import { userEvent, waitFor, within } from '@storybook/testing-library';
 import { StoryObj } from '@storybook/vue3';
 import { rest } from 'msw';
-import { commonHandlers } from '../../../.storybook/mocks';
+import { commonHandlers } from '../../../.storybook/mocks.js';
 import MkUrl from './MkUrl.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/components/global/MkUserName.stories.impl.ts b/packages/frontend/src/components/global/MkUserName.stories.impl.ts
index 01455e492d..8f47a6c1ab 100644
--- a/packages/frontend/src/components/global/MkUserName.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkUserName.stories.impl.ts
@@ -6,7 +6,7 @@
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { expect } from '@storybook/jest';
 import { StoryObj } from '@storybook/vue3';
-import { userDetailed } from '../../../.storybook/fakes';
+import { userDetailed } from '../../../.storybook/fakes.js';
 import MkUserName from './MkUserName.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue
index 9da8f8c379..99ed8adbef 100644
--- a/packages/frontend/src/components/global/RouterView.vue
+++ b/packages/frontend/src/components/global/RouterView.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { inject, onBeforeUnmount, provide, shallowRef, ref } from 'vue';
-import { Resolved, Router } from '@/nirax';
+import { Resolved, Router } from '@/nirax.js';
 import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/components/page/page.block.vue b/packages/frontend/src/components/page/page.block.vue
index c039a1c7b7..7dbbaa03b4 100644
--- a/packages/frontend/src/components/page/page.block.vue
+++ b/packages/frontend/src/components/page/page.block.vue
@@ -14,7 +14,7 @@ import XText from './page.text.vue';
 import XSection from './page.section.vue';
 import XImage from './page.image.vue';
 import XNote from './page.note.vue';
-import { Block } from './block.type';
+import { Block } from './block.type.js';
 
 function getComponent(type: string) {
 	switch (type) {
diff --git a/packages/frontend/src/components/page/page.image.vue b/packages/frontend/src/components/page/page.image.vue
index 80a75f02a5..29aebf63e5 100644
--- a/packages/frontend/src/components/page/page.image.vue
+++ b/packages/frontend/src/components/page/page.image.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import { ImageBlock } from './block.type';
+import { ImageBlock } from './block.type.js';
 import MediaImage from '@/components/MkMediaImage.vue';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/components/page/page.note.vue b/packages/frontend/src/components/page/page.note.vue
index d052ce2c1f..5ca707dbc2 100644
--- a/packages/frontend/src/components/page/page.note.vue
+++ b/packages/frontend/src/components/page/page.note.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, Ref, ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import { NoteBlock } from './block.type';
+import { NoteBlock } from './block.type.js';
 import MkNote from '@/components/MkNote.vue';
 import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
 import * as os from '@/os.js';
diff --git a/packages/frontend/src/components/page/page.section.vue b/packages/frontend/src/components/page/page.section.vue
index 84d0399cf8..e4e5a43b59 100644
--- a/packages/frontend/src/components/page/page.section.vue
+++ b/packages/frontend/src/components/page/page.section.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { defineAsyncComponent } from 'vue';
 import * as Misskey from 'misskey-js';
-import { SectionBlock } from './block.type';
+import { SectionBlock } from './block.type.js';
 
 const XBlock = defineAsyncComponent(() => import('./page.block.vue'));
 
diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue
index 892522d4b5..c0849a6d42 100644
--- a/packages/frontend/src/components/page/page.text.vue
+++ b/packages/frontend/src/components/page/page.text.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { defineAsyncComponent } from 'vue';
 import * as mfm from '@sharkey/sfm-js';
 import * as Misskey from 'misskey-js';
-import { TextBlock } from './block.type';
+import { TextBlock } from './block.type.js';
 import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
 
 const MkUrlPreview = defineAsyncComponent(() => import('@/components/MkUrlPreview.vue'));
diff --git a/packages/frontend/src/directives/hotkey.ts b/packages/frontend/src/directives/hotkey.ts
index 85f06c098d..13e548299f 100644
--- a/packages/frontend/src/directives/hotkey.ts
+++ b/packages/frontend/src/directives/hotkey.ts
@@ -4,7 +4,7 @@
  */
 
 import { Directive } from 'vue';
-import { makeHotkey } from '../scripts/hotkey';
+import { makeHotkey } from '../scripts/hotkey.js';
 
 export default {
 	mounted(el, binding) {
diff --git a/packages/frontend/src/directives/index.ts b/packages/frontend/src/directives/index.ts
index fad6960948..fcd7c3091e 100644
--- a/packages/frontend/src/directives/index.ts
+++ b/packages/frontend/src/directives/index.ts
@@ -5,17 +5,17 @@
 
 import { App } from 'vue';
 
-import userPreview from './user-preview';
-import getSize from './get-size';
-import ripple from './ripple';
-import tooltip from './tooltip';
-import hotkey from './hotkey';
-import appear from './appear';
-import anim from './anim';
-import clickAnime from './click-anime';
-import panel from './panel';
-import adaptiveBorder from './adaptive-border';
-import adaptiveBg from './adaptive-bg';
+import userPreview from './user-preview.js';
+import getSize from './get-size.js';
+import ripple from './ripple.js';
+import tooltip from './tooltip.js';
+import hotkey from './hotkey.js';
+import appear from './appear.js';
+import anim from './anim.js';
+import clickAnime from './click-anime.js';
+import panel from './panel.js';
+import adaptiveBorder from './adaptive-border.js';
+import adaptiveBg from './adaptive-bg.js';
 
 export default function(app: App) {
 	for (const [key, value] of Object.entries(directives)) {
diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue
index 353030b1b9..bac3aa154a 100644
--- a/packages/frontend/src/pages/admin/_header_.vue
+++ b/packages/frontend/src/pages/admin/_header_.vue
@@ -38,7 +38,7 @@ import tinycolor from 'tinycolor2';
 import { popupMenu } from '@/os.js';
 import { scrollToTop } from '@/scripts/scroll.js';
 import MkButton from '@/components/MkButton.vue';
-import { globalEvents } from '@/events';
+import { globalEvents } from '@/events.js';
 import { injectPageMetadata } from '@/scripts/page-metadata.js';
 
 type Tab = {
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index b3c06454ae..548e56d37e 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -31,7 +31,7 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { useRouter } from '@/router.js';
 import MkButton from '@/components/MkButton.vue';
-import { rolesCache } from '@/cache';
+import { rolesCache } from '@/cache.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 07cce6d9a5..9cb48130d8 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -258,7 +258,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { instance } from '@/instance.js';
 import { useRouter } from '@/router.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import { ROLE_POLICIES } from '@/const';
+import { ROLE_POLICIES } from '@/const.js';
 
 const router = useRouter();
 const baseRoleQ = ref('');
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index 041cc0a204..9b5f0224cc 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -35,7 +35,7 @@ import * as os from '@/os.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { url } from '@/config.js';
 import MkButton from '@/components/MkButton.vue';
-import { clipsCache } from '@/cache';
+import { clipsCache } from '@/cache.js';
 import { isSupportShare } from '@/scripts/navigator.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 
diff --git a/packages/frontend/src/pages/my-antennas/edit.vue b/packages/frontend/src/pages/my-antennas/edit.vue
index a325eb9b0f..6cb368ca9d 100644
--- a/packages/frontend/src/pages/my-antennas/edit.vue
+++ b/packages/frontend/src/pages/my-antennas/edit.vue
@@ -16,7 +16,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { useRouter } from '@/router.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { antennasCache } from '@/cache';
+import { antennasCache } from '@/cache.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue
index 2a5fe07957..b46fb7a5d7 100644
--- a/packages/frontend/src/pages/my-antennas/index.vue
+++ b/packages/frontend/src/pages/my-antennas/index.vue
@@ -32,7 +32,7 @@ import { onActivated, computed } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { antennasCache } from '@/cache';
+import { antennasCache } from '@/cache.js';
 import { infoImageUrl } from '@/instance.js';
 
 const antennas = computed(() => antennasCache.value.value ?? []);
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index 31ccf9ce2d..05a3ec4d2e 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -33,7 +33,7 @@ import MkClipPreview from '@/components/MkClipPreview.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { clipsCache } from '@/cache';
+import { clipsCache } from '@/cache.js';
 
 const pagination = {
 	endpoint: 'clips/list' as const,
diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue
index b0d750b218..3379cf43d4 100644
--- a/packages/frontend/src/pages/my-lists/index.vue
+++ b/packages/frontend/src/pages/my-lists/index.vue
@@ -35,7 +35,7 @@ import MkAvatars from '@/components/MkAvatars.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { userListsCache } from '@/cache';
+import { userListsCache } from '@/cache.js';
 import { infoImageUrl } from '@/instance.js';
 import { $i } from '@/account.js';
 
diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue
index 0112543781..f3c9ec8926 100644
--- a/packages/frontend/src/pages/settings/navbar.vue
+++ b/packages/frontend/src/pages/settings/navbar.vue
@@ -52,7 +52,7 @@ import MkButton from '@/components/MkButton.vue';
 import FormSlot from '@/components/form/slot.vue';
 import MkContainer from '@/components/MkContainer.vue';
 import * as os from '@/os.js';
-import { navbarItemDef } from '@/navbar';
+import { navbarItemDef } from '@/navbar.js';
 import { defaultStore } from '@/store.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
 import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/pages/settings/theme.manage.vue b/packages/frontend/src/pages/settings/theme.manage.vue
index a2ecd0574f..366c39c010 100644
--- a/packages/frontend/src/pages/settings/theme.manage.vue
+++ b/packages/frontend/src/pages/settings/theme.manage.vue
@@ -40,7 +40,7 @@ import MkButton from '@/components/MkButton.vue';
 import { Theme, getBuiltinThemesRef } from '@/scripts/theme.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import * as os from '@/os.js';
-import { getThemes, removeTheme } from '@/theme-store';
+import { getThemes, removeTheme } from '@/theme-store.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue
index 58c0b8fc82..cb9c714441 100644
--- a/packages/frontend/src/pages/settings/theme.vue
+++ b/packages/frontend/src/pages/settings/theme.vue
@@ -85,7 +85,7 @@ import { ColdDeviceStorage, defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
 import { uniqueBy } from '@/scripts/array.js';
-import { fetchThemes, getThemes } from '@/theme-store';
+import { fetchThemes, getThemes } from '@/theme-store.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { miLocalStorage } from '@/local-storage.js';
 
diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue
index 6c4a54eb5c..4b4196d0a9 100644
--- a/packages/frontend/src/pages/theme-editor.vue
+++ b/packages/frontend/src/pages/theme-editor.vue
@@ -91,7 +91,7 @@ import darkTheme from '@/themes/_dark.json5';
 import { host } from '@/config.js';
 import * as os from '@/os.js';
 import { ColdDeviceStorage, defaultStore } from '@/store.js';
-import { addTheme } from '@/theme-store';
+import { addTheme } from '@/theme-store.js';
 import { i18n } from '@/i18n.js';
 import { useLeaveGuard } from '@/scripts/use-leave-guard.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
diff --git a/packages/frontend/src/pages/user/home.stories.impl.ts b/packages/frontend/src/pages/user/home.stories.impl.ts
index 80b4f1a9e9..a2ef5d50d1 100644
--- a/packages/frontend/src/pages/user/home.stories.impl.ts
+++ b/packages/frontend/src/pages/user/home.stories.impl.ts
@@ -6,8 +6,8 @@
 /* eslint-disable @typescript-eslint/explicit-function-return-type */
 import { StoryObj } from '@storybook/vue3';
 import { rest } from 'msw';
-import { userDetailed } from '../../../.storybook/fakes';
-import { commonHandlers } from '../../../.storybook/mocks';
+import { userDetailed } from '../../../.storybook/fakes.js';
+import { commonHandlers } from '../../../.storybook/mocks.js';
 import home_ from './home.vue';
 export const Default = {
 	render(args) {
diff --git a/packages/frontend/src/scripts/upload/compress-config.ts b/packages/frontend/src/scripts/upload/compress-config.ts
index 8fe64c8b76..2deb9cbb81 100644
--- a/packages/frontend/src/scripts/upload/compress-config.ts
+++ b/packages/frontend/src/scripts/upload/compress-config.ts
@@ -4,7 +4,7 @@
  */
 
 import isAnimated from 'is-file-animated';
-import { isWebpSupported } from './isWebpSupported';
+import { isWebpSupported } from './isWebpSupported.js';
 import type { BrowserImageResizerConfig } from 'browser-image-resizer';
 
 const compressTypeMap = {
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 6b69e1accf..6ece7d86d7 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { defineAsyncComponent, ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import { swInject } from './sw-inject';
+import { swInject } from './sw-inject.js';
 import XNotification from './notification.vue';
 import { popups, pendingApiRequestsCount } from '@/os.js';
 import { uploads } from '@/scripts/upload.js';
@@ -56,7 +56,7 @@ import { $i } from '@/account.js';
 import { useStream } from '@/stream.js';
 import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
-import { globalEvents } from '@/events';
+import { globalEvents } from '@/events.js';
 
 const XStreamIndicator = defineAsyncComponent(() => import('./stream-indicator.vue'));
 const XUpload = defineAsyncComponent(() => import('./upload.vue'));
diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
index 346e5976ba..618be2db88 100644
--- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
+++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
@@ -50,9 +50,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, defineAsyncComponent, toRef } from 'vue';
-import { openInstanceMenu } from './common';
+import { openInstanceMenu } from './common.js';
 import * as os from '@/os.js';
-import { navbarItemDef } from '@/navbar';
+import { navbarItemDef } from '@/navbar.js';
 import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue
index 62531a4f4d..f0e0271128 100644
--- a/packages/frontend/src/ui/classic.header.vue
+++ b/packages/frontend/src/ui/classic.header.vue
@@ -48,9 +48,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
-import { openInstanceMenu } from './_common_/common';
+import { openInstanceMenu } from './_common_/common.js';
 import * as os from '@/os.js';
-import { navbarItemDef } from '@/navbar';
+import { navbarItemDef } from '@/navbar.js';
 import { openAccountMenu as openAccountMenu_, $i } from '@/account.js';
 import MkButton from '@/components/MkButton.vue';
 import { defaultStore } from '@/store.js';
diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue
index 77e074f2ff..9ed7e452e3 100644
--- a/packages/frontend/src/ui/deck/column.vue
+++ b/packages/frontend/src/ui/deck/column.vue
@@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onBeforeUnmount, onMounted, provide, watch, shallowRef, ref, computed } from 'vue';
-import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn, Column } from './deck-store';
+import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn, Column } from './deck-store.js';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { MenuItem } from '@/types/menu.js';
diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue
index 3e0ec6aac0..45ecc476e7 100644
--- a/packages/frontend/src/ui/deck/list-column.vue
+++ b/packages/frontend/src/ui/deck/list-column.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { watch, shallowRef, ref } from 'vue';
 import XColumn from './column.vue';
-import { updateColumn, Column } from './deck-store';
+import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue
index 4e17cb80fd..cd567040f4 100644
--- a/packages/frontend/src/ui/deck/main-column.vue
+++ b/packages/frontend/src/ui/deck/main-column.vue
@@ -26,7 +26,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { mainRouter } from '@/router.js';
 import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
-import { useScrollPositionManager } from '@/nirax';
+import { useScrollPositionManager } from '@/nirax.js';
 import { getScrollContainer } from '@/scripts/scroll.js';
 
 defineProps<{
diff --git a/packages/frontend/src/widgets/server-metric/index.vue b/packages/frontend/src/widgets/server-metric/index.vue
index 57fbb6811b..ddfc6e68fa 100644
--- a/packages/frontend/src/widgets/server-metric/index.vue
+++ b/packages/frontend/src/widgets/server-metric/index.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onUnmounted, ref } from 'vue';
-import { useWidgetPropsManager, Widget, WidgetComponentExpose } from '../widget';
+import { useWidgetPropsManager, Widget, WidgetComponentExpose } from '../widget.js';
 import XCpuMemory from './cpu-mem.vue';
 import XNet from './net.vue';
 import XCpu from './cpu.vue';
diff --git a/packages/frontend/test/init.ts b/packages/frontend/test/init.ts
index dfc02378d5..6d93ff8cb0 100644
--- a/packages/frontend/test/init.ts
+++ b/packages/frontend/test/init.ts
@@ -10,7 +10,7 @@ const fetchMocker = createFetchMock(vi);
 fetchMocker.enableMocks();
 
 // Set i18n
-import locales from '../../../locales';
+import locales from '../../../locales/index.js';
 import { updateI18n } from '@/i18n.js';
 updateI18n(locales['en-US']);
 
@@ -28,7 +28,7 @@ vi.mock('@/store.js', () => {
 					media: false,
 					avatar: false,
 					urlPreview: false,
-					code: false,		
+					code: false,
 				},
 
 			},
diff --git a/packages/frontend/vite.config.local-dev.ts b/packages/frontend/vite.config.local-dev.ts
index aea46f4231..5a6f511c66 100644
--- a/packages/frontend/vite.config.local-dev.ts
+++ b/packages/frontend/vite.config.local-dev.ts
@@ -1,6 +1,6 @@
 import dns from 'dns';
 import { defineConfig } from 'vite';
-import locales from '../../locales';
+import locales from '../../locales/index.js';
 import { getConfig } from './vite.config.js';
 
 dns.setDefaultResultOrder('ipv4first');
diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts
index 8a1c56e6b1..ea8c4ced60 100644
--- a/packages/frontend/vite.config.ts
+++ b/packages/frontend/vite.config.ts
@@ -3,10 +3,10 @@ import pluginReplace from '@rollup/plugin-replace';
 import pluginVue from '@vitejs/plugin-vue';
 import { type UserConfig, defineConfig } from 'vite';
 
-import locales from '../../locales';
+import locales from '../../locales/index.js';
 import meta from '../../package.json';
-import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name';
-import pluginJson5 from './vite.json5';
+import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js';
+import pluginJson5 from './vite.json5.js';
 
 const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue'];
 
@@ -26,6 +26,7 @@ const hash = (str: string, seed = 0): number => {
 };
 
 const BASE62_DIGITS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+
 function toBase62(n: number): string {
 	if (n === 0) {
 		return '0';

From 7ce353bcc742aa351415acba9414da2d52bbd2ad Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Sun, 24 Dec 2023 17:54:00 +0900
Subject: [PATCH 367/435] chore(misskey-js): update misskey-js with api.json
 (#12778)

pnpm build && pnpm build-misskey-js-with-types && pnpm --filter misskey-js api
---
 packages/misskey-js/etc/misskey-js.api.md     | 136 ++-
 .../misskey-js/src/autogen/apiClientJSDoc.ts  | 157 ++--
 packages/misskey-js/src/autogen/endpoint.ts   | 100 ++-
 packages/misskey-js/src/autogen/entities.ts   |  36 +-
 packages/misskey-js/src/autogen/models.ts     |   5 +-
 packages/misskey-js/src/autogen/types.ts      | 838 ++++++++++++------
 6 files changed, 889 insertions(+), 383 deletions(-)

diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index ea4e0c4163..653372ba2c 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -21,6 +21,11 @@ declare namespace acct {
 }
 export { acct }
 
+// Warning: (ae-forgotten-export) The symbol "components" needs to be exported by the entry point index.d.ts
+//
+// @public (undocumented)
+type Ad = components['schemas']['Ad'];
+
 // Warning: (ae-forgotten-export) The symbol "operations" needs to be exported by the entry point index.d.ts
 //
 // @public (undocumented)
@@ -41,15 +46,24 @@ type AdminAccountsDeleteRequest = operations['admin/accounts/delete']['requestBo
 // @public (undocumented)
 type AdminAccountsFindByEmailRequest = operations['admin/accounts/find-by-email']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminAccountsFindByEmailResponse = operations['admin/accounts/find-by-email']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminAdCreateRequest = operations['admin/ad/create']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminAdCreateResponse = operations['admin/ad/create']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminAdDeleteRequest = operations['admin/ad/delete']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type AdminAdListRequest = operations['admin/ad/list']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminAdListResponse = operations['admin/ad/list']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminAdUpdateRequest = operations['admin/ad/update']['requestBody']['content']['application/json'];
 
@@ -167,12 +181,18 @@ type AdminFederationRemoveAllFollowingRequest = operations['admin/federation/rem
 // @public (undocumented)
 type AdminFederationUpdateInstanceRequest = operations['admin/federation/update-instance']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminGetIndexStatsResponse = operations['admin/get-index-stats']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminGetTableStatsResponse = operations['admin/get-table-stats']['responses']['200']['content']['application/json'];
 
 // @public (undocumented)
 type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminGetUserIpsResponse = operations['admin/get-user-ips']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminInviteCreateRequest = operations['admin/invite/create']['requestBody']['content']['application/json'];
 
@@ -257,6 +277,9 @@ type AdminRolesUpdateRequest = operations['admin/roles/update']['requestBody']['
 // @public (undocumented)
 type AdminRolesUsersRequest = operations['admin/roles/users']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type AdminRolesUsersResponse = operations['admin/roles/users']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type AdminSendEmailRequest = operations['admin/send-email']['requestBody']['content']['application/json'];
 
@@ -299,8 +322,6 @@ type AdminUpdateMetaRequest = operations['admin/update-meta']['requestBody']['co
 // @public (undocumented)
 type AdminUpdateUserNoteRequest = operations['admin/update-user-note']['requestBody']['content']['application/json'];
 
-// Warning: (ae-forgotten-export) The symbol "components" needs to be exported by the entry point index.d.ts
-//
 // @public (undocumented)
 type Announcement = components['schemas']['Announcement'];
 
@@ -989,6 +1010,9 @@ type EmptyResponse = Record<string, unknown> | undefined;
 // @public (undocumented)
 type EndpointRequest = operations['endpoint']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type EndpointResponse = operations['endpoint']['responses']['200']['content']['application/json'];
+
 // Warning: (ae-forgotten-export) The symbol "Overwrite" needs to be exported by the entry point index.d.ts
 // Warning: (ae-forgotten-export) The symbol "Endpoints_2" needs to be exported by the entry point index.d.ts
 //
@@ -1038,9 +1062,12 @@ declare namespace entities {
         AdminAccountsCreateResponse,
         AdminAccountsDeleteRequest,
         AdminAccountsFindByEmailRequest,
+        AdminAccountsFindByEmailResponse,
         AdminAdCreateRequest,
+        AdminAdCreateResponse,
         AdminAdDeleteRequest,
         AdminAdListRequest,
+        AdminAdListResponse,
         AdminAdUpdateRequest,
         AdminAnnouncementsCreateRequest,
         AdminAnnouncementsCreateResponse,
@@ -1080,8 +1107,10 @@ declare namespace entities {
         AdminFederationRefreshRemoteInstanceMetadataRequest,
         AdminFederationRemoveAllFollowingRequest,
         AdminFederationUpdateInstanceRequest,
+        AdminGetIndexStatsResponse,
         AdminGetTableStatsResponse,
         AdminGetUserIpsRequest,
+        AdminGetUserIpsResponse,
         AdminInviteCreateRequest,
         AdminInviteCreateResponse,
         AdminInviteListRequest,
@@ -1123,6 +1152,7 @@ declare namespace entities {
         AdminRolesUnassignRequest,
         AdminRolesUpdateDefaultPoliciesRequest,
         AdminRolesUsersRequest,
+        AdminRolesUsersResponse,
         AnnouncementsRequest,
         AnnouncementsResponse,
         AntennasCreateRequest,
@@ -1250,6 +1280,7 @@ declare namespace entities {
         EmailAddressAvailableRequest,
         EmailAddressAvailableResponse,
         EndpointRequest,
+        EndpointResponse,
         EndpointsResponse,
         FederationFollowersRequest,
         FederationFollowersResponse,
@@ -1263,6 +1294,7 @@ declare namespace entities {
         FederationUsersRequest,
         FederationUsersResponse,
         FederationStatsRequest,
+        FederationStatsResponse,
         FollowingCreateRequest,
         FollowingCreateResponse,
         FollowingDeleteRequest,
@@ -1292,6 +1324,7 @@ declare namespace entities {
         GalleryPostsUnlikeRequest,
         GalleryPostsUpdateRequest,
         GalleryPostsUpdateResponse,
+        GetOnlineUsersCountResponse,
         GetAvatarDecorationsResponse,
         HashtagsListRequest,
         HashtagsListResponse,
@@ -1305,14 +1338,19 @@ declare namespace entities {
         IResponse,
         I2faDoneRequest,
         I2faKeyDoneRequest,
+        I2faKeyDoneResponse,
         I2faPasswordLessRequest,
         I2faRegisterKeyRequest,
+        I2faRegisterKeyResponse,
         I2faRegisterRequest,
+        I2faRegisterResponse,
         I2faUpdateKeyRequest,
         I2faRemoveKeyRequest,
         I2faUnregisterRequest,
         IAppsRequest,
+        IAppsResponse,
         IAuthorizedAppsRequest,
+        IAuthorizedAppsResponse,
         IClaimAchievementRequest,
         IChangePasswordRequest,
         IDeleteAccountRequest,
@@ -1341,11 +1379,16 @@ declare namespace entities {
         IReadAnnouncementRequest,
         IRegenerateTokenRequest,
         IRegistryGetAllRequest,
+        IRegistryGetAllResponse,
         IRegistryGetDetailRequest,
+        IRegistryGetDetailResponse,
         IRegistryGetRequest,
+        IRegistryGetResponse,
         IRegistryKeysWithTypeRequest,
+        IRegistryKeysWithTypeResponse,
         IRegistryKeysRequest,
         IRegistryRemoveRequest,
+        IRegistryScopesWithDomainResponse,
         IRegistrySetRequest,
         IRevokeTokenRequest,
         ISigninHistoryRequest,
@@ -1353,11 +1396,16 @@ declare namespace entities {
         IUnpinRequest,
         IUnpinResponse,
         IUpdateEmailRequest,
+        IUpdateEmailResponse,
         IUpdateRequest,
         IUpdateResponse,
         IMoveRequest,
+        IMoveResponse,
         IWebhooksCreateRequest,
+        IWebhooksCreateResponse,
+        IWebhooksListResponse,
         IWebhooksShowRequest,
+        IWebhooksShowResponse,
         IWebhooksUpdateRequest,
         IWebhooksDeleteRequest,
         InviteCreateResponse,
@@ -1445,6 +1493,7 @@ declare namespace entities {
         PagesUnlikeRequest,
         PagesUpdateRequest,
         FlashCreateRequest,
+        FlashCreateResponse,
         FlashDeleteRequest,
         FlashFeaturedResponse,
         FlashLikeRequest,
@@ -1463,10 +1512,12 @@ declare namespace entities {
         RolesShowRequest,
         RolesShowResponse,
         RolesUsersRequest,
+        RolesUsersResponse,
         RolesNotesRequest,
         RolesNotesResponse,
         RequestResetPasswordRequest,
         ResetPasswordRequest,
+        ServerInfoResponse,
         StatsResponse,
         SwShowRegistrationRequest,
         SwShowRegistrationResponse,
@@ -1476,6 +1527,7 @@ declare namespace entities {
         SwRegisterResponse,
         SwUnregisterRequest,
         TestRequest,
+        TestResponse,
         UsernameAvailableRequest,
         UsernameAvailableResponse,
         UsersRequest,
@@ -1509,6 +1561,7 @@ declare namespace entities {
         UsersListsCreateFromPublicResponse,
         UsersListsUpdateMembershipRequest,
         UsersListsGetMembershipsRequest,
+        UsersListsGetMembershipsResponse,
         UsersNotesRequest,
         UsersNotesResponse,
         UsersPagesRequest,
@@ -1529,9 +1582,12 @@ declare namespace entities {
         UsersShowRequest,
         UsersShowResponse,
         UsersAchievementsRequest,
+        UsersAchievementsResponse,
         UsersUpdateMemoRequest,
         FetchRssRequest,
+        FetchRssResponse,
         FetchExternalResourcesRequest,
+        FetchExternalResourcesResponse,
         RetentionResponse,
         Error_2 as Error,
         UserLite,
@@ -1542,6 +1598,7 @@ declare namespace entities {
         UserDetailed,
         User,
         UserList,
+        Ad,
         Announcement,
         App,
         Note,
@@ -1606,6 +1663,9 @@ type FederationShowInstanceResponse = operations['federation/show-instance']['re
 // @public (undocumented)
 type FederationStatsRequest = operations['federation/stats']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type FederationStatsResponse = operations['federation/stats']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type FederationUpdateRemoteUserRequest = operations['federation/update-remote-user']['requestBody']['content']['application/json'];
 
@@ -1618,6 +1678,9 @@ type FederationUsersResponse = operations['federation/users']['responses']['200'
 // @public (undocumented)
 type FetchExternalResourcesRequest = operations['fetch-external-resources']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type FetchExternalResourcesResponse = operations['fetch-external-resources']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type FetchLike = (input: string, init?: {
     method?: string;
@@ -1635,12 +1698,18 @@ type FetchLike = (input: string, init?: {
 // @public (undocumented)
 type FetchRssRequest = operations['fetch-rss']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type FetchRssResponse = operations['fetch-rss']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type Flash = components['schemas']['Flash'];
 
 // @public (undocumented)
 type FlashCreateRequest = operations['flash/create']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type FlashCreateResponse = operations['flash/create']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type FlashDeleteRequest = operations['flash/delete']['requestBody']['content']['application/json'];
 
@@ -1776,6 +1845,9 @@ type GalleryPostsUpdateResponse = operations['gallery/posts/update']['responses'
 // @public (undocumented)
 type GetAvatarDecorationsResponse = operations['get-avatar-decorations']['responses']['200']['content']['application/json'];
 
+// @public (undocumented)
+type GetOnlineUsersCountResponse = operations['get-online-users-count']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type Hashtag = components['schemas']['Hashtag'];
 
@@ -1812,15 +1884,24 @@ type I2faDoneRequest = operations['i/2fa/done']['requestBody']['content']['appli
 // @public (undocumented)
 type I2faKeyDoneRequest = operations['i/2fa/key-done']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type I2faKeyDoneResponse = operations['i/2fa/key-done']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type I2faPasswordLessRequest = operations['i/2fa/password-less']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type I2faRegisterKeyRequest = operations['i/2fa/register-key']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type I2faRegisterKeyResponse = operations['i/2fa/register-key']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type I2faRegisterRequest = operations['i/2fa/register']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type I2faRegisterResponse = operations['i/2fa/register']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type I2faRemoveKeyRequest = operations['i/2fa/remove-key']['requestBody']['content']['application/json'];
 
@@ -1833,9 +1914,15 @@ type I2faUpdateKeyRequest = operations['i/2fa/update-key']['requestBody']['conte
 // @public (undocumented)
 type IAppsRequest = operations['i/apps']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IAppsResponse = operations['i/apps']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IAuthorizedAppsRequest = operations['i/authorized-apps']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IAuthorizedAppsResponse = operations['i/authorized-apps']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IChangePasswordRequest = operations['i/change-password']['requestBody']['content']['application/json'];
 
@@ -1887,6 +1974,9 @@ type IImportUserListsRequest = operations['i/import-user-lists']['requestBody'][
 // @public (undocumented)
 type IMoveRequest = operations['i/move']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IMoveResponse = operations['i/move']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type INotificationsGroupedRequest = operations['i/notifications-grouped']['requestBody']['content']['application/json'];
 
@@ -1944,21 +2034,36 @@ type IRegenerateTokenRequest = operations['i/regenerate-token']['requestBody']['
 // @public (undocumented)
 type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IRegistryGetAllResponse = operations['i/registry/get-all']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IRegistryGetDetailRequest = operations['i/registry/get-detail']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IRegistryGetDetailResponse = operations['i/registry/get-detail']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IRegistryGetResponse = operations['i/registry/get']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json'];
 
 // @public (undocumented)
 type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IRegistryKeysWithTypeResponse = operations['i/registry/keys-with-type']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IRegistryRemoveRequest = operations['i/registry/remove']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IRegistryScopesWithDomainResponse = operations['i/registry/scopes-with-domain']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content']['application/json'];
 
@@ -1986,6 +2091,9 @@ type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['appl
 // @public (undocumented)
 type IUpdateEmailRequest = operations['i/update-email']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IUpdateEmailResponse = operations['i/update-email']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json'];
 
@@ -1995,12 +2103,21 @@ type IUpdateResponse = operations['i/update']['responses']['200']['content']['ap
 // @public (undocumented)
 type IWebhooksCreateRequest = operations['i/webhooks/create']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IWebhooksCreateResponse = operations['i/webhooks/create']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IWebhooksDeleteRequest = operations['i/webhooks/delete']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IWebhooksListResponse = operations['i/webhooks/list']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IWebhooksShowRequest = operations['i/webhooks/show']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type IWebhooksShowResponse = operations['i/webhooks/show']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
 
@@ -2469,6 +2586,12 @@ type RolesShowResponse = operations['roles/show']['responses']['200']['content']
 // @public (undocumented)
 type RolesUsersRequest = operations['roles/users']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type RolesUsersResponse = operations['roles/users']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
+type ServerInfoResponse = operations['server-info']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type ServerStats = {
     cpu: number;
@@ -2565,6 +2688,9 @@ type SwUpdateRegistrationResponse = operations['sw/update-registration']['respon
 // @public (undocumented)
 type TestRequest = operations['test']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type TestResponse = operations['test']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 function toString_2(acct: Acct): string;
 
@@ -2595,6 +2721,9 @@ type UsernameAvailableResponse = operations['username/available']['responses']['
 // @public (undocumented)
 type UsersAchievementsRequest = operations['users/achievements']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type UsersAchievementsResponse = operations['users/achievements']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type UsersClipsRequest = operations['users/clips']['requestBody']['content']['application/json'];
 
@@ -2658,6 +2787,9 @@ type UsersListsFavoriteRequest = operations['users/lists/favorite']['requestBody
 // @public (undocumented)
 type UsersListsGetMembershipsRequest = operations['users/lists/get-memberships']['requestBody']['content']['application/json'];
 
+// @public (undocumented)
+type UsersListsGetMembershipsResponse = operations['users/lists/get-memberships']['responses']['200']['content']['application/json'];
+
 // @public (undocumented)
 type UsersListsListRequest = operations['users/lists/list']['requestBody']['content']['application/json'];
 
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 7d58dcb5c8..436f76dbd6 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -1,6 +1,6 @@
 /*
- * version: 2023.11.0-beta.3
- * generatedAt: 2023-12-08T04:57:48.424Z
+ * version: 2023.12.0
+ * generatedAt: 2023-12-24T08:46:11.020Z
  */
 
 import type { SwitchCaseResponseType } from '../api.js';
@@ -11,7 +11,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/meta', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -22,7 +22,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/abuse-user-reports', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -33,7 +33,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *No*
+     * **Credential required**: *No* / **Permission**: *write:admin*
      */
     request<E extends 'admin/accounts/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -44,7 +44,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/accounts/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -55,7 +55,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/accounts/find-by-email', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -66,7 +66,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/ad/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -77,7 +77,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/ad/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -88,7 +88,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/ad/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -99,7 +99,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/ad/update', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -110,7 +110,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/announcements/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -121,7 +121,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/announcements/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -132,7 +132,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/announcements/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -143,7 +143,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/announcements/update', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -154,7 +154,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/avatar-decorations/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -165,7 +165,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/avatar-decorations/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -176,7 +176,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/avatar-decorations/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -187,7 +187,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/avatar-decorations/update', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -198,7 +198,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/delete-all-files-of-a-user', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -209,7 +209,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/unset-user-avatar', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -220,7 +220,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/unset-user-banner', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -231,7 +231,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/drive/clean-remote-files', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -242,7 +242,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/drive/cleanup', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -253,7 +253,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/drive/files', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -264,7 +264,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/drive/show-file', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -275,7 +275,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/emoji/add-aliases-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -286,7 +286,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/emoji/add', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -297,7 +297,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/emoji/copy', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -308,7 +308,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/emoji/delete-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -319,7 +319,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/emoji/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -330,8 +330,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/emoji/import-zip', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -342,7 +341,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/emoji/list-remote', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -353,7 +352,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/emoji/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -364,7 +363,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/emoji/remove-aliases-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -375,7 +374,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/emoji/set-aliases-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -386,7 +385,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/emoji/set-category-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -397,7 +396,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/emoji/set-license-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -408,7 +407,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/emoji/update', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -419,7 +418,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/federation/delete-all-files', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -430,7 +429,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/federation/refresh-remote-instance-metadata', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -441,7 +440,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/federation/remove-all-following', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -452,7 +451,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/federation/update-instance', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -463,7 +462,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/get-index-stats', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -474,7 +473,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/get-table-stats', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -485,7 +484,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/get-user-ips', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -496,7 +495,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/invite/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -507,7 +506,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/invite/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -518,7 +517,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/promo/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -529,7 +528,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/queue/clear', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -540,7 +539,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/queue/deliver-delayed', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -551,7 +550,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/queue/inbox-delayed', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -562,7 +561,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/queue/promote', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -573,7 +572,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/queue/stats', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -584,7 +583,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/relays/add', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -595,7 +594,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/relays/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -606,7 +605,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/relays/remove', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -617,7 +616,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/reset-password', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -628,7 +627,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/resolve-abuse-user-report', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -639,7 +638,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/send-email', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -650,7 +649,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/server-info', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -661,7 +660,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/show-moderation-logs', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -672,7 +671,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/show-user', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -683,7 +682,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/show-users', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -694,7 +693,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/suspend-user', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -705,7 +704,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/unsuspend-user', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -716,7 +715,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/update-meta', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -727,7 +726,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/delete-account', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -738,7 +737,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/update-user-note', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -749,7 +748,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/roles/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -760,7 +759,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/roles/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -771,7 +770,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/roles/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -782,7 +781,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     request<E extends 'admin/roles/show', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -793,7 +792,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/roles/update', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -804,7 +803,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/roles/assign', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -815,7 +814,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/roles/unassign', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -826,7 +825,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     request<E extends 'admin/roles/update-default-policies', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -837,7 +836,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *No*
+     * **Credential required**: *No* / **Permission**: *read:admin*
      */
     request<E extends 'admin/roles/users', P extends Endpoints[E]['req']>(
       endpoint: E,
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 5efe582434..00f2595e2c 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -1,6 +1,6 @@
 /*
- * version: 2023.11.0-beta.3
- * generatedAt: 2023-12-08T04:57:48.415Z
+ * version: 2023.12.0
+ * generatedAt: 2023-12-24T08:46:11.016Z
  */
 
 import type {
@@ -13,9 +13,12 @@ import type {
 	AdminAccountsCreateResponse,
 	AdminAccountsDeleteRequest,
 	AdminAccountsFindByEmailRequest,
+	AdminAccountsFindByEmailResponse,
 	AdminAdCreateRequest,
+	AdminAdCreateResponse,
 	AdminAdDeleteRequest,
 	AdminAdListRequest,
+	AdminAdListResponse,
 	AdminAdUpdateRequest,
 	AdminAnnouncementsCreateRequest,
 	AdminAnnouncementsCreateResponse,
@@ -55,8 +58,10 @@ import type {
 	AdminFederationRefreshRemoteInstanceMetadataRequest,
 	AdminFederationRemoveAllFollowingRequest,
 	AdminFederationUpdateInstanceRequest,
+	AdminGetIndexStatsResponse,
 	AdminGetTableStatsResponse,
 	AdminGetUserIpsRequest,
+	AdminGetUserIpsResponse,
 	AdminInviteCreateRequest,
 	AdminInviteCreateResponse,
 	AdminInviteListRequest,
@@ -98,6 +103,7 @@ import type {
 	AdminRolesUnassignRequest,
 	AdminRolesUpdateDefaultPoliciesRequest,
 	AdminRolesUsersRequest,
+	AdminRolesUsersResponse,
 	AnnouncementsRequest,
 	AnnouncementsResponse,
 	AntennasCreateRequest,
@@ -225,6 +231,7 @@ import type {
 	EmailAddressAvailableRequest,
 	EmailAddressAvailableResponse,
 	EndpointRequest,
+	EndpointResponse,
 	EndpointsResponse,
 	FederationFollowersRequest,
 	FederationFollowersResponse,
@@ -238,6 +245,7 @@ import type {
 	FederationUsersRequest,
 	FederationUsersResponse,
 	FederationStatsRequest,
+	FederationStatsResponse,
 	FollowingCreateRequest,
 	FollowingCreateResponse,
 	FollowingDeleteRequest,
@@ -267,6 +275,7 @@ import type {
 	GalleryPostsUnlikeRequest,
 	GalleryPostsUpdateRequest,
 	GalleryPostsUpdateResponse,
+	GetOnlineUsersCountResponse,
 	GetAvatarDecorationsResponse,
 	HashtagsListRequest,
 	HashtagsListResponse,
@@ -280,14 +289,19 @@ import type {
 	IResponse,
 	I2faDoneRequest,
 	I2faKeyDoneRequest,
+	I2faKeyDoneResponse,
 	I2faPasswordLessRequest,
 	I2faRegisterKeyRequest,
+	I2faRegisterKeyResponse,
 	I2faRegisterRequest,
+	I2faRegisterResponse,
 	I2faUpdateKeyRequest,
 	I2faRemoveKeyRequest,
 	I2faUnregisterRequest,
 	IAppsRequest,
+	IAppsResponse,
 	IAuthorizedAppsRequest,
+	IAuthorizedAppsResponse,
 	IClaimAchievementRequest,
 	IChangePasswordRequest,
 	IDeleteAccountRequest,
@@ -316,11 +330,16 @@ import type {
 	IReadAnnouncementRequest,
 	IRegenerateTokenRequest,
 	IRegistryGetAllRequest,
+	IRegistryGetAllResponse,
 	IRegistryGetDetailRequest,
+	IRegistryGetDetailResponse,
 	IRegistryGetRequest,
+	IRegistryGetResponse,
 	IRegistryKeysWithTypeRequest,
+	IRegistryKeysWithTypeResponse,
 	IRegistryKeysRequest,
 	IRegistryRemoveRequest,
+	IRegistryScopesWithDomainResponse,
 	IRegistrySetRequest,
 	IRevokeTokenRequest,
 	ISigninHistoryRequest,
@@ -328,11 +347,16 @@ import type {
 	IUnpinRequest,
 	IUnpinResponse,
 	IUpdateEmailRequest,
+	IUpdateEmailResponse,
 	IUpdateRequest,
 	IUpdateResponse,
 	IMoveRequest,
+	IMoveResponse,
 	IWebhooksCreateRequest,
+	IWebhooksCreateResponse,
+	IWebhooksListResponse,
 	IWebhooksShowRequest,
+	IWebhooksShowResponse,
 	IWebhooksUpdateRequest,
 	IWebhooksDeleteRequest,
 	InviteCreateResponse,
@@ -420,6 +444,7 @@ import type {
 	PagesUnlikeRequest,
 	PagesUpdateRequest,
 	FlashCreateRequest,
+	FlashCreateResponse,
 	FlashDeleteRequest,
 	FlashFeaturedResponse,
 	FlashLikeRequest,
@@ -438,10 +463,12 @@ import type {
 	RolesShowRequest,
 	RolesShowResponse,
 	RolesUsersRequest,
+	RolesUsersResponse,
 	RolesNotesRequest,
 	RolesNotesResponse,
 	RequestResetPasswordRequest,
 	ResetPasswordRequest,
+	ServerInfoResponse,
 	StatsResponse,
 	SwShowRegistrationRequest,
 	SwShowRegistrationResponse,
@@ -451,6 +478,7 @@ import type {
 	SwRegisterResponse,
 	SwUnregisterRequest,
 	TestRequest,
+	TestResponse,
 	UsernameAvailableRequest,
 	UsernameAvailableResponse,
 	UsersRequest,
@@ -484,6 +512,7 @@ import type {
 	UsersListsCreateFromPublicResponse,
 	UsersListsUpdateMembershipRequest,
 	UsersListsGetMembershipsRequest,
+	UsersListsGetMembershipsResponse,
 	UsersNotesRequest,
 	UsersNotesResponse,
 	UsersPagesRequest,
@@ -504,9 +533,12 @@ import type {
 	UsersShowRequest,
 	UsersShowResponse,
 	UsersAchievementsRequest,
+	UsersAchievementsResponse,
 	UsersUpdateMemoRequest,
 	FetchRssRequest,
+	FetchRssResponse,
 	FetchExternalResourcesRequest,
+	FetchExternalResourcesResponse,
 	RetentionResponse,
 } from './entities.js';
 
@@ -515,10 +547,10 @@ export type Endpoints = {
 	'admin/abuse-user-reports': { req: AdminAbuseUserReportsRequest; res: AdminAbuseUserReportsResponse };
 	'admin/accounts/create': { req: AdminAccountsCreateRequest; res: AdminAccountsCreateResponse };
 	'admin/accounts/delete': { req: AdminAccountsDeleteRequest; res: EmptyResponse };
-	'admin/accounts/find-by-email': { req: AdminAccountsFindByEmailRequest; res: EmptyResponse };
-	'admin/ad/create': { req: AdminAdCreateRequest; res: EmptyResponse };
+	'admin/accounts/find-by-email': { req: AdminAccountsFindByEmailRequest; res: AdminAccountsFindByEmailResponse };
+	'admin/ad/create': { req: AdminAdCreateRequest; res: AdminAdCreateResponse };
 	'admin/ad/delete': { req: AdminAdDeleteRequest; res: EmptyResponse };
-	'admin/ad/list': { req: AdminAdListRequest; res: EmptyResponse };
+	'admin/ad/list': { req: AdminAdListRequest; res: AdminAdListResponse };
 	'admin/ad/update': { req: AdminAdUpdateRequest; res: EmptyResponse };
 	'admin/announcements/create': { req: AdminAnnouncementsCreateRequest; res: AdminAnnouncementsCreateResponse };
 	'admin/announcements/delete': { req: AdminAnnouncementsDeleteRequest; res: EmptyResponse };
@@ -552,9 +584,9 @@ export type Endpoints = {
 	'admin/federation/refresh-remote-instance-metadata': { req: AdminFederationRefreshRemoteInstanceMetadataRequest; res: EmptyResponse };
 	'admin/federation/remove-all-following': { req: AdminFederationRemoveAllFollowingRequest; res: EmptyResponse };
 	'admin/federation/update-instance': { req: AdminFederationUpdateInstanceRequest; res: EmptyResponse };
-	'admin/get-index-stats': { req: EmptyRequest; res: EmptyResponse };
+	'admin/get-index-stats': { req: EmptyRequest; res: AdminGetIndexStatsResponse };
 	'admin/get-table-stats': { req: EmptyRequest; res: AdminGetTableStatsResponse };
-	'admin/get-user-ips': { req: AdminGetUserIpsRequest; res: EmptyResponse };
+	'admin/get-user-ips': { req: AdminGetUserIpsRequest; res: AdminGetUserIpsResponse };
 	'admin/invite/create': { req: AdminInviteCreateRequest; res: AdminInviteCreateResponse };
 	'admin/invite/list': { req: AdminInviteListRequest; res: AdminInviteListResponse };
 	'admin/promo/create': { req: AdminPromoCreateRequest; res: EmptyResponse };
@@ -586,7 +618,7 @@ export type Endpoints = {
 	'admin/roles/assign': { req: AdminRolesAssignRequest; res: EmptyResponse };
 	'admin/roles/unassign': { req: AdminRolesUnassignRequest; res: EmptyResponse };
 	'admin/roles/update-default-policies': { req: AdminRolesUpdateDefaultPoliciesRequest; res: EmptyResponse };
-	'admin/roles/users': { req: AdminRolesUsersRequest; res: EmptyResponse };
+	'admin/roles/users': { req: AdminRolesUsersRequest; res: AdminRolesUsersResponse };
 	'announcements': { req: AnnouncementsRequest; res: AnnouncementsResponse };
 	'antennas/create': { req: AntennasCreateRequest; res: AntennasCreateResponse };
 	'antennas/delete': { req: AntennasDeleteRequest; res: EmptyResponse };
@@ -660,7 +692,7 @@ export type Endpoints = {
 	'drive/folders/update': { req: DriveFoldersUpdateRequest; res: DriveFoldersUpdateResponse };
 	'drive/stream': { req: DriveStreamRequest; res: DriveStreamResponse };
 	'email-address/available': { req: EmailAddressAvailableRequest; res: EmailAddressAvailableResponse };
-	'endpoint': { req: EndpointRequest; res: EmptyResponse };
+	'endpoint': { req: EndpointRequest; res: EndpointResponse };
 	'endpoints': { req: EmptyRequest; res: EndpointsResponse };
 	'export-custom-emojis': { req: EmptyRequest; res: EmptyResponse };
 	'federation/followers': { req: FederationFollowersRequest; res: FederationFollowersResponse };
@@ -669,7 +701,7 @@ export type Endpoints = {
 	'federation/show-instance': { req: FederationShowInstanceRequest; res: FederationShowInstanceResponse };
 	'federation/update-remote-user': { req: FederationUpdateRemoteUserRequest; res: EmptyResponse };
 	'federation/users': { req: FederationUsersRequest; res: FederationUsersResponse };
-	'federation/stats': { req: FederationStatsRequest; res: EmptyResponse };
+	'federation/stats': { req: FederationStatsRequest; res: FederationStatsResponse };
 	'following/create': { req: FollowingCreateRequest; res: FollowingCreateResponse };
 	'following/delete': { req: FollowingDeleteRequest; res: FollowingDeleteResponse };
 	'following/update': { req: FollowingUpdateRequest; res: FollowingUpdateResponse };
@@ -688,7 +720,7 @@ export type Endpoints = {
 	'gallery/posts/show': { req: GalleryPostsShowRequest; res: GalleryPostsShowResponse };
 	'gallery/posts/unlike': { req: GalleryPostsUnlikeRequest; res: EmptyResponse };
 	'gallery/posts/update': { req: GalleryPostsUpdateRequest; res: GalleryPostsUpdateResponse };
-	'get-online-users-count': { req: EmptyRequest; res: EmptyResponse };
+	'get-online-users-count': { req: EmptyRequest; res: GetOnlineUsersCountResponse };
 	'get-avatar-decorations': { req: EmptyRequest; res: GetAvatarDecorationsResponse };
 	'hashtags/list': { req: HashtagsListRequest; res: HashtagsListResponse };
 	'hashtags/search': { req: HashtagsSearchRequest; res: HashtagsSearchResponse };
@@ -697,15 +729,15 @@ export type Endpoints = {
 	'hashtags/users': { req: HashtagsUsersRequest; res: HashtagsUsersResponse };
 	'i': { req: EmptyRequest; res: IResponse };
 	'i/2fa/done': { req: I2faDoneRequest; res: EmptyResponse };
-	'i/2fa/key-done': { req: I2faKeyDoneRequest; res: EmptyResponse };
+	'i/2fa/key-done': { req: I2faKeyDoneRequest; res: I2faKeyDoneResponse };
 	'i/2fa/password-less': { req: I2faPasswordLessRequest; res: EmptyResponse };
-	'i/2fa/register-key': { req: I2faRegisterKeyRequest; res: EmptyResponse };
-	'i/2fa/register': { req: I2faRegisterRequest; res: EmptyResponse };
+	'i/2fa/register-key': { req: I2faRegisterKeyRequest; res: I2faRegisterKeyResponse };
+	'i/2fa/register': { req: I2faRegisterRequest; res: I2faRegisterResponse };
 	'i/2fa/update-key': { req: I2faUpdateKeyRequest; res: EmptyResponse };
 	'i/2fa/remove-key': { req: I2faRemoveKeyRequest; res: EmptyResponse };
 	'i/2fa/unregister': { req: I2faUnregisterRequest; res: EmptyResponse };
-	'i/apps': { req: IAppsRequest; res: EmptyResponse };
-	'i/authorized-apps': { req: IAuthorizedAppsRequest; res: EmptyResponse };
+	'i/apps': { req: IAppsRequest; res: IAppsResponse };
+	'i/authorized-apps': { req: IAuthorizedAppsRequest; res: IAuthorizedAppsResponse };
 	'i/claim-achievement': { req: IClaimAchievementRequest; res: EmptyResponse };
 	'i/change-password': { req: IChangePasswordRequest; res: EmptyResponse };
 	'i/delete-account': { req: IDeleteAccountRequest; res: EmptyResponse };
@@ -732,23 +764,23 @@ export type Endpoints = {
 	'i/read-all-unread-notes': { req: EmptyRequest; res: EmptyResponse };
 	'i/read-announcement': { req: IReadAnnouncementRequest; res: EmptyResponse };
 	'i/regenerate-token': { req: IRegenerateTokenRequest; res: EmptyResponse };
-	'i/registry/get-all': { req: IRegistryGetAllRequest; res: EmptyResponse };
-	'i/registry/get-detail': { req: IRegistryGetDetailRequest; res: EmptyResponse };
-	'i/registry/get': { req: IRegistryGetRequest; res: EmptyResponse };
-	'i/registry/keys-with-type': { req: IRegistryKeysWithTypeRequest; res: EmptyResponse };
+	'i/registry/get-all': { req: IRegistryGetAllRequest; res: IRegistryGetAllResponse };
+	'i/registry/get-detail': { req: IRegistryGetDetailRequest; res: IRegistryGetDetailResponse };
+	'i/registry/get': { req: IRegistryGetRequest; res: IRegistryGetResponse };
+	'i/registry/keys-with-type': { req: IRegistryKeysWithTypeRequest; res: IRegistryKeysWithTypeResponse };
 	'i/registry/keys': { req: IRegistryKeysRequest; res: EmptyResponse };
 	'i/registry/remove': { req: IRegistryRemoveRequest; res: EmptyResponse };
-	'i/registry/scopes-with-domain': { req: EmptyRequest; res: EmptyResponse };
+	'i/registry/scopes-with-domain': { req: EmptyRequest; res: IRegistryScopesWithDomainResponse };
 	'i/registry/set': { req: IRegistrySetRequest; res: EmptyResponse };
 	'i/revoke-token': { req: IRevokeTokenRequest; res: EmptyResponse };
 	'i/signin-history': { req: ISigninHistoryRequest; res: ISigninHistoryResponse };
 	'i/unpin': { req: IUnpinRequest; res: IUnpinResponse };
-	'i/update-email': { req: IUpdateEmailRequest; res: EmptyResponse };
+	'i/update-email': { req: IUpdateEmailRequest; res: IUpdateEmailResponse };
 	'i/update': { req: IUpdateRequest; res: IUpdateResponse };
-	'i/move': { req: IMoveRequest; res: EmptyResponse };
-	'i/webhooks/create': { req: IWebhooksCreateRequest; res: EmptyResponse };
-	'i/webhooks/list': { req: EmptyRequest; res: EmptyResponse };
-	'i/webhooks/show': { req: IWebhooksShowRequest; res: EmptyResponse };
+	'i/move': { req: IMoveRequest; res: IMoveResponse };
+	'i/webhooks/create': { req: IWebhooksCreateRequest; res: IWebhooksCreateResponse };
+	'i/webhooks/list': { req: EmptyRequest; res: IWebhooksListResponse };
+	'i/webhooks/show': { req: IWebhooksShowRequest; res: IWebhooksShowResponse };
 	'i/webhooks/update': { req: IWebhooksUpdateRequest; res: EmptyResponse };
 	'i/webhooks/delete': { req: IWebhooksDeleteRequest; res: EmptyResponse };
 	'invite/create': { req: EmptyRequest; res: InviteCreateResponse };
@@ -807,7 +839,7 @@ export type Endpoints = {
 	'pages/show': { req: PagesShowRequest; res: PagesShowResponse };
 	'pages/unlike': { req: PagesUnlikeRequest; res: EmptyResponse };
 	'pages/update': { req: PagesUpdateRequest; res: EmptyResponse };
-	'flash/create': { req: FlashCreateRequest; res: EmptyResponse };
+	'flash/create': { req: FlashCreateRequest; res: FlashCreateResponse };
 	'flash/delete': { req: FlashDeleteRequest; res: EmptyResponse };
 	'flash/featured': { req: EmptyRequest; res: FlashFeaturedResponse };
 	'flash/like': { req: FlashLikeRequest; res: EmptyResponse };
@@ -821,18 +853,18 @@ export type Endpoints = {
 	'promo/read': { req: PromoReadRequest; res: EmptyResponse };
 	'roles/list': { req: EmptyRequest; res: RolesListResponse };
 	'roles/show': { req: RolesShowRequest; res: RolesShowResponse };
-	'roles/users': { req: RolesUsersRequest; res: EmptyResponse };
+	'roles/users': { req: RolesUsersRequest; res: RolesUsersResponse };
 	'roles/notes': { req: RolesNotesRequest; res: RolesNotesResponse };
 	'request-reset-password': { req: RequestResetPasswordRequest; res: EmptyResponse };
 	'reset-db': { req: EmptyRequest; res: EmptyResponse };
 	'reset-password': { req: ResetPasswordRequest; res: EmptyResponse };
-	'server-info': { req: EmptyRequest; res: EmptyResponse };
+	'server-info': { req: EmptyRequest; res: ServerInfoResponse };
 	'stats': { req: EmptyRequest; res: StatsResponse };
 	'sw/show-registration': { req: SwShowRegistrationRequest; res: SwShowRegistrationResponse };
 	'sw/update-registration': { req: SwUpdateRegistrationRequest; res: SwUpdateRegistrationResponse };
 	'sw/register': { req: SwRegisterRequest; res: SwRegisterResponse };
 	'sw/unregister': { req: SwUnregisterRequest; res: EmptyResponse };
-	'test': { req: TestRequest; res: EmptyResponse };
+	'test': { req: TestRequest; res: TestResponse };
 	'username/available': { req: UsernameAvailableRequest; res: UsernameAvailableResponse };
 	'users': { req: UsersRequest; res: UsersResponse };
 	'users/clips': { req: UsersClipsRequest; res: UsersClipsResponse };
@@ -852,7 +884,7 @@ export type Endpoints = {
 	'users/lists/update': { req: UsersListsUpdateRequest; res: UsersListsUpdateResponse };
 	'users/lists/create-from-public': { req: UsersListsCreateFromPublicRequest; res: UsersListsCreateFromPublicResponse };
 	'users/lists/update-membership': { req: UsersListsUpdateMembershipRequest; res: EmptyResponse };
-	'users/lists/get-memberships': { req: UsersListsGetMembershipsRequest; res: EmptyResponse };
+	'users/lists/get-memberships': { req: UsersListsGetMembershipsRequest; res: UsersListsGetMembershipsResponse };
 	'users/notes': { req: UsersNotesRequest; res: UsersNotesResponse };
 	'users/pages': { req: UsersPagesRequest; res: UsersPagesResponse };
 	'users/flashs': { req: UsersFlashsRequest; res: UsersFlashsResponse };
@@ -863,9 +895,9 @@ export type Endpoints = {
 	'users/search-by-username-and-host': { req: UsersSearchByUsernameAndHostRequest; res: UsersSearchByUsernameAndHostResponse };
 	'users/search': { req: UsersSearchRequest; res: UsersSearchResponse };
 	'users/show': { req: UsersShowRequest; res: UsersShowResponse };
-	'users/achievements': { req: UsersAchievementsRequest; res: EmptyResponse };
+	'users/achievements': { req: UsersAchievementsRequest; res: UsersAchievementsResponse };
 	'users/update-memo': { req: UsersUpdateMemoRequest; res: EmptyResponse };
-	'fetch-rss': { req: FetchRssRequest; res: EmptyResponse };
-	'fetch-external-resources': { req: FetchExternalResourcesRequest; res: EmptyResponse };
+	'fetch-rss': { req: FetchRssRequest; res: FetchRssResponse };
+	'fetch-external-resources': { req: FetchExternalResourcesRequest; res: FetchExternalResourcesResponse };
 	'retention': { req: EmptyRequest; res: RetentionResponse };
 }
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 4de3c80a7f..0f03fd3446 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -1,6 +1,6 @@
 /*
- * version: 2023.11.0-beta.3
- * generatedAt: 2023-12-08T04:57:48.409Z
+ * version: 2023.12.0
+ * generatedAt: 2023-12-24T08:46:11.014Z
  */
 
 import { operations } from './types.js';
@@ -15,9 +15,12 @@ export type AdminAccountsCreateRequest = operations['admin/accounts/create']['re
 export type AdminAccountsCreateResponse = operations['admin/accounts/create']['responses']['200']['content']['application/json'];
 export type AdminAccountsDeleteRequest = operations['admin/accounts/delete']['requestBody']['content']['application/json'];
 export type AdminAccountsFindByEmailRequest = operations['admin/accounts/find-by-email']['requestBody']['content']['application/json'];
+export type AdminAccountsFindByEmailResponse = operations['admin/accounts/find-by-email']['responses']['200']['content']['application/json'];
 export type AdminAdCreateRequest = operations['admin/ad/create']['requestBody']['content']['application/json'];
+export type AdminAdCreateResponse = operations['admin/ad/create']['responses']['200']['content']['application/json'];
 export type AdminAdDeleteRequest = operations['admin/ad/delete']['requestBody']['content']['application/json'];
 export type AdminAdListRequest = operations['admin/ad/list']['requestBody']['content']['application/json'];
+export type AdminAdListResponse = operations['admin/ad/list']['responses']['200']['content']['application/json'];
 export type AdminAdUpdateRequest = operations['admin/ad/update']['requestBody']['content']['application/json'];
 export type AdminAnnouncementsCreateRequest = operations['admin/announcements/create']['requestBody']['content']['application/json'];
 export type AdminAnnouncementsCreateResponse = operations['admin/announcements/create']['responses']['200']['content']['application/json'];
@@ -57,8 +60,10 @@ export type AdminFederationDeleteAllFilesRequest = operations['admin/federation/
 export type AdminFederationRefreshRemoteInstanceMetadataRequest = operations['admin/federation/refresh-remote-instance-metadata']['requestBody']['content']['application/json'];
 export type AdminFederationRemoveAllFollowingRequest = operations['admin/federation/remove-all-following']['requestBody']['content']['application/json'];
 export type AdminFederationUpdateInstanceRequest = operations['admin/federation/update-instance']['requestBody']['content']['application/json'];
+export type AdminGetIndexStatsResponse = operations['admin/get-index-stats']['responses']['200']['content']['application/json'];
 export type AdminGetTableStatsResponse = operations['admin/get-table-stats']['responses']['200']['content']['application/json'];
 export type AdminGetUserIpsRequest = operations['admin/get-user-ips']['requestBody']['content']['application/json'];
+export type AdminGetUserIpsResponse = operations['admin/get-user-ips']['responses']['200']['content']['application/json'];
 export type AdminInviteCreateRequest = operations['admin/invite/create']['requestBody']['content']['application/json'];
 export type AdminInviteCreateResponse = operations['admin/invite/create']['responses']['200']['content']['application/json'];
 export type AdminInviteListRequest = operations['admin/invite/list']['requestBody']['content']['application/json'];
@@ -100,6 +105,7 @@ export type AdminRolesAssignRequest = operations['admin/roles/assign']['requestB
 export type AdminRolesUnassignRequest = operations['admin/roles/unassign']['requestBody']['content']['application/json'];
 export type AdminRolesUpdateDefaultPoliciesRequest = operations['admin/roles/update-default-policies']['requestBody']['content']['application/json'];
 export type AdminRolesUsersRequest = operations['admin/roles/users']['requestBody']['content']['application/json'];
+export type AdminRolesUsersResponse = operations['admin/roles/users']['responses']['200']['content']['application/json'];
 export type AnnouncementsRequest = operations['announcements']['requestBody']['content']['application/json'];
 export type AnnouncementsResponse = operations['announcements']['responses']['200']['content']['application/json'];
 export type AntennasCreateRequest = operations['antennas/create']['requestBody']['content']['application/json'];
@@ -227,6 +233,7 @@ export type DriveStreamResponse = operations['drive/stream']['responses']['200']
 export type EmailAddressAvailableRequest = operations['email-address/available']['requestBody']['content']['application/json'];
 export type EmailAddressAvailableResponse = operations['email-address/available']['responses']['200']['content']['application/json'];
 export type EndpointRequest = operations['endpoint']['requestBody']['content']['application/json'];
+export type EndpointResponse = operations['endpoint']['responses']['200']['content']['application/json'];
 export type EndpointsResponse = operations['endpoints']['responses']['200']['content']['application/json'];
 export type FederationFollowersRequest = operations['federation/followers']['requestBody']['content']['application/json'];
 export type FederationFollowersResponse = operations['federation/followers']['responses']['200']['content']['application/json'];
@@ -240,6 +247,7 @@ export type FederationUpdateRemoteUserRequest = operations['federation/update-re
 export type FederationUsersRequest = operations['federation/users']['requestBody']['content']['application/json'];
 export type FederationUsersResponse = operations['federation/users']['responses']['200']['content']['application/json'];
 export type FederationStatsRequest = operations['federation/stats']['requestBody']['content']['application/json'];
+export type FederationStatsResponse = operations['federation/stats']['responses']['200']['content']['application/json'];
 export type FollowingCreateRequest = operations['following/create']['requestBody']['content']['application/json'];
 export type FollowingCreateResponse = operations['following/create']['responses']['200']['content']['application/json'];
 export type FollowingDeleteRequest = operations['following/delete']['requestBody']['content']['application/json'];
@@ -269,6 +277,7 @@ export type GalleryPostsShowResponse = operations['gallery/posts/show']['respons
 export type GalleryPostsUnlikeRequest = operations['gallery/posts/unlike']['requestBody']['content']['application/json'];
 export type GalleryPostsUpdateRequest = operations['gallery/posts/update']['requestBody']['content']['application/json'];
 export type GalleryPostsUpdateResponse = operations['gallery/posts/update']['responses']['200']['content']['application/json'];
+export type GetOnlineUsersCountResponse = operations['get-online-users-count']['responses']['200']['content']['application/json'];
 export type GetAvatarDecorationsResponse = operations['get-avatar-decorations']['responses']['200']['content']['application/json'];
 export type HashtagsListRequest = operations['hashtags/list']['requestBody']['content']['application/json'];
 export type HashtagsListResponse = operations['hashtags/list']['responses']['200']['content']['application/json'];
@@ -282,14 +291,19 @@ export type HashtagsUsersResponse = operations['hashtags/users']['responses']['2
 export type IResponse = operations['i']['responses']['200']['content']['application/json'];
 export type I2faDoneRequest = operations['i/2fa/done']['requestBody']['content']['application/json'];
 export type I2faKeyDoneRequest = operations['i/2fa/key-done']['requestBody']['content']['application/json'];
+export type I2faKeyDoneResponse = operations['i/2fa/key-done']['responses']['200']['content']['application/json'];
 export type I2faPasswordLessRequest = operations['i/2fa/password-less']['requestBody']['content']['application/json'];
 export type I2faRegisterKeyRequest = operations['i/2fa/register-key']['requestBody']['content']['application/json'];
+export type I2faRegisterKeyResponse = operations['i/2fa/register-key']['responses']['200']['content']['application/json'];
 export type I2faRegisterRequest = operations['i/2fa/register']['requestBody']['content']['application/json'];
+export type I2faRegisterResponse = operations['i/2fa/register']['responses']['200']['content']['application/json'];
 export type I2faUpdateKeyRequest = operations['i/2fa/update-key']['requestBody']['content']['application/json'];
 export type I2faRemoveKeyRequest = operations['i/2fa/remove-key']['requestBody']['content']['application/json'];
 export type I2faUnregisterRequest = operations['i/2fa/unregister']['requestBody']['content']['application/json'];
 export type IAppsRequest = operations['i/apps']['requestBody']['content']['application/json'];
+export type IAppsResponse = operations['i/apps']['responses']['200']['content']['application/json'];
 export type IAuthorizedAppsRequest = operations['i/authorized-apps']['requestBody']['content']['application/json'];
+export type IAuthorizedAppsResponse = operations['i/authorized-apps']['responses']['200']['content']['application/json'];
 export type IClaimAchievementRequest = operations['i/claim-achievement']['requestBody']['content']['application/json'];
 export type IChangePasswordRequest = operations['i/change-password']['requestBody']['content']['application/json'];
 export type IDeleteAccountRequest = operations['i/delete-account']['requestBody']['content']['application/json'];
@@ -318,11 +332,16 @@ export type IPinResponse = operations['i/pin']['responses']['200']['content']['a
 export type IReadAnnouncementRequest = operations['i/read-announcement']['requestBody']['content']['application/json'];
 export type IRegenerateTokenRequest = operations['i/regenerate-token']['requestBody']['content']['application/json'];
 export type IRegistryGetAllRequest = operations['i/registry/get-all']['requestBody']['content']['application/json'];
+export type IRegistryGetAllResponse = operations['i/registry/get-all']['responses']['200']['content']['application/json'];
 export type IRegistryGetDetailRequest = operations['i/registry/get-detail']['requestBody']['content']['application/json'];
+export type IRegistryGetDetailResponse = operations['i/registry/get-detail']['responses']['200']['content']['application/json'];
 export type IRegistryGetRequest = operations['i/registry/get']['requestBody']['content']['application/json'];
+export type IRegistryGetResponse = operations['i/registry/get']['responses']['200']['content']['application/json'];
 export type IRegistryKeysWithTypeRequest = operations['i/registry/keys-with-type']['requestBody']['content']['application/json'];
+export type IRegistryKeysWithTypeResponse = operations['i/registry/keys-with-type']['responses']['200']['content']['application/json'];
 export type IRegistryKeysRequest = operations['i/registry/keys']['requestBody']['content']['application/json'];
 export type IRegistryRemoveRequest = operations['i/registry/remove']['requestBody']['content']['application/json'];
+export type IRegistryScopesWithDomainResponse = operations['i/registry/scopes-with-domain']['responses']['200']['content']['application/json'];
 export type IRegistrySetRequest = operations['i/registry/set']['requestBody']['content']['application/json'];
 export type IRevokeTokenRequest = operations['i/revoke-token']['requestBody']['content']['application/json'];
 export type ISigninHistoryRequest = operations['i/signin-history']['requestBody']['content']['application/json'];
@@ -330,11 +349,16 @@ export type ISigninHistoryResponse = operations['i/signin-history']['responses']
 export type IUnpinRequest = operations['i/unpin']['requestBody']['content']['application/json'];
 export type IUnpinResponse = operations['i/unpin']['responses']['200']['content']['application/json'];
 export type IUpdateEmailRequest = operations['i/update-email']['requestBody']['content']['application/json'];
+export type IUpdateEmailResponse = operations['i/update-email']['responses']['200']['content']['application/json'];
 export type IUpdateRequest = operations['i/update']['requestBody']['content']['application/json'];
 export type IUpdateResponse = operations['i/update']['responses']['200']['content']['application/json'];
 export type IMoveRequest = operations['i/move']['requestBody']['content']['application/json'];
+export type IMoveResponse = operations['i/move']['responses']['200']['content']['application/json'];
 export type IWebhooksCreateRequest = operations['i/webhooks/create']['requestBody']['content']['application/json'];
+export type IWebhooksCreateResponse = operations['i/webhooks/create']['responses']['200']['content']['application/json'];
+export type IWebhooksListResponse = operations['i/webhooks/list']['responses']['200']['content']['application/json'];
 export type IWebhooksShowRequest = operations['i/webhooks/show']['requestBody']['content']['application/json'];
+export type IWebhooksShowResponse = operations['i/webhooks/show']['responses']['200']['content']['application/json'];
 export type IWebhooksUpdateRequest = operations['i/webhooks/update']['requestBody']['content']['application/json'];
 export type IWebhooksDeleteRequest = operations['i/webhooks/delete']['requestBody']['content']['application/json'];
 export type InviteCreateResponse = operations['invite/create']['responses']['200']['content']['application/json'];
@@ -422,6 +446,7 @@ export type PagesShowResponse = operations['pages/show']['responses']['200']['co
 export type PagesUnlikeRequest = operations['pages/unlike']['requestBody']['content']['application/json'];
 export type PagesUpdateRequest = operations['pages/update']['requestBody']['content']['application/json'];
 export type FlashCreateRequest = operations['flash/create']['requestBody']['content']['application/json'];
+export type FlashCreateResponse = operations['flash/create']['responses']['200']['content']['application/json'];
 export type FlashDeleteRequest = operations['flash/delete']['requestBody']['content']['application/json'];
 export type FlashFeaturedResponse = operations['flash/featured']['responses']['200']['content']['application/json'];
 export type FlashLikeRequest = operations['flash/like']['requestBody']['content']['application/json'];
@@ -440,10 +465,12 @@ export type RolesListResponse = operations['roles/list']['responses']['200']['co
 export type RolesShowRequest = operations['roles/show']['requestBody']['content']['application/json'];
 export type RolesShowResponse = operations['roles/show']['responses']['200']['content']['application/json'];
 export type RolesUsersRequest = operations['roles/users']['requestBody']['content']['application/json'];
+export type RolesUsersResponse = operations['roles/users']['responses']['200']['content']['application/json'];
 export type RolesNotesRequest = operations['roles/notes']['requestBody']['content']['application/json'];
 export type RolesNotesResponse = operations['roles/notes']['responses']['200']['content']['application/json'];
 export type RequestResetPasswordRequest = operations['request-reset-password']['requestBody']['content']['application/json'];
 export type ResetPasswordRequest = operations['reset-password']['requestBody']['content']['application/json'];
+export type ServerInfoResponse = operations['server-info']['responses']['200']['content']['application/json'];
 export type StatsResponse = operations['stats']['responses']['200']['content']['application/json'];
 export type SwShowRegistrationRequest = operations['sw/show-registration']['requestBody']['content']['application/json'];
 export type SwShowRegistrationResponse = operations['sw/show-registration']['responses']['200']['content']['application/json'];
@@ -453,6 +480,7 @@ export type SwRegisterRequest = operations['sw/register']['requestBody']['conten
 export type SwRegisterResponse = operations['sw/register']['responses']['200']['content']['application/json'];
 export type SwUnregisterRequest = operations['sw/unregister']['requestBody']['content']['application/json'];
 export type TestRequest = operations['test']['requestBody']['content']['application/json'];
+export type TestResponse = operations['test']['responses']['200']['content']['application/json'];
 export type UsernameAvailableRequest = operations['username/available']['requestBody']['content']['application/json'];
 export type UsernameAvailableResponse = operations['username/available']['responses']['200']['content']['application/json'];
 export type UsersRequest = operations['users']['requestBody']['content']['application/json'];
@@ -486,6 +514,7 @@ export type UsersListsCreateFromPublicRequest = operations['users/lists/create-f
 export type UsersListsCreateFromPublicResponse = operations['users/lists/create-from-public']['responses']['200']['content']['application/json'];
 export type UsersListsUpdateMembershipRequest = operations['users/lists/update-membership']['requestBody']['content']['application/json'];
 export type UsersListsGetMembershipsRequest = operations['users/lists/get-memberships']['requestBody']['content']['application/json'];
+export type UsersListsGetMembershipsResponse = operations['users/lists/get-memberships']['responses']['200']['content']['application/json'];
 export type UsersNotesRequest = operations['users/notes']['requestBody']['content']['application/json'];
 export type UsersNotesResponse = operations['users/notes']['responses']['200']['content']['application/json'];
 export type UsersPagesRequest = operations['users/pages']['requestBody']['content']['application/json'];
@@ -506,7 +535,10 @@ export type UsersSearchResponse = operations['users/search']['responses']['200']
 export type UsersShowRequest = operations['users/show']['requestBody']['content']['application/json'];
 export type UsersShowResponse = operations['users/show']['responses']['200']['content']['application/json'];
 export type UsersAchievementsRequest = operations['users/achievements']['requestBody']['content']['application/json'];
+export type UsersAchievementsResponse = operations['users/achievements']['responses']['200']['content']['application/json'];
 export type UsersUpdateMemoRequest = operations['users/update-memo']['requestBody']['content']['application/json'];
 export type FetchRssRequest = operations['fetch-rss']['requestBody']['content']['application/json'];
+export type FetchRssResponse = operations['fetch-rss']['responses']['200']['content']['application/json'];
 export type FetchExternalResourcesRequest = operations['fetch-external-resources']['requestBody']['content']['application/json'];
+export type FetchExternalResourcesResponse = operations['fetch-external-resources']['responses']['200']['content']['application/json'];
 export type RetentionResponse = operations['retention']['responses']['200']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index 2c25e82d12..5ae7723630 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -1,6 +1,6 @@
 /*
- * version: 2023.11.0-beta.3
- * generatedAt: 2023-12-08T04:57:48.405Z
+ * version: 2023.12.0
+ * generatedAt: 2023-12-24T08:46:11.013Z
  */
 
 import { components } from './types.js';
@@ -13,6 +13,7 @@ export type MeDetailed = components['schemas']['MeDetailed'];
 export type UserDetailed = components['schemas']['UserDetailed'];
 export type User = components['schemas']['User'];
 export type UserList = components['schemas']['UserList'];
+export type Ad = components['schemas']['Ad'];
 export type Announcement = components['schemas']['Announcement'];
 export type App = components['schemas']['App'];
 export type Note = components['schemas']['Note'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index cecc2c872f..6ff98f5013 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -2,8 +2,8 @@
 /* eslint @typescript-eslint/no-explicit-any: 0 */
 
 /*
- * version: 2023.11.0-beta.3
- * generatedAt: 2023-12-08T04:57:48.142Z
+ * version: 2023.12.0
+ * generatedAt: 2023-12-24T08:46:10.930Z
  */
 
 /**
@@ -22,7 +22,7 @@ export type paths = {
      * admin/meta
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/meta'];
   };
@@ -31,7 +31,7 @@ export type paths = {
      * admin/abuse-user-reports
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/abuse-user-reports'];
   };
@@ -40,7 +40,7 @@ export type paths = {
      * admin/accounts/create
      * @description No description provided.
      *
-     * **Credential required**: *No*
+     * **Credential required**: *No* / **Permission**: *write:admin*
      */
     post: operations['admin/accounts/create'];
   };
@@ -49,7 +49,7 @@ export type paths = {
      * admin/accounts/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/accounts/delete'];
   };
@@ -58,7 +58,7 @@ export type paths = {
      * admin/accounts/find-by-email
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/accounts/find-by-email'];
   };
@@ -67,7 +67,7 @@ export type paths = {
      * admin/ad/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/ad/create'];
   };
@@ -76,7 +76,7 @@ export type paths = {
      * admin/ad/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/ad/delete'];
   };
@@ -85,7 +85,7 @@ export type paths = {
      * admin/ad/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/ad/list'];
   };
@@ -94,7 +94,7 @@ export type paths = {
      * admin/ad/update
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/ad/update'];
   };
@@ -103,7 +103,7 @@ export type paths = {
      * admin/announcements/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/announcements/create'];
   };
@@ -112,7 +112,7 @@ export type paths = {
      * admin/announcements/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/announcements/delete'];
   };
@@ -121,7 +121,7 @@ export type paths = {
      * admin/announcements/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/announcements/list'];
   };
@@ -130,7 +130,7 @@ export type paths = {
      * admin/announcements/update
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/announcements/update'];
   };
@@ -139,7 +139,7 @@ export type paths = {
      * admin/avatar-decorations/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/avatar-decorations/create'];
   };
@@ -148,7 +148,7 @@ export type paths = {
      * admin/avatar-decorations/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/avatar-decorations/delete'];
   };
@@ -157,7 +157,7 @@ export type paths = {
      * admin/avatar-decorations/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/avatar-decorations/list'];
   };
@@ -166,7 +166,7 @@ export type paths = {
      * admin/avatar-decorations/update
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/avatar-decorations/update'];
   };
@@ -175,7 +175,7 @@ export type paths = {
      * admin/delete-all-files-of-a-user
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/delete-all-files-of-a-user'];
   };
@@ -184,7 +184,7 @@ export type paths = {
      * admin/unset-user-avatar
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/unset-user-avatar'];
   };
@@ -193,7 +193,7 @@ export type paths = {
      * admin/unset-user-banner
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/unset-user-banner'];
   };
@@ -202,7 +202,7 @@ export type paths = {
      * admin/drive/clean-remote-files
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/drive/clean-remote-files'];
   };
@@ -211,7 +211,7 @@ export type paths = {
      * admin/drive/cleanup
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/drive/cleanup'];
   };
@@ -220,7 +220,7 @@ export type paths = {
      * admin/drive/files
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/drive/files'];
   };
@@ -229,7 +229,7 @@ export type paths = {
      * admin/drive/show-file
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/drive/show-file'];
   };
@@ -238,7 +238,7 @@ export type paths = {
      * admin/emoji/add-aliases-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/emoji/add-aliases-bulk'];
   };
@@ -247,7 +247,7 @@ export type paths = {
      * admin/emoji/add
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/emoji/add'];
   };
@@ -256,7 +256,7 @@ export type paths = {
      * admin/emoji/copy
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/emoji/copy'];
   };
@@ -265,7 +265,7 @@ export type paths = {
      * admin/emoji/delete-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/emoji/delete-bulk'];
   };
@@ -274,7 +274,7 @@ export type paths = {
      * admin/emoji/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/emoji/delete'];
   };
@@ -283,8 +283,7 @@ export type paths = {
      * admin/emoji/import-zip
      * @description No description provided.
      *
-     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/emoji/import-zip'];
   };
@@ -293,7 +292,7 @@ export type paths = {
      * admin/emoji/list-remote
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/emoji/list-remote'];
   };
@@ -302,7 +301,7 @@ export type paths = {
      * admin/emoji/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/emoji/list'];
   };
@@ -311,7 +310,7 @@ export type paths = {
      * admin/emoji/remove-aliases-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/emoji/remove-aliases-bulk'];
   };
@@ -320,7 +319,7 @@ export type paths = {
      * admin/emoji/set-aliases-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/emoji/set-aliases-bulk'];
   };
@@ -329,7 +328,7 @@ export type paths = {
      * admin/emoji/set-category-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/emoji/set-category-bulk'];
   };
@@ -338,7 +337,7 @@ export type paths = {
      * admin/emoji/set-license-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/emoji/set-license-bulk'];
   };
@@ -347,7 +346,7 @@ export type paths = {
      * admin/emoji/update
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/emoji/update'];
   };
@@ -356,7 +355,7 @@ export type paths = {
      * admin/federation/delete-all-files
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/federation/delete-all-files'];
   };
@@ -365,7 +364,7 @@ export type paths = {
      * admin/federation/refresh-remote-instance-metadata
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/federation/refresh-remote-instance-metadata'];
   };
@@ -374,7 +373,7 @@ export type paths = {
      * admin/federation/remove-all-following
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/federation/remove-all-following'];
   };
@@ -383,7 +382,7 @@ export type paths = {
      * admin/federation/update-instance
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/federation/update-instance'];
   };
@@ -392,7 +391,7 @@ export type paths = {
      * admin/get-index-stats
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/get-index-stats'];
   };
@@ -401,7 +400,7 @@ export type paths = {
      * admin/get-table-stats
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/get-table-stats'];
   };
@@ -410,7 +409,7 @@ export type paths = {
      * admin/get-user-ips
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/get-user-ips'];
   };
@@ -419,7 +418,7 @@ export type paths = {
      * admin/invite/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/invite/create'];
   };
@@ -428,7 +427,7 @@ export type paths = {
      * admin/invite/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/invite/list'];
   };
@@ -437,7 +436,7 @@ export type paths = {
      * admin/promo/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/promo/create'];
   };
@@ -446,7 +445,7 @@ export type paths = {
      * admin/queue/clear
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/queue/clear'];
   };
@@ -455,7 +454,7 @@ export type paths = {
      * admin/queue/deliver-delayed
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/queue/deliver-delayed'];
   };
@@ -464,7 +463,7 @@ export type paths = {
      * admin/queue/inbox-delayed
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/queue/inbox-delayed'];
   };
@@ -473,7 +472,7 @@ export type paths = {
      * admin/queue/promote
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/queue/promote'];
   };
@@ -482,7 +481,7 @@ export type paths = {
      * admin/queue/stats
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/queue/stats'];
   };
@@ -491,7 +490,7 @@ export type paths = {
      * admin/relays/add
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/relays/add'];
   };
@@ -500,7 +499,7 @@ export type paths = {
      * admin/relays/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/relays/list'];
   };
@@ -509,7 +508,7 @@ export type paths = {
      * admin/relays/remove
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/relays/remove'];
   };
@@ -518,7 +517,7 @@ export type paths = {
      * admin/reset-password
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/reset-password'];
   };
@@ -527,7 +526,7 @@ export type paths = {
      * admin/resolve-abuse-user-report
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/resolve-abuse-user-report'];
   };
@@ -536,7 +535,7 @@ export type paths = {
      * admin/send-email
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/send-email'];
   };
@@ -545,7 +544,7 @@ export type paths = {
      * admin/server-info
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/server-info'];
   };
@@ -554,7 +553,7 @@ export type paths = {
      * admin/show-moderation-logs
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/show-moderation-logs'];
   };
@@ -563,7 +562,7 @@ export type paths = {
      * admin/show-user
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/show-user'];
   };
@@ -572,7 +571,7 @@ export type paths = {
      * admin/show-users
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/show-users'];
   };
@@ -581,7 +580,7 @@ export type paths = {
      * admin/suspend-user
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/suspend-user'];
   };
@@ -590,7 +589,7 @@ export type paths = {
      * admin/unsuspend-user
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/unsuspend-user'];
   };
@@ -599,7 +598,7 @@ export type paths = {
      * admin/update-meta
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/update-meta'];
   };
@@ -608,7 +607,7 @@ export type paths = {
      * admin/delete-account
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/delete-account'];
   };
@@ -617,7 +616,7 @@ export type paths = {
      * admin/update-user-note
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/update-user-note'];
   };
@@ -626,7 +625,7 @@ export type paths = {
      * admin/roles/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/roles/create'];
   };
@@ -635,7 +634,7 @@ export type paths = {
      * admin/roles/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/roles/delete'];
   };
@@ -644,7 +643,7 @@ export type paths = {
      * admin/roles/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/roles/list'];
   };
@@ -653,7 +652,7 @@ export type paths = {
      * admin/roles/show
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:admin*
      */
     post: operations['admin/roles/show'];
   };
@@ -662,7 +661,7 @@ export type paths = {
      * admin/roles/update
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/roles/update'];
   };
@@ -671,7 +670,7 @@ export type paths = {
      * admin/roles/assign
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/roles/assign'];
   };
@@ -680,7 +679,7 @@ export type paths = {
      * admin/roles/unassign
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/roles/unassign'];
   };
@@ -689,7 +688,7 @@ export type paths = {
      * admin/roles/update-default-policies
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:admin*
      */
     post: operations['admin/roles/update-default-policies'];
   };
@@ -698,7 +697,7 @@ export type paths = {
      * admin/roles/users
      * @description No description provided.
      *
-     * **Credential required**: *No*
+     * **Credential required**: *No* / **Permission**: *read:admin*
      */
     post: operations['admin/roles/users'];
   };
@@ -3478,6 +3477,8 @@ export type components = {
           flipH?: boolean;
           /** Format: url */
           url: string;
+          offsetX?: number;
+          offsetY?: number;
         }[];
       isBot?: boolean;
       isCat?: boolean;
@@ -3540,7 +3541,9 @@ export type components = {
       pinnedPage: components['schemas']['Page'] | null;
       publicReactions: boolean;
       /** @enum {string} */
-      ffVisibility: 'public' | 'followers' | 'private';
+      followingVisibility: 'public' | 'followers' | 'private';
+      /** @enum {string} */
+      followersVisibility: 'public' | 'followers' | 'private';
       /** @default false */
       twoFactorEnabled: boolean;
       /** @default false */
@@ -3627,18 +3630,10 @@ export type components = {
           /** @enum {string} */
           type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
         };
-        achievementEarned?: {
-          /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
-        };
         receiveFollowRequest?: {
           /** @enum {string} */
           type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
         };
-        followRequestAccepted?: {
-          /** @enum {string} */
-          type: 'all' | 'following' | 'follower' | 'mutualFollow' | 'list' | 'never';
-        };
       };
       emailNotificationTypes: string[];
       achievements: {
@@ -3670,6 +3665,7 @@ export type components = {
         userListLimit: number;
         userEachUserListsLimit: number;
         rateLimitFactor: number;
+        avatarDecorationLimit: number;
       };
       email?: string | null;
       emailVerified?: boolean | null;
@@ -3700,6 +3696,24 @@ export type components = {
       userIds?: string[];
       isPublic: boolean;
     };
+    Ad: {
+      /**
+       * Format: id
+       * @example xxxxxxxxxx
+       */
+      id: string;
+      /** Format: date-time */
+      expiresAt: string;
+      /** Format: date-time */
+      startsAt: string;
+      place: string;
+      priority: string;
+      ratio: number;
+      url: string;
+      imageUrl: string;
+      memo: string;
+      dayOfWeek: number;
+    };
     Announcement: {
       /**
        * Format: id
@@ -3815,7 +3829,7 @@ export type components = {
       /** Format: date-time */
       createdAt: string;
       /** @enum {string} */
-      type: 'note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'achievementEarned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped';
+      type: 'note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'reaction:grouped' | 'renote:grouped';
       user?: components['schemas']['UserLite'] | null;
       /** Format: id */
       userId?: string | null;
@@ -4341,6 +4355,11 @@ export type components = {
           priority: number;
           useDefault: boolean;
         };
+        avatarDecorationLimit: {
+          value: number | boolean;
+          priority: number;
+          useDefault: boolean;
+        };
       };
       usersCount: number;
     });
@@ -4362,7 +4381,7 @@ export type operations = {
    * admin/meta
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/meta': {
     responses: {
@@ -4397,6 +4416,7 @@ export type operations = {
             hiddenTags: string[];
             blockedHosts: string[];
             sensitiveWords: string[];
+            bannedEmailDomains?: string[];
             preservedUsernames: string[];
             hcaptchaSecretKey: string | null;
             recaptchaSecretKey: string | null;
@@ -4501,7 +4521,7 @@ export type operations = {
    * admin/abuse-user-reports
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/abuse-user-reports': {
     requestBody: {
@@ -4593,7 +4613,7 @@ export type operations = {
    * admin/accounts/create
    * @description No description provided.
    *
-   * **Credential required**: *No*
+   * **Credential required**: *No* / **Permission**: *write:admin*
    */
   'admin/accounts/create': {
     requestBody: {
@@ -4647,7 +4667,7 @@ export type operations = {
    * admin/accounts/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/accounts/delete': {
     requestBody: {
@@ -4699,7 +4719,7 @@ export type operations = {
    * admin/accounts/find-by-email
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/accounts/find-by-email': {
     requestBody: {
@@ -4710,9 +4730,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['User'];
+        };
       };
       /** @description Client error */
       400: {
@@ -4750,7 +4772,7 @@ export type operations = {
    * admin/ad/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/ad/create': {
     requestBody: {
@@ -4769,9 +4791,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Ad'];
+        };
       };
       /** @description Client error */
       400: {
@@ -4809,7 +4833,7 @@ export type operations = {
    * admin/ad/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/ad/delete': {
     requestBody: {
@@ -4861,7 +4885,7 @@ export type operations = {
    * admin/ad/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/ad/list': {
     requestBody: {
@@ -4879,9 +4903,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Ad'][];
+        };
       };
       /** @description Client error */
       400: {
@@ -4919,7 +4945,7 @@ export type operations = {
    * admin/ad/update
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/ad/update': {
     requestBody: {
@@ -4980,7 +5006,7 @@ export type operations = {
    * admin/announcements/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/announcements/create': {
     requestBody: {
@@ -5069,7 +5095,7 @@ export type operations = {
    * admin/announcements/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/announcements/delete': {
     requestBody: {
@@ -5121,7 +5147,7 @@ export type operations = {
    * admin/announcements/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/announcements/list': {
     requestBody: {
@@ -5195,7 +5221,7 @@ export type operations = {
    * admin/announcements/update
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/announcements/update': {
     requestBody: {
@@ -5258,7 +5284,7 @@ export type operations = {
    * admin/avatar-decorations/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/avatar-decorations/create': {
     requestBody: {
@@ -5312,7 +5338,7 @@ export type operations = {
    * admin/avatar-decorations/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/avatar-decorations/delete': {
     requestBody: {
@@ -5364,7 +5390,7 @@ export type operations = {
    * admin/avatar-decorations/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/avatar-decorations/list': {
     requestBody: {
@@ -5438,7 +5464,7 @@ export type operations = {
    * admin/avatar-decorations/update
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/avatar-decorations/update': {
     requestBody: {
@@ -5494,7 +5520,7 @@ export type operations = {
    * admin/delete-all-files-of-a-user
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/delete-all-files-of-a-user': {
     requestBody: {
@@ -5546,7 +5572,7 @@ export type operations = {
    * admin/unset-user-avatar
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/unset-user-avatar': {
     requestBody: {
@@ -5598,7 +5624,7 @@ export type operations = {
    * admin/unset-user-banner
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/unset-user-banner': {
     requestBody: {
@@ -5650,7 +5676,7 @@ export type operations = {
    * admin/drive/clean-remote-files
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/drive/clean-remote-files': {
     responses: {
@@ -5694,7 +5720,7 @@ export type operations = {
    * admin/drive/cleanup
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/drive/cleanup': {
     responses: {
@@ -5738,7 +5764,7 @@ export type operations = {
    * admin/drive/files
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/drive/files': {
     requestBody: {
@@ -5809,7 +5835,7 @@ export type operations = {
    * admin/drive/show-file
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/drive/show-file': {
     requestBody: {
@@ -5913,7 +5939,7 @@ export type operations = {
    * admin/emoji/add-aliases-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/emoji/add-aliases-bulk': {
     requestBody: {
@@ -5965,7 +5991,7 @@ export type operations = {
    * admin/emoji/add
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/emoji/add': {
     requestBody: {
@@ -6025,7 +6051,7 @@ export type operations = {
    * admin/emoji/copy
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/emoji/copy': {
     requestBody: {
@@ -6082,7 +6108,7 @@ export type operations = {
    * admin/emoji/delete-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/emoji/delete-bulk': {
     requestBody: {
@@ -6133,7 +6159,7 @@ export type operations = {
    * admin/emoji/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/emoji/delete': {
     requestBody: {
@@ -6185,8 +6211,7 @@ export type operations = {
    * admin/emoji/import-zip
    * @description No description provided.
    *
-   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/emoji/import-zip': {
     requestBody: {
@@ -6238,7 +6263,7 @@ export type operations = {
    * admin/emoji/list-remote
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/emoji/list-remote': {
     requestBody: {
@@ -6312,7 +6337,7 @@ export type operations = {
    * admin/emoji/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/emoji/list': {
     requestBody: {
@@ -6381,7 +6406,7 @@ export type operations = {
    * admin/emoji/remove-aliases-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/emoji/remove-aliases-bulk': {
     requestBody: {
@@ -6433,7 +6458,7 @@ export type operations = {
    * admin/emoji/set-aliases-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/emoji/set-aliases-bulk': {
     requestBody: {
@@ -6485,7 +6510,7 @@ export type operations = {
    * admin/emoji/set-category-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/emoji/set-category-bulk': {
     requestBody: {
@@ -6538,7 +6563,7 @@ export type operations = {
    * admin/emoji/set-license-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/emoji/set-license-bulk': {
     requestBody: {
@@ -6591,7 +6616,7 @@ export type operations = {
    * admin/emoji/update
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/emoji/update': {
     requestBody: {
@@ -6653,7 +6678,7 @@ export type operations = {
    * admin/federation/delete-all-files
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/federation/delete-all-files': {
     requestBody: {
@@ -6704,7 +6729,7 @@ export type operations = {
    * admin/federation/refresh-remote-instance-metadata
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/federation/refresh-remote-instance-metadata': {
     requestBody: {
@@ -6755,7 +6780,7 @@ export type operations = {
    * admin/federation/remove-all-following
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/federation/remove-all-following': {
     requestBody: {
@@ -6806,7 +6831,7 @@ export type operations = {
    * admin/federation/update-instance
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/federation/update-instance': {
     requestBody: {
@@ -6858,13 +6883,18 @@ export type operations = {
    * admin/get-index-stats
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/get-index-stats': {
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              tablename: string;
+              indexname: string;
+            }[];
+        };
       };
       /** @description Client error */
       400: {
@@ -6902,7 +6932,7 @@ export type operations = {
    * admin/get-table-stats
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/get-table-stats': {
     responses: {
@@ -6948,7 +6978,7 @@ export type operations = {
    * admin/get-user-ips
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/get-user-ips': {
     requestBody: {
@@ -6960,9 +6990,15 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              ip: string;
+              /** Format: date-time */
+              createdAt: string;
+            }[];
+        };
       };
       /** @description Client error */
       400: {
@@ -7000,7 +7036,7 @@ export type operations = {
    * admin/invite/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/invite/create': {
     requestBody: {
@@ -7055,7 +7091,7 @@ export type operations = {
    * admin/invite/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/invite/list': {
     requestBody: {
@@ -7118,7 +7154,7 @@ export type operations = {
    * admin/promo/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/promo/create': {
     requestBody: {
@@ -7171,7 +7207,7 @@ export type operations = {
    * admin/queue/clear
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/queue/clear': {
     responses: {
@@ -7215,7 +7251,7 @@ export type operations = {
    * admin/queue/deliver-delayed
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/queue/deliver-delayed': {
     responses: {
@@ -7261,7 +7297,7 @@ export type operations = {
    * admin/queue/inbox-delayed
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/queue/inbox-delayed': {
     responses: {
@@ -7307,7 +7343,7 @@ export type operations = {
    * admin/queue/promote
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/queue/promote': {
     requestBody: {
@@ -7359,7 +7395,7 @@ export type operations = {
    * admin/queue/stats
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/queue/stats': {
     responses: {
@@ -7410,7 +7446,7 @@ export type operations = {
    * admin/relays/add
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/relays/add': {
     requestBody: {
@@ -7473,7 +7509,7 @@ export type operations = {
    * admin/relays/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/relays/list': {
     responses: {
@@ -7529,7 +7565,7 @@ export type operations = {
    * admin/relays/remove
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/relays/remove': {
     requestBody: {
@@ -7580,7 +7616,7 @@ export type operations = {
    * admin/reset-password
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/reset-password': {
     requestBody: {
@@ -7636,7 +7672,7 @@ export type operations = {
    * admin/resolve-abuse-user-report
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/resolve-abuse-user-report': {
     requestBody: {
@@ -7690,7 +7726,7 @@ export type operations = {
    * admin/send-email
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/send-email': {
     requestBody: {
@@ -7743,7 +7779,7 @@ export type operations = {
    * admin/server-info
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/server-info': {
     responses: {
@@ -7813,7 +7849,7 @@ export type operations = {
    * admin/show-moderation-logs
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/show-moderation-logs': {
     requestBody: {
@@ -7884,7 +7920,7 @@ export type operations = {
    * admin/show-user
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/show-user': {
     requestBody: {
@@ -7938,7 +7974,7 @@ export type operations = {
    * admin/show-users
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/show-users': {
     requestBody: {
@@ -8013,7 +8049,7 @@ export type operations = {
    * admin/suspend-user
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/suspend-user': {
     requestBody: {
@@ -8065,7 +8101,7 @@ export type operations = {
    * admin/unsuspend-user
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/unsuspend-user': {
     requestBody: {
@@ -8117,7 +8153,7 @@ export type operations = {
    * admin/update-meta
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/update-meta': {
     requestBody: {
@@ -8207,6 +8243,7 @@ export type operations = {
           enableServerMachineStats?: boolean;
           enableIdenticonGeneration?: boolean;
           serverRules?: string[];
+          bannedEmailDomains?: string[];
           preservedUsernames?: string[];
           manifestJsonOverride?: string;
           enableFanoutTimeline?: boolean;
@@ -8261,7 +8298,7 @@ export type operations = {
    * admin/delete-account
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/delete-account': {
     requestBody: {
@@ -8315,7 +8352,7 @@ export type operations = {
    * admin/update-user-note
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/update-user-note': {
     requestBody: {
@@ -8368,7 +8405,7 @@ export type operations = {
    * admin/roles/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/roles/create': {
     requestBody: {
@@ -8436,7 +8473,7 @@ export type operations = {
    * admin/roles/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/roles/delete': {
     requestBody: {
@@ -8488,7 +8525,7 @@ export type operations = {
    * admin/roles/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/roles/list': {
     responses: {
@@ -8534,7 +8571,7 @@ export type operations = {
    * admin/roles/show
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:admin*
    */
   'admin/roles/show': {
     requestBody: {
@@ -8588,7 +8625,7 @@ export type operations = {
    * admin/roles/update
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/roles/update': {
     requestBody: {
@@ -8655,7 +8692,7 @@ export type operations = {
    * admin/roles/assign
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/roles/assign': {
     requestBody: {
@@ -8710,7 +8747,7 @@ export type operations = {
    * admin/roles/unassign
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/roles/unassign': {
     requestBody: {
@@ -8764,7 +8801,7 @@ export type operations = {
    * admin/roles/update-default-policies
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:admin*
    */
   'admin/roles/update-default-policies': {
     requestBody: {
@@ -8815,7 +8852,7 @@ export type operations = {
    * admin/roles/users
    * @description No description provided.
    *
-   * **Credential required**: *No*
+   * **Credential required**: *No* / **Permission**: *read:admin*
    */
   'admin/roles/users': {
     requestBody: {
@@ -8833,9 +8870,19 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ({
+              /** Format: misskey:id */
+              id: string;
+              /** Format: date-time */
+              createdAt: string;
+              user: components['schemas']['UserDetailed'];
+              /** Format: date-time */
+              expiresAt: string | null;
+            })[];
+        };
       };
       /** @description Client error */
       400: {
@@ -13177,6 +13224,17 @@ export type operations = {
       };
     };
     responses: {
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            params: {
+                name: string;
+                type: string;
+              }[];
+          } | null;
+        };
+      };
       /** @description OK (without any results) */
       204: {
         content: never;
@@ -13679,9 +13737,64 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            topSubInstances: ({
+                id?: string;
+                firstRetrievedAt?: string;
+                host?: string;
+                usersCount?: number;
+                notesCount?: number;
+                followingCount?: number;
+                followersCount?: number;
+                isNotResponding?: boolean;
+                isSuspended?: boolean;
+                isBlocked?: boolean;
+                softwareName?: string;
+                softwareVersion?: string;
+                openRegistrations?: boolean;
+                name?: string;
+                description?: string;
+                maintainerName?: string;
+                maintainerEmail?: string;
+                isSilenced?: boolean;
+                iconUrl?: string;
+                faviconUrl?: string;
+                themeColor?: string;
+                infoUpdatedAt?: string | null;
+                latestRequestReceivedAt?: string | null;
+              })[];
+            otherFollowersCount: number;
+            topPubInstances: ({
+                id?: string;
+                firstRetrievedAt?: string;
+                host?: string;
+                usersCount?: number;
+                notesCount?: number;
+                followingCount?: number;
+                followersCount?: number;
+                isNotResponding?: boolean;
+                isSuspended?: boolean;
+                isBlocked?: boolean;
+                softwareName?: string;
+                softwareVersion?: string;
+                openRegistrations?: boolean;
+                name?: string;
+                description?: string;
+                maintainerName?: string;
+                maintainerEmail?: string;
+                isSilenced?: boolean;
+                iconUrl?: string;
+                faviconUrl?: string;
+                themeColor?: string;
+                infoUpdatedAt?: string | null;
+                latestRequestReceivedAt?: string | null;
+              })[];
+            otherFollowingCount: number;
+          };
+        };
       };
       /** @description Client error */
       400: {
@@ -14745,9 +14858,13 @@ export type operations = {
    */
   'get-online-users-count': {
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            count: number;
+          };
+        };
       };
       /** @description Client error */
       400: {
@@ -15243,9 +15360,14 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            id: string;
+            name: string;
+          };
+        };
       };
       /** @description Client error */
       400: {
@@ -15348,9 +15470,45 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            rp: {
+              id: string | null;
+            };
+            user: {
+              id: string;
+              name: string;
+              displayName: string;
+            };
+            challenge: string;
+            pubKeyCredParams: {
+                type: string;
+                alg: number;
+              }[];
+            timeout: number | null;
+            excludeCredentials: (({
+                id: string;
+                type: string;
+                transports: ('ble' | 'cable' | 'hybrid' | 'internal' | 'nfc' | 'smart-card' | 'usb')[];
+              })[]) | null;
+            authenticatorSelection: ({
+              /** @enum {string} */
+              authenticatorAttachment: 'cross-platform' | 'platform';
+              requireResidentKey: boolean;
+              /** @enum {string} */
+              userVerification: 'discouraged' | 'preferred' | 'required';
+            }) | null;
+            /** @enum {string|null} */
+            attestation: 'direct' | 'enterprise' | 'indirect' | 'none' | null;
+            extensions: ({
+              appid: string | null;
+              credProps: boolean | null;
+              hmacCreateSecret: boolean | null;
+            }) | null;
+          };
+        };
       };
       /** @description Client error */
       400: {
@@ -15401,9 +15559,17 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            qr: string;
+            url: string;
+            secret: string;
+            label: string;
+            issuer: string;
+          };
+        };
       };
       /** @description Client error */
       400: {
@@ -15614,9 +15780,20 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              /** Format: misskey:id */
+              id: string;
+              name: string;
+              /** Format: date-time */
+              createdAt: string;
+              /** Format: date-time */
+              lastUsedAt: string;
+              permission: string[];
+            }[];
+        };
       };
       /** @description Client error */
       400: {
@@ -15674,9 +15851,18 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ({
+              /** Format: misskey:id */
+              id: string;
+              name: string;
+              callbackUrl: string | null;
+              permission: string[];
+              isAuthorized: boolean;
+            })[];
+        };
       };
       /** @description Client error */
       400: {
@@ -16728,8 +16914,8 @@ export type operations = {
           untilId?: string;
           /** @default true */
           markAsRead?: boolean;
-          includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
-          excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
+          includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
+          excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
         };
       };
     };
@@ -16796,8 +16982,8 @@ export type operations = {
           untilId?: string;
           /** @default true */
           markAsRead?: boolean;
-          includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
-          excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
+          includeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
+          excludeTypes?: ('note' | 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollEnded' | 'receiveFollowRequest' | 'followRequestAccepted' | 'roleAssigned' | 'achievementEarned' | 'app' | 'test' | 'pollVote' | 'groupInvited')[];
         };
       };
     };
@@ -17185,9 +17371,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': Record<string, never>;
+        };
       };
       /** @description Client error */
       400: {
@@ -17239,9 +17427,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': Record<string, never>;
+        };
       };
       /** @description Client error */
       400: {
@@ -17293,9 +17483,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': Record<string, never>;
+        };
       };
       /** @description Client error */
       400: {
@@ -17346,9 +17538,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': Record<string, never>;
+        };
       };
       /** @description Client error */
       400: {
@@ -17498,9 +17692,14 @@ export type operations = {
    */
   'i/registry/scopes-with-domain': {
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ({
+              scopes: string[][];
+              domain: string | null;
+            })[];
+        };
       };
       /** @description Client error */
       400: {
@@ -17774,9 +17973,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['UserDetailed'];
+        };
       };
       /** @description Client error */
       400: {
@@ -17839,6 +18040,8 @@ export type operations = {
               id: string;
               angle?: number | null;
               flipH?: boolean | null;
+              offsetX?: number | null;
+              offsetY?: number | null;
             })[];
           /** Format: misskey:id */
           bannerId?: string | null;
@@ -17861,7 +18064,9 @@ export type operations = {
           alwaysMarkNsfw?: boolean;
           autoSensitive?: boolean;
           /** @enum {string} */
-          ffVisibility?: 'public' | 'followers' | 'private';
+          followingVisibility?: 'public' | 'followers' | 'private';
+          /** @enum {string} */
+          followersVisibility?: 'public' | 'followers' | 'private';
           /** Format: misskey:id */
           pinnedPageId?: string | null;
           mutedWords?: (string[] | string)[];
@@ -17934,9 +18139,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': Record<string, never>;
+        };
       };
       /** @description Client error */
       400: {
@@ -17995,9 +18202,24 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            /** Format: misskey:id */
+            id: string;
+            /** Format: misskey:id */
+            userId: string;
+            name: string;
+            on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
+            url: string;
+            secret: string;
+            active: boolean;
+            /** Format: date-time */
+            latestSentAt: string | null;
+            latestStatus: number | null;
+          };
+        };
       };
       /** @description Client error */
       400: {
@@ -18039,9 +18261,24 @@ export type operations = {
    */
   'i/webhooks/list': {
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': ({
+              /** Format: misskey:id */
+              id: string;
+              /** Format: misskey:id */
+              userId: string;
+              name: string;
+              on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
+              url: string;
+              secret: string;
+              active: boolean;
+              /** Format: date-time */
+              latestSentAt: string | null;
+              latestStatus: number | null;
+            })[];
+        };
       };
       /** @description Client error */
       400: {
@@ -18091,9 +18328,24 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            /** Format: misskey:id */
+            id: string;
+            /** Format: misskey:id */
+            userId: string;
+            name: string;
+            on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
+            url: string;
+            secret: string;
+            active: boolean;
+            /** Format: date-time */
+            latestSentAt: string | null;
+            latestStatus: number | null;
+          };
+        };
       };
       /** @description Client error */
       400: {
@@ -21612,9 +21864,11 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': components['schemas']['Flash'];
+        };
       };
       /** @description Client error */
       400: {
@@ -22362,9 +22616,15 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              /** Format: misskey:id */
+              id: string;
+              user: components['schemas']['User'];
+            }[];
+        };
       };
       /** @description Client error */
       400: {
@@ -22622,9 +22882,24 @@ export type operations = {
    */
   'server-info': {
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            machine: string;
+            cpu: {
+              model: string;
+              cores: number;
+            };
+            mem: {
+              total: number;
+            };
+            fs: {
+              total: number;
+              used: number;
+            };
+          };
+        };
       };
       /** @description Client error */
       400: {
@@ -22968,9 +23243,19 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            /** Format: misskey:id */
+            id: string;
+            required: boolean;
+            string: string;
+            default: string;
+            /** @default hello */
+            nullableDefault: string | null;
+          };
+        };
       };
       /** @description Client error */
       400: {
@@ -24119,9 +24404,20 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              /** Format: misskey:id */
+              id: string;
+              /** Format: date-time */
+              createdAt: string;
+              /** Format: misskey:id */
+              userId: string;
+              user: components['schemas']['User'];
+              withReplies: boolean;
+            }[];
+        };
       };
       /** @description Client error */
       400: {
@@ -24789,9 +25085,14 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+              name: string;
+              unlockedAt: number;
+            }[];
+        };
       };
       /** @description Client error */
       400: {
@@ -24894,9 +25195,13 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            items: Record<string, never>[];
+          };
+        };
       };
       /** @description Client error */
       400: {
@@ -24946,9 +25251,14 @@ export type operations = {
       };
     };
     responses: {
-      /** @description OK (without any results) */
-      204: {
-        content: never;
+      /** @description OK (with results) */
+      200: {
+        content: {
+          'application/json': {
+            type: string;
+            data: string;
+          };
+        };
       };
       /** @description Client error */
       400: {

From 34cdba6c111bdf9a4ea508fe5fc1dda43d04d6e3 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Mon, 25 Dec 2023 11:56:00 +0900
Subject: [PATCH 368/435] =?UTF-8?q?fix:=20=E8=87=AA=E5=88=86=E3=81=AEdirec?=
 =?UTF-8?q?t=20note=E3=81=8Cuser=20list=20timeline=E3=81=AB=E8=BF=BD?=
 =?UTF-8?q?=E5=8A=A0=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=20(#12782)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: 自分のdirect noteがuser list timelineに追加されない

* docs(changelog): Fix: 自分のdirect noteがuser list timelineに追加されない
---
 CHANGELOG.md                                   | 1 +
 packages/backend/src/core/NoteCreateService.ts | 1 +
 2 files changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 432b39afb6..baff811e87 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@
 
 ### General
 - Enhance: ローカリゼーションの更新
+- Fix: 自分のdirect noteがuser list timelineに追加されない
 
 ### Client
 - 
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 583fa97aff..6309313f11 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -1172,6 +1172,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 				// ダイレクトのとき、そのリストが対象外のユーザーの場合
 				if (
 					note.visibility === 'specified' &&
+					note.userId !== userListMembership.userListUserId &&
 					!note.visibleUserIds.some(v => v === userListMembership.userListUserId)
 				) continue;
 

From adf13f79bdadbf7c5bc68cb49bb54f31a9a99952 Mon Sep 17 00:00:00 2001
From: Sayamame-beans <61457993+Sayamame-beans@users.noreply.github.com>
Date: Mon, 25 Dec 2023 14:49:06 +0900
Subject: [PATCH 369/435] =?UTF-8?q?fix(frontend):=20=E3=83=A2=E3=83=87?=
 =?UTF-8?q?=E3=83=AD=E3=82=B0=E8=A1=A8=E7=A4=BA=E3=81=AE"logYellow"?=
 =?UTF-8?q?=E3=81=8C=E6=A9=9F=E8=83=BD=E3=81=97=E3=81=A6=E3=81=84=E3=81=AA?=
 =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1279?=
 =?UTF-8?q?4)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: logYellow of moderation log was not working

* docs(changelog): Fix: 一部のモデログ(logYellowでの表示対象)について、表示の色が変わらない問題を修正
---
 CHANGELOG.md                                        | 2 +-
 packages/frontend/src/pages/admin/modlog.ModLog.vue | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index baff811e87..5db3783e10 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,7 +19,7 @@
 - Fix: 自分のdirect noteがuser list timelineに追加されない
 
 ### Client
-- 
+- Fix: 一部のモデログ(logYellowでの表示対象)について、表示の色が変わらない問題を修正
 
 ### Server
 - Enhance: センシティブワードの設定がハッシュタグトレンドにも適用されるようになりました
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index 4436fbac89..322d2d531c 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -149,7 +149,7 @@ const props = defineProps<{
 }
 
 .logYellow {
-	color: var(--warning);
+	color: var(--warn);
 }
 
 .logRed {

From 9c17e0f97683b92e68cb100c64e7ab0eb31a68b3 Mon Sep 17 00:00:00 2001
From: FineArchs <133759614+FineArchs@users.noreply.github.com>
Date: Mon, 25 Dec 2023 18:03:06 +0900
Subject: [PATCH 370/435] =?UTF-8?q?Feat:=20=E3=82=AF=E3=83=AA=E3=83=83?=
 =?UTF-8?q?=E3=82=AF=E3=82=A4=E3=83=99=E3=83=B3=E3=83=88=E3=82=92=E7=99=BA?=
 =?UTF-8?q?=E7=94=9F=E3=81=95=E3=81=9B=E3=82=8BMFM=E6=A7=8B=E6=96=87?=
 =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=20(#12798)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Update MkMisskeyFlavoredMarkdown.ts

* fix MkMisskeyFlavoredMarkdown.ts

* Update MkAsUi.vue

* Update ui.ts

* Fix MkMisskeyFlavoredMarkdown.ts

* Update CHANGELOG.md

* fix ui.ts

* revert CHANGELOG.md

* Update CHANGELOG.md
---
 CHANGELOG.md                                      |  1 +
 packages/frontend/src/components/MkAsUi.vue       |  2 +-
 .../global/MkMisskeyFlavoredMarkdown.ts           | 15 +++++++++++++--
 packages/frontend/src/scripts/aiscript/ui.ts      |  6 ++++++
 4 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5db3783e10..e452eb3845 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@
 
 ### Client
 - Fix: 一部のモデログ(logYellowでの表示対象)について、表示の色が変わらない問題を修正
+- Feat: AiScript専用のMFM構文`$[clickable.ev=EVENTNAME ...]`を追加。`Mk:C:mfm`のオプション`onClickEv`に関数を渡すと、クリック時に`EVENTNAME`を引数にして呼び出す
 
 ### Server
 - Enhance: センシティブワードの設定がハッシュタグトレンドにも適用されるようになりました
diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue
index 60978eb0bd..8475233dfa 100644
--- a/packages/frontend/src/components/MkAsUi.vue
+++ b/packages/frontend/src/components/MkAsUi.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</template>
 	</div>
 	<span v-else-if="c.type === 'text'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }">{{ c.text }}</span>
-	<Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }" :text="c.text"/>
+	<Mfm v-else-if="c.type === 'mfm'" :class="{ [$style.fontSerif]: c.font === 'serif', [$style.fontMonospace]: c.font === 'monospace' }" :style="{ fontSize: c.size ? `${c.size * 100}%` : null, fontWeight: c.bold ? 'bold' : null, color: c.color ?? null }" :text="c.text" @clickEv="c.onClickEv"/>
 	<MkButton v-else-if="c.type === 'button'" :primary="c.primary" :rounded="c.rounded" :disabled="c.disabled" :small="size === 'small'" inline @click="c.onClick">{{ c.text }}</MkButton>
 	<div v-else-if="c.type === 'buttons'" class="_buttons" :style="{ justifyContent: align }">
 		<MkButton v-for="button in c.buttons" :primary="button.primary" :rounded="button.rounded" :disabled="button.disabled" inline :small="size === 'small'" @click="button.onClick">{{ button.text }}</MkButton>
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index 4c6034c901..33d5786fb1 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { VNode, h, defineAsyncComponent } from 'vue';
+import { VNode, h, defineAsyncComponent, SetupContext } from 'vue';
 import * as mfm from '@sharkey/sfm-js';
 import * as Misskey from 'misskey-js';
 import MkUrl from '@/components/global/MkUrl.vue';
@@ -44,8 +44,12 @@ type MfmProps = {
 	isAnim?: boolean;
 };
 
+type MfmEvents = {
+	clickEv(id: string): void;
+};
+
 // eslint-disable-next-line import/no-default-export
-export default function(props: MfmProps) {
+export default function(props: MfmProps, context: SetupContext<MfmEvents>) {
 	const isNote = props.isNote ?? true;
 	const shouldNyaize = props.nyaize ? props.nyaize === 'respect' ? props.author?.isCat ? props.author?.speakAsCat : false : false : false;
 
@@ -290,6 +294,13 @@ export default function(props: MfmProps) {
 							}),
 						]);
 					}
+					case 'clickable': {
+						return h('span', { onClick(ev: MouseEvent): void {
+							ev.stopPropagation();
+							ev.preventDefault();
+							context.emit('clickEv', token.props.args.ev ?? '');
+						} }, genEl(token.children, scale));
+					}
 				}
 				if (style === undefined) {
 					return h('span', {}, ['$[', token.props.name, ' ', ...genEl(token.children, scale), ']']);
diff --git a/packages/frontend/src/scripts/aiscript/ui.ts b/packages/frontend/src/scripts/aiscript/ui.ts
index 75b9248432..08ba1e6d9b 100644
--- a/packages/frontend/src/scripts/aiscript/ui.ts
+++ b/packages/frontend/src/scripts/aiscript/ui.ts
@@ -47,6 +47,7 @@ export type AsUiMfm = AsUiComponentBase & {
 	bold?: boolean;
 	color?: string;
 	font?: 'serif' | 'sans-serif' | 'monospace';
+	onClickEv?: (evId: string) => void
 };
 
 export type AsUiButton = AsUiComponentBase & {
@@ -230,6 +231,8 @@ function getMfmOptions(def: values.Value | undefined): Omit<AsUiMfm, 'id' | 'typ
 	if (color) utils.assertString(color);
 	const font = def.value.get('font');
 	if (font) utils.assertString(font);
+	const onClickEv = def.value.get('onClickEv');
+	if (onClickEv) utils.assertFunction(onClickEv);
 
 	return {
 		text: text?.value,
@@ -237,6 +240,9 @@ function getMfmOptions(def: values.Value | undefined): Omit<AsUiMfm, 'id' | 'typ
 		bold: bold?.value,
 		color: color?.value,
 		font: font?.value,
+		onClickEv: (evId: string) => {
+			if (onClickEv) call(onClickEv, values.STR(evId));
+		},
 	};
 }
 

From 6876eca6b532081a691683870ad96fa08bcf7ab9 Mon Sep 17 00:00:00 2001
From: Soli <personal@str08.net>
Date: Tue, 26 Dec 2023 11:40:31 +0900
Subject: [PATCH 371/435] =?UTF-8?q?fix(frontend):=20=E3=83=AD=E3=83=BC?=
 =?UTF-8?q?=E3=83=AB=E3=82=A2=E3=82=B5=E3=82=A4=E3=83=B3=E6=99=82=E3=81=AE?=
 =?UTF-8?q?=E9=80=9A=E7=9F=A5=E3=81=A7=EF=BC=8C=E3=83=AD=E3=83=BC=E3=83=AB?=
 =?UTF-8?q?=E3=82=A2=E3=82=A4=E3=82=B3=E3=83=B3=E3=81=8C=E7=B8=AE=E5=B0=8F?=
 =?UTF-8?q?=E3=81=95=E3=82=8C=E3=81=9A=E3=81=AB=E8=A1=A8=E7=A4=BA=E3=81=95?=
 =?UTF-8?q?=E3=82=8C=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?=
 =?UTF-8?q?=20(misskey-dev#12805)=20(#12806)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                        | 1 +
 packages/frontend/src/components/MkNotification.vue | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e452eb3845..709f97b7bf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
 ### Server
 - Enhance: センシティブワードの設定がハッシュタグトレンドにも適用されるようになりました
 - Fix: 1702718871541-ffVisibility.jsのdownが壊れている
+- Fix: ロールアサイン時の通知で,ロールアイコンが縮小されずに表示される問題を修正
 
 ## 2023.12.0
 
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 2901139220..ed79ca0d86 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<i v-else-if="notification.type === 'quote'" class="ph-quotes ph-bold ph-lg"></i>
 			<i v-else-if="notification.type === 'pollEnded'" class="ph-chart-bar-horizontal ph-bold ph-lg"></i>
 			<i v-else-if="notification.type === 'achievementEarned'" class="ph-trophy ph-bold ph-lg"></i>
-			<img v-else-if="notification.type === 'roleAssigned'" :src="notification.role.iconUrl" alt=""/>
+			<img v-else-if="notification.type === 'roleAssigned'" style="height: 1.3em; vertical-align: -22%;" :src="notification.role.iconUrl" alt=""/>
 			<!-- notification.reaction が null になることはまずないが、ここでoptional chaining使うと一部ブラウザで刺さるので念の為 -->
 			<MkReactionIcon
 				v-else-if="notification.type === 'reaction'"

From f9c7b44ef8606954645104ac7bf5f24559a7ce10 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: Tue, 26 Dec 2023 11:40:55 +0900
Subject: [PATCH 372/435] =?UTF-8?q?(dev)=20Issue=20Template=E3=81=AB?=
 =?UTF-8?q?=E3=80=81=E8=87=AA=E5=88=86=E3=81=A7=E5=AE=9F=E8=A3=85=E3=81=97?=
 =?UTF-8?q?=E3=81=A6PR=E3=82=92=E5=87=BA=E3=81=97=E3=81=9F=E3=81=84?=
 =?UTF-8?q?=E3=81=8B=E3=81=AE=E6=84=8F=E6=80=9D=E8=A1=A8=E6=98=8E=E3=82=92?=
 =?UTF-8?q?=E8=BF=BD=E5=8A=A0=20(#12799)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Update 01_bug-report.yml

* Update 02_feature-request.yml
---
 .gitea/ISSUE_TEMPLATE/01_bug-report.yml | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/.gitea/ISSUE_TEMPLATE/01_bug-report.yml b/.gitea/ISSUE_TEMPLATE/01_bug-report.yml
index be6d9f401b..a2cdebe549 100644
--- a/.gitea/ISSUE_TEMPLATE/01_bug-report.yml
+++ b/.gitea/ISSUE_TEMPLATE/01_bug-report.yml
@@ -87,4 +87,10 @@ body:
         * OS and Architecture:
       render: markdown
     validations:
-      required: false
\ No newline at end of file
+      required: false
+
+  - type: checkboxes
+    attributes:
+      label: Do you want to address this bug yourself?
+      options:
+        - label: Yes, I will patch the bug myself and send a pull request

From 8daff4a998e9b8371f59de7af8909dd8dfc39a9e Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Tue, 26 Dec 2023 14:19:35 +0900
Subject: [PATCH 373/435] =?UTF-8?q?refactor(frontend):=20Reactivity?=
 =?UTF-8?q?=E3=81=A7=E5=9E=8B=E3=82=92=E6=98=8E=E7=A4=BA=E3=81=99=E3=82=8B?=
 =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=20(#12791)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor(frontend): Reactivityで型を明示するように

* fix: プロパティの参照が誤っているのを修正

* fix: 初期化の値を空配列に書き換えていた部分をnullに置き換え
---
 .../src/server/api/endpoints/admin/meta.ts    |  4 ++
 .../server/api/endpoints/federation/stats.ts  | 72 +++----------------
 .../backend/src/server/api/endpoints/meta.ts  | 16 ++++-
 .../server/api/endpoints/notes/translate.ts   |  4 ++
 .../src/components/MkAchievements.vue         |  2 +-
 .../frontend/src/components/MkCaptcha.vue     |  2 +-
 packages/frontend/src/components/MkLink.vue   |  2 +-
 .../frontend/src/components/MkMarquee.vue     |  2 +-
 packages/frontend/src/components/MkNote.vue   |  2 +-
 .../src/components/MkNoteDetailed.vue         |  4 +-
 .../src/components/MkPasswordDialog.vue       |  2 +-
 .../frontend/src/components/MkPostForm.vue    |  4 +-
 .../frontend/src/components/MkRolePreview.vue |  3 +-
 packages/frontend/src/components/MkSelect.vue |  8 +--
 packages/frontend/src/components/MkSignin.vue |  8 ---
 .../src/components/MkSignupDialog.form.vue    |  6 +-
 .../frontend/src/components/MkSparkle.vue     |  9 ++-
 .../MkUserAnnouncementEditDialog.vue          |  2 +-
 .../frontend/src/components/MkWidgets.vue     |  2 +-
 .../frontend/src/components/form/suspense.vue |  2 +-
 .../src/components/page/page.note.vue         |  4 +-
 packages/frontend/src/pages/about.vue         |  3 +-
 packages/frontend/src/pages/admin-file.vue    |  5 +-
 packages/frontend/src/pages/admin-user.vue    |  6 +-
 .../frontend/src/pages/admin/_header_.vue     |  2 +-
 packages/frontend/src/pages/admin/ads.vue     |  3 +-
 .../src/pages/admin/bot-protection.vue        |  3 +-
 .../frontend/src/pages/admin/branding.vue     |  6 +-
 .../src/pages/admin/email-settings.vue        |  2 +-
 packages/frontend/src/pages/admin/files.vue   |  2 +-
 packages/frontend/src/pages/admin/index.vue   |  8 +--
 packages/frontend/src/pages/admin/modlog.vue  |  2 +-
 .../src/pages/admin/overview.federation.vue   | 44 +++++++-----
 .../src/pages/admin/overview.instances.vue    |  3 +-
 .../src/pages/admin/overview.moderators.vue   |  3 +-
 .../frontend/src/pages/admin/overview.pie.vue |  9 ++-
 .../src/pages/admin/overview.stats.vue        |  3 +-
 .../src/pages/admin/overview.users.vue        |  3 +-
 .../frontend/src/pages/admin/overview.vue     | 50 +++++++------
 .../src/pages/admin/proxy-account.vue         |  5 +-
 .../frontend/src/pages/admin/queue.chart.vue  | 10 +--
 packages/frontend/src/pages/admin/relays.vue  |  5 +-
 .../frontend/src/pages/admin/roles.edit.vue   |  5 +-
 .../frontend/src/pages/admin/settings.vue     |  4 +-
 .../frontend/src/pages/antenna-timeline.vue   |  3 +-
 packages/frontend/src/pages/api-console.vue   |  2 +-
 .../frontend/src/pages/avatar-decorations.vue |  3 +-
 .../frontend/src/pages/channel-editor.vue     |  9 +--
 .../src/pages/custom-emojis-manager.vue       |  6 +-
 packages/frontend/src/pages/drive.vue         |  3 +-
 .../frontend/src/pages/emoji-edit-dialog.vue  |  4 +-
 packages/frontend/src/pages/explore.roles.vue |  3 +-
 packages/frontend/src/pages/explore.users.vue |  5 +-
 .../frontend/src/pages/flash/flash-edit.vue   |  5 +-
 packages/frontend/src/pages/flash/flash.vue   |  5 +-
 packages/frontend/src/pages/gallery/edit.vue  | 11 +--
 packages/frontend/src/pages/gallery/post.vue  |  5 +-
 packages/frontend/src/pages/list.vue          |  5 +-
 .../frontend/src/pages/my-antennas/edit.vue   |  3 +-
 .../frontend/src/pages/my-antennas/editor.vue | 11 ++-
 .../frontend/src/pages/my-clips/index.vue     |  3 +-
 packages/frontend/src/pages/note.vue          |  2 +-
 .../page-editor/els/page-editor.el.image.vue  |  3 +-
 .../page-editor/els/page-editor.el.note.vue   |  3 +-
 .../pages/page-editor/page-editor.blocks.vue  |  5 +-
 .../src/pages/page-editor/page-editor.vue     | 15 ++--
 packages/frontend/src/pages/page.vue          |  5 +-
 packages/frontend/src/pages/registry.keys.vue |  2 +-
 .../frontend/src/pages/registry.value.vue     |  4 +-
 packages/frontend/src/pages/registry.vue      |  3 +-
 packages/frontend/src/pages/role.vue          |  5 +-
 packages/frontend/src/pages/search.note.vue   |  2 +-
 .../frontend/src/pages/settings/accounts.vue  |  2 +-
 packages/frontend/src/pages/settings/apps.vue |  2 +-
 .../frontend/src/pages/settings/drive.vue     |  7 +-
 .../frontend/src/pages/settings/index.vue     |  6 +-
 .../pages/settings/statusbar.statusbar.vue    |  3 +-
 .../frontend/src/pages/settings/statusbar.vue |  3 +-
 .../src/pages/settings/theme.install.vue      |  2 +-
 .../src/pages/settings/theme.manage.vue       |  2 +-
 .../frontend/src/pages/user-list-timeline.vue |  3 +-
 .../frontend/src/pages/user/followers.vue     |  2 +-
 .../frontend/src/pages/user/following.vue     |  2 +-
 packages/frontend/src/pages/user/index.vue    |  2 +-
 packages/frontend/src/pages/welcome.vue       |  3 +-
 .../frontend/src/scripts/get-note-menu.ts     |  2 +-
 .../frontend/src/scripts/use-chart-tooltip.ts |  8 ++-
 packages/frontend/src/ui/classic.vue          |  6 +-
 packages/frontend/src/ui/visitor.vue          |  3 +-
 .../src/widgets/WidgetActivity.calendar.vue   |  7 +-
 .../src/widgets/WidgetActivity.chart.vue      | 15 ++--
 .../frontend/src/widgets/WidgetActivity.vue   | 11 ++-
 .../frontend/src/widgets/WidgetFederation.vue |  5 +-
 .../src/widgets/WidgetInstanceCloud.vue       |  3 +-
 .../frontend/src/widgets/WidgetPhotos.vue     |  3 +-
 .../frontend/src/widgets/WidgetSlideshow.vue  |  3 +-
 .../frontend/src/widgets/WidgetTrends.vue     |  3 +-
 .../frontend/src/widgets/WidgetUserList.vue   |  5 +-
 .../src/widgets/server-metric/cpu-mem.vue     | 11 +--
 .../src/widgets/server-metric/cpu.vue         |  3 +-
 .../src/widgets/server-metric/disk.vue        |  3 +-
 .../src/widgets/server-metric/index.vue       |  3 +-
 .../src/widgets/server-metric/mem.vue         |  3 +-
 .../src/widgets/server-metric/net.vue         | 11 +--
 .../misskey-js/src/autogen/apiClientJSDoc.ts  |  2 +-
 packages/misskey-js/src/autogen/endpoint.ts   |  2 +-
 packages/misskey-js/src/autogen/entities.ts   |  2 +-
 packages/misskey-js/src/autogen/models.ts     |  2 +-
 packages/misskey-js/src/autogen/types.ts      | 69 +++++-------------
 109 files changed, 363 insertions(+), 342 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 80f0dc5747..218ab42ffd 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -400,6 +400,10 @@ export const meta = {
 				type: 'string',
 				optional: false, nullable: true,
 			},
+			shortName: {
+				type: 'string',
+				optional: false, nullable: true,
+			},
 			objectStorageS3ForcePathStyle: {
 				type: 'boolean',
 				optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/federation/stats.ts b/packages/backend/src/server/api/endpoints/federation/stats.ts
index 6548142d41..262aa68776 100644
--- a/packages/backend/src/server/api/endpoints/federation/stats.ts
+++ b/packages/backend/src/server/api/endpoints/federation/stats.ts
@@ -29,37 +29,10 @@ export const meta = {
 				optional: false,
 				nullable: false,
 				items: {
-					properties: {
-						id: { type: 'string' },
-						firstRetrievedAt: { type: 'string' },
-						host: { type: 'string' },
-						usersCount: { type: 'number' },
-						notesCount: { type: 'number' },
-						followingCount: { type: 'number' },
-						followersCount: { type: 'number' },
-						isNotResponding: { type: 'boolean' },
-						isSuspended: { type: 'boolean' },
-						isBlocked: { type: 'boolean' },
-						softwareName: { type: 'string' },
-						softwareVersion: { type: 'string' },
-						openRegistrations: { type: 'boolean' },
-						name: { type: 'string' },
-						description: { type: 'string' },
-						maintainerName: { type: 'string' },
-						maintainerEmail: { type: 'string' },
-						isSilenced: { type: 'boolean' },
-						iconUrl: { type: 'string' },
-						faviconUrl: { type: 'string' },
-						themeColor: { type: 'string' },
-						infoUpdatedAt: {
-							type: 'string',
-							nullable: true,
-						},
-						latestRequestReceivedAt: {
-							type: 'string',
-							nullable: true,
-						},
-					}
+					type: 'object',
+					optional: false,
+					nullable: false,
+					ref: 'FederationInstance',
 				},
 			},
 			otherFollowersCount: { type: 'number' },
@@ -68,42 +41,15 @@ export const meta = {
 				optional: false,
 				nullable: false,
 				items: {
-					properties: {
-						id: { type: 'string' },
-						firstRetrievedAt: { type: 'string' },
-						host: { type: 'string' },
-						usersCount: { type: 'number' },
-						notesCount: { type: 'number' },
-						followingCount: { type: 'number' },
-						followersCount: { type: 'number' },
-						isNotResponding: { type: 'boolean' },
-						isSuspended: { type: 'boolean' },
-						isBlocked: { type: 'boolean' },
-						softwareName: { type: 'string' },
-						softwareVersion: { type: 'string' },
-						openRegistrations: { type: 'boolean' },
-						name: { type: 'string' },
-						description: { type: 'string' },
-						maintainerName: { type: 'string' },
-						maintainerEmail: { type: 'string' },
-						isSilenced: { type: 'boolean' },
-						iconUrl: { type: 'string' },
-						faviconUrl: { type: 'string' },
-						themeColor: { type: 'string' },
-						infoUpdatedAt: {
-							type: 'string',
-							nullable: true,
-						},
-						latestRequestReceivedAt: {
-							type: 'string',
-							nullable: true,
-						},
-					}
+					type: 'object',
+					optional: false,
+					nullable: false,
+					ref: 'FederationInstance',
 				},
 			},
 			otherFollowingCount: { type: 'number' },
 		},
-	}
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index 8048ed3ada..9ba22f89b9 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -172,20 +172,34 @@ export const meta = {
 					type: 'object',
 					optional: false, nullable: false,
 					properties: {
-						place: {
+						id: {
 							type: 'string',
 							optional: false, nullable: false,
+							format: 'id',
+							example: 'xxxxxxxxxx',
 						},
 						url: {
 							type: 'string',
 							optional: false, nullable: false,
 							format: 'url',
 						},
+						place: {
+							type: 'string',
+							optional: false, nullable: false,
+						},
+						ratio: {
+							type: 'number',
+							optional: false, nullable: false,
+						},
 						imageUrl: {
 							type: 'string',
 							optional: false, nullable: false,
 							format: 'url',
 						},
+						dayOfWeek: {
+							type: 'integer',
+							optional: false, nullable: false,
+						},
 					},
 				},
 			},
diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts
index a1561c944c..d46bd69795 100644
--- a/packages/backend/src/server/api/endpoints/notes/translate.ts
+++ b/packages/backend/src/server/api/endpoints/notes/translate.ts
@@ -21,6 +21,10 @@ export const meta = {
 	res: {
 		type: 'object',
 		optional: false, nullable: false,
+		properties: {
+			sourceLang: { type: 'string' },
+			text: { type: 'string' },
+		},
 	},
 
 	errors: {
diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue
index 40f9ad4057..cdd9cb87b1 100644
--- a/packages/frontend/src/components/MkAchievements.vue
+++ b/packages/frontend/src/components/MkAchievements.vue
@@ -67,7 +67,7 @@ const props = withDefaults(defineProps<{
 	withDescription: true,
 });
 
-const achievements = ref();
+const achievements = ref<Misskey.entities.UsersAchievementsResponse | null>(null);
 const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x)));
 
 function fetch() {
diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue
index 14e59acad2..40bca11e64 100644
--- a/packages/frontend/src/components/MkCaptcha.vue
+++ b/packages/frontend/src/components/MkCaptcha.vue
@@ -26,7 +26,7 @@ export type Captcha = {
 	getResponse(id: string): string;
 };
 
-type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile';
+export type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile';
 
 type CaptchaContainer = {
 	readonly [_ in CaptchaProvider]?: Captcha;
diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue
index e16307c762..bda683002d 100644
--- a/packages/frontend/src/components/MkLink.vue
+++ b/packages/frontend/src/components/MkLink.vue
@@ -29,7 +29,7 @@ const self = props.url.startsWith(local);
 const attr = self ? 'to' : 'href';
 const target = self ? null : '_blank';
 
-const el = ref();
+const el = ref<HTMLElement>();
 
 useTooltip(el, (showing) => {
 	os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), {
diff --git a/packages/frontend/src/components/MkMarquee.vue b/packages/frontend/src/components/MkMarquee.vue
index f9d0573227..145b60c8e7 100644
--- a/packages/frontend/src/components/MkMarquee.vue
+++ b/packages/frontend/src/components/MkMarquee.vue
@@ -27,7 +27,7 @@ export default {
 		},
 	},
 	setup(props) {
-		const contentEl = ref();
+		const contentEl = ref<HTMLElement>();
 
 		function calc() {
 			const eachLength = contentEl.value.offsetWidth / props.repeat;
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index cdd2cccb2e..8a3b4cef48 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -289,7 +289,7 @@ const isDeleted = ref(false);
 const renoted = ref(false);
 const muted = ref(checkMute(appearNote.value, $i?.mutedWords));
 const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords));
-const translation = ref<any>(null);
+const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null);
 const translating = ref(false);
 const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance);
 const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i.id));
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index a793a85ff9..e287890e2c 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -309,7 +309,7 @@ const showContent = ref(defaultStore.state.uncollapseCW);
 const isDeleted = ref(false);
 const renoted = ref(false);
 const muted = ref($i ? checkWordMute(appearNote.value, $i, $i.mutedWords) : false);
-const translation = ref(null);
+const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null);
 const translating = ref(false);
 const parsed = appearNote.value.text ? mfm.parse(appearNote.value.text) : null;
 const urls = parsed ? extractUrlFromMfm(parsed).filter(u => u !== renoteUrl && u !== renoteUri) : null;
@@ -353,7 +353,7 @@ provide('react', (reaction: string) => {
 });
 
 const tab = ref('replies');
-const reactionTabType = ref(null);
+const reactionTabType = ref<string | null>(null);
 
 const renotesPagination = computed(() => ({
 	endpoint: 'notes/renotes',
diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue
index 711c54c7f1..85dd402730 100644
--- a/packages/frontend/src/components/MkPasswordDialog.vue
+++ b/packages/frontend/src/components/MkPasswordDialog.vue
@@ -52,7 +52,7 @@ const emit = defineEmits<{
 const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
 const passwordInput = shallowRef<InstanceType<typeof MkInput>>();
 const password = ref('');
-const token = ref(null);
+const token = ref<string | null>(null);
 
 function onClose() {
 	emit('cancelled');
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index b26ce2932a..8838da15a9 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -187,14 +187,14 @@ watch(showPreview, () => defaultStore.set('showPreview', showPreview.value));
 const cw = ref<string | null>(props.initialCw ?? null);
 const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
 const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
-const visibleUsers = ref([]);
+const visibleUsers = ref<Misskey.entities.UserDetailed[]>([]);
 if (props.initialVisibleUsers) {
 	props.initialVisibleUsers.forEach(pushVisibleUser);
 }
 const reactionAcceptance = ref(defaultStore.state.reactionAcceptance);
 const autocomplete = ref(null);
 const draghover = ref(false);
-const quoteId = ref(null);
+const quoteId = ref<string | null>(null);
 const hasNotSpecifiedMentions = ref(false);
 const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]'));
 const imeText = ref('');
diff --git a/packages/frontend/src/components/MkRolePreview.vue b/packages/frontend/src/components/MkRolePreview.vue
index 0e8ce35609..bd1767155b 100644
--- a/packages/frontend/src/components/MkRolePreview.vue
+++ b/packages/frontend/src/components/MkRolePreview.vue
@@ -28,10 +28,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { } from 'vue';
+import * as Misskey from 'misskey-js';
 import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
-	role: any;
+	role: Misskey.entities.Role;
 	forModeration: boolean;
 	detailed: boolean;
 }>(), {
diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue
index d8c8c4998b..665ae2b813 100644
--- a/packages/frontend/src/components/MkSelect.vue
+++ b/packages/frontend/src/components/MkSelect.vue
@@ -65,10 +65,10 @@ const opening = ref(false);
 const changed = ref(false);
 const invalid = ref(false);
 const filled = computed(() => v.value !== '' && v.value != null);
-const inputEl = ref(null);
-const prefixEl = ref(null);
-const suffixEl = ref(null);
-const container = ref(null);
+const inputEl = ref<HTMLObjectElement | null>(null);
+const prefixEl = ref<HTMLElement | null>(null);
+const suffixEl = ref<HTMLElement | null>(null);
+const container = ref<HTMLElement | null>(null);
 const height =
 	props.small ? 33 :
 	props.large ? 39 :
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index 08830fca7a..c884ce53ea 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -71,8 +71,6 @@ const host = ref(toUnicode(configHost));
 const totpLogin = ref(false);
 const queryingKey = ref(false);
 const credentialRequest = ref<CredentialRequestOptions | null>(null);
-const hCaptchaResponse = ref(null);
-const reCaptchaResponse = ref(null);
 
 const emit = defineEmits<{
 	(ev: 'login', v: any): void;
@@ -126,8 +124,6 @@ async function queryKey(): Promise<void> {
 				username: username.value,
 				password: password.value,
 				credential: credential.toJSON(),
-				'hcaptcha-response': hCaptchaResponse.value,
-				'g-recaptcha-response': reCaptchaResponse.value,
 			});
 		}).then(res => {
 			emit('login', res);
@@ -149,8 +145,6 @@ function onSubmit(): void {
 			os.api('signin', {
 				username: username.value,
 				password: password.value,
-				'hcaptcha-response': hCaptchaResponse.value,
-				'g-recaptcha-response': reCaptchaResponse.value,
 			}).then(res => {
 				totpLogin.value = true;
 				signing.value = false;
@@ -168,8 +162,6 @@ function onSubmit(): void {
 		os.api('signin', {
 			username: username.value,
 			password: password.value,
-			'hcaptcha-response': hCaptchaResponse.value,
-			'g-recaptcha-response': reCaptchaResponse.value,
 			token: user.value?.twoFactorEnabled ? token.value : undefined,
 		}).then(res => {
 			emit('login', res);
diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue
index 3c5dd0ce29..9984b09c1a 100644
--- a/packages/frontend/src/components/MkSignupDialog.form.vue
+++ b/packages/frontend/src/components/MkSignupDialog.form.vue
@@ -121,9 +121,9 @@ const emailState = ref<null | 'wait' | 'ok' | 'unavailable:used' | 'unavailable:
 const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>('');
 const passwordRetypeState = ref<null | 'match' | 'not-match'>(null);
 const submitting = ref<boolean>(false);
-const hCaptchaResponse = ref(null);
-const reCaptchaResponse = ref(null);
-const turnstileResponse = ref(null);
+const hCaptchaResponse = ref<string | null>(null);
+const reCaptchaResponse = ref<string | null>(null);
+const turnstileResponse = ref<string | null>(null);
 const usernameAbortController = ref<null | AbortController>(null);
 const emailAbortController = ref<null | AbortController>(null);
 
diff --git a/packages/frontend/src/components/MkSparkle.vue b/packages/frontend/src/components/MkSparkle.vue
index a7cd1692bf..269825e25e 100644
--- a/packages/frontend/src/components/MkSparkle.vue
+++ b/packages/frontend/src/components/MkSparkle.vue
@@ -72,7 +72,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, onUnmounted, ref, shallowRef } from 'vue';
 
-const particles = ref([]);
+const particles = ref<{
+	id: string,
+	x: number,
+	y: number,
+	size: number,
+	dur: number,
+	color: string
+}[]>([]);
 const el = shallowRef<HTMLElement>();
 const width = ref(0);
 const height = ref(0);
diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
index e1237659c2..3fbadbe34f 100644
--- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
+++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
@@ -66,7 +66,7 @@ const props = defineProps<{
 	announcement?: any,
 }>();
 
-const dialog = ref(null);
+const dialog = ref<InstanceType<typeof MkModalWindow> | null>(null);
 const title = ref<string>(props.announcement ? props.announcement.title : '');
 const text = ref<string>(props.announcement ? props.announcement.text : '');
 const icon = ref<string>(props.announcement ? props.announcement.icon : 'info');
diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue
index a5d5ff733f..bc1f33c43e 100644
--- a/packages/frontend/src/components/MkWidgets.vue
+++ b/packages/frontend/src/components/MkWidgets.vue
@@ -84,7 +84,7 @@ const widgetRefs = {};
 const configWidget = (id: string) => {
 	widgetRefs[id].configure();
 };
-const widgetAdderSelected = ref(null);
+const widgetAdderSelected = ref<string | null>(null);
 const addWidget = () => {
 	if (widgetAdderSelected.value == null) return;
 
diff --git a/packages/frontend/src/components/form/suspense.vue b/packages/frontend/src/components/form/suspense.vue
index af5daa10ff..933f00b081 100644
--- a/packages/frontend/src/components/form/suspense.vue
+++ b/packages/frontend/src/components/form/suspense.vue
@@ -30,7 +30,7 @@ const props = defineProps<{
 const pending = ref(true);
 const resolved = ref(false);
 const rejected = ref(false);
-const result = ref(null);
+const result = ref<any>(null);
 
 const process = () => {
 	if (props.p == null) {
diff --git a/packages/frontend/src/components/page/page.note.vue b/packages/frontend/src/components/page/page.note.vue
index 5ca707dbc2..d885ebb1d6 100644
--- a/packages/frontend/src/components/page/page.note.vue
+++ b/packages/frontend/src/components/page/page.note.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onMounted, Ref, ref } from 'vue';
+import { onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { NoteBlock } from './block.type.js';
 import MkNote from '@/components/MkNote.vue';
@@ -23,7 +23,7 @@ const props = defineProps<{
 	page: Misskey.entities.Page,
 }>();
 
-const note: Ref<Misskey.entities.Note | null> = ref(null);
+const note = ref<Misskey.entities.Note | null>(null);
 
 onMounted(() => {
 	os.api('notes/show', { noteId: props.block.note })
diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue
index ff6ed2a624..b532314745 100644
--- a/packages/frontend/src/pages/about.vue
+++ b/packages/frontend/src/pages/about.vue
@@ -103,6 +103,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, watch, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import XEmojis from './about.emojis.vue';
 import XFederation from './about.federation.vue';
 import { version, host } from '@/config.js';
@@ -126,7 +127,7 @@ const props = withDefaults(defineProps<{
 	initialTab: 'overview',
 });
 
-const stats = ref(null);
+const stats = ref<Misskey.entities.StatsResponse | null>(null);
 const tab = ref(props.initialTab);
 
 watch(tab, () => {
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue
index 8479b0ddea..845beebbaf 100644
--- a/packages/frontend/src/pages/admin-file.vue
+++ b/packages/frontend/src/pages/admin-file.vue
@@ -68,6 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkObjectView from '@/components/MkObjectView.vue';
@@ -83,8 +84,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { iAmAdmin, iAmModerator } from '@/account.js';
 
 const tab = ref('overview');
-const file = ref<any>(null);
-const info = ref<any>(null);
+const file = ref<Misskey.entities.DriveFile | null>(null);
+const info = ref<Misskey.entities.AdminDriveShowFileResponse | null>(null);
 const isSensitive = ref<boolean>(false);
 
 const props = defineProps<{
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 5225b4a831..c87ec22ef6 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -225,9 +225,9 @@ const tab = ref(props.initialTab);
 const chartSrc = ref('per-user-notes');
 const user = ref<null | Misskey.entities.UserDetailed>();
 const init = ref<ReturnType<typeof createFetcher>>();
-const info = ref();
-const ips = ref(null);
-const ap = ref(null);
+const info = ref<any>();
+const ips = ref<Misskey.entities.AdminGetUserIpsResponse | null>(null);
+const ap = ref<any>(null);
 const moderator = ref(false);
 const silenced = ref(false);
 const approved = ref(false);
diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue
index bac3aa154a..6f1a31616a 100644
--- a/packages/frontend/src/pages/admin/_header_.vue
+++ b/packages/frontend/src/pages/admin/_header_.vue
@@ -70,7 +70,7 @@ const metadata = injectPageMetadata();
 const el = shallowRef<HTMLElement>(null);
 const tabRefs = {};
 const tabHighlightEl = shallowRef<HTMLElement | null>(null);
-const bg = ref(null);
+const bg = ref<string | null>(null);
 const height = ref(0);
 const hasTabs = computed(() => {
 	return props.tabs && props.tabs.length > 0;
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 9de9da7d98..8a1e03c30d 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -86,6 +86,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
@@ -98,7 +99,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-const ads = ref<any[]>([]);
+const ads = ref<Misskey.entities.Ad[]>([]);
 
 // ISO形式はTZがUTCになってしまうので、TZ分ずらして時間を初期化
 const localTime = new Date();
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index 034a6fdcc5..eebea51bf1 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -65,6 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { defineAsyncComponent, ref } from 'vue';
+import type { CaptchaProvider } from '@/components/MkCaptcha.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -76,7 +77,7 @@ import { i18n } from '@/i18n.js';
 
 const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
 
-const provider = ref(null);
+const provider = ref<CaptchaProvider | null>(null);
 const hcaptchaSiteKey = ref<string | null>(null);
 const hcaptchaSecretKey = ref<string | null>(null);
 const recaptchaSiteKey = ref<string | null>(null);
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index 3750a84aac..fc6a9e0d67 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -121,9 +121,9 @@ const app192IconUrl = ref<string | null>(null);
 const app512IconUrl = ref<string | null>(null);
 const bannerUrl = ref<string | null>(null);
 const backgroundImageUrl = ref<string | null>(null);
-const themeColor = ref<any>(null);
-const defaultLightTheme = ref<any>(null);
-const defaultDarkTheme = ref<any>(null);
+const themeColor = ref<string | null>(null);
+const defaultLightTheme = ref<string | null>(null);
+const defaultDarkTheme = ref<string | null>(null);
 const defaultLike = ref<string>('');
 const serverErrorImageUrl = ref<string | null>(null);
 const infoImageUrl = ref<string | null>(null);
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index 3fee3d553a..819619df90 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -79,7 +79,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 
 const enableEmail = ref<boolean>(false);
-const email = ref<any>(null);
+const email = ref<string | null>(null);
 const smtpSecure = ref<boolean>(false);
 const smtpHost = ref<string>('');
 const smtpPort = ref<number>(0);
diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue
index 9a355865a4..6808da6088 100644
--- a/packages/frontend/src/pages/admin/files.vue
+++ b/packages/frontend/src/pages/admin/files.vue
@@ -46,7 +46,7 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const origin = ref('local');
-const type = ref(null);
+const type = ref<string | null>(null);
 const searchHost = ref('');
 const userId = ref('');
 const viewMode = ref('grid');
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index 3aa74b1b91..1b41a48cb4 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue';
+import { ComputedRef, Ref, onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue';
 import { i18n } from '@/i18n.js';
 import MkSuperMenu from '@/components/MkSuperMenu.vue';
 import MkInfo from '@/components/MkInfo.vue';
@@ -37,7 +37,7 @@ import { instance } from '@/instance.js';
 import * as os from '@/os.js';
 import { lookupUser, lookupUserByEmail } from '@/scripts/lookup-user.js';
 import { useRouter } from '@/router.js';
-import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
+import { PageMetadata, definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
 
 const isEmpty = (x: string | null) => x == null || x === '';
 
@@ -52,10 +52,10 @@ const indexInfo = {
 provide('shouldOmitHeaderTitle', false);
 
 const INFO = ref(indexInfo);
-const childInfo = ref(null);
+const childInfo: Ref<ComputedRef<PageMetadata> | null> = ref(null);
 const narrow = ref(false);
 const view = ref(null);
-const el = ref(null);
+const el = ref<HTMLDivElement | null>(null);
 const pageProps = ref({});
 let noMaintainerInformation = isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail);
 let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile;
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
index a50d401ba2..acb0336491 100644
--- a/packages/frontend/src/pages/admin/modlog.vue
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -42,7 +42,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const logs = shallowRef<InstanceType<typeof MkPagination>>();
 
-const type = ref(null);
+const type = ref<string | null>(null);
 const moderatorId = ref('');
 
 const pagination = {
diff --git a/packages/frontend/src/pages/admin/overview.federation.vue b/packages/frontend/src/pages/admin/overview.federation.vue
index 03e33e57c4..2fad222bda 100644
--- a/packages/frontend/src/pages/admin/overview.federation.vue
+++ b/packages/frontend/src/pages/admin/overview.federation.vue
@@ -47,15 +47,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, ref } from 'vue';
-import XPie from './overview.pie.vue';
+import XPie, { type InstanceForPie } from './overview.pie.vue';
 import * as os from '@/os.js';
 import number from '@/filters/number.js';
 import MkNumberDiff from '@/components/MkNumberDiff.vue';
 import { i18n } from '@/i18n.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 
-const topSubInstancesForPie = ref<any>(null);
-const topPubInstancesForPie = ref<any>(null);
+const topSubInstancesForPie = ref<InstanceForPie[] | null>(null);
+const topPubInstancesForPie = ref<InstanceForPie[] | null>(null);
 const federationPubActive = ref<number | null>(null);
 const federationPubActiveDiff = ref<number | null>(null);
 const federationSubActive = ref<number | null>(null);
@@ -72,22 +72,28 @@ onMounted(async () => {
 	federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
 
 	os.apiGet('federation/stats', { limit: 10 }).then(res => {
-		topSubInstancesForPie.value = res.topSubInstances.map(x => ({
-			name: x.host,
-			color: x.themeColor,
-			value: x.followersCount,
-			onClick: () => {
-				os.pageWindow(`/instance-info/${x.host}`);
-			},
-		})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowersCount }]);
-		topPubInstancesForPie.value = res.topPubInstances.map(x => ({
-			name: x.host,
-			color: x.themeColor,
-			value: x.followingCount,
-			onClick: () => {
-				os.pageWindow(`/instance-info/${x.host}`);
-			},
-		})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowingCount }]);
+		topSubInstancesForPie.value = [
+			...res.topSubInstances.map(x => ({
+				name: x.host,
+				color: x.themeColor,
+				value: x.followersCount,
+				onClick: () => {
+					os.pageWindow(`/instance-info/${x.host}`);
+				},
+			})),
+			{ name: '(other)', color: '#80808080', value: res.otherFollowersCount },
+		];
+		topPubInstancesForPie.value = [
+			...res.topPubInstances.map(x => ({
+				name: x.host,
+				color: x.themeColor,
+				value: x.followingCount,
+				onClick: () => {
+					os.pageWindow(`/instance-info/${x.host}`);
+				},
+			})),
+			{ name: '(other)', color: '#80808080', value: res.otherFollowingCount },
+		];
 	});
 
 	fetching.value = false;
diff --git a/packages/frontend/src/pages/admin/overview.instances.vue b/packages/frontend/src/pages/admin/overview.instances.vue
index da9decaab0..de34f0c09b 100644
--- a/packages/frontend/src/pages/admin/overview.instances.vue
+++ b/packages/frontend/src/pages/admin/overview.instances.vue
@@ -18,12 +18,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
 import { defaultStore } from '@/store.js';
 
-const instances = ref([]);
+const instances = ref<Misskey.entities.FederationInstance[]>([]);
 const fetching = ref(true);
 
 const fetch = async () => {
diff --git a/packages/frontend/src/pages/admin/overview.moderators.vue b/packages/frontend/src/pages/admin/overview.moderators.vue
index c6e81b4a18..3034bdd57e 100644
--- a/packages/frontend/src/pages/admin/overview.moderators.vue
+++ b/packages/frontend/src/pages/admin/overview.moderators.vue
@@ -18,10 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
 import { defaultStore } from '@/store.js';
 
-const moderators = ref<any>(null);
+const moderators = ref<Misskey.entities.UserDetailed[] | null>(null);
 const fetching = ref(true);
 
 onMounted(async () => {
diff --git a/packages/frontend/src/pages/admin/overview.pie.vue b/packages/frontend/src/pages/admin/overview.pie.vue
index a67b67402d..95c1f57b29 100644
--- a/packages/frontend/src/pages/admin/overview.pie.vue
+++ b/packages/frontend/src/pages/admin/overview.pie.vue
@@ -13,10 +13,17 @@ import { Chart } from 'chart.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { initChart } from '@/scripts/init-chart.js';
 
+export type InstanceForPie = {
+	name: string,
+	color: string | null,
+	value: number,
+	onClick?: () => void
+};
+
 initChart();
 
 const props = defineProps<{
-	data: { name: string; value: number; color: string; onClick?: () => void }[];
+	data: InstanceForPie[];
 }>();
 
 const chartEl = shallowRef<HTMLCanvasElement>(null);
diff --git a/packages/frontend/src/pages/admin/overview.stats.vue b/packages/frontend/src/pages/admin/overview.stats.vue
index b853aee55d..adbfe3f9e2 100644
--- a/packages/frontend/src/pages/admin/overview.stats.vue
+++ b/packages/frontend/src/pages/admin/overview.stats.vue
@@ -62,6 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
 import MkNumberDiff from '@/components/MkNumberDiff.vue';
 import MkNumber from '@/components/MkNumber.vue';
@@ -69,7 +70,7 @@ import { i18n } from '@/i18n.js';
 import { customEmojis } from '@/custom-emojis.js';
 import { defaultStore } from '@/store.js';
 
-const stats = ref<any>(null);
+const stats = ref<Misskey.entities.StatsResponse | null>(null);
 const usersComparedToThePrevDay = ref<number>();
 const notesComparedToThePrevDay = ref<number>();
 const onlineUsersCount = ref(0);
diff --git a/packages/frontend/src/pages/admin/overview.users.vue b/packages/frontend/src/pages/admin/overview.users.vue
index 6b8dd90747..79579367c1 100644
--- a/packages/frontend/src/pages/admin/overview.users.vue
+++ b/packages/frontend/src/pages/admin/overview.users.vue
@@ -18,12 +18,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import { defaultStore } from '@/store.js';
 
-const newUsers = ref(null);
+const newUsers = ref<Misskey.entities.UserDetailed[] | null>(null);
 const fetching = ref(true);
 
 const fetch = async () => {
diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue
index edeab30cbb..9f2920ee0c 100644
--- a/packages/frontend/src/pages/admin/overview.vue
+++ b/packages/frontend/src/pages/admin/overview.vue
@@ -66,6 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { markRaw, onMounted, onBeforeUnmount, nextTick, shallowRef, ref, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import XFederation from './overview.federation.vue';
 import XInstances from './overview.instances.vue';
 import XQueue from './overview.queue.vue';
@@ -76,6 +77,7 @@ import XStats from './overview.stats.vue';
 import XRetention from './overview.retention.vue';
 import XModerators from './overview.moderators.vue';
 import XHeatmap from './overview.heatmap.vue';
+import type { InstanceForPie } from './overview.pie.vue';
 import * as os from '@/os.js';
 import { useStream } from '@/stream.js';
 import { i18n } from '@/i18n.js';
@@ -83,15 +85,15 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 
 const rootEl = shallowRef<HTMLElement>();
-const serverInfo = ref<any>(null);
-const topSubInstancesForPie = ref<any>(null);
-const topPubInstancesForPie = ref<any>(null);
+const serverInfo = ref<Misskey.entities.ServerInfoResponse | null>(null);
+const topSubInstancesForPie = ref<InstanceForPie[] | null>(null);
+const topPubInstancesForPie = ref<InstanceForPie[] | null>(null);
 const federationPubActive = ref<number | null>(null);
 const federationPubActiveDiff = ref<number | null>(null);
 const federationSubActive = ref<number | null>(null);
 const federationSubActiveDiff = ref<number | null>(null);
-const newUsers = ref(null);
-const activeInstances = shallowRef(null);
+const newUsers = ref<Misskey.entities.UserDetailed[] | null>(null);
+const activeInstances = shallowRef<Misskey.entities.FederationInstance | null>(null);
 const queueStatsConnection = markRaw(useStream().useChannel('queueStats'));
 const now = new Date();
 const filesPagination = {
@@ -123,22 +125,28 @@ onMounted(async () => {
 	});
 
 	os.apiGet('federation/stats', { limit: 10 }).then(res => {
-		topSubInstancesForPie.value = res.topSubInstances.map(x => ({
-			name: x.host,
-			color: x.themeColor,
-			value: x.followersCount,
-			onClick: () => {
-				os.pageWindow(`/instance-info/${x.host}`);
-			},
-		})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowersCount }]);
-		topPubInstancesForPie.value = res.topPubInstances.map(x => ({
-			name: x.host,
-			color: x.themeColor,
-			value: x.followingCount,
-			onClick: () => {
-				os.pageWindow(`/instance-info/${x.host}`);
-			},
-		})).concat([{ name: '(other)', color: '#80808080', value: res.otherFollowingCount }]);
+		topSubInstancesForPie.value = [
+			...res.topSubInstances.map(x => ({
+				name: x.host,
+				color: x.themeColor,
+				value: x.followersCount,
+				onClick: () => {
+					os.pageWindow(`/instance-info/${x.host}`);
+				},
+			})),
+			{ name: '(other)', color: '#80808080', value: res.otherFollowersCount },
+		];
+		topPubInstancesForPie.value = [
+			...res.topPubInstances.map(x => ({
+				name: x.host,
+				color: x.themeColor,
+				value: x.followingCount,
+				onClick: () => {
+					os.pageWindow(`/instance-info/${x.host}`);
+				},
+			})),
+			{ name: '(other)', color: '#80808080', value: res.otherFollowingCount },
+		];
 	});
 
 	os.api('admin/server-info').then(serverInfoResponse => {
diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue
index d65e78afa5..1425749bd4 100644
--- a/packages/frontend/src/pages/admin/proxy-account.vue
+++ b/packages/frontend/src/pages/admin/proxy-account.vue
@@ -22,6 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
@@ -31,8 +32,8 @@ import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-const proxyAccount = ref<any>(null);
-const proxyAccountId = ref<any>(null);
+const proxyAccount = ref<Misskey.entities.UserDetailed | null>(null);
+const proxyAccountId = ref<string | null>(null);
 
 async function init() {
 	const meta = await os.api('admin/meta');
diff --git a/packages/frontend/src/pages/admin/queue.chart.vue b/packages/frontend/src/pages/admin/queue.chart.vue
index 9612f78624..b829dd5738 100644
--- a/packages/frontend/src/pages/admin/queue.chart.vue
+++ b/packages/frontend/src/pages/admin/queue.chart.vue
@@ -62,7 +62,7 @@ const activeSincePrevTick = ref(0);
 const active = ref(0);
 const delayed = ref(0);
 const waiting = ref(0);
-const jobs = ref([]);
+const jobs = ref<(string | number)[][]>([]);
 const chartProcess = shallowRef<InstanceType<typeof XChart>>();
 const chartActive = shallowRef<InstanceType<typeof XChart>>();
 const chartDelayed = shallowRef<InstanceType<typeof XChart>>();
@@ -104,9 +104,11 @@ const onStatsLog = (statsLog) => {
 };
 
 onMounted(() => {
-	os.api(props.domain === 'inbox' ? 'admin/queue/inbox-delayed' : props.domain === 'deliver' ? 'admin/queue/deliver-delayed' : null, {}).then(result => {
-		jobs.value = result;
-	});
+	if (props.domain === 'inbox' || props.domain === 'deliver') {
+		os.api(`admin/queue/${props.domain}-delayed`).then(result => {
+			jobs.value = result;
+		});
+	}
 
 	connection.on('stats', onStats);
 	connection.on('statsLog', onStatsLog);
diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue
index b4d6490e09..578c29ee6c 100644
--- a/packages/frontend/src/pages/admin/relays.vue
+++ b/packages/frontend/src/pages/admin/relays.vue
@@ -25,13 +25,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-const relays = ref<any[]>([]);
+const relays = ref<Misskey.entities.AdminRelaysListResponse>([]);
 
 async function addRelay() {
 	const { canceled, result: inbox } = await os.inputText({
@@ -66,7 +67,7 @@ function remove(inbox: string) {
 }
 
 function refresh() {
-	os.api('admin/relays/list').then((relayList: any) => {
+	os.api('admin/relays/list').then(relayList => {
 		relays.value = relayList;
 	});
 }
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index 548e56d37e..980c311156 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -23,6 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import { v4 as uuid } from 'uuid';
 import XHeader from './_header_.vue';
 import XEditor from './roles.editor.vue';
@@ -39,8 +40,8 @@ const props = defineProps<{
 	id?: string;
 }>();
 
-const role = ref(null);
-const data = ref(null);
+const role = ref<Misskey.entities.Role | null>(null);
+const data = ref<any>(null);
 
 if (props.id) {
 	role.value = await os.api('admin/roles/show', {
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index 07d7acf11c..649f22e644 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -173,8 +173,8 @@ const pinnedUsers = ref<string>('');
 const cacheRemoteFiles = ref<boolean>(false);
 const cacheRemoteSensitiveFiles = ref<boolean>(false);
 const enableServiceWorker = ref<boolean>(false);
-const swPublicKey = ref<any>(null);
-const swPrivateKey = ref<any>(null);
+const swPublicKey = ref<string | null>(null);
+const swPrivateKey = ref<string | null>(null);
 const enableFanoutTimeline = ref<boolean>(false);
 const enableFanoutTimelineDbFallback = ref<boolean>(false);
 const perLocalUserUserTimelineCacheMax = ref<number>(0);
diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue
index c8a4c3f8dc..9abf0b9776 100644
--- a/packages/frontend/src/pages/antenna-timeline.vue
+++ b/packages/frontend/src/pages/antenna-timeline.vue
@@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, watch, ref, shallowRef } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import { scroll } from '@/scripts/scroll.js';
 import * as os from '@/os.js';
@@ -38,7 +39,7 @@ const props = defineProps<{
 	antennaId: string;
 }>();
 
-const antenna = ref(null);
+const antenna = ref<Misskey.entities.Antenna | null>(null);
 const queue = ref(0);
 const rootEl = shallowRef<HTMLElement>();
 const tlEl = shallowRef<InstanceType<typeof MkTimeline>>();
diff --git a/packages/frontend/src/pages/api-console.vue b/packages/frontend/src/pages/api-console.vue
index 946ff3b7ba..dcdb5b8fe3 100644
--- a/packages/frontend/src/pages/api-console.vue
+++ b/packages/frontend/src/pages/api-console.vue
@@ -46,7 +46,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const body = ref('{}');
 const endpoint = ref('');
-const endpoints = ref<any[]>([]);
+const endpoints = ref<string[]>([]);
 const sending = ref(false);
 const res = ref('');
 const withCredential = ref(true);
diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue
index 8e2eb2df25..30b100a7fb 100644
--- a/packages/frontend/src/pages/avatar-decorations.vue
+++ b/packages/frontend/src/pages/avatar-decorations.vue
@@ -35,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
@@ -43,7 +44,7 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkFolder from '@/components/MkFolder.vue';
 
-const avatarDecorations = ref<any[]>([]);
+const avatarDecorations = ref<Misskey.entities.AdminAvatarDecorationsListResponse>([]);
 
 function add() {
 	avatarDecorations.value.unshift({
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index 5408536bb9..bd38da7b63 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -70,6 +70,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref, watch, defineAsyncComponent } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkColorInput from '@/components/MkColorInput.vue';
@@ -90,15 +91,15 @@ const props = defineProps<{
 	channelId?: string;
 }>();
 
-const channel = ref(null);
-const name = ref(null);
-const description = ref(null);
+const channel = ref<Misskey.entities.Channel | null>(null);
+const name = ref<string | null>(null);
+const description = ref<string | null>(null);
 const bannerUrl = ref<string | null>(null);
 const bannerId = ref<string | null>(null);
 const color = ref('#000');
 const isSensitive = ref(false);
 const allowRenoteToExternal = ref(true);
-const pinnedNotes = ref([]);
+const pinnedNotes = ref<Partial<Misskey.entities.Note>[]>([]);
 
 watch(() => bannerId.value, async () => {
 	if (bannerId.value == null) {
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 9f68b6b485..bc2a268f34 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -88,9 +88,9 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
 
 const tab = ref('local');
-const query = ref(null);
-const queryRemote = ref(null);
-const host = ref(null);
+const query = ref<string | null>(null);
+const queryRemote = ref<string | null>(null);
+const host = ref<string | null>(null);
 const selectMode = ref(false);
 const selectedEmojis = ref<string[]>([]);
 
diff --git a/packages/frontend/src/pages/drive.vue b/packages/frontend/src/pages/drive.vue
index 7c88abc167..f3a3af677f 100644
--- a/packages/frontend/src/pages/drive.vue
+++ b/packages/frontend/src/pages/drive.vue
@@ -11,11 +11,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import XDrive from '@/components/MkDrive.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-const folder = ref(null);
+const folder = ref<Misskey.entities.DriveFolder | null>(null);
 
 const headerActions = computed(() => []);
 
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index ce6b5ae5f0..82cfa92f6a 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -92,7 +92,7 @@ const props = defineProps<{
 	emoji?: any,
 }>();
 
-const dialog = ref(null);
+const dialog = ref<InstanceType<typeof MkModalWindow> | null>(null);
 const name = ref<string>(props.emoji ? props.emoji.name : '');
 const category = ref<string>(props.emoji ? props.emoji.category : '');
 const aliases = ref<string>(props.emoji ? props.emoji.aliases.join(' ') : '');
@@ -100,7 +100,7 @@ const license = ref<string>(props.emoji ? (props.emoji.license ?? '') : '');
 const isSensitive = ref(props.emoji ? props.emoji.isSensitive : false);
 const localOnly = ref(props.emoji ? props.emoji.localOnly : false);
 const roleIdsThatCanBeUsedThisEmojiAsReaction = ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []);
-const rolesThatCanBeUsedThisEmojiAsReaction = ref([]);
+const rolesThatCanBeUsedThisEmojiAsReaction = ref<Misskey.entities.Role[]>([]);
 const file = ref<Misskey.entities.DriveFile>();
 
 watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => {
diff --git a/packages/frontend/src/pages/explore.roles.vue b/packages/frontend/src/pages/explore.roles.vue
index 929da19426..d30e107e97 100644
--- a/packages/frontend/src/pages/explore.roles.vue
+++ b/packages/frontend/src/pages/explore.roles.vue
@@ -13,10 +13,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
 import * as os from '@/os.js';
 
-const roles = ref();
+const roles = ref<Misskey.entities.Role[] | null>(null);
 
 os.api('roles/list').then(res => {
 	roles.value = res.filter(x => x.target === 'manual').sort((a, b) => b.displayOrder - a.displayOrder);
diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue
index 741a3bc219..fbca2b8ede 100644
--- a/packages/frontend/src/pages/explore.users.vue
+++ b/packages/frontend/src/pages/explore.users.vue
@@ -64,6 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { watch, ref, shallowRef, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkUserList from '@/components/MkUserList.vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import MkTab from '@/components/MkTab.vue';
@@ -76,8 +77,8 @@ const props = defineProps<{
 
 const origin = ref('local');
 const tagsEl = shallowRef<InstanceType<typeof MkFoldableSection>>();
-const tagsLocal = ref([]);
-const tagsRemote = ref([]);
+const tagsLocal = ref<Misskey.entities.Hashtag[]>([]);
+const tagsRemote = ref<Misskey.entities.Hashtag[]>([]);
 
 watch(() => props.tag, () => {
 	if (tagsEl.value) tagsEl.value.toggleContent(props.tag == null);
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index a4c2c0bc37..21e6d00613 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -35,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
@@ -364,8 +365,8 @@ const props = defineProps<{
 	id?: string;
 }>();
 
-const flash = ref(null);
-const visibility = ref('public');
+const flash = ref<Misskey.entities.Flash | null>(null);
+const visibility = ref<Misskey.entities.FlashUpdateRequest['visibility']>('public');
 
 if (props.id) {
 	flash.value = await os.api('flash/show', {
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index c7ffd5c966..5fae1248e9 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -58,6 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, onDeactivated, onUnmounted, Ref, ref, watch, shallowRef } from 'vue';
+import * as Misskey from 'misskey-js';
 import { Interpreter, Parser, values } from '@syuilo/aiscript';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
@@ -78,8 +79,8 @@ const props = defineProps<{
 	id: string;
 }>();
 
-const flash = ref(null);
-const error = ref(null);
+const flash = ref<Misskey.entities.Flash | null>(null);
+const error = ref<any>(null);
 
 function fetchFlash() {
 	flash.value = null;
diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue
index ef6286f27f..857317c48f 100644
--- a/packages/frontend/src/pages/gallery/edit.vue
+++ b/packages/frontend/src/pages/gallery/edit.vue
@@ -39,6 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, watch, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
@@ -56,10 +57,10 @@ const props = defineProps<{
 	postId?: string;
 }>();
 
-const init = ref(null);
-const files = ref([]);
-const description = ref(null);
-const title = ref(null);
+const init = ref<(() => Promise<any>) | null>(null);
+const files = ref<Misskey.entities.DriveFile[]>([]);
+const description = ref<string | null>(null);
+const title = ref<string | null>(null);
 const isSensitive = ref(false);
 
 function selectFile(evt) {
@@ -109,7 +110,7 @@ watch(() => props.postId, () => {
 	init.value = () => props.postId ? os.api('gallery/posts/show', {
 		postId: props.postId,
 	}).then(post => {
-		files.value = post.files;
+		files.value = post.files ?? [];
 		title.value = post.title;
 		description.value = post.description;
 		isSensitive.value = post.isSensitive;
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index f9fa691580..54a8790ef9 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -63,6 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, watch, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
@@ -84,8 +85,8 @@ const props = defineProps<{
 	postId: string;
 }>();
 
-const post = ref(null);
-const error = ref(null);
+const post = ref<Misskey.entities.GalleryPost | null>(null);
+const error = ref<any>(null);
 const otherPostsPagination = {
 	endpoint: 'users/gallery/posts' as const,
 	limit: 6,
diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue
index 731ad9f2ae..d6c6685a79 100644
--- a/packages/frontend/src/pages/list.vue
+++ b/packages/frontend/src/pages/list.vue
@@ -35,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { watch, computed, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
 import { userPage } from '@/filters/user.js';
 import { i18n } from '@/i18n.js';
@@ -47,9 +48,9 @@ const props = defineProps<{
 	listId: string;
 }>();
 
-const list = ref(null);
+const list = ref<Misskey.entities.UserList | null>(null);
 const error = ref();
-const users = ref([]);
+const users = ref<Misskey.entities.UserDetailed[]>([]);
 
 function fetchList(): void {
 	os.api('users/lists/show', {
diff --git a/packages/frontend/src/pages/my-antennas/edit.vue b/packages/frontend/src/pages/my-antennas/edit.vue
index 6cb368ca9d..851b32527c 100644
--- a/packages/frontend/src/pages/my-antennas/edit.vue
+++ b/packages/frontend/src/pages/my-antennas/edit.vue
@@ -11,6 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import XAntenna from './editor.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
@@ -20,7 +21,7 @@ import { antennasCache } from '@/cache.js';
 
 const router = useRouter();
 
-const antenna = ref<any>(null);
+const antenna = ref<Misskey.entities.Antenna | null>(null);
 
 const props = defineProps<{
 	antennaId: string
diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue
index 5eacf15efb..0fc7f862a3 100644
--- a/packages/frontend/src/pages/my-antennas/editor.vue
+++ b/packages/frontend/src/pages/my-antennas/editor.vue
@@ -60,7 +60,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
-	antenna: any
+	antenna: Misskey.entities.Antenna
 }>();
 
 const emit = defineEmits<{
@@ -70,8 +70,8 @@ const emit = defineEmits<{
 }>();
 
 const name = ref<string>(props.antenna.name);
-const src = ref<string>(props.antenna.src);
-const userListId = ref<any>(props.antenna.userListId);
+const src = ref<Misskey.entities.AntennasCreateRequest['src']>(props.antenna.src);
+const userListId = ref<string | null>(props.antenna.userListId);
 const users = ref<string>(props.antenna.users.join('\n'));
 const keywords = ref<string>(props.antenna.keywords.map(x => x.join(' ')).join('\n'));
 const excludeKeywords = ref<string>(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n'));
@@ -80,7 +80,7 @@ const localOnly = ref<boolean>(props.antenna.localOnly);
 const withReplies = ref<boolean>(props.antenna.withReplies);
 const withFile = ref<boolean>(props.antenna.withFile);
 const notify = ref<boolean>(props.antenna.notify);
-const userLists = ref<any>(null);
+const userLists = ref<Misskey.entities.UserList[] | null>(null);
 
 watch(() => src.value, async () => {
 	if (src.value === 'list' && userLists.value === null) {
@@ -107,8 +107,7 @@ async function saveAntenna() {
 		await os.apiWithDialog('antennas/create', antennaData);
 		emit('created');
 	} else {
-		antennaData['antennaId'] = props.antenna.id;
-		await os.apiWithDialog('antennas/update', antennaData);
+		await os.apiWithDialog('antennas/update', { ...antennaData, antennaId: props.antenna.id });
 		emit('updated');
 	}
 }
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index 05a3ec4d2e..d787e53bb0 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -27,6 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { watch, ref, shallowRef, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkClipPreview from '@/components/MkClipPreview.vue';
@@ -42,7 +43,7 @@ const pagination = {
 };
 
 const tab = ref('my');
-const favorites = ref();
+const favorites = ref<Misskey.entities.Clip[] | null>(null);
 
 const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
 
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index 1e62ca9f61..a98a7bde2c 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -67,7 +67,7 @@ const props = defineProps<{
 }>();
 
 const note = ref<null | Misskey.entities.Note>();
-const clips = ref();
+const clips = ref<Misskey.entities.Clip[]>();
 const showPrev = ref(false);
 const showNext = ref(false);
 const expandAllCws = ref(false);
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
index f97c5ea1a7..459454a9be 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
@@ -22,6 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 /* eslint-disable vue/no-mutating-props */
 import { onMounted, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import XContainer from '../page-editor.container.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
 import * as os from '@/os.js';
@@ -35,7 +36,7 @@ const emit = defineEmits<{
 	(ev: 'update:modelValue', value: any): void;
 }>();
 
-const file = ref<any>(null);
+const file = ref<Misskey.entities.DriveFile | null>(null);
 
 async function choose() {
 	os.selectDriveFile(false).then((fileResponse) => {
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
index fc11ca8543..442558cc2a 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
@@ -24,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 /* eslint-disable vue/no-mutating-props */
 import { watch, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import XContainer from '../page-editor.container.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
@@ -41,7 +42,7 @@ const emit = defineEmits<{
 }>();
 
 const id = ref<any>(props.modelValue.note);
-const note = ref<any>(null);
+const note = ref<Misskey.entities.Note | null>(null);
 
 watch(id, async () => {
 	if (id.value && (id.value.startsWith('http://') || id.value.startsWith('https://'))) {
diff --git a/packages/frontend/src/pages/page-editor/page-editor.blocks.vue b/packages/frontend/src/pages/page-editor/page-editor.blocks.vue
index 2a52d7611e..52220d36bb 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.blocks.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.blocks.vue
@@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { defineAsyncComponent } from 'vue';
+import * as Misskey from 'misskey-js';
 import XSection from './els/page-editor.el.section.vue';
 import XText from './els/page-editor.el.text.vue';
 import XImage from './els/page-editor.el.image.vue';
@@ -34,11 +35,11 @@ function getComponent(type: string) {
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
 const props = defineProps<{
-	modelValue: any[];
+	modelValue: Misskey.entities.Page['content'];
 }>();
 
 const emit = defineEmits<{
-	(ev: 'update:modelValue', value: any[]): void;
+	(ev: 'update:modelValue', value: Misskey.entities.Page['content']): void;
 }>();
 
 function updateItem(v) {
diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue
index a6e68686df..8c4696b04b 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.vue
@@ -62,6 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, provide, watch, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import { v4 as uuid } from 'uuid';
 import XBlocks from './page-editor.blocks.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -84,16 +85,16 @@ const props = defineProps<{
 const tab = ref('settings');
 const author = ref($i);
 const readonly = ref(false);
-const page = ref(null);
-const pageId = ref(null);
-const currentName = ref(null);
+const page = ref<Misskey.entities.Page | null>(null);
+const pageId = ref<string | null>(null);
+const currentName = ref<string | null>(null);
 const title = ref('');
-const summary = ref(null);
+const summary = ref<string | null>(null);
 const name = ref(Date.now().toString());
-const eyeCatchingImage = ref(null);
-const eyeCatchingImageId = ref(null);
+const eyeCatchingImage = ref<Misskey.entities.DriveFile | null>(null);
+const eyeCatchingImageId = ref<string | null>(null);
 const font = ref('sans-serif');
-const content = ref([]);
+const content = ref<Misskey.entities.Page['content']>([]);
 const alignCenter = ref(false);
 const hideTitleWhenPinned = ref(false);
 
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 2cfb37ffa7..6b06da9a24 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -77,6 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, watch, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import XPage from '@/components/page/page.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
@@ -99,8 +100,8 @@ const props = defineProps<{
 	username: string;
 }>();
 
-const page = ref(null);
-const error = ref(null);
+const page = ref<Misskey.entities.Page | null>(null);
+const error = ref<any>(null);
 const otherPostsPagination = {
 	endpoint: 'users/pages' as const,
 	limit: 6,
diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue
index 7e4a0b508d..95aa64f8d3 100644
--- a/packages/frontend/src/pages/registry.keys.vue
+++ b/packages/frontend/src/pages/registry.keys.vue
@@ -51,7 +51,7 @@ const props = defineProps<{
 
 const scope = computed(() => props.path ? props.path.split('/') : []);
 
-const keys = ref(null);
+const keys = ref<any>(null);
 
 function fetchKeys() {
 	os.api('i/registry/keys-with-type', {
diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue
index baa88ec008..fb3cc4a556 100644
--- a/packages/frontend/src/pages/registry.value.vue
+++ b/packages/frontend/src/pages/registry.value.vue
@@ -64,8 +64,8 @@ const props = defineProps<{
 const scope = computed(() => props.path.split('/').slice(0, -1));
 const key = computed(() => props.path.split('/').at(-1));
 
-const value = ref(null);
-const valueForEditor = ref(null);
+const value = ref<any>(null);
+const valueForEditor = ref<string | null>(null);
 
 function fetchValue() {
 	os.api('i/registry/get-detail', {
diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue
index 0572102849..7d1dd751ab 100644
--- a/packages/frontend/src/pages/registry.vue
+++ b/packages/frontend/src/pages/registry.vue
@@ -23,6 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import JSON5 from 'json5';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
@@ -31,7 +32,7 @@ import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
 import MkButton from '@/components/MkButton.vue';
 
-const scopesWithDomain = ref(null);
+const scopesWithDomain = ref<Misskey.entities.IRegistryScopesWithDomainResponse | null>(null);
 
 function fetchScopes() {
 	os.api('i/registry/scopes-with-domain').then(res => {
diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue
index c99f66f012..6dce4f187d 100644
--- a/packages/frontend/src/pages/role.vue
+++ b/packages/frontend/src/pages/role.vue
@@ -37,6 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, watch, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
 import MkUserList from '@/components/MkUserList.vue';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -53,7 +54,7 @@ const props = withDefaults(defineProps<{
 });
 
 const tab = ref(props.initialTab);
-const role = ref();
+const role = ref<Misskey.entities.Role>();
 const error = ref();
 const visible = ref(false);
 
@@ -62,7 +63,7 @@ watch(() => props.role, () => {
 		roleId: props.role,
 	}).then(res => {
 		role.value = res;
-		document.title = `${role.value?.name} | ${instanceName}`;
+		document.title = `${role.value.name} | ${instanceName}`;
 		visible.value = res.isExplorable && res.isPublic;
 	}).catch((err) => {
 		if (err.code === 'NO_SUCH_ROLE') {
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index acfa5b9fdf..f824d9e0a0 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -68,7 +68,7 @@ const key = ref(0);
 const searchQuery = ref('');
 const searchOrigin = ref('combined');
 const notePagination = ref();
-const user = ref(null);
+const user = ref<any>(null);
 const isLocalOnly = ref(false);
 const order = ref(false);
 const filetype = ref(null);
diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue
index 642100342e..697ce27f2f 100644
--- a/packages/frontend/src/pages/settings/accounts.vue
+++ b/packages/frontend/src/pages/settings/accounts.vue
@@ -29,7 +29,7 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 
-const storedAccounts = ref<any>(null);
+const storedAccounts = ref<{ id: string, token: string }[] | null>(null);
 const accounts = ref<Misskey.entities.UserDetailed[]>([]);
 
 const init = async () => {
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index 424d9fd4c9..f492dc6d31 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -54,7 +54,7 @@ import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkButton from '@/components/MkButton.vue';
 import { infoImageUrl } from '@/instance.js';
 
-const list = ref<any>(null);
+const list = ref<InstanceType<typeof FormPagination>>();
 
 const pagination = {
 	endpoint: 'i/apps' as const,
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index fc2ce45bf0..166e49ac54 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -54,6 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import tinycolor from 'tinycolor2';
 import FormLink from '@/components/form/link.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
@@ -69,9 +70,9 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { $i } from '@/account.js';
 
 const fetching = ref(true);
-const usage = ref<any>(null);
-const capacity = ref<any>(null);
-const uploadFolder = ref<any>(null);
+const usage = ref<number | null>(null);
+const capacity = ref<number | null>(null);
+const uploadFolder = ref<Misskey.entities.DriveFolder | null>(null);
 const alwaysMarkNsfw = ref($i.alwaysMarkNsfw);
 
 const meterStyle = computed(() => {
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index 558aed67a5..96575e097b 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script setup lang="ts">
-import { computed, onActivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue';
+import { ComputedRef, Ref, computed, onActivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue';
 import { i18n } from '@/i18n.js';
 import MkInfo from '@/components/MkInfo.vue';
 import MkSuperMenu from '@/components/MkSuperMenu.vue';
@@ -35,7 +35,7 @@ import { signout, $i } from '@/account.js';
 import { clearCache } from '@/scripts/clear-cache.js';
 import { instance } from '@/instance.js';
 import { useRouter } from '@/router.js';
-import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
+import { PageMetadata, definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
 import * as os from '@/os.js';
 
 const indexInfo = {
@@ -45,7 +45,7 @@ const indexInfo = {
 };
 const INFO = ref(indexInfo);
 const el = shallowRef<HTMLElement | null>(null);
-const childInfo = ref(null);
+const childInfo: Ref<ComputedRef<PageMetadata> | null> = ref(null);
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/settings/statusbar.statusbar.vue b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
index f1b7dcc0a8..de5f1a3db9 100644
--- a/packages/frontend/src/pages/settings/statusbar.statusbar.vue
+++ b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
@@ -87,6 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { reactive, watch } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkSelect from '@/components/MkSelect.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
@@ -99,7 +100,7 @@ import { deepClone } from '@/scripts/clone.js';
 
 const props = defineProps<{
 	_id: string;
-	userLists: any[] | null;
+	userLists: Misskey.entities.UserList[] | null;
 }>();
 
 const statusbar = reactive(deepClone(defaultStore.state.statusbars.find(x => x.id === props._id)));
diff --git a/packages/frontend/src/pages/settings/statusbar.vue b/packages/frontend/src/pages/settings/statusbar.vue
index ae6a2deaf9..c45e386ac5 100644
--- a/packages/frontend/src/pages/settings/statusbar.vue
+++ b/packages/frontend/src/pages/settings/statusbar.vue
@@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, ref, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import { v4 as uuid } from 'uuid';
 import XStatusbar from './statusbar.statusbar.vue';
 import MkFolder from '@/components/MkFolder.vue';
@@ -27,7 +28,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const statusbars = defaultStore.reactiveState.statusbars;
 
-const userLists = ref();
+const userLists = ref<Misskey.entities.UserList[] | null>(null);
 
 onMounted(() => {
 	os.api('users/lists/list').then(res => {
diff --git a/packages/frontend/src/pages/settings/theme.install.vue b/packages/frontend/src/pages/settings/theme.install.vue
index d3ba9bc0aa..d377590b9d 100644
--- a/packages/frontend/src/pages/settings/theme.install.vue
+++ b/packages/frontend/src/pages/settings/theme.install.vue
@@ -25,7 +25,7 @@ import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-const installThemeCode = ref(null);
+const installThemeCode = ref<string | null>(null);
 
 async function install(code: string): Promise<void> {
 	try {
diff --git a/packages/frontend/src/pages/settings/theme.manage.vue b/packages/frontend/src/pages/settings/theme.manage.vue
index 366c39c010..f7856d122f 100644
--- a/packages/frontend/src/pages/settings/theme.manage.vue
+++ b/packages/frontend/src/pages/settings/theme.manage.vue
@@ -46,7 +46,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const installedThemes = ref(getThemes());
 const builtinThemes = getBuiltinThemesRef();
-const selectedThemeId = ref(null);
+const selectedThemeId = ref<string | null>(null);
 
 const themes = computed(() => [...installedThemes.value, ...builtinThemes.value]);
 
diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue
index 6d203a2882..3ec23df7b8 100644
--- a/packages/frontend/src/pages/user-list-timeline.vue
+++ b/packages/frontend/src/pages/user-list-timeline.vue
@@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, watch, ref, shallowRef } from 'vue';
+import * as Misskey from 'misskey-js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import { scroll } from '@/scripts/scroll.js';
 import * as os from '@/os.js';
@@ -38,7 +39,7 @@ const props = defineProps<{
 	listId: string;
 }>();
 
-const list = ref(null);
+const list = ref<Misskey.entities.UserList | null>(null);
 const queue = ref(0);
 const tlEl = shallowRef<InstanceType<typeof MkTimeline>>();
 const rootEl = shallowRef<HTMLElement>();
diff --git a/packages/frontend/src/pages/user/followers.vue b/packages/frontend/src/pages/user/followers.vue
index 5da950e853..36f1b4543e 100644
--- a/packages/frontend/src/pages/user/followers.vue
+++ b/packages/frontend/src/pages/user/followers.vue
@@ -32,7 +32,7 @@ const props = withDefaults(defineProps<{
 });
 
 const user = ref<null | Misskey.entities.UserDetailed>(null);
-const error = ref(null);
+const error = ref<any>(null);
 
 function fetchUser(): void {
 	if (props.acct == null) return;
diff --git a/packages/frontend/src/pages/user/following.vue b/packages/frontend/src/pages/user/following.vue
index 6ba134cd78..43876b77c0 100644
--- a/packages/frontend/src/pages/user/following.vue
+++ b/packages/frontend/src/pages/user/following.vue
@@ -32,7 +32,7 @@ const props = withDefaults(defineProps<{
 });
 
 const user = ref<null | Misskey.entities.UserDetailed>(null);
-const error = ref(null);
+const error = ref<any>(null);
 
 function fetchUser(): void {
 	if (props.acct == null) return;
diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue
index c0064d2503..44b4f84ca3 100644
--- a/packages/frontend/src/pages/user/index.vue
+++ b/packages/frontend/src/pages/user/index.vue
@@ -58,7 +58,7 @@ const props = withDefaults(defineProps<{
 
 const tab = ref(props.page);
 const user = ref<null | Misskey.entities.UserDetailed>(null);
-const error = ref(null);
+const error = ref<any>(null);
 
 function fetchUser(): void {
 	if (props.acct == null) return;
diff --git a/packages/frontend/src/pages/welcome.vue b/packages/frontend/src/pages/welcome.vue
index f7d262cc8a..7f0af1b83e 100644
--- a/packages/frontend/src/pages/welcome.vue
+++ b/packages/frontend/src/pages/welcome.vue
@@ -12,13 +12,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import XSetup from './welcome.setup.vue';
 import XEntrance from './welcome.entrance.a.vue';
 import { instanceName } from '@/config.js';
 import * as os from '@/os.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-const meta = ref(null);
+const meta = ref<Misskey.entities.MetaResponse | null>(null);
 
 os.api('meta', { detail: true }).then(res => {
 	meta.value = res;
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index e23986ea4a..a409f1b775 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -133,7 +133,7 @@ export function getCopyNoteOriginLinkMenu(note: misskey.entities.Note, text: str
 export function getNoteMenu(props: {
 	note: Misskey.entities.Note;
 	menuButton: Ref<HTMLElement>;
-	translation: Ref<any>;
+	translation: Ref<Misskey.entities.NotesTranslateResponse | null>;
 	translating: Ref<boolean>;
 	isDeleted: Ref<boolean>;
 	currentClip?: Misskey.entities.Clip;
diff --git a/packages/frontend/src/scripts/use-chart-tooltip.ts b/packages/frontend/src/scripts/use-chart-tooltip.ts
index daf915c7e3..3d6489c3b8 100644
--- a/packages/frontend/src/scripts/use-chart-tooltip.ts
+++ b/packages/frontend/src/scripts/use-chart-tooltip.ts
@@ -11,8 +11,12 @@ export function useChartTooltip(opts: { position: 'top' | 'middle' } = { positio
 	const tooltipShowing = ref(false);
 	const tooltipX = ref(0);
 	const tooltipY = ref(0);
-	const tooltipTitle = ref(null);
-	const tooltipSeries = ref(null);
+	const tooltipTitle = ref<string | null>(null);
+	const tooltipSeries = ref<{
+		backgroundColor: string;
+		borderColor: string;
+		text: string;
+	}[] | null>(null);
 	let disposeTooltipComponent;
 
 	os.popup(MkChartTooltip, {
diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue
index aadeaea46f..3bb9097985 100644
--- a/packages/frontend/src/ui/classic.vue
+++ b/packages/frontend/src/ui/classic.vue
@@ -71,8 +71,8 @@ const globalHeaderHeight = ref(0);
 const wallpaper = miLocalStorage.getItem('wallpaper') != null;
 const showMenuOnTop = computed(() => defaultStore.state.menuDisplay === 'top');
 const live2d = shallowRef<HTMLIFrameElement>();
-const widgetsLeft = ref();
-const widgetsRight = ref();
+const widgetsLeft = ref<HTMLElement>();
+const widgetsRight = ref<HTMLElement>();
 
 provide('router', mainRouter);
 provideMetadataReceiver((info) => {
@@ -84,7 +84,7 @@ provideMetadataReceiver((info) => {
 provide('shouldHeaderThin', showMenuOnTop.value);
 provide('forceSpacerMin', true);
 
-function attachSticky(el) {
+function attachSticky(el: HTMLElement) {
 	const sticky = new StickySidebar(el, 0, defaultStore.state.menuDisplay === 'top' ? 60 : 0); // TODO: ヘッダーの高さを60pxと決め打ちしているのを直す
 	window.addEventListener('scroll', () => {
 		sticky.calc(window.scrollY);
diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue
index 459ba946b6..78d7e23689 100644
--- a/packages/frontend/src/ui/visitor.vue
+++ b/packages/frontend/src/ui/visitor.vue
@@ -68,6 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ComputedRef, onMounted, provide, ref, computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import XCommon from './_common_/common.vue';
 import { instanceName } from '@/config.js';
 import * as os from '@/os.js';
@@ -102,7 +103,7 @@ const isTimelineAvailable = ref(instance.policies?.ltlAvailable || instance.poli
 const showMenu = ref(false);
 const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD);
 const narrow = ref(window.innerWidth < 1280);
-const meta = ref();
+const meta = ref<Misskey.entities.MetaResponse>();
 
 const keymap = computed(() => {
 	return {
diff --git a/packages/frontend/src/widgets/WidgetActivity.calendar.vue b/packages/frontend/src/widgets/WidgetActivity.calendar.vue
index aa9fb0a106..bb5a2676dd 100644
--- a/packages/frontend/src/widgets/WidgetActivity.calendar.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.calendar.vue
@@ -36,7 +36,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 const props = defineProps<{
-	activity: any[]
+	activity: {
+		total: number;
+		notes: number;
+		replies: number;
+		renotes: number;
+	}[]
 }>();
 
 for (const d of props.activity) {
diff --git a/packages/frontend/src/widgets/WidgetActivity.chart.vue b/packages/frontend/src/widgets/WidgetActivity.chart.vue
index a207071324..0e87ec3ec3 100644
--- a/packages/frontend/src/widgets/WidgetActivity.chart.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.chart.vue
@@ -36,17 +36,22 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 const props = defineProps<{
-	activity: any[]
+	activity: {
+		total: number;
+		notes: number;
+		replies: number;
+		renotes: number;
+	}[]
 }>();
 
 const viewBoxX = ref(147);
 const viewBoxY = ref(60);
 const zoom = ref(1);
 const pos = ref(0);
-const pointsNote = ref<any>(null);
-const pointsReply = ref<any>(null);
-const pointsRenote = ref<any>(null);
-const pointsTotal = ref<any>(null);
+const pointsNote = ref<string>();
+const pointsReply = ref<string>();
+const pointsRenote = ref<string>();
+const pointsTotal = ref<string>();
 
 function dragListen(fn) {
 	window.addEventListener('mousemove', fn);
diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue
index b107a47d8c..d2842143b1 100644
--- a/packages/frontend/src/widgets/WidgetActivity.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.vue
@@ -12,8 +12,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<div>
 		<MkLoading v-if="fetching"/>
 		<template v-else>
-			<XCalendar v-show="widgetProps.view === 0" :activity="[].concat(activity)"/>
-			<XChart v-show="widgetProps.view === 1" :activity="[].concat(activity)"/>
+			<XCalendar v-show="widgetProps.view === 0" :activity="activity ?? []"/>
+			<XChart v-show="widgetProps.view === 1" :activity="activity ?? []"/>
 		</template>
 	</div>
 </MkContainer>
@@ -59,7 +59,12 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
 	emit,
 );
 
-const activity = ref(null);
+const activity = ref<{
+	total: number;
+	notes: number;
+	replies: number;
+	renotes: number;
+}[] | null>(null);
 const fetching = ref(true);
 
 const toggleView = () => {
diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue
index 605c24aaa3..9be7d084e9 100644
--- a/packages/frontend/src/widgets/WidgetFederation.vue
+++ b/packages/frontend/src/widgets/WidgetFederation.vue
@@ -26,6 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
@@ -56,8 +57,8 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 	emit,
 );
 
-const instances = ref([]);
-const charts = ref([]);
+const instances = ref<Misskey.entities.FederationInstance[]>([]);
+const charts = ref<Misskey.entities.ChartsInstanceResponse[]>([]);
 const fetching = ref(true);
 
 const fetch = async () => {
diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
index 0fc96c0d35..38323ed040 100644
--- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
@@ -19,6 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { shallowRef } from 'vue';
+import * as Misskey from 'misskey-js';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
@@ -48,7 +49,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 );
 
 const cloud = shallowRef<InstanceType<typeof MkTagCloud> | null>();
-const activeInstances = shallowRef(null);
+const activeInstances = shallowRef<Misskey.entities.FederationInstance[] | null>(null);
 
 function onInstanceClick(i) {
 	os.pageWindow(`/instance-info/${i.host}`);
diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue
index 808f023174..ff9b6e19f5 100644
--- a/packages/frontend/src/widgets/WidgetPhotos.vue
+++ b/packages/frontend/src/widgets/WidgetPhotos.vue
@@ -23,6 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onUnmounted, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import { useStream } from '@/stream.js';
@@ -57,7 +58,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 );
 
 const connection = useStream().useChannel('main');
-const images = ref([]);
+const images = ref<Misskey.entities.DriveFile[]>([]);
 const fetching = ref(true);
 
 const onDriveFileCreated = (file) => {
diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue
index eccb9a00bf..7e39a05881 100644
--- a/packages/frontend/src/widgets/WidgetSlideshow.vue
+++ b/packages/frontend/src/widgets/WidgetSlideshow.vue
@@ -18,6 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, ref, shallowRef } from 'vue';
+import * as Misskey from 'misskey-js';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
@@ -49,7 +50,7 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
 	emit,
 );
 
-const images = ref([]);
+const images = ref<Misskey.entities.DriveFile[]>([]);
 const fetching = ref(true);
 const slideA = shallowRef<HTMLElement>();
 const slideB = shallowRef<HTMLElement>();
diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue
index 738cd70b03..3416a1c0a7 100644
--- a/packages/frontend/src/widgets/WidgetTrends.vue
+++ b/packages/frontend/src/widgets/WidgetTrends.vue
@@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
@@ -54,7 +55,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 	emit,
 );
 
-const stats = ref([]);
+const stats = ref<Misskey.entities.HashtagsTrendResponse>([]);
 const fetching = ref(true);
 
 const fetch = () => {
diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue
index f5c1d2d3a3..c40328d2fa 100644
--- a/packages/frontend/src/widgets/WidgetUserList.vue
+++ b/packages/frontend/src/widgets/WidgetUserList.vue
@@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
@@ -58,8 +59,8 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
 	emit,
 );
 
-const list = ref();
-const users = ref([]);
+const list = ref<Misskey.entities.UserList>();
+const users = ref<Misskey.entities.UserDetailed[]>([]);
 const fetching = ref(true);
 
 async function chooseList() {
diff --git a/packages/frontend/src/widgets/server-metric/cpu-mem.vue b/packages/frontend/src/widgets/server-metric/cpu-mem.vue
index 9196ae209f..f13b6a370d 100644
--- a/packages/frontend/src/widgets/server-metric/cpu-mem.vue
+++ b/packages/frontend/src/widgets/server-metric/cpu-mem.vue
@@ -76,11 +76,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, onBeforeUnmount, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import { v4 as uuid } from 'uuid';
 
 const props = defineProps<{
 	connection: any,
-	meta: any
+	meta: Misskey.entities.ServerInfoResponse
 }>();
 
 const viewBoxX = ref<number>(50);
@@ -94,10 +95,10 @@ const cpuPolylinePoints = ref<string>('');
 const memPolylinePoints = ref<string>('');
 const cpuPolygonPoints = ref<string>('');
 const memPolygonPoints = ref<string>('');
-const cpuHeadX = ref<any>(null);
-const cpuHeadY = ref<any>(null);
-const memHeadX = ref<any>(null);
-const memHeadY = ref<any>(null);
+const cpuHeadX = ref<number>();
+const cpuHeadY = ref<number>();
+const memHeadX = ref<number>();
+const memHeadY = ref<number>();
 const cpuP = ref<string>('');
 const memP = ref<string>('');
 
diff --git a/packages/frontend/src/widgets/server-metric/cpu.vue b/packages/frontend/src/widgets/server-metric/cpu.vue
index cffbdb27ce..35c20c8935 100644
--- a/packages/frontend/src/widgets/server-metric/cpu.vue
+++ b/packages/frontend/src/widgets/server-metric/cpu.vue
@@ -16,11 +16,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, onBeforeUnmount, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import XPie from './pie.vue';
 
 const props = defineProps<{
 	connection: any,
-	meta: any
+	meta: Misskey.entities.ServerInfoResponse
 }>();
 
 const usage = ref<number>(0);
diff --git a/packages/frontend/src/widgets/server-metric/disk.vue b/packages/frontend/src/widgets/server-metric/disk.vue
index 18f8560265..0704854878 100644
--- a/packages/frontend/src/widgets/server-metric/disk.vue
+++ b/packages/frontend/src/widgets/server-metric/disk.vue
@@ -17,11 +17,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed } from 'vue';
+import * as Misskey from 'misskey-js';
 import XPie from './pie.vue';
 import bytes from '@/filters/bytes.js';
 
 const props = defineProps<{
-	meta: any; // TODO
+	meta: Misskey.entities.ServerInfoResponse;
 }>();
 
 const usage = computed(() => props.meta.fs.used / props.meta.fs.total);
diff --git a/packages/frontend/src/widgets/server-metric/index.vue b/packages/frontend/src/widgets/server-metric/index.vue
index ddfc6e68fa..9a785d9112 100644
--- a/packages/frontend/src/widgets/server-metric/index.vue
+++ b/packages/frontend/src/widgets/server-metric/index.vue
@@ -21,6 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onUnmounted, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import { useWidgetPropsManager, Widget, WidgetComponentExpose } from '../widget.js';
 import XCpuMemory from './cpu-mem.vue';
 import XNet from './net.vue';
@@ -65,7 +66,7 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
 	emit,
 );
 
-const meta = ref(null);
+const meta = ref<Misskey.entities.ServerInfoResponse | null>(null);
 
 os.apiGet('server-info', {}).then(res => {
 	meta.value = res;
diff --git a/packages/frontend/src/widgets/server-metric/mem.vue b/packages/frontend/src/widgets/server-metric/mem.vue
index 118fd68fe8..34a1f1ae3d 100644
--- a/packages/frontend/src/widgets/server-metric/mem.vue
+++ b/packages/frontend/src/widgets/server-metric/mem.vue
@@ -17,12 +17,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, onBeforeUnmount, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import XPie from './pie.vue';
 import bytes from '@/filters/bytes.js';
 
 const props = defineProps<{
 	connection: any,
-	meta: any
+	meta: Misskey.entities.ServerInfoResponse
 }>();
 
 const usage = ref<number>(0);
diff --git a/packages/frontend/src/widgets/server-metric/net.vue b/packages/frontend/src/widgets/server-metric/net.vue
index e6a8bfc22a..7af88a94eb 100644
--- a/packages/frontend/src/widgets/server-metric/net.vue
+++ b/packages/frontend/src/widgets/server-metric/net.vue
@@ -50,11 +50,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, onBeforeUnmount, ref } from 'vue';
+import * as Misskey from 'misskey-js';
 import bytes from '@/filters/bytes.js';
 
 const props = defineProps<{
 	connection: any,
-	meta: any
+	meta: Misskey.entities.ServerInfoResponse
 }>();
 
 const viewBoxX = ref<number>(50);
@@ -64,10 +65,10 @@ const inPolylinePoints = ref<string>('');
 const outPolylinePoints = ref<string>('');
 const inPolygonPoints = ref<string>('');
 const outPolygonPoints = ref<string>('');
-const inHeadX = ref<any>(null);
-const inHeadY = ref<any>(null);
-const outHeadX = ref<any>(null);
-const outHeadY = ref<any>(null);
+const inHeadX = ref<number>();
+const inHeadY = ref<number>();
+const outHeadX = ref<number>();
+const outHeadY = ref<number>();
 const inRecent = ref<number>(0);
 const outRecent = ref<number>(0);
 
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 436f76dbd6..7f4094845a 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0
- * generatedAt: 2023-12-24T08:46:11.020Z
+ * generatedAt: 2023-12-25T03:48:32.008Z
  */
 
 import type { SwitchCaseResponseType } from '../api.js';
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 00f2595e2c..5e05759047 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0
- * generatedAt: 2023-12-24T08:46:11.016Z
+ * generatedAt: 2023-12-25T03:48:32.001Z
  */
 
 import type {
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 0f03fd3446..ceb2f242ac 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0
- * generatedAt: 2023-12-24T08:46:11.014Z
+ * generatedAt: 2023-12-25T03:48:31.996Z
  */
 
 import { operations } from './types.js';
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index 5ae7723630..a7fde6c1a3 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0
- * generatedAt: 2023-12-24T08:46:11.013Z
+ * generatedAt: 2023-12-25T03:48:31.993Z
  */
 
 import { components } from './types.js';
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 6ff98f5013..28fe5654e6 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -3,7 +3,7 @@
 
 /*
  * version: 2023.12.0
- * generatedAt: 2023-12-24T08:46:10.930Z
+ * generatedAt: 2023-12-25T03:48:31.850Z
  */
 
 /**
@@ -4474,6 +4474,7 @@ export type operations = {
             maintainerEmail: string | null;
             maintainerName: string | null;
             name: string | null;
+            shortName: string | null;
             objectStorageS3ForcePathStyle: boolean;
             privacyPolicyUrl: string | null;
             repositoryUrl: string;
@@ -13741,57 +13742,9 @@ export type operations = {
       200: {
         content: {
           'application/json': {
-            topSubInstances: ({
-                id?: string;
-                firstRetrievedAt?: string;
-                host?: string;
-                usersCount?: number;
-                notesCount?: number;
-                followingCount?: number;
-                followersCount?: number;
-                isNotResponding?: boolean;
-                isSuspended?: boolean;
-                isBlocked?: boolean;
-                softwareName?: string;
-                softwareVersion?: string;
-                openRegistrations?: boolean;
-                name?: string;
-                description?: string;
-                maintainerName?: string;
-                maintainerEmail?: string;
-                isSilenced?: boolean;
-                iconUrl?: string;
-                faviconUrl?: string;
-                themeColor?: string;
-                infoUpdatedAt?: string | null;
-                latestRequestReceivedAt?: string | null;
-              })[];
+            topSubInstances: components['schemas']['FederationInstance'][];
             otherFollowersCount: number;
-            topPubInstances: ({
-                id?: string;
-                firstRetrievedAt?: string;
-                host?: string;
-                usersCount?: number;
-                notesCount?: number;
-                followingCount?: number;
-                followersCount?: number;
-                isNotResponding?: boolean;
-                isSuspended?: boolean;
-                isBlocked?: boolean;
-                softwareName?: string;
-                softwareVersion?: string;
-                openRegistrations?: boolean;
-                name?: string;
-                description?: string;
-                maintainerName?: string;
-                maintainerEmail?: string;
-                isSilenced?: boolean;
-                iconUrl?: string;
-                faviconUrl?: string;
-                themeColor?: string;
-                infoUpdatedAt?: string | null;
-                latestRequestReceivedAt?: string | null;
-              })[];
+            topPubInstances: components['schemas']['FederationInstance'][];
             otherFollowingCount: number;
           };
         };
@@ -18752,11 +18705,18 @@ export type operations = {
             iconUrl: string | null;
             maxNoteTextLength: number;
             ads: {
-                place: string;
+                /**
+                 * Format: id
+                 * @example xxxxxxxxxx
+                 */
+                id: string;
                 /** Format: url */
                 url: string;
+                place: string;
+                ratio: number;
                 /** Format: url */
                 imageUrl: string;
+                dayOfWeek: number;
               }[];
             /** @default 0 */
             notesPerOneAd: number;
@@ -21057,7 +21017,10 @@ export type operations = {
       /** @description OK (with results) */
       200: {
         content: {
-          'application/json': Record<string, never>;
+          'application/json': {
+            sourceLang: string;
+            text: string;
+          };
         };
       };
       /** @description Client error */

From 790f509ebef29893beda4edbb689f3cf04166655 Mon Sep 17 00:00:00 2001
From: shiosyakeyakini <blueskis382@gmail.com>
Date: Tue, 26 Dec 2023 18:42:37 +0900
Subject: [PATCH 374/435] =?UTF-8?q?fix(backend):=20=E9=9D=9E=E3=82=BB?=
 =?UTF-8?q?=E3=83=B3=E3=82=B7=E3=83=86=E3=82=A3=E3=83=96=E3=81=AE=E3=81=BF?=
 =?UTF-8?q?=EF=BC=88=E3=83=AA=E3=83=A2=E3=83=BC=E3=83=88=E3=81=AF=E3=81=84?=
 =?UTF-8?q?=E3=81=84=E3=81=AD=E3=81=AE=E3=81=BF=EF=BC=89=E3=81=8C=E6=98=A8?=
 =?UTF-8?q?=E6=97=A5=E3=81=97=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84=E5=95=8F?=
 =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3=20(#12801)=20(#12802)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: sorairo <sorairo@shiosyakeyakini.info>
Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                 | 1 +
 packages/backend/src/core/ReactionService.ts | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 709f97b7bf..52f5c07ab3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
 ### Server
 - Enhance: センシティブワードの設定がハッシュタグトレンドにも適用されるようになりました
 - Fix: 1702718871541-ffVisibility.jsのdownが壊れている
+- Fix:「非センシティブのみ(リモートはいいねのみ)」を設定していても、センシティブに設定されたカスタム絵文字をリアクションできる問題を修正
 - Fix: ロールアサイン時の通知で,ロールアイコンが縮小されずに表示される問題を修正
 
 ## 2023.12.0
diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts
index 82ee9ae4ed..0daee34ce5 100644
--- a/packages/backend/src/core/ReactionService.ts
+++ b/packages/backend/src/core/ReactionService.ts
@@ -141,7 +141,7 @@ export class ReactionService {
 						reaction = reacterHost ? `:${name}@${reacterHost}:` : `:${name}:`;
 
 						// センシティブ
-						if ((note.reactionAcceptance === 'nonSensitiveOnly') && emoji.isSensitive) {
+						if ((note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && emoji.isSensitive) {
 							reaction = FALLBACK;
 						}
 					} else {

From 6415a789ae208d3cfca7ddff4cd8139695fdacad Mon Sep 17 00:00:00 2001
From: GrapeApple0 <84321396+GrapeApple0@users.noreply.github.com>
Date: Tue, 26 Dec 2023 21:40:27 +0900
Subject: [PATCH 375/435] =?UTF-8?q?refactor:=20pagination=E3=81=AE?=
 =?UTF-8?q?=E5=9E=8B=E3=82=92=E6=98=8E=E7=A4=BA=E3=81=99=E3=82=8B=20(#1280?=
 =?UTF-8?q?9)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* refactor: paginationの型を明示する

* asではなくsatisfiesを使うように
---
 .../frontend/src/components/MkFileListForAdmin.vue   |  4 ++--
 packages/frontend/src/components/MkNoteDetailed.vue  |  6 +++---
 .../src/components/MkUserSetupDialog.Follow.vue      |  6 +++---
 packages/frontend/src/pages/about.federation.vue     |  2 +-
 packages/frontend/src/pages/admin-user.vue           |  4 ++--
 packages/frontend/src/pages/admin/abuses.vue         |  4 ++--
 packages/frontend/src/pages/admin/federation.vue     |  4 ++--
 packages/frontend/src/pages/admin/invites.vue        |  8 ++++----
 packages/frontend/src/pages/admin/modlog.vue         |  4 ++--
 packages/frontend/src/pages/admin/roles.role.vue     |  4 ++--
 packages/frontend/src/pages/admin/users.vue          |  4 ++--
 packages/frontend/src/pages/announcements.vue        |  6 +++---
 packages/frontend/src/pages/channels.vue             | 10 +++++-----
 .../frontend/src/pages/custom-emojis-manager.vue     |  6 +++---
 packages/frontend/src/pages/favorites.vue            |  4 ++--
 packages/frontend/src/pages/flash/flash-index.vue    |  8 ++++----
 packages/frontend/src/pages/follow-requests.vue      |  4 ++--
 packages/frontend/src/pages/gallery/index.vue        | 12 ++++++------
 packages/frontend/src/pages/gallery/post.vue         |  4 ++--
 packages/frontend/src/pages/instance-info.vue        |  4 ++--
 packages/frontend/src/pages/invite.vue               |  4 ++--
 packages/frontend/src/pages/my-clips/index.vue       |  4 ++--
 packages/frontend/src/pages/my-lists/list.vue        |  4 ++--
 packages/frontend/src/pages/page.vue                 |  4 ++--
 packages/frontend/src/pages/pages.vue                |  8 ++++----
 packages/frontend/src/pages/settings/apps.vue        |  4 ++--
 .../frontend/src/pages/settings/drive-cleaner.vue    |  4 ++--
 packages/frontend/src/pages/settings/mute-block.vue  |  8 ++++----
 packages/frontend/src/pages/settings/security.vue    |  4 ++--
 packages/frontend/src/pages/settings/webhook.vue     |  4 ++--
 packages/frontend/src/pages/user/clips.vue           |  4 ++--
 packages/frontend/src/pages/user/flashs.vue          |  4 ++--
 packages/frontend/src/pages/user/follow-list.vue     |  6 +++---
 packages/frontend/src/pages/user/gallery.vue         |  4 ++--
 packages/frontend/src/pages/user/lists.vue           |  4 ++--
 packages/frontend/src/pages/user/pages.vue           |  4 ++--
 packages/frontend/src/pages/user/reactions.vue       |  4 ++--
 37 files changed, 93 insertions(+), 93 deletions(-)

diff --git a/packages/frontend/src/components/MkFileListForAdmin.vue b/packages/frontend/src/components/MkFileListForAdmin.vue
index eb0d4d61ac..b60f058df0 100644
--- a/packages/frontend/src/components/MkFileListForAdmin.vue
+++ b/packages/frontend/src/components/MkFileListForAdmin.vue
@@ -38,14 +38,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
 import bytes from '@/filters/bytes.js';
 import { i18n } from '@/i18n.js';
 import { dateString } from '@/filters/date.js';
 
 const props = defineProps<{
-	pagination: any;
+	pagination: Paging;
 	viewMode: 'grid' | 'list';
 }>();
 </script>
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index e287890e2c..a3fc3058d9 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -253,7 +253,7 @@ import { checkAnimationFromMfm } from '@/scripts/check-animated-mfm.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 import MkButton from '@/components/MkButton.vue';
 
@@ -361,7 +361,7 @@ const renotesPagination = computed(() => ({
 	params: {
 		noteId: appearNote.value.id,
 	},
-}));
+} satisfies Paging));
 
 const reactionsPagination = computed(() => ({
 	endpoint: 'notes/reactions',
@@ -370,7 +370,7 @@ const reactionsPagination = computed(() => ({
 		noteId: appearNote.value.id,
 		type: reactionTabType.value,
 	},
-}));
+} satisfies Paging));
 
 async function addReplyTo(replyNote: Misskey.entities.Note) {
 		replies.value.unshift(replyNote);
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
index 5f3f5b81dd..d924a54ffb 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
@@ -37,15 +37,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { i18n } from '@/i18n.js';
 import MkFolder from '@/components/MkFolder.vue';
 import XUser from '@/components/MkUserSetupDialog.User.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 
-const pinnedUsers = { endpoint: 'pinned-users', noPaging: true };
+const pinnedUsers = { endpoint: 'pinned-users', noPaging: true } satisfies Paging;
 
 const popularUsers = { endpoint: 'users', limit: 10, noPaging: true, params: {
 	state: 'alive',
 	origin: 'local',
 	sort: '+follower',
-} };
+} } satisfies Paging;
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue
index 0de000ee3e..ed4e3bfe7a 100644
--- a/packages/frontend/src/pages/about.federation.vue
+++ b/packages/frontend/src/pages/about.federation.vue
@@ -82,7 +82,7 @@ const pagination = {
 			state.value === 'nsfw' ? { nsfw: true } :
 			{}),
 	})),
-} as Paging;
+} satisfies Paging;
 
 function getStatus(instance) {
 	if (instance.isSuspended) return 'Suspended';
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index c87ec22ef6..094d6454f2 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -212,7 +212,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 import { iAmAdmin, $i } from '@/account.js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 
 const props = withDefaults(defineProps<{
 	userId: string;
@@ -247,7 +247,7 @@ const announcementsPagination = {
 	params: computed(() => ({
 		userId: props.userId,
 	})),
-};
+} satisfies Paging;
 const expandedRoles = ref([]);
 
 function createFetcher() {
diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue
index 92688989d2..03dd6560ec 100644
--- a/packages/frontend/src/pages/admin/abuses.vue
+++ b/packages/frontend/src/pages/admin/abuses.vue
@@ -56,7 +56,7 @@ import { computed, shallowRef, ref } from 'vue';
 
 import XHeader from './_header_.vue';
 import MkSelect from '@/components/MkSelect.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import XAbuseReport from '@/components/MkAbuseReport.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -77,7 +77,7 @@ const pagination = {
 		reporterOrigin: reporterOrigin.value,
 		targetUserOrigin: targetUserOrigin.value,
 	})),
-};
+} satisfies Paging;
 
 function resolved(reportId) {
 	reports.value.removeItem(reportId);
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index 1888a0eb16..55ba0becce 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -63,7 +63,7 @@ import { computed, ref } from 'vue';
 import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
 import FormSplit from '@/components/form/split.vue';
 import { i18n } from '@/i18n.js';
@@ -90,7 +90,7 @@ const pagination = {
 			state.value === 'nsfw' ? { nsfw: true } :
 			{}),
 	})),
-};
+} satisfies Paging;
 
 function getStatus(instance) {
 	if (instance.isSuspended) return 'Suspended';
diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue
index 6314d0ce4e..fe2924d735 100644
--- a/packages/frontend/src/pages/admin/invites.vue
+++ b/packages/frontend/src/pages/admin/invites.vue
@@ -73,7 +73,7 @@ const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
 const type = ref('all');
 const sort = ref('+createdAt');
 
-const pagination: Paging = {
+const pagination = {
 	endpoint: 'admin/invite/list' as const,
 	limit: 10,
 	params: computed(() => ({
@@ -81,7 +81,7 @@ const pagination: Paging = {
 		sort: sort.value,
 	})),
 	offsetMode: true,
-};
+} satisfies Paging;
 
 const expiresAt = ref('');
 const noExpirationDate = ref(true);
@@ -97,10 +97,10 @@ async function createWithOptions() {
 	os.alert({
 		type: 'success',
 		title: i18n.ts.inviteCodeCreated,
-		text: tickets?.map(x => x.code).join('\n'),
+		text: tickets.map(x => x.code).join('\n'),
 	});
 
-	tickets?.forEach(ticket => pagingComponent.value?.prepend(ticket));
+	tickets.forEach(ticket => pagingComponent.value?.prepend(ticket));
 }
 
 function deleted(id: string) {
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
index acb0336491..ecd0d5c09c 100644
--- a/packages/frontend/src/pages/admin/modlog.vue
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -36,7 +36,7 @@ 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 MkPagination, { Paging } from '@/components/MkPagination.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
@@ -52,7 +52,7 @@ const pagination = {
 		type: type.value,
 		userId: moderatorId.value === '' ? null : moderatorId.value,
 	})),
-};
+} satisfies Paging;
 
 console.log(Misskey);
 
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 92818cc3de..77155bdecd 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -73,7 +73,7 @@ import { useRouter } from '@/router.js';
 import MkButton from '@/components/MkButton.vue';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import { infoImageUrl } from '@/instance.js';
 
 const router = useRouter();
@@ -88,7 +88,7 @@ const usersPagination = {
 	params: computed(() => ({
 		roleId: props.id,
 	})),
-};
+} satisfies Paging;
 
 const expandedItems = ref([]);
 
diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue
index 1bc4eb4089..a5cb0afdae 100644
--- a/packages/frontend/src/pages/admin/users.vue
+++ b/packages/frontend/src/pages/admin/users.vue
@@ -62,7 +62,7 @@ import { computed, shallowRef, ref } from 'vue';
 import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import * as os from '@/os.js';
 import { lookupUser } from '@/scripts/lookup-user.js';
 import { i18n } from '@/i18n.js';
@@ -88,7 +88,7 @@ const pagination = {
 		hostname: searchHost.value,
 	})),
 	offsetMode: true,
-};
+} satisfies Paging;
 
 function searchUser() {
 	os.selectUser().then(user => {
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index 705115abb0..42af188608 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, computed } from 'vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import * as os from '@/os.js';
@@ -55,7 +55,7 @@ const paginationCurrent = {
 	params: {
 		isActive: true,
 	},
-};
+} satisfies Paging;
 
 const paginationPast = {
 	endpoint: 'announcements' as const,
@@ -63,7 +63,7 @@ const paginationPast = {
 	params: {
 		isActive: false,
 	},
-};
+} satisfies Paging;
 
 const paginationEl = ref<InstanceType<typeof MkPagination>>();
 
diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue
index 182703f9da..43be0bce47 100644
--- a/packages/frontend/src/pages/channels.vue
+++ b/packages/frontend/src/pages/channels.vue
@@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, onMounted, ref } from 'vue';
 import MkChannelPreview from '@/components/MkChannelPreview.vue';
 import MkChannelList from '@/components/MkChannelList.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -83,20 +83,20 @@ onMounted(() => {
 const featuredPagination = {
 	endpoint: 'channels/featured' as const,
 	noPaging: true,
-};
+} satisfies Paging;
 const favoritesPagination = {
 	endpoint: 'channels/my-favorites' as const,
 	limit: 100,
 	noPaging: true,
-};
+} satisfies Paging;
 const followingPagination = {
 	endpoint: 'channels/followed' as const,
 	limit: 10,
-};
+} satisfies Paging;
 const ownedPagination = {
 	endpoint: 'channels/owned' as const,
 	limit: 10,
-};
+} satisfies Paging;
 
 async function search() {
 	const query = searchQuery.value.toString().trim();
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index bc2a268f34..e70d6143a4 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -77,7 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, defineAsyncComponent, ref, shallowRef } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import FormSplit from '@/components/form/split.vue';
 import { selectFile } from '@/scripts/select-file.js';
@@ -100,7 +100,7 @@ const pagination = {
 	params: computed(() => ({
 		query: (query.value && query.value !== '') ? query.value : null,
 	})),
-};
+} satisfies Paging;
 
 const remotePagination = {
 	endpoint: 'admin/emoji/list-remote' as const,
@@ -109,7 +109,7 @@ const remotePagination = {
 		query: (queryRemote.value && queryRemote.value !== '') ? queryRemote.value : null,
 		host: (host.value && host.value !== '') ? host.value : null,
 	})),
-};
+} satisfies Paging;
 
 const selectAll = () => {
 	if (selectedEmojis.value.length > 0) {
diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue
index 10f4a96a98..256dfcbc88 100644
--- a/packages/frontend/src/pages/favorites.vue
+++ b/packages/frontend/src/pages/favorites.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkNote from '@/components/MkNote.vue';
 import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
 import { i18n } from '@/i18n.js';
@@ -36,7 +36,7 @@ import { infoImageUrl } from '@/instance.js';
 const pagination = {
 	endpoint: 'i/favorites' as const,
 	limit: 10,
-};
+} satisfies Paging;
 
 definePageMetadata({
 	title: i18n.ts.favorites,
diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue
index 2b9346fcac..e4e43b2828 100644
--- a/packages/frontend/src/pages/flash/flash-index.vue
+++ b/packages/frontend/src/pages/flash/flash-index.vue
@@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
 import MkFlashPreview from '@/components/MkFlashPreview.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import { useRouter } from '@/router.js';
 import { i18n } from '@/i18n.js';
@@ -53,15 +53,15 @@ const tab = ref('featured');
 const featuredFlashsPagination = {
 	endpoint: 'flash/featured' as const,
 	noPaging: true,
-};
+} satisfies Paging;
 const myFlashsPagination = {
 	endpoint: 'flash/my' as const,
 	limit: 5,
-};
+} satisfies Paging;
 const likedFlashsPagination = {
 	endpoint: 'flash/my-likes' as const,
 	limit: 5,
-};
+} satisfies Paging;
 
 function create() {
 	router.push('/play/new');
diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue
index d750664221..62c185df99 100644
--- a/packages/frontend/src/pages/follow-requests.vue
+++ b/packages/frontend/src/pages/follow-requests.vue
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { shallowRef, computed } from 'vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import { userPage, acct } from '@/filters/user.js';
 import * as os from '@/os.js';
@@ -51,7 +51,7 @@ const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
 const pagination = {
 	endpoint: 'following/requests/list' as const,
 	limit: 10,
-};
+} satisfies Paging;
 
 function accept(user) {
 	os.api('following/requests/accept', { userId: user.id }).then(() => {
diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue
index 936d9b8393..06896d05ca 100644
--- a/packages/frontend/src/pages/gallery/index.vue
+++ b/packages/frontend/src/pages/gallery/index.vue
@@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { watch, ref, computed } from 'vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
@@ -68,19 +68,19 @@ const tagsRef = ref();
 const recentPostsPagination = {
 	endpoint: 'gallery/posts' as const,
 	limit: 6,
-};
+} satisfies Paging;
 const popularPostsPagination = {
 	endpoint: 'gallery/featured' as const,
 	noPaging: true,
-};
+} satisfies Paging;
 const myPostsPagination = {
 	endpoint: 'i/gallery/posts' as const,
 	limit: 5,
-};
+} satisfies Paging;
 const likedPostsPagination = {
 	endpoint: 'i/gallery/likes' as const,
 	limit: 5,
-};
+} satisfies Paging;
 
 const tagUsersPagination = computed(() => ({
 	endpoint: 'hashtags/users' as const,
@@ -90,7 +90,7 @@ const tagUsersPagination = computed(() => ({
 		origin: 'combined',
 		sort: '+follower',
 	},
-}));
+} satisfies Paging));
 
 watch(() => props.tag, () => {
 	if (tagsRef.value) tagsRef.value.tags.toggleContent(props.tag == null);
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 54a8790ef9..5c3fbb0d56 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -67,7 +67,7 @@ import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
 import { url } from '@/config.js';
@@ -93,7 +93,7 @@ const otherPostsPagination = {
 	params: computed(() => ({
 		userId: post.value.user.id,
 	})),
-};
+} satisfies Paging;
 
 function fetchPost() {
 	post.value = null;
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 683a31c36d..87816a8552 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -135,7 +135,7 @@ import { iAmModerator, iAmAdmin } from '@/account.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
 import { dateString } from '@/filters/date.js';
 
@@ -162,7 +162,7 @@ const usersPagination = {
 		hostname: props.host,
 	},
 	offsetMode: true,
-};
+} satisfies Paging;
 
 async function fetch(): Promise<void> {
 	if (iAmAdmin) {
diff --git a/packages/frontend/src/pages/invite.vue b/packages/frontend/src/pages/invite.vue
index 6ac78a2068..390f98ec32 100644
--- a/packages/frontend/src/pages/invite.vue
+++ b/packages/frontend/src/pages/invite.vue
@@ -52,10 +52,10 @@ const currentInviteLimit = ref<null | number>(null);
 const inviteLimit = (($i != null && $i.policies.inviteLimit) || (($i == null && instance.policies.inviteLimit))) as number;
 const inviteLimitCycle = (($i != null && $i.policies.inviteLimitCycle) || ($i == null && instance.policies.inviteLimitCycle)) as number;
 
-const pagination: Paging = {
+const pagination = {
 	endpoint: 'invite/list' as const,
 	limit: 10,
-};
+} satisfies Paging;
 
 const resetCycle = computed<null | string>(() => {
 	if (!inviteLimitCycle) return null;
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index d787e53bb0..ef6d2ef43a 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { watch, ref, shallowRef, computed } from 'vue';
 import * as Misskey from 'misskey-js';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkClipPreview from '@/components/MkClipPreview.vue';
 import * as os from '@/os.js';
@@ -40,7 +40,7 @@ const pagination = {
 	endpoint: 'clips/list' as const,
 	noPaging: true,
 	limit: 10,
-};
+} satisfies Paging;
 
 const tab = ref('my');
 const favorites = ref<Misskey.entities.Clip[] | null>(null);
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index df9cdb0fce..02dd1be477 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -68,7 +68,7 @@ import MkInput from '@/components/MkInput.vue';
 import { userListsCache } from '@/cache.js';
 import { $i } from '@/account.js';
 import { defaultStore } from '@/store.js';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 
 const {
 	enableInfiniteScroll,
@@ -88,7 +88,7 @@ const membershipsPagination = {
 	params: computed(() => ({
 		listId: props.listId,
 	})),
-};
+} satisfies Paging;
 
 function fetchList() {
 	os.api('users/lists/show', {
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 6b06da9a24..0154724aec 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -85,7 +85,7 @@ import { url } from '@/config.js';
 import MkMediaImage from '@/components/MkMediaImage.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
 import MkContainer from '@/components/MkContainer.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkPagePreview from '@/components/MkPagePreview.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -108,7 +108,7 @@ const otherPostsPagination = {
 	params: computed(() => ({
 		userId: page.value.user.id,
 	})),
-};
+} satisfies Paging;
 const path = computed(() => props.username + '/' + props.pageName);
 
 function fetchPage() {
diff --git a/packages/frontend/src/pages/pages.vue b/packages/frontend/src/pages/pages.vue
index a7ca433ed3..ea77913b7f 100644
--- a/packages/frontend/src/pages/pages.vue
+++ b/packages/frontend/src/pages/pages.vue
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
 import MkPagePreview from '@/components/MkPagePreview.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import { useRouter } from '@/router.js';
 import { i18n } from '@/i18n.js';
@@ -51,15 +51,15 @@ const tab = ref('featured');
 const featuredPagesPagination = {
 	endpoint: 'pages/featured' as const,
 	noPaging: true,
-};
+} satisfies Paging;
 const myPagesPagination = {
 	endpoint: 'i/pages' as const,
 	limit: 5,
-};
+} satisfies Paging;
 const likedPagesPagination = {
 	endpoint: 'i/page-likes' as const,
 	limit: 5,
-};
+} satisfies Paging;
 
 function create() {
 	router.push('/pages/new');
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index f492dc6d31..a0f195b5a0 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, computed } from 'vue';
-import FormPagination from '@/components/MkPagination.vue';
+import FormPagination, { Paging } from '@/components/MkPagination.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -63,7 +63,7 @@ const pagination = {
 	params: {
 		sort: '+lastUsedAt',
 	},
-};
+} satisfies Paging;
 
 function revoke(token) {
 	os.api('i/revoke-token', { tokenId: token.id }).then(() => {
diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue
index 601479b73c..012c682d06 100644
--- a/packages/frontend/src/pages/settings/drive-cleaner.vue
+++ b/packages/frontend/src/pages/settings/drive-cleaner.vue
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, ref, watch } from 'vue';
 import tinycolor from 'tinycolor2';
 import * as os from '@/os.js';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
 import { i18n } from '@/i18n.js';
 import bytes from '@/filters/bytes.js';
@@ -64,7 +64,7 @@ const pagination = {
 	endpoint: 'drive/files' as const,
 	limit: 10,
 	params: computed(() => ({ sort: sortMode.value })),
-};
+} satisfies Paging;
 
 const sortOptions = [
 	{ value: 'sizeDesc', displayName: i18n.ts._drivecleaner.orderBySizeDesc },
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index a996a03cce..d7bfb0c431 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -129,7 +129,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, computed } from 'vue';
 import XInstanceMute from './mute-block.instance-mute.vue';
 import XWordMute from './mute-block.word-mute.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import { userPage } from '@/filters/user.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -142,17 +142,17 @@ import MkFolder from '@/components/MkFolder.vue';
 const renoteMutingPagination = {
 	endpoint: 'renote-mute/list' as const,
 	limit: 10,
-};
+} satisfies Paging;
 
 const mutingPagination = {
 	endpoint: 'mute/list' as const,
 	limit: 10,
-};
+} satisfies Paging;
 
 const blockingPagination = {
 	endpoint: 'blocking/list' as const,
 	limit: 10,
-};
+} satisfies Paging;
 
 const expandedRenoteMuteItems = ref([]);
 const expandedMuteItems = ref([]);
diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue
index 9ae479e6e4..7fbcb2c8af 100644
--- a/packages/frontend/src/pages/settings/security.vue
+++ b/packages/frontend/src/pages/settings/security.vue
@@ -45,7 +45,7 @@ import X2fa from './2fa.vue';
 import FormSection from '@/components/form/section.vue';
 import FormSlot from '@/components/form/slot.vue';
 import MkButton from '@/components/MkButton.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -53,7 +53,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 const pagination = {
 	endpoint: 'i/signin-history' as const,
 	limit: 5,
-};
+} satisfies Paging;
 
 async function change() {
 	const { canceled: canceled2, result: newPassword } = await os.inputText({
diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue
index c391458274..da51520323 100644
--- a/packages/frontend/src/pages/settings/webhook.vue
+++ b/packages/frontend/src/pages/settings/webhook.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed } from 'vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import FormSection from '@/components/form/section.vue';
 import FormLink from '@/components/form/link.vue';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -44,7 +44,7 @@ const pagination = {
 	endpoint: 'i/webhooks/list' as const,
 	limit: 100,
 	noPaging: true,
-};
+} satisfies Paging;
 
 const headerActions = computed(() => []);
 
diff --git a/packages/frontend/src/pages/user/clips.vue b/packages/frontend/src/pages/user/clips.vue
index eaae472516..da17975fdf 100644
--- a/packages/frontend/src/pages/user/clips.vue
+++ b/packages/frontend/src/pages/user/clips.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 
 const props = defineProps<{
 	user: Misskey.entities.User;
@@ -31,7 +31,7 @@ const pagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-};
+} satisfies Paging;
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/user/flashs.vue b/packages/frontend/src/pages/user/flashs.vue
index 5e93a0b04c..267b1a2b87 100644
--- a/packages/frontend/src/pages/user/flashs.vue
+++ b/packages/frontend/src/pages/user/flashs.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkFlashPreview from '@/components/MkFlashPreview.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 
 const props = defineProps<{
 	user: Misskey.entities.User;
@@ -27,5 +27,5 @@ const pagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-};
+} satisfies Paging;
 </script>
diff --git a/packages/frontend/src/pages/user/follow-list.vue b/packages/frontend/src/pages/user/follow-list.vue
index 19b7290353..7c0cb72067 100644
--- a/packages/frontend/src/pages/user/follow-list.vue
+++ b/packages/frontend/src/pages/user/follow-list.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkUserInfo from '@/components/MkUserInfo.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 
 const props = defineProps<{
 	user: Misskey.entities.User;
@@ -30,7 +30,7 @@ const followingPagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-};
+} satisfies Paging;
 
 const followersPagination = {
 	endpoint: 'users/followers' as const,
@@ -38,7 +38,7 @@ const followersPagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-};
+} satisfies Paging;
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/user/gallery.vue b/packages/frontend/src/pages/user/gallery.vue
index 0d806100d9..f7ec850965 100644
--- a/packages/frontend/src/pages/user/gallery.vue
+++ b/packages/frontend/src/pages/user/gallery.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 
 const props = withDefaults(defineProps<{
 	user: Misskey.entities.User;
@@ -30,7 +30,7 @@ const pagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-};
+} satisfies Paging;
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/user/lists.vue b/packages/frontend/src/pages/user/lists.vue
index c58a8abdfb..545cc3b691 100644
--- a/packages/frontend/src/pages/user/lists.vue
+++ b/packages/frontend/src/pages/user/lists.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import {} from 'vue';
 import * as Misskey from 'misskey-js';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkStickyContainer from '@/components/global/MkStickyContainer.vue';
 import MkSpacer from '@/components/global/MkSpacer.vue';
 import MkAvatars from '@/components/MkAvatars.vue';
@@ -37,7 +37,7 @@ const pagination = {
 	params: {
 		userId: props.user.id,
 	},
-};
+} satisfies Paging;
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/user/pages.vue b/packages/frontend/src/pages/user/pages.vue
index 94ec80d05e..03373c3dfe 100644
--- a/packages/frontend/src/pages/user/pages.vue
+++ b/packages/frontend/src/pages/user/pages.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkPagePreview from '@/components/MkPagePreview.vue';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 
 const props = defineProps<{
 	user: Misskey.entities.User;
@@ -27,5 +27,5 @@ const pagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-};
+} satisfies Paging;
 </script>
diff --git a/packages/frontend/src/pages/user/reactions.vue b/packages/frontend/src/pages/user/reactions.vue
index 916b6615d5..dea66c6a17 100644
--- a/packages/frontend/src/pages/user/reactions.vue
+++ b/packages/frontend/src/pages/user/reactions.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
-import MkPagination from '@/components/MkPagination.vue';
+import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkNote from '@/components/MkNote.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 
@@ -35,7 +35,7 @@ const pagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-};
+} satisfies Paging;
 </script>
 
 <style lang="scss" module>

From 82822e29d9414556b6cc1ea0d8c8dbbdb7f4fd1d Mon Sep 17 00:00:00 2001
From: Chocolate Pie <106949016+chocolate-pie@users.noreply.github.com>
Date: Wed, 27 Dec 2023 15:08:59 +0900
Subject: [PATCH 376/435] Merge pull request from GHSA-7pxq-6xx9-xpgm
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: fix improper authorization when accessing with third-party application

* refactor: refactor type definitions

* fix: get rid of unnecessary access limitation

* enhance: サードパーティアプリケーションがWebsocket APIを使えるように

* fix: add missing parentheses

* Revert "fix(backend): add missing kind definition for admin endpoints to improve security"

This reverts commit 5150053275594278e9eb23e72d98b16593c4c230.

* frontend: 翻訳の抜けを訂正, read:adminとwrite:adminはアクセス発行トークンのデフォルトでは非表示にする

* enhance(test): misskey-ghsa-7pxq-6xx9-xpgmに関するテストを追加

* enhance(test): Websocket APIに対するテストも追加

* enhance(refactor): `@/misc/api-permissions.ts`を`misskey-js/permissions`に統合

* fix(frontend): アクセストークン発行UIで全ての権限を有効にした際、管理者用APIへのアクセスも許可してしまう問題を修正

* enhance(backend): Websocketの接続に最低限必要な権限を変更

* fix(backend): `/api/admin/meta`をサードパーティアプリケーションからはアクセスできないように

* fix(backend): エンドポイントにアクセスするために必要な権限を変更

* fix(frontend/locale): Add missing type declaration

* chore: update `misskey-js/src/autogen`

---------

Co-authored-by: tamaina <tamaina@hotmail.co.jp>
---
 CHANGELOG.md                                  |   1 -
 locales/index.d.ts                            |  49 ++
 locales/ja-JP.yml                             |  49 ++
 packages/backend/src/misc/api-permissions.ts  |  40 --
 .../backend/src/server/api/ApiCallService.ts  |   3 +-
 .../server/api/StreamingApiServerService.ts   |   4 +
 packages/backend/src/server/api/endpoints.ts  |  20 +-
 .../api/endpoints/admin/abuse-user-reports.ts |   3 +-
 .../api/endpoints/admin/accounts/create.ts    |   2 +-
 .../api/endpoints/admin/accounts/delete.ts    |   3 +-
 .../endpoints/admin/accounts/find-by-email.ts |   3 +-
 .../server/api/endpoints/admin/ad/create.ts   |   3 +-
 .../server/api/endpoints/admin/ad/delete.ts   |   3 +-
 .../src/server/api/endpoints/admin/ad/list.ts |   3 +-
 .../server/api/endpoints/admin/ad/update.ts   |   3 +-
 .../endpoints/admin/announcements/create.ts   |   3 +-
 .../endpoints/admin/announcements/delete.ts   |   3 +-
 .../api/endpoints/admin/announcements/list.ts |   3 +-
 .../endpoints/admin/announcements/update.ts   |   3 +-
 .../admin/avatar-decorations/create.ts        |   3 +-
 .../admin/avatar-decorations/delete.ts        |   3 +-
 .../admin/avatar-decorations/list.ts          |   3 +-
 .../admin/avatar-decorations/update.ts        |   3 +-
 .../api/endpoints/admin/delete-account.ts     |   3 +-
 .../admin/delete-all-files-of-a-user.ts       |   3 +-
 .../admin/drive/clean-remote-files.ts         |   3 +-
 .../api/endpoints/admin/drive/cleanup.ts      |   3 +-
 .../server/api/endpoints/admin/drive/files.ts |   3 +-
 .../api/endpoints/admin/drive/show-file.ts    |   3 +-
 .../endpoints/admin/emoji/add-aliases-bulk.ts |   3 +-
 .../server/api/endpoints/admin/emoji/add.ts   |   3 +-
 .../server/api/endpoints/admin/emoji/copy.ts  |   3 +-
 .../api/endpoints/admin/emoji/delete-bulk.ts  |   3 +-
 .../api/endpoints/admin/emoji/delete.ts       |   3 +-
 .../api/endpoints/admin/emoji/import-zip.ts   |   2 +-
 .../api/endpoints/admin/emoji/list-remote.ts  |   3 +-
 .../server/api/endpoints/admin/emoji/list.ts  |   3 +-
 .../admin/emoji/remove-aliases-bulk.ts        |   3 +-
 .../endpoints/admin/emoji/set-aliases-bulk.ts |   3 +-
 .../admin/emoji/set-category-bulk.ts          |   3 +-
 .../endpoints/admin/emoji/set-license-bulk.ts |   3 +-
 .../api/endpoints/admin/emoji/update.ts       |   3 +-
 .../admin/federation/delete-all-files.ts      |   3 +-
 .../refresh-remote-instance-metadata.ts       |   3 +-
 .../admin/federation/remove-all-following.ts  |   3 +-
 .../admin/federation/update-instance.ts       |   3 +-
 .../api/endpoints/admin/get-index-stats.ts    |   3 +-
 .../api/endpoints/admin/get-table-stats.ts    |   3 +-
 .../api/endpoints/admin/get-user-ips.ts       |   5 +-
 .../api/endpoints/admin/invite/create.ts      |   3 +-
 .../server/api/endpoints/admin/invite/list.ts |   3 +-
 .../src/server/api/endpoints/admin/meta.ts    |   3 +-
 .../api/endpoints/admin/promo/create.ts       |   3 +-
 .../server/api/endpoints/admin/queue/clear.ts |   3 +-
 .../endpoints/admin/queue/deliver-delayed.ts  |   3 +-
 .../endpoints/admin/queue/inbox-delayed.ts    |   3 +-
 .../api/endpoints/admin/queue/promote.ts      |   3 +-
 .../server/api/endpoints/admin/queue/stats.ts |   3 +-
 .../server/api/endpoints/admin/relays/add.ts  |   3 +-
 .../server/api/endpoints/admin/relays/list.ts |   3 +-
 .../api/endpoints/admin/relays/remove.ts      |   3 +-
 .../api/endpoints/admin/reset-password.ts     |   3 +-
 .../admin/resolve-abuse-user-report.ts        |   3 +-
 .../api/endpoints/admin/roles/assign.ts       |   3 +-
 .../api/endpoints/admin/roles/create.ts       |   3 +-
 .../api/endpoints/admin/roles/delete.ts       |   3 +-
 .../server/api/endpoints/admin/roles/list.ts  |   3 +-
 .../server/api/endpoints/admin/roles/show.ts  |   3 +-
 .../api/endpoints/admin/roles/unassign.ts     |   3 +-
 .../admin/roles/update-default-policies.ts    |   3 +-
 .../api/endpoints/admin/roles/update.ts       |   3 +-
 .../server/api/endpoints/admin/roles/users.ts |   3 +-
 .../server/api/endpoints/admin/send-email.ts  |   3 +-
 .../server/api/endpoints/admin/server-info.ts |   3 +-
 .../endpoints/admin/show-moderation-logs.ts   |   3 +-
 .../server/api/endpoints/admin/show-user.ts   |   3 +-
 .../server/api/endpoints/admin/show-users.ts  |   3 +-
 .../api/endpoints/admin/suspend-user.ts       |   3 +-
 .../api/endpoints/admin/unset-user-avatar.ts  |   3 +-
 .../api/endpoints/admin/unset-user-banner.ts  |   3 +-
 .../api/endpoints/admin/unsuspend-user.ts     |   3 +-
 .../server/api/endpoints/admin/update-meta.ts |   3 +-
 .../api/endpoints/admin/update-user-note.ts   |   3 +-
 .../src/server/api/endpoints/ap/get.ts        |   1 +
 .../src/server/api/endpoints/ap/show.ts       |   1 +
 .../federation/update-remote-user.ts          |   2 +-
 .../api/endpoints/fetch-external-resources.ts |   1 +
 .../backend/src/server/api/endpoints/i.ts     |   1 +
 .../api/endpoints/i/claim-achievement.ts      |   1 +
 .../api/endpoints/i/registry/get-all.ts       |   1 +
 .../api/endpoints/i/registry/get-detail.ts    |   1 +
 .../server/api/endpoints/i/registry/get.ts    |   1 +
 .../endpoints/i/registry/keys-with-type.ts    |   1 +
 .../server/api/endpoints/i/registry/keys.ts   |   1 +
 .../server/api/endpoints/i/registry/remove.ts |   1 +
 .../server/api/endpoints/i/registry/set.ts    |   1 +
 .../src/server/api/endpoints/invite/create.ts |   1 +
 .../src/server/api/endpoints/invite/delete.ts |   1 +
 .../src/server/api/endpoints/invite/limit.ts  |   1 +
 .../src/server/api/endpoints/invite/list.ts   |   1 +
 .../src/server/api/endpoints/my/apps.ts       |   1 +
 .../api/endpoints/notes/hybrid-timeline.ts    |   1 +
 .../server/api/endpoints/notes/mentions.ts    |   1 +
 .../endpoints/notes/polls/recommendation.ts   |   1 +
 .../src/server/api/endpoints/notes/state.ts   |   1 +
 .../server/api/endpoints/notes/timeline.ts    |   1 +
 .../server/api/endpoints/notes/translate.ts   |   1 +
 .../api/endpoints/notes/user-list-timeline.ts |   1 +
 .../src/server/api/endpoints/promo/read.ts    |   1 +
 .../src/server/api/endpoints/roles/list.ts    |   1 +
 .../src/server/api/endpoints/roles/notes.ts   |   1 +
 .../src/server/api/endpoints/sw/register.ts   |   1 +
 .../api/endpoints/sw/show-registration.ts     |   1 +
 .../api/endpoints/sw/update-registration.ts   |   1 +
 .../api/endpoints/users/achievements.ts       |   4 +-
 .../users/lists/create-from-public.ts         |   1 +
 .../api/endpoints/users/lists/favorite.ts     |   1 +
 .../api/endpoints/users/lists/unfavorite.ts   |   1 +
 .../server/api/endpoints/users/relation.ts    |   1 +
 .../api/endpoints/users/report-abuse.ts       |   1 +
 .../src/server/api/stream/ChannelsService.ts  |   3 +-
 .../src/server/api/stream/Connection.ts       |   5 +
 .../backend/src/server/api/stream/channel.ts  |   8 +
 .../src/server/api/stream/channels/admin.ts   |   8 +-
 .../src/server/api/stream/channels/antenna.ts |   8 +-
 .../src/server/api/stream/channels/channel.ts |   7 +-
 .../src/server/api/stream/channels/drive.ts   |   8 +-
 .../api/stream/channels/global-timeline.ts    |   7 +-
 .../src/server/api/stream/channels/hashtag.ts |   7 +-
 .../api/stream/channels/home-timeline.ts      |   8 +-
 .../api/stream/channels/hybrid-timeline.ts    |   8 +-
 .../api/stream/channels/local-timeline.ts     |   7 +-
 .../src/server/api/stream/channels/main.ts    |   8 +-
 .../server/api/stream/channels/queue-stats.ts |   7 +-
 .../api/stream/channels/role-timeline.ts      |   7 +-
 .../api/stream/channels/server-stats.ts       |   7 +-
 .../server/api/stream/channels/user-list.ts   |   7 +-
 packages/backend/test/e2e/api.ts              |  43 +-
 packages/backend/test/e2e/streaming.ts        |  25 +-
 packages/backend/test/utils.ts                |  10 +
 .../src/components/MkTokenGenerateWindow.vue  |   9 +-
 .../misskey-js/src/autogen/apiClientJSDoc.ts  | 226 ++++-----
 packages/misskey-js/src/autogen/endpoint.ts   |   2 +-
 packages/misskey-js/src/autogen/entities.ts   |   2 +-
 packages/misskey-js/src/autogen/models.ts     |   2 +-
 packages/misskey-js/src/autogen/types.ts      | 450 +++++++++---------
 packages/misskey-js/src/consts.ts             |  50 +-
 147 files changed, 795 insertions(+), 579 deletions(-)
 delete mode 100644 packages/backend/src/misc/api-permissions.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 52f5c07ab3..8b71f6540d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -132,7 +132,6 @@
 - Fix: モデレーションログがモデレーターは閲覧できないように修正
 - Fix: ハッシュタグのトレンド除外設定が即時に効果を持つように修正
 - Fix: HTTP Digestヘッダのアルゴリズム部分に大文字の"SHA-256"しか使えない
-- Fix: 管理者用APIのアクセス権限が適切に設定されていない問題を修正
 
 ## 2023.11.1
 
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 02274cc086..157b8f44d5 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -2107,6 +2107,55 @@ export interface Locale {
         "write:flash": string;
         "read:flash-likes": string;
         "write:flash-likes": string;
+        "read:admin:abuse-user-reports": string;
+        "write:admin:delete-account": string;
+        "write:admin:delete-all-files-of-a-user": string;
+        "read:admin:index-stats": string;
+        "read:admin:table-stats": string;
+        "read:admin:user-ips": string;
+        "read:admin:meta": string;
+        "write:admin:reset-password": string;
+        "write:admin:resolve-abuse-user-report": string;
+        "write:admin:send-email": string;
+        "read:admin:server-info": string;
+        "read:admin:show-moderation-log": string;
+        "read:admin:show-user": string;
+        "read:admin:show-users": string;
+        "write:admin:suspend-user": string;
+        "write:admin:unset-user-avatar": string;
+        "write:admin:unset-user-banner": string;
+        "write:admin:unsuspend-user": string;
+        "write:admin:meta": string;
+        "write:admin:user-note": string;
+        "write:admin:roles": string;
+        "read:admin:roles": string;
+        "write:admin:relays": string;
+        "read:admin:relays": string;
+        "write:admin:invite-codes": string;
+        "read:admin:invite-codes": string;
+        "write:admin:announcements": string;
+        "read:admin:announcements": string;
+        "write:admin:avatar-decorations": string;
+        "read:admin:avatar-decorations": string;
+        "write:admin:federation": string;
+        "write:admin:account": string;
+        "read:admin:account": string;
+        "write:admin:emoji": string;
+        "read:admin:emoji": string;
+        "write:admin:queue": string;
+        "read:admin:queue": string;
+        "write:admin:promo": string;
+        "write:admin:drive": string;
+        "read:admin:drive": string;
+        "read:admin:stream": string;
+        "write:admin:ad": string;
+        "read:admin:ad": string;
+        "write:invite-codes": string;
+        "read:invite-codes": string;
+        "write:clip-favorite": string;
+        "read:clip-favorite": string;
+        "read:federation": string;
+        "write:report-abuse": string;
     };
     "_auth": {
         "shareAccessTitle": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index d6d6daec50..7cb678b5f3 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2012,6 +2012,55 @@ _permissions:
   "write:flash": "Playを操作する"
   "read:flash-likes": "Playのいいねを見る"
   "write:flash-likes": "Playのいいねを操作する"
+  "read:admin:abuse-user-reports": "ユーザーからの通報を見る"
+  "write:admin:delete-account": "ユーザーアカウントを削除する"
+  "write:admin:delete-all-files-of-a-user": "ユーザーのすべてのファイルを削除する"
+  "read:admin:index-stats": "データベースインデックスに関する情報を見る"
+  "read:admin:table-stats": "データベーステーブルに関する情報を見る"
+  "read:admin:user-ips": "ユーザーのIPアドレスを見る"
+  "read:admin:meta": "インスタンスのメタデータを見る"
+  "write:admin:reset-password": "ユーザーのパスワードをリセットする"
+  "write:admin:resolve-abuse-user-report": "ユーザーからの通報を解決する"
+  "write:admin:send-email": "メールを送る"
+  "read:admin:server-info": "サーバーの情報を見る"
+  "read:admin:show-moderation-log": "モデレーションログを見る"
+  "read:admin:show-user": "ユーザーのプライベートな情報を見る"
+  "read:admin:show-users": "ユーザーのプライベートな情報を見る"
+  "write:admin:suspend-user": "ユーザーを凍結する"
+  "write:admin:unset-user-avatar": "ユーザーのアバターを削除する"
+  "write:admin:unset-user-banner": "ユーザーのバーナーを削除する"
+  "write:admin:unsuspend-user": "ユーザーの凍結を解除する"
+  "write:admin:meta": "インスタンスのメタデータを操作する"
+  "write:admin:user-note": "モデレーションノートを操作する"
+  "write:admin:roles": "ロールを操作する"
+  "read:admin:roles": "ロールを見る"
+  "write:admin:relays": "リレーを操作する"
+  "read:admin:relays": "リレーを見る"
+  "write:admin:invite-codes": "招待コードを操作する"
+  "read:admin:invite-codes": "招待コードを見る"
+  "write:admin:announcements": "お知らせを操作する"
+  "read:admin:announcements": "お知らせを見る"
+  "write:admin:avatar-decorations": "アバターデコレーションを操作する"
+  "read:admin:avatar-decorations": "アバターデコレーションを見る"
+  "write:admin:federation": "連合に関する情報を操作する"
+  "write:admin:account": "ユーザーアカウントを操作する"
+  "read:admin:account": "ユーザーに関する情報を見る"
+  "write:admin:emoji": "絵文字を操作する"
+  "read:admin:emoji": "絵文字を見る"
+  "write:admin:queue": "ジョブキューを操作する"
+  "read:admin:queue": "ジョブキューに関する情報を見る"
+  "write:admin:promo": "プロモーションノートを操作する"
+  "write:admin:drive": "ユーザーのドライブを操作する"
+  "read:admin:drive": "ユーザーのドライブの関する情報を見る"
+  "read:admin:stream": "管理者用のWebsocket APIを使う"
+  "write:admin:ad": "広告を操作する"
+  "read:admin:ad": "広告を見る"
+  "write:invite-codes": "招待コードを作成する"
+  "read:invite-codes": "招待コードを取得する"
+  "write:clip-favorite": "クリップのいいねを操作する"
+  "read:clip-favorite": "クリップのいいねを見る"
+  "read:federation": "連合に関する情報を取得する"
+  "write:report-abuse": "違反を報告する"
 
 _auth:
   shareAccessTitle: "アプリへのアクセス許可"
diff --git a/packages/backend/src/misc/api-permissions.ts b/packages/backend/src/misc/api-permissions.ts
deleted file mode 100644
index 57c9308844..0000000000
--- a/packages/backend/src/misc/api-permissions.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and other misskey contributors
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-export const kinds = [
-	'read:account',
-	'write:account',
-	'read:blocks',
-	'write:blocks',
-	'read:drive',
-	'write:drive',
-	'read:favorites',
-	'write:favorites',
-	'read:following',
-	'write:following',
-	'read:messaging',
-	'write:messaging',
-	'read:mutes',
-	'write:mutes',
-	'write:notes',
-	'read:notifications',
-	'write:notifications',
-	'read:reactions',
-	'write:reactions',
-	'write:votes',
-	'read:pages',
-	'write:pages',
-	'write:page-likes',
-	'read:page-likes',
-	'read:user-groups',
-	'write:user-groups',
-	'read:channels',
-	'write:channels',
-	'read:gallery',
-	'write:gallery',
-	'read:gallery-likes',
-	'write:gallery-likes',
-];
-// IF YOU ADD KINDS(PERMISSIONS), YOU MUST ADD TRANSLATIONS (under _permissions).
diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts
index 66f171a5d8..56f804dee8 100644
--- a/packages/backend/src/server/api/ApiCallService.ts
+++ b/packages/backend/src/server/api/ApiCallService.ts
@@ -330,7 +330,8 @@ export class ApiCallService implements OnApplicationShutdown {
 			}
 		}
 
-		if (token && ep.meta.kind && !token.permission.some(p => p === ep.meta.kind)) {
+		if (token && ((ep.meta.kind && !token.permission.some(p => p === ep.meta.kind))
+			|| (!ep.meta.kind && (ep.meta.requireCredential || ep.meta.requireModerator || ep.meta.requireAdmin)))) {
 			throw new ApiError({
 				message: 'Your app does not have the necessary permissions to use this endpoint.',
 				code: 'PERMISSION_DENIED',
diff --git a/packages/backend/src/server/api/StreamingApiServerService.ts b/packages/backend/src/server/api/StreamingApiServerService.ts
index dc3a00617c..3b387d92ca 100644
--- a/packages/backend/src/server/api/StreamingApiServerService.ts
+++ b/packages/backend/src/server/api/StreamingApiServerService.ts
@@ -71,6 +71,10 @@ export class StreamingApiServerService {
 
 			try {
 				[user, app] = await this.authenticateService.authenticate(token);
+
+				if (app !== null && !app.permission.some(p => p === 'read:account')) {
+					throw new AuthenticationError('Your app does not have necessary permissions to use websocket API.');
+				}
 			} catch (e) {
 				if (e instanceof AuthenticationError) {
 					socket.write([
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index de858d5304..f82bf257fc 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -4,6 +4,7 @@
  */
 
 import type { Schema } from '@/misc/json-schema.js';
+import { permissions } from 'misskey-js';
 import { RolePolicies } from '@/core/RoleService.js';
 
 import * as ep___admin_meta from './endpoints/admin/meta.js';
@@ -750,7 +751,7 @@ const eps = [
 	['sponsors', ep___sponsors],
 ];
 
-export interface IEndpointMeta {
+interface IEndpointMetaBase {
 	readonly stability?: 'deprecated' | 'experimental' | 'stable';
 
 	readonly tags?: ReadonlyArray<string>;
@@ -849,6 +850,23 @@ export interface IEndpointMeta {
 	readonly cacheSec?: number;
 }
 
+export type IEndpointMeta = (Omit<IEndpointMetaBase, 'requireCrential' | 'requireModerator' | 'requireAdmin'> & {
+	requireCredential?: false,
+	requireAdmin?: false,
+	requireModerator?: false,
+}) | (Omit<IEndpointMetaBase, 'secure'> & {
+	secure: true,
+}) | (Omit<IEndpointMetaBase, 'requireCredential' | 'kind'> & {
+	requireCredential: true,
+	kind: (typeof permissions)[number],
+}) | (Omit<IEndpointMetaBase, 'requireModerator' | 'kind'> & {
+	requireModerator: true,
+	kind: (typeof permissions)[number],
+}) | (Omit<IEndpointMetaBase, 'requireAdmin' | 'kind'> & {
+	requireAdmin: true,
+	kind: (typeof permissions)[number],
+})
+
 export interface IEndpoint {
 	name: string;
 	meta: IEndpointMeta;
diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
index 484118cd46..3484d6707a 100644
--- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
+++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts
@@ -13,10 +13,9 @@ import { AbuseUserReportEntityService } from '@/core/entities/AbuseUserReportEnt
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:abuse-user-reports',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
index 07f24d2995..a2f9bf6945 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
@@ -15,7 +15,7 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
+	secure: true,
 
 	res: {
 		type: 'object',
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts
index 86f4b0709b..52d8c8ce18 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts
@@ -14,10 +14,9 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireAdmin: true,
+	kind: 'write:admin:account',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
index bc292fd53a..93673453d6 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/find-by-email.ts
@@ -13,10 +13,9 @@ import { ApiError } from '@/server/api/error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireAdmin: true,
+	kind: 'read:admin:account',
 
 	errors: {
 		userNotFound: {
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
index 087ae4befc..041b10f9f7 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
@@ -13,10 +13,9 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:ad',
 	res: {
 		type: 'object',
 		optional: false,
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts
index ba655a6aa3..5b18b347d3 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts
@@ -13,10 +13,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:ad',
 
 	errors: {
 		noSuchAd: {
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
index 12528917dc..586c1f44db 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
@@ -12,10 +12,9 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:ad',
 	res: {
 		type: 'array',
 		optional: false,
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts
index b83c163004..bf96e44b0c 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts
@@ -13,10 +13,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:ad',
 
 	errors: {
 		noSuchAd: {
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 fb432336e4..c9df70c76b 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts
@@ -10,10 +10,9 @@ import { AnnouncementService } from '@/core/AnnouncementService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:announcements',
 
 	res: {
 		type: 'object',
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 e84e63c666..939333345e 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts
@@ -13,10 +13,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:announcements',
 
 	errors: {
 		noSuchAnnouncement: {
diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
index e98ef0b169..429b138599 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts
@@ -14,10 +14,9 @@ import { IdService } from '@/core/IdService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:announcements',
 
 	res: {
 		type: 'array',
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 e2ec344899..db6db8356d 100644
--- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts
@@ -13,10 +13,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:announcements',
 
 	errors: {
 		noSuchAnnouncement: {
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts
index 158435ed21..4ac74253cc 100644
--- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts
@@ -10,10 +10,9 @@ import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageAvatarDecorations',
+	kind: 'write:admin:avatar-decorations',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts
index 06083cc180..88977f801a 100644
--- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/delete.ts
@@ -12,10 +12,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageAvatarDecorations',
+	kind: 'write:admin:avatar-decorations',
 	errors: {
 	},
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts
index 49a8718bce..33122c3eef 100644
--- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts
@@ -15,10 +15,9 @@ import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageAvatarDecorations',
+	kind: 'read:admin:avatar-decorations',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts
index 3d8f3d63de..6211345f96 100644
--- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts
@@ -12,10 +12,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageAvatarDecorations',
+	kind: 'write:admin:avatar-decorations',
 
 	errors: {
 	},
diff --git a/packages/backend/src/server/api/endpoints/admin/delete-account.ts b/packages/backend/src/server/api/endpoints/admin/delete-account.ts
index adc446d14b..2c82c2879d 100644
--- a/packages/backend/src/server/api/endpoints/admin/delete-account.ts
+++ b/packages/backend/src/server/api/endpoints/admin/delete-account.ts
@@ -12,10 +12,9 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireAdmin: true,
+	kind: 'write:admin:delete-account',
 
 	res: {
 	},
diff --git a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts
index 1fdbbfb12e..7d33065f2e 100644
--- a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts
@@ -12,10 +12,9 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireAdmin: true,
+	kind: 'write:admin:delete-all-files-of-a-user',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts
index 3f23319a5f..af2bb6b1ca 100644
--- a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts
+++ b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts
@@ -10,10 +10,9 @@ import { QueueService } from '@/core/QueueService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:drive',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts
index fd8fa46a47..a3b221284b 100644
--- a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts
+++ b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts
@@ -13,10 +13,9 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:drive',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts
index 816bbfbc45..37fa439bcf 100644
--- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts
+++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts
@@ -13,10 +13,9 @@ import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.j
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:drive',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts
index 61cb843558..3aeb3e45e3 100644
--- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts
+++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts
@@ -14,10 +14,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:drive',
 
 	errors: {
 		noSuchFile: {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
index 5333adb624..1cd8125c52 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts
@@ -10,10 +10,9 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'write:admin:emoji',
 } as const;
 
 export const paramDef = {
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 7e484c612b..0868e24948 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts
@@ -14,10 +14,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'write:admin:emoji',
 
 	errors: {
 		noSuchFile: {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
index a24c72b9b8..611b64be07 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts
@@ -17,10 +17,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'write:admin:emoji',
 
 	errors: {
 		noSuchEmoji: {
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 c483794a40..450695984a 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
@@ -10,10 +10,9 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'write:admin:emoji',
 } as const;
 
 export const paramDef = {
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 e15af7717b..e1e6e7c2c4 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts
@@ -10,10 +10,9 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'write:admin:emoji',
 
 	errors: {
 		noSuchEmoji: {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts
index b75616f3cc..208616c0ac 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts
@@ -8,7 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js';
 import { QueueService } from '@/core/QueueService.js';
 
 export const meta = {
-	kind: 'write:admin',
+	secure: true,
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
 } as const;
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
index a383e09338..f3e0c1ef1f 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts
@@ -15,10 +15,9 @@ import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'read:admin:emoji',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
index 210b3639c3..59e87253f6 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts
@@ -15,10 +15,9 @@ import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'read:admin:emoji',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
index 8e92db1daf..26dd43e926 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts
@@ -10,10 +10,9 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'write:admin:emoji',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
index 5a06b5b32f..18961976f9 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts
@@ -10,10 +10,9 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'write:admin:emoji',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
index b3e9c6df13..c680f2e2d4 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts
@@ -10,10 +10,9 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'write:admin:emoji',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts
index c59d13ad16..47c692b613 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts
@@ -10,10 +10,9 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'write:admin:emoji',
 } as const;
 
 export const paramDef = {
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 61d857b7b0..550bb0052b 100644
--- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts
@@ -13,10 +13,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireRolePolicy: 'canManageCustomEmojis',
+	kind: 'write:admin:emoji',
 
 	errors: {
 		noSuchEmoji: {
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts
index b81297413c..57612850b4 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts
@@ -12,10 +12,9 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:federation',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts
index 6cc4e3087f..0d061c685f 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts
@@ -13,10 +13,9 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:federation',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts
index 18884dfca6..c15fb83454 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts
@@ -12,10 +12,9 @@ import { QueueService } from '@/core/QueueService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:federation',
 } as const;
 
 export const paramDef = {
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 cf4ac70cc9..188ab69532 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
@@ -14,10 +14,9 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:federation',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts
index b81d9857d7..0b50212119 100644
--- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts
+++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts
@@ -11,8 +11,7 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	requireCredential: true,
 	requireAdmin: true,
-
-	kind: 'read:admin',
+	kind: 'read:admin:index-stats',
 
 	tags: ['admin'],
 	res: {
diff --git a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts
index c104f653ef..0d44b288cb 100644
--- a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts
+++ b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts
@@ -11,8 +11,7 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	requireCredential: true,
 	requireAdmin: true,
-
-	kind: 'read:admin',
+	kind: 'read:admin:table-stats',
 
 	tags: ['admin'],
 
diff --git a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
index 76c32f2a9f..1b437f718b 100644
--- a/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
+++ b/packages/backend/src/server/api/endpoints/admin/get-user-ips.ts
@@ -12,10 +12,9 @@ import { IdService } from '@/core/IdService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:user-ips',
 	res: {
 		type: 'array',
 		optional: false,
@@ -34,7 +33,7 @@ export const meta = {
 				},
 			},
 		},
-	}
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/invite/create.ts b/packages/backend/src/server/api/endpoints/admin/invite/create.ts
index 96de772edc..396b84623f 100644
--- a/packages/backend/src/server/api/endpoints/admin/invite/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/invite/create.ts
@@ -16,10 +16,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:invite-codes',
 
 	errors: {
 		invalidDateTime: {
diff --git a/packages/backend/src/server/api/endpoints/admin/invite/list.ts b/packages/backend/src/server/api/endpoints/admin/invite/list.ts
index 3b7dc72e11..d293dcadc6 100644
--- a/packages/backend/src/server/api/endpoints/admin/invite/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/invite/list.ts
@@ -12,10 +12,9 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:invite-codes',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 218ab42ffd..4fd2a568ad 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -13,10 +13,9 @@ import { DEFAULT_POLICIES } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['meta'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireAdmin: true,
+	kind: 'read:admin:meta',
 
 	res: {
 		type: 'object',
diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts
index e2befec50f..ab69dfba96 100644
--- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts
@@ -13,10 +13,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:promo',
 
 	errors: {
 		noSuchNote: {
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 1d565e8f24..9912043c8b 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts
@@ -11,10 +11,9 @@ import { QueueService } from '@/core/QueueService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:queue',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts
index 30005fc666..8473909103 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts
@@ -11,10 +11,9 @@ import type { DeliverQueue } from '@/core/QueueModule.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:queue',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
index aa8b6edee5..19f7cb85c0 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts
@@ -11,10 +11,9 @@ import type { InboxQueue } from '@/core/QueueModule.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:queue',
 
 	res: {
 		type: 'array',
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 8f46cd6375..d06780e044 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/promote.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/promote.ts
@@ -11,10 +11,9 @@ import { QueueService } from '@/core/QueueService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:queue',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts
index 1d92e2bf86..189690b703 100644
--- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts
+++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts
@@ -10,10 +10,9 @@ import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, Obj
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:emoji',
 
 	res: {
 		type: 'object',
diff --git a/packages/backend/src/server/api/endpoints/admin/relays/add.ts b/packages/backend/src/server/api/endpoints/admin/relays/add.ts
index 53b83560cf..d55dff7b0c 100644
--- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts
+++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts
@@ -12,10 +12,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:relays',
 
 	errors: {
 		invalidUrl: {
diff --git a/packages/backend/src/server/api/endpoints/admin/relays/list.ts b/packages/backend/src/server/api/endpoints/admin/relays/list.ts
index 35c8e05487..61ea287bff 100644
--- a/packages/backend/src/server/api/endpoints/admin/relays/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/relays/list.ts
@@ -10,10 +10,9 @@ import { RelayService } from '@/core/RelayService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:relays',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts
index fdc53cb708..8a6dd4e152 100644
--- a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts
+++ b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts
@@ -10,10 +10,9 @@ import { RelayService } from '@/core/RelayService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:relays',
 } as const;
 
 export const paramDef = {
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 d37a526db7..506cd609ae 100644
--- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts
+++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts
@@ -15,10 +15,9 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:reset-password',
 
 	res: {
 		type: 'object',
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 fb26c82a9d..26c4038b98 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
@@ -15,10 +15,9 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:resolve-abuse-user-report',
 } as const;
 
 export const paramDef = {
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 bbd4cfabbe..8eb3d2bf59 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/assign.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/assign.ts
@@ -13,10 +13,9 @@ import { RoleService } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:roles',
 
 	errors: {
 		noSuchRole: {
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 ac6085d921..de23d2fb11 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/create.ts
@@ -11,10 +11,9 @@ import { RoleService } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireAdmin: true,
+	kind: 'write:admin:roles',
 
 	res: {
 		type: 'object',
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 f60d6754a5..9e2968e317 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/delete.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/delete.ts
@@ -13,10 +13,9 @@ import { RoleService } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireAdmin: true,
+	kind: 'write:admin:roles',
 
 	errors: {
 		noSuchRole: {
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/list.ts b/packages/backend/src/server/api/endpoints/admin/roles/list.ts
index 30917ce984..d3d1a10a69 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/list.ts
@@ -12,10 +12,9 @@ import { RoleEntityService } from '@/core/entities/RoleEntityService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:roles',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/show.ts b/packages/backend/src/server/api/endpoints/admin/roles/show.ts
index 91e32d95be..ad4345e5a5 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/show.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/show.ts
@@ -13,10 +13,9 @@ import { RoleEntityService } from '@/core/entities/RoleEntityService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
-	kind: 'read:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:roles',
 
 	errors: {
 		noSuchRole: {
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 701fea1ed5..c11265252c 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/unassign.ts
@@ -13,10 +13,9 @@ import { RoleService } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:roles',
 
 	errors: {
 		noSuchRole: {
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts
index 066fc73234..203f749a6e 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update-default-policies.ts
@@ -11,10 +11,9 @@ import { MetaService } from '@/core/MetaService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireAdmin: true,
+	kind: 'write:admin:roles',
 } as const;
 
 export const paramDef = {
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 6cfcd8ca4a..74d5aae5d8 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/update.ts
@@ -14,10 +14,9 @@ import { RoleService } from '@/core/RoleService.js';
 export const meta = {
 	tags: ['admin', 'role'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireAdmin: true,
+	kind: 'write:admin:roles',
 
 	errors: {
 		noSuchRole: {
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
index 6a0f7f9987..66f4d9d26b 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
@@ -16,10 +16,9 @@ import { ApiError } from '../../../error.js';
 export const meta = {
 	tags: ['admin', 'role', 'users'],
 
-	kind: 'read:admin',
-
 	requireCredential: false,
 	requireAdmin: true,
+	kind: 'read:admin:roles',
 
 	errors: {
 		noSuchRole: {
diff --git a/packages/backend/src/server/api/endpoints/admin/send-email.ts b/packages/backend/src/server/api/endpoints/admin/send-email.ts
index d22066909e..d20aee656c 100644
--- a/packages/backend/src/server/api/endpoints/admin/send-email.ts
+++ b/packages/backend/src/server/api/endpoints/admin/send-email.ts
@@ -10,10 +10,9 @@ import { EmailService } from '@/core/EmailService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:send-email',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts
index d3c3bebff6..374712f57d 100644
--- a/packages/backend/src/server/api/endpoints/admin/server-info.ts
+++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts
@@ -14,11 +14,10 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'read:admin:server-info',
 
 	tags: ['admin', 'meta'],
 
-	kind: 'read:admin',
-
 	res: {
 		type: 'object',
 		optional: false, nullable: false,
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 c82532ed67..f3601be9bb 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
@@ -15,8 +15,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireAdmin: true,
-
-	kind: 'read:admin',
+	kind: 'read:admin:show-moderation-log',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts
index 76032cfb3d..ea22f9eeb9 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts
@@ -16,8 +16,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
-
-	kind: 'read:admin',
+	kind: 'read:admin:show-user',
 
 	res: {
 		type: 'object',
diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts
index f29ebc7fd3..6267fb97b2 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts
@@ -16,8 +16,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
-
-	kind: 'read:admin',
+	kind: 'read:admin:show-users',
 
 	res: {
 		type: 'array',
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 35c3f37481..a26fa81c13 100644
--- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts
@@ -19,10 +19,9 @@ import { QueueService } from '@/core/QueueService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:suspend-user',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts b/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts
index 2309493937..8b22fad1d4 100644
--- a/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unset-user-avatar.ts
@@ -12,10 +12,9 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:unset-user-avatar',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts b/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts
index 468c634e5b..5ec359c0ef 100644
--- a/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unset-user-banner.ts
@@ -12,10 +12,9 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:unset-user-banner',
 } as const;
 
 export const paramDef = {
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 8cdd317eae..9c896f0e64 100644
--- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts
@@ -13,10 +13,9 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:unsuspend-user',
 } as const;
 
 export const paramDef = {
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 5d982b5093..5c916fe340 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -12,10 +12,9 @@ import { MetaService } from '@/core/MetaService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireAdmin: true,
+	kind: 'write:admin:meta',
 } as const;
 
 export const paramDef = {
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 dd0b777373..e582147e72 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
@@ -12,10 +12,9 @@ import { ModerationLogService } from '@/core/ModerationLogService.js';
 export const meta = {
 	tags: ['admin'],
 
-	kind: 'write:admin',
-
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:user-note',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts
index a4a7fd2037..e0ef5d413a 100644
--- a/packages/backend/src/server/api/endpoints/ap/get.ts
+++ b/packages/backend/src/server/api/endpoints/ap/get.ts
@@ -12,6 +12,7 @@ export const meta = {
 	tags: ['federation'],
 
 	requireCredential: true,
+	kind: 'read:federation',
 
 	limit: {
 		duration: ms('1hour'),
diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts
index 6cdd617561..8ab16880fa 100644
--- a/packages/backend/src/server/api/endpoints/ap/show.ts
+++ b/packages/backend/src/server/api/endpoints/ap/show.ts
@@ -25,6 +25,7 @@ export const meta = {
 	tags: ['federation'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	limit: {
 		duration: ms('1minute'),
diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts
index c0aa882088..e6198ff601 100644
--- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts
+++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts
@@ -11,7 +11,7 @@ import { GetterService } from '@/server/api/GetterService.js';
 export const meta = {
 	tags: ['federation'],
 
-	requireCredential: true,
+	requireCredential: false,
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/fetch-external-resources.ts b/packages/backend/src/server/api/endpoints/fetch-external-resources.ts
index 6391a2f580..cbe579eb6b 100644
--- a/packages/backend/src/server/api/endpoints/fetch-external-resources.ts
+++ b/packages/backend/src/server/api/endpoints/fetch-external-resources.ts
@@ -14,6 +14,7 @@ export const meta = {
 	tags: ['meta'],
 
 	requireCredential: true,
+	secure: true,
 
 	limit: {
 		duration: ms('1hour'),
diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts
index c0530bf392..c24e049180 100644
--- a/packages/backend/src/server/api/endpoints/i.ts
+++ b/packages/backend/src/server/api/endpoints/i.ts
@@ -14,6 +14,7 @@ export const meta = {
 	tags: ['account'],
 
 	requireCredential: true,
+	kind: "read:account",
 
 	res: {
 		type: 'object',
diff --git a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts
index 303c0a7f87..3580d6ba1b 100644
--- a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts
+++ b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts
@@ -11,6 +11,7 @@ import { MetaService } from '@/core/MetaService.js';
 export const meta = {
 	requireCredential: true,
 	prohibitMoved: true,
+	kind: 'write:account',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts
index bd6e85a074..79a81cb73f 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts
@@ -9,6 +9,7 @@ import { RegistryApiService } from '@/core/RegistryApiService.js';
 
 export const meta = {
 	requireCredential: true,
+	kind: 'read:account',
 
 	res: {
 		type: 'object',
diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts
index 2352beb130..d9b26cab2c 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts
@@ -10,6 +10,7 @@ import { ApiError } from '../../../error.js';
 
 export const meta = {
 	requireCredential: true,
+	kind: 'read:account',
 
 	errors: {
 		noSuchKey: {
diff --git a/packages/backend/src/server/api/endpoints/i/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts
index 4155a43e0d..c373410256 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/get.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts
@@ -10,6 +10,7 @@ import { ApiError } from '../../../error.js';
 
 export const meta = {
 	requireCredential: true,
+	kind: 'read:account',
 
 	errors: {
 		noSuchKey: {
diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts
index b411cdd3d9..a91dcd9543 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts
@@ -9,6 +9,7 @@ import { RegistryApiService } from '@/core/RegistryApiService.js';
 
 export const meta = {
 	requireCredential: true,
+	kind: 'read:account',
 
 	res: {
 		type: 'object',
diff --git a/packages/backend/src/server/api/endpoints/i/registry/keys.ts b/packages/backend/src/server/api/endpoints/i/registry/keys.ts
index 04e120d752..ad203d5203 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/keys.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/keys.ts
@@ -9,6 +9,7 @@ import { RegistryApiService } from '@/core/RegistryApiService.js';
 
 export const meta = {
 	requireCredential: true,
+	kind: 'read:account',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/registry/remove.ts b/packages/backend/src/server/api/endpoints/i/registry/remove.ts
index ba8100b547..9cbe271b91 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/remove.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/remove.ts
@@ -12,6 +12,7 @@ import { ApiError } from '../../../error.js';
 
 export const meta = {
 	requireCredential: true,
+	kind: 'write:account',
 
 	errors: {
 		noSuchKey: {
diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts
index 58bb450bce..c61d5b8727 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/set.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts
@@ -9,6 +9,7 @@ import { RegistryApiService } from '@/core/RegistryApiService.js';
 
 export const meta = {
 	requireCredential: true,
+	kind: 'write:account',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/invite/create.ts b/packages/backend/src/server/api/endpoints/invite/create.ts
index d82fa50e4f..4f37f2f4bb 100644
--- a/packages/backend/src/server/api/endpoints/invite/create.ts
+++ b/packages/backend/src/server/api/endpoints/invite/create.ts
@@ -19,6 +19,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireRolePolicy: 'canInvite',
+	kind: 'write:invite-codes',
 
 	errors: {
 		exceededCreateLimit: {
diff --git a/packages/backend/src/server/api/endpoints/invite/delete.ts b/packages/backend/src/server/api/endpoints/invite/delete.ts
index 3b57775739..d84430a49f 100644
--- a/packages/backend/src/server/api/endpoints/invite/delete.ts
+++ b/packages/backend/src/server/api/endpoints/invite/delete.ts
@@ -15,6 +15,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireRolePolicy: 'canInvite',
+	kind: 'write:invite-codes',
 
 	errors: {
 		noSuchCode: {
diff --git a/packages/backend/src/server/api/endpoints/invite/limit.ts b/packages/backend/src/server/api/endpoints/invite/limit.ts
index 1f4190c948..fc3bb9bdc2 100644
--- a/packages/backend/src/server/api/endpoints/invite/limit.ts
+++ b/packages/backend/src/server/api/endpoints/invite/limit.ts
@@ -16,6 +16,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireRolePolicy: 'canInvite',
+	kind: 'read:invite-codes',
 
 	res: {
 		type: 'object',
diff --git a/packages/backend/src/server/api/endpoints/invite/list.ts b/packages/backend/src/server/api/endpoints/invite/list.ts
index 2107516ce4..6734f27e14 100644
--- a/packages/backend/src/server/api/endpoints/invite/list.ts
+++ b/packages/backend/src/server/api/endpoints/invite/list.ts
@@ -15,6 +15,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireRolePolicy: 'canInvite',
+	kind: 'read:invite-codes',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/my/apps.ts b/packages/backend/src/server/api/endpoints/my/apps.ts
index 98c317346f..1b70b85b07 100644
--- a/packages/backend/src/server/api/endpoints/my/apps.ts
+++ b/packages/backend/src/server/api/endpoints/my/apps.ts
@@ -13,6 +13,7 @@ export const meta = {
 	tags: ['account', 'app'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
index 48b8a3f4b6..13cfb31ad0 100644
--- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts
@@ -25,6 +25,7 @@ export const meta = {
 	tags: ['notes'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts
index 6fab024d17..2317f8f7b2 100644
--- a/packages/backend/src/server/api/endpoints/notes/mentions.ts
+++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts
@@ -16,6 +16,7 @@ export const meta = {
 	tags: ['notes'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
index af7ff8bdcd..90af29a695 100644
--- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
+++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
@@ -14,6 +14,7 @@ export const meta = {
 	tags: ['notes'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts
index b5fd47723c..20faea566d 100644
--- a/packages/backend/src/server/api/endpoints/notes/state.ts
+++ b/packages/backend/src/server/api/endpoints/notes/state.ts
@@ -12,6 +12,7 @@ export const meta = {
 	tags: ['notes'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	res: {
 		type: 'object',
diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts
index 8e2de0cd37..3dcebe7e29 100644
--- a/packages/backend/src/server/api/endpoints/notes/timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts
@@ -22,6 +22,7 @@ export const meta = {
 	tags: ['notes'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts
index d46bd69795..698c37b616 100644
--- a/packages/backend/src/server/api/endpoints/notes/translate.ts
+++ b/packages/backend/src/server/api/endpoints/notes/translate.ts
@@ -17,6 +17,7 @@ export const meta = {
 	tags: ['notes'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	res: {
 		type: 'object',
diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
index 10d3a7a697..71c2b8054e 100644
--- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
+++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts
@@ -22,6 +22,7 @@ export const meta = {
 	tags: ['notes', 'lists'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts
index 7d07c92178..f427939a7a 100644
--- a/packages/backend/src/server/api/endpoints/promo/read.ts
+++ b/packages/backend/src/server/api/endpoints/promo/read.ts
@@ -15,6 +15,7 @@ export const meta = {
 	tags: ['notes'],
 
 	requireCredential: true,
+	kind: 'write:account',
 
 	errors: {
 		noSuchNote: {
diff --git a/packages/backend/src/server/api/endpoints/roles/list.ts b/packages/backend/src/server/api/endpoints/roles/list.ts
index dc2be8e11d..d40e937d4e 100644
--- a/packages/backend/src/server/api/endpoints/roles/list.ts
+++ b/packages/backend/src/server/api/endpoints/roles/list.ts
@@ -13,6 +13,7 @@ export const meta = {
 	tags: ['role'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	res: {
 		type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts
index 7010df22c9..4ce3fc8908 100644
--- a/packages/backend/src/server/api/endpoints/roles/notes.ts
+++ b/packages/backend/src/server/api/endpoints/roles/notes.ts
@@ -18,6 +18,7 @@ export const meta = {
 	tags: ['role', 'notes'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	errors: {
 		noSuchRole: {
diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts
index 9ab062326d..bb50048d94 100644
--- a/packages/backend/src/server/api/endpoints/sw/register.ts
+++ b/packages/backend/src/server/api/endpoints/sw/register.ts
@@ -14,6 +14,7 @@ export const meta = {
 	tags: ['account'],
 
 	requireCredential: true,
+	secure: true,
 
 	description: 'Register to receive push notifications.',
 
diff --git a/packages/backend/src/server/api/endpoints/sw/show-registration.ts b/packages/backend/src/server/api/endpoints/sw/show-registration.ts
index 126299e3f7..15d3df8587 100644
--- a/packages/backend/src/server/api/endpoints/sw/show-registration.ts
+++ b/packages/backend/src/server/api/endpoints/sw/show-registration.ts
@@ -12,6 +12,7 @@ export const meta = {
 	tags: ['account'],
 
 	requireCredential: true,
+	secure: true,
 
 	description: 'Check push notification registration exists.',
 
diff --git a/packages/backend/src/server/api/endpoints/sw/update-registration.ts b/packages/backend/src/server/api/endpoints/sw/update-registration.ts
index a1a97df0be..7bf59784a2 100644
--- a/packages/backend/src/server/api/endpoints/sw/update-registration.ts
+++ b/packages/backend/src/server/api/endpoints/sw/update-registration.ts
@@ -13,6 +13,7 @@ export const meta = {
 	tags: ['account'],
 
 	requireCredential: true,
+	secure: true,
 
 	description: 'Update push notification registration.',
 
diff --git a/packages/backend/src/server/api/endpoints/users/achievements.ts b/packages/backend/src/server/api/endpoints/users/achievements.ts
index d6ad718dfa..3a584a819a 100644
--- a/packages/backend/src/server/api/endpoints/users/achievements.ts
+++ b/packages/backend/src/server/api/endpoints/users/achievements.ts
@@ -9,7 +9,7 @@ import type { UserProfilesRepository } from '@/models/_.js';
 import { DI } from '@/di-symbols.js';
 
 export const meta = {
-	requireCredential: true,
+	requireCredential: false,
 
 	res: {
 		type: 'array',
@@ -24,7 +24,7 @@ export const meta = {
 				},
 			},
 		},
-	}
+	},
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
index 4eb37c3e43..fa2e3338b8 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts
@@ -18,6 +18,7 @@ import { UserListService } from '@/core/UserListService.js';
 export const meta = {
 	requireCredential: true,
 	prohibitMoved: true,
+	kind: 'write:account',
 	res: {
 		type: 'object',
 		optional: false, nullable: false,
diff --git a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts
index 2ecf0a1256..864cdc2ee0 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/favorite.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/favorite.ts
@@ -12,6 +12,7 @@ import { DI } from '@/di-symbols.js';
 
 export const meta = {
 	requireCredential: true,
+	kind: 'write:account',
 	errors: {
 		noSuchList: {
 			message: 'No such user list.',
diff --git a/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts b/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts
index 23611ab8c4..d51d57343e 100644
--- a/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts
+++ b/packages/backend/src/server/api/endpoints/users/lists/unfavorite.ts
@@ -11,6 +11,7 @@ import { DI } from '@/di-symbols.js';
 
 export const meta = {
 	requireCredential: true,
+	kind: 'write:account',
 	errors: {
 		noSuchList: {
 			message: 'No such user list.',
diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts
index 326042ed3d..26b61c9fb2 100644
--- a/packages/backend/src/server/api/endpoints/users/relation.ts
+++ b/packages/backend/src/server/api/endpoints/users/relation.ts
@@ -11,6 +11,7 @@ export const meta = {
 	tags: ['users'],
 
 	requireCredential: true,
+	kind: 'read:account',
 
 	description: 'Show the different kinds of relations between the authenticated user and the specified user(s).',
 
diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts
index 649802744f..bdaf78758b 100644
--- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts
+++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts
@@ -20,6 +20,7 @@ export const meta = {
 	tags: ['users'],
 
 	requireCredential: true,
+	kind: 'write:report-abuse',
 
 	description: 'File a report.',
 
diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts
index f9f2f15aff..3fc3f4d31a 100644
--- a/packages/backend/src/server/api/stream/ChannelsService.ts
+++ b/packages/backend/src/server/api/stream/ChannelsService.ts
@@ -20,6 +20,7 @@ import { AntennaChannelService } from './channels/antenna.js';
 import { DriveChannelService } from './channels/drive.js';
 import { HashtagChannelService } from './channels/hashtag.js';
 import { RoleTimelineChannelService } from './channels/role-timeline.js';
+import { type MiChannelService } from './channel.js';
 
 @Injectable()
 export class ChannelsService {
@@ -43,7 +44,7 @@ export class ChannelsService {
 	}
 
 	@bindThis
-	public getChannelService(name: string) {
+	public getChannelService(name: string): MiChannelService<boolean> {
 		switch (name) {
 			case 'main': return this.mainChannelService;
 			case 'homeTimeline': return this.homeTimelineChannelService;
diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts
index 4180ccc56a..a89fbcc5e5 100644
--- a/packages/backend/src/server/api/stream/Connection.ts
+++ b/packages/backend/src/server/api/stream/Connection.ts
@@ -248,6 +248,11 @@ export default class Connection {
 			return;
 		}
 
+		if (this.token && ((channelService.kind && !this.token.permission.some(p => p === channelService.kind))
+			|| (!channelService.kind && channelService.requireCredential))) {
+			return;
+		}
+
 		// 共有可能チャンネルに接続しようとしていて、かつそのチャンネルに既に接続していたら無意味なので無視
 		if (channelService.shouldShare && this.channels.some(c => c.chName === channel)) {
 			return;
diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts
index 46b0709773..80df3803eb 100644
--- a/packages/backend/src/server/api/stream/channel.ts
+++ b/packages/backend/src/server/api/stream/channel.ts
@@ -16,6 +16,7 @@ export default abstract class Channel {
 	public abstract readonly chName: string;
 	public static readonly shouldShare: boolean;
 	public static readonly requireCredential: boolean;
+	public static readonly kind?: string | null;
 
 	protected get user() {
 		return this.connection.user;
@@ -76,3 +77,10 @@ export default abstract class Channel {
 
 	public onMessage?(type: string, body: any): void;
 }
+
+export type MiChannelService<T extends boolean> = {
+	shouldShare: boolean;
+	requireCredential: T;
+	kind: T extends true ? string : string | null | undefined;
+	create: (id: string, connection: Connection) => Channel;
+}
diff --git a/packages/backend/src/server/api/stream/channels/admin.ts b/packages/backend/src/server/api/stream/channels/admin.ts
index bfb36d9cb8..b8f369ce84 100644
--- a/packages/backend/src/server/api/stream/channels/admin.ts
+++ b/packages/backend/src/server/api/stream/channels/admin.ts
@@ -5,12 +5,13 @@
 
 import { Injectable } from '@nestjs/common';
 import { bindThis } from '@/decorators.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class AdminChannel extends Channel {
 	public readonly chName = 'admin';
 	public static shouldShare = true;
-	public static requireCredential = true;
+	public static requireCredential = true as const;
+	public static kind = 'read:admin:stream';
 
 	@bindThis
 	public async init(params: any) {
@@ -22,9 +23,10 @@ class AdminChannel extends Channel {
 }
 
 @Injectable()
-export class AdminChannelService {
+export class AdminChannelService implements MiChannelService<true> {
 	public readonly shouldShare = AdminChannel.shouldShare;
 	public readonly requireCredential = AdminChannel.requireCredential;
+	public readonly kind = AdminChannel.kind;
 
 	constructor(
 	) {
diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts
index a48e6ba5c6..200db8eb0e 100644
--- a/packages/backend/src/server/api/stream/channels/antenna.ts
+++ b/packages/backend/src/server/api/stream/channels/antenna.ts
@@ -8,12 +8,13 @@ import { isUserRelated } from '@/misc/is-user-related.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import type { GlobalEvents } from '@/core/GlobalEventService.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class AntennaChannel extends Channel {
 	public readonly chName = 'antenna';
 	public static shouldShare = false;
-	public static requireCredential = false;
+	public static requireCredential = true as const;
+	public static kind = 'read:account';
 	private antennaId: string;
 
 	constructor(
@@ -62,9 +63,10 @@ class AntennaChannel extends Channel {
 }
 
 @Injectable()
-export class AntennaChannelService {
+export class AntennaChannelService implements MiChannelService<true> {
 	public readonly shouldShare = AntennaChannel.shouldShare;
 	public readonly requireCredential = AntennaChannel.requireCredential;
+	public readonly kind = AntennaChannel.kind;
 
 	constructor(
 		private noteEntityService: NoteEntityService,
diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts
index 57034231a3..20275249b8 100644
--- a/packages/backend/src/server/api/stream/channels/channel.ts
+++ b/packages/backend/src/server/api/stream/channels/channel.ts
@@ -8,12 +8,12 @@ import { isUserRelated } from '@/misc/is-user-related.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class ChannelChannel extends Channel {
 	public readonly chName = 'channel';
 	public static shouldShare = false;
-	public static requireCredential = false;
+	public static requireCredential = false as const;
 	private channelId: string;
 
 	constructor(
@@ -65,9 +65,10 @@ class ChannelChannel extends Channel {
 }
 
 @Injectable()
-export class ChannelChannelService {
+export class ChannelChannelService implements MiChannelService<false> {
 	public readonly shouldShare = ChannelChannel.shouldShare;
 	public readonly requireCredential = ChannelChannel.requireCredential;
+	public readonly kind = ChannelChannel.kind;
 
 	constructor(
 		private noteEntityService: NoteEntityService,
diff --git a/packages/backend/src/server/api/stream/channels/drive.ts b/packages/backend/src/server/api/stream/channels/drive.ts
index 83f53c1836..4bf34a72c9 100644
--- a/packages/backend/src/server/api/stream/channels/drive.ts
+++ b/packages/backend/src/server/api/stream/channels/drive.ts
@@ -5,12 +5,13 @@
 
 import { Injectable } from '@nestjs/common';
 import { bindThis } from '@/decorators.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class DriveChannel extends Channel {
 	public readonly chName = 'drive';
 	public static shouldShare = true;
-	public static requireCredential = true;
+	public static requireCredential = true as const;
+	public static kind = 'read:account';
 
 	@bindThis
 	public async init(params: any) {
@@ -22,9 +23,10 @@ class DriveChannel extends Channel {
 }
 
 @Injectable()
-export class DriveChannelService {
+export class DriveChannelService implements MiChannelService<true> {
 	public readonly shouldShare = DriveChannel.shouldShare;
 	public readonly requireCredential = DriveChannel.requireCredential;
+	public readonly kind = DriveChannel.kind;
 
 	constructor(
 	) {
diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts
index f64a13bcc5..e05e380aae 100644
--- a/packages/backend/src/server/api/stream/channels/global-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts
@@ -12,12 +12,12 @@ import { MetaService } from '@/core/MetaService.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class GlobalTimelineChannel extends Channel {
 	public readonly chName = 'globalTimeline';
 	public static shouldShare = false;
-	public static requireCredential = false;
+	public static requireCredential = false as const;
 	private withRenotes: boolean;
 	private withFiles: boolean;
 	private withBots: boolean;
@@ -96,9 +96,10 @@ class GlobalTimelineChannel extends Channel {
 }
 
 @Injectable()
-export class GlobalTimelineChannelService {
+export class GlobalTimelineChannelService implements MiChannelService<false> {
 	public readonly shouldShare = GlobalTimelineChannel.shouldShare;
 	public readonly requireCredential = GlobalTimelineChannel.requireCredential;
+	public readonly kind = GlobalTimelineChannel.kind;
 
 	constructor(
 		private metaService: MetaService,
diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts
index f30b29cfd6..3d4f2fc528 100644
--- a/packages/backend/src/server/api/stream/channels/hashtag.ts
+++ b/packages/backend/src/server/api/stream/channels/hashtag.ts
@@ -9,12 +9,12 @@ import { isUserRelated } from '@/misc/is-user-related.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class HashtagChannel extends Channel {
 	public readonly chName = 'hashtag';
 	public static shouldShare = false;
-	public static requireCredential = false;
+	public static requireCredential = false as const;
 	private q: string[][];
 
 	constructor(
@@ -70,9 +70,10 @@ class HashtagChannel extends Channel {
 }
 
 @Injectable()
-export class HashtagChannelService {
+export class HashtagChannelService implements MiChannelService<false> {
 	public readonly shouldShare = HashtagChannel.shouldShare;
 	public readonly requireCredential = HashtagChannel.requireCredential;
+	public readonly kind = HashtagChannel.kind;
 
 	constructor(
 		private noteEntityService: NoteEntityService,
diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts
index 534973f834..3b499b7bf3 100644
--- a/packages/backend/src/server/api/stream/channels/home-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts
@@ -10,12 +10,13 @@ import { isInstanceMuted } from '@/misc/is-instance-muted.js';
 import type { Packed } from '@/misc/json-schema.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class HomeTimelineChannel extends Channel {
 	public readonly chName = 'homeTimeline';
 	public static shouldShare = false;
-	public static requireCredential = true;
+	public static requireCredential = true as const;
+	public static kind = 'read:account';
 	private withRenotes: boolean;
 	private withFiles: boolean;
 
@@ -101,9 +102,10 @@ class HomeTimelineChannel extends Channel {
 }
 
 @Injectable()
-export class HomeTimelineChannelService {
+export class HomeTimelineChannelService implements MiChannelService<true> {
 	public readonly shouldShare = HomeTimelineChannel.shouldShare;
 	public readonly requireCredential = HomeTimelineChannel.requireCredential;
+	public readonly kind = HomeTimelineChannel.kind;
 
 	constructor(
 		private noteEntityService: NoteEntityService,
diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
index 746c661d31..26cbbebe83 100644
--- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts
@@ -12,12 +12,13 @@ import { MetaService } from '@/core/MetaService.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class HybridTimelineChannel extends Channel {
 	public readonly chName = 'hybridTimeline';
 	public static shouldShare = false;
-	public static requireCredential = true;
+	public static requireCredential = true as const;
+	public static kind = 'read:account';
 	private withRenotes: boolean;
 	private withReplies: boolean;
 	private withBots: boolean;
@@ -119,9 +120,10 @@ class HybridTimelineChannel extends Channel {
 }
 
 @Injectable()
-export class HybridTimelineChannelService {
+export class HybridTimelineChannelService implements MiChannelService<true> {
 	public readonly shouldShare = HybridTimelineChannel.shouldShare;
 	public readonly requireCredential = HybridTimelineChannel.requireCredential;
+	public readonly kind = HybridTimelineChannel.kind;
 
 	constructor(
 		private metaService: MetaService,
diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts
index 419159197a..40342b6c7b 100644
--- a/packages/backend/src/server/api/stream/channels/local-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts
@@ -11,12 +11,12 @@ import { MetaService } from '@/core/MetaService.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class LocalTimelineChannel extends Channel {
 	public readonly chName = 'localTimeline';
 	public static shouldShare = false;
-	public static requireCredential = false;
+	public static requireCredential = false as const;
 	private withRenotes: boolean;
 	private withReplies: boolean;
 	private withBots: boolean;
@@ -95,9 +95,10 @@ class LocalTimelineChannel extends Channel {
 }
 
 @Injectable()
-export class LocalTimelineChannelService {
+export class LocalTimelineChannelService implements MiChannelService<false> {
 	public readonly shouldShare = LocalTimelineChannel.shouldShare;
 	public readonly requireCredential = LocalTimelineChannel.requireCredential;
+	public readonly kind = LocalTimelineChannel.kind;
 
 	constructor(
 		private metaService: MetaService,
diff --git a/packages/backend/src/server/api/stream/channels/main.ts b/packages/backend/src/server/api/stream/channels/main.ts
index f969d02337..ab605e3ec5 100644
--- a/packages/backend/src/server/api/stream/channels/main.ts
+++ b/packages/backend/src/server/api/stream/channels/main.ts
@@ -7,12 +7,13 @@ import { Injectable } from '@nestjs/common';
 import { isInstanceMuted, isUserFromMutedInstance } from '@/misc/is-instance-muted.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class MainChannel extends Channel {
 	public readonly chName = 'main';
 	public static shouldShare = true;
-	public static requireCredential = true;
+	public static requireCredential = true as const;
+	public static kind = 'read:account';
 
 	constructor(
 		private noteEntityService: NoteEntityService,
@@ -63,9 +64,10 @@ class MainChannel extends Channel {
 }
 
 @Injectable()
-export class MainChannelService {
+export class MainChannelService implements MiChannelService<true> {
 	public readonly shouldShare = MainChannel.shouldShare;
 	public readonly requireCredential = MainChannel.requireCredential;
+	public readonly kind = MainChannel.kind;
 
 	constructor(
 		private noteEntityService: NoteEntityService,
diff --git a/packages/backend/src/server/api/stream/channels/queue-stats.ts b/packages/backend/src/server/api/stream/channels/queue-stats.ts
index f0dc472303..5ceb2c3bbc 100644
--- a/packages/backend/src/server/api/stream/channels/queue-stats.ts
+++ b/packages/backend/src/server/api/stream/channels/queue-stats.ts
@@ -6,14 +6,14 @@
 import Xev from 'xev';
 import { Injectable } from '@nestjs/common';
 import { bindThis } from '@/decorators.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 const ev = new Xev();
 
 class QueueStatsChannel extends Channel {
 	public readonly chName = 'queueStats';
 	public static shouldShare = true;
-	public static requireCredential = false;
+	public static requireCredential = false as const;
 
 	constructor(id: string, connection: Channel['connection']) {
 		super(id, connection);
@@ -53,9 +53,10 @@ class QueueStatsChannel extends Channel {
 }
 
 @Injectable()
-export class QueueStatsChannelService {
+export class QueueStatsChannelService implements MiChannelService<false> {
 	public readonly shouldShare = QueueStatsChannel.shouldShare;
 	public readonly requireCredential = QueueStatsChannel.requireCredential;
+	public readonly kind = QueueStatsChannel.kind;
 
 	constructor(
 	) {
diff --git a/packages/backend/src/server/api/stream/channels/role-timeline.ts b/packages/backend/src/server/api/stream/channels/role-timeline.ts
index 38d3604cc5..b3bbb77dbf 100644
--- a/packages/backend/src/server/api/stream/channels/role-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/role-timeline.ts
@@ -10,12 +10,12 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
 import type { GlobalEvents } from '@/core/GlobalEventService.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class RoleTimelineChannel extends Channel {
 	public readonly chName = 'roleTimeline';
 	public static shouldShare = false;
-	public static requireCredential = false;
+	public static requireCredential = false as const;
 	private roleId: string;
 
 	constructor(
@@ -67,9 +67,10 @@ class RoleTimelineChannel extends Channel {
 }
 
 @Injectable()
-export class RoleTimelineChannelService {
+export class RoleTimelineChannelService implements MiChannelService<false> {
 	public readonly shouldShare = RoleTimelineChannel.shouldShare;
 	public readonly requireCredential = RoleTimelineChannel.requireCredential;
+	public readonly kind = RoleTimelineChannel.kind;
 
 	constructor(
 		private noteEntityService: NoteEntityService,
diff --git a/packages/backend/src/server/api/stream/channels/server-stats.ts b/packages/backend/src/server/api/stream/channels/server-stats.ts
index cacae275a8..615b6946cc 100644
--- a/packages/backend/src/server/api/stream/channels/server-stats.ts
+++ b/packages/backend/src/server/api/stream/channels/server-stats.ts
@@ -6,14 +6,14 @@
 import Xev from 'xev';
 import { Injectable } from '@nestjs/common';
 import { bindThis } from '@/decorators.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 const ev = new Xev();
 
 class ServerStatsChannel extends Channel {
 	public readonly chName = 'serverStats';
 	public static shouldShare = true;
-	public static requireCredential = false;
+	public static requireCredential = false as const;
 
 	constructor(id: string, connection: Channel['connection']) {
 		super(id, connection);
@@ -53,9 +53,10 @@ class ServerStatsChannel extends Channel {
 }
 
 @Injectable()
-export class ServerStatsChannelService {
+export class ServerStatsChannelService implements MiChannelService<false> {
 	public readonly shouldShare = ServerStatsChannel.shouldShare;
 	public readonly requireCredential = ServerStatsChannel.requireCredential;
+	public readonly kind = ServerStatsChannel.kind;
 
 	constructor(
 	) {
diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts
index fe293e2b4d..909b5a5e03 100644
--- a/packages/backend/src/server/api/stream/channels/user-list.ts
+++ b/packages/backend/src/server/api/stream/channels/user-list.ts
@@ -11,12 +11,12 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { DI } from '@/di-symbols.js';
 import { bindThis } from '@/decorators.js';
 import { isInstanceMuted } from '@/misc/is-instance-muted.js';
-import Channel from '../channel.js';
+import Channel, { type MiChannelService } from '../channel.js';
 
 class UserListChannel extends Channel {
 	public readonly chName = 'userList';
 	public static shouldShare = false;
-	public static requireCredential = false;
+	public static requireCredential = false as const;
 	private listId: string;
 	private membershipsMap: Record<string, Pick<MiUserListMembership, 'withReplies'> | undefined> = {};
 	private listUsersClock: NodeJS.Timeout;
@@ -137,9 +137,10 @@ class UserListChannel extends Channel {
 }
 
 @Injectable()
-export class UserListChannelService {
+export class UserListChannelService implements MiChannelService<false> {
 	public readonly shouldShare = UserListChannel.shouldShare;
 	public readonly requireCredential = UserListChannel.requireCredential;
+	public readonly kind = UserListChannel.kind;
 
 	constructor(
 		@Inject(DI.userListsRepository)
diff --git a/packages/backend/test/e2e/api.ts b/packages/backend/test/e2e/api.ts
index 15da74931d..cf24228b83 100644
--- a/packages/backend/test/e2e/api.ts
+++ b/packages/backend/test/e2e/api.ts
@@ -7,7 +7,7 @@ process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
 import { IncomingMessage } from 'http';
-import { signup, api, startServer, successfulApiCall, failedApiCall, uploadFile, waitFire, connectStream, relativeFetch } from '../utils.js';
+import { signup, api, startServer, successfulApiCall, failedApiCall, uploadFile, waitFire, connectStream, relativeFetch, createAppToken } from '../utils.js';
 import type { INestApplicationContext } from '@nestjs/common';
 import type * as misskey from 'misskey-js';
 
@@ -89,6 +89,11 @@ describe('API', () => {
 	});
 
 	test('管理者専用のAPIのアクセス制限', async () => {
+		const application = await createAppToken(alice, ['read:account']);
+		const application2 = await createAppToken(alice, ['read:admin:index-stats']);
+		const application3 = await createAppToken(bob, []);
+		const application4 = await createAppToken(bob, ['read:admin:index-stats']);
+
 		// aliceは管理者、APIを使える
 		await successfulApiCall({
 			endpoint: '/admin/get-index-stats',
@@ -128,6 +133,42 @@ describe('API', () => {
 			code: 'AUTHENTICATION_FAILED',
 			id: 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14',
 		});
+
+		await successfulApiCall({
+			endpoint: '/admin/get-index-stats',
+			parameters: {},
+			user: { token: application2 },
+		});
+
+		await failedApiCall({
+			endpoint: '/admin/get-index-stats',
+			parameters: {},
+			user: { token: application },
+		}, {
+			status: 403,
+			code: 'PERMISSION_DENIED',
+			id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838',
+		});
+
+		await failedApiCall({
+			endpoint: '/admin/get-index-stats',
+			parameters: {},
+			user: { token: application3 },
+		}, {
+			status: 403,
+			code: 'ROLE_PERMISSION_DENIED',
+			id: 'c3d38592-54c0-429d-be96-5636b0431a61',
+		});
+
+		await failedApiCall({
+			endpoint: '/admin/get-index-stats',
+			parameters: {},
+			user: { token: application4 },
+		}, {
+			status: 403,
+			code: 'ROLE_PERMISSION_DENIED',
+			id: 'c3d38592-54c0-429d-be96-5636b0431a61',
+		});
 	});
 
 	describe('Authentication header', () => {
diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
index c4824f50ce..288c54bdbc 100644
--- a/packages/backend/test/e2e/streaming.ts
+++ b/packages/backend/test/e2e/streaming.ts
@@ -6,8 +6,9 @@
 process.env.NODE_ENV = 'test';
 
 import * as assert from 'assert';
+import { WebSocket } from 'ws';
 import { MiFollowing } from '@/models/Following.js';
-import { signup, api, post, startServer, initTestDb, waitFire } from '../utils.js';
+import { signup, api, post, startServer, initTestDb, waitFire, createAppToken, port } from '../utils.js';
 import type { INestApplicationContext } from '@nestjs/common';
 import type * as misskey from 'misskey-js';
 
@@ -560,6 +561,28 @@ describe('Streaming', () => {
 			});
 		});
 
+		test('Authentication', async () => {
+			const application = await createAppToken(ayano, []);
+			const application2 = await createAppToken(ayano, ['read:account']);
+			const socket = new WebSocket(`ws://127.0.0.1:${port}/streaming?i=${application}`);
+			const established = await new Promise<boolean>((resolve, reject) => {
+				socket.on('error', () => resolve(false));
+				socket.on('unexpected-response', () => resolve(false));
+				setTimeout(() => resolve(true), 3000);
+			});
+
+			socket.close();
+			assert.strictEqual(established, false);
+
+			const fired = await waitFire(
+				{ token: application2 }, 'hybridTimeline',
+				() => api('notes/create', { text: 'Hello, world!' }, ayano),
+				msg => msg.type === 'note' && msg.body.userId === ayano.id,
+			);
+
+			assert.strictEqual(fired, true);
+		});
+
 		// XXX: QueryFailedError: duplicate key value violates unique constraint "IDX_347fec870eafea7b26c8a73bac"
 		/*
 		describe('Hashtag Timeline', () => {
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index 97118d73c0..db7629d2c4 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -6,6 +6,7 @@
 import * as assert from 'node:assert';
 import { readFile } from 'node:fs/promises';
 import { isAbsolute, basename } from 'node:path';
+import { randomUUID } from 'node:crypto';
 import { inspect } from 'node:util';
 import WebSocket, { ClientOptions } from 'ws';
 import fetch, { File, RequestInit } from 'node-fetch';
@@ -126,6 +127,15 @@ export const post = async (user: UserToken, params?: misskey.Endpoints['notes/cr
 	return res.body ? res.body.createdNote : null;
 };
 
+export const createAppToken = async (user: UserToken, permissions: (typeof misskey.permissions)[number][]) => {
+	const res = await api('miauth/gen-token', {
+		session: randomUUID(),
+		permission: permissions,
+	}, user);
+
+	return (res.body as misskey.entities.MiauthGenTokenResponse).token;
+};
+
 // 非公開ノートをAPI越しに見たときのノート NoteEntityService.ts
 export const hiddenNote = (note: any): any => {
 	const temp = {
diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue
index f5fa86a908..8e8e26ed5f 100644
--- a/packages/frontend/src/components/MkTokenGenerateWindow.vue
+++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue
@@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
 			</div>
 			<div class="_gaps_s">
-				<MkSwitch v-for="kind in (initialPermissions || Misskey.permissions)" :key="kind" v-model="permissions[kind]">{{ i18n.t(`_permissions.${kind}`) }}</MkSwitch>
+				<MkSwitch v-for="kind in Object.keys(permissions)" :key="kind" v-model="permissions[kind]">{{ i18n.t(`_permissions.${kind}`) }}</MkSwitch>
 			</div>
 		</div>
 	</MkSpacer>
@@ -54,7 +54,7 @@ const props = withDefaults(defineProps<{
 	title?: string | null;
 	information?: string | null;
 	initialName?: string | null;
-	initialPermissions?: string[] | null;
+	initialPermissions?: (typeof Misskey.permissions)[number][] | null;
 }>(), {
 	title: null,
 	information: null,
@@ -67,16 +67,17 @@ const emit = defineEmits<{
 	(ev: 'done', result: { name: string | null, permissions: string[] }): void;
 }>();
 
+const defaultPermissions = Misskey.permissions.filter(p => !p.startsWith('read:admin') && !p.startsWith('write:admin'));
 const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
 const name = ref(props.initialName);
-const permissions = ref({});
+const permissions = ref(<Record<(typeof Misskey.permissions)[number], boolean>>{});
 
 if (props.initialPermissions) {
 	for (const kind of props.initialPermissions) {
 		permissions.value[kind] = true;
 	}
 } else {
-	for (const kind of Misskey.permissions) {
+	for (const kind of defaultPermissions) {
 		permissions.value[kind] = false;
 	}
 }
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 7f4094845a..758beaf3a0 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0
- * generatedAt: 2023-12-25T03:48:32.008Z
+ * generatedAt: 2023-12-26T23:35:09.494Z
  */
 
 import type { SwitchCaseResponseType } from '../api.js';
@@ -11,7 +11,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:meta*
      */
     request<E extends 'admin/meta', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -22,7 +22,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-user-reports*
      */
     request<E extends 'admin/abuse-user-reports', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -33,7 +33,8 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *No* / **Permission**: *write:admin*
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *No*
      */
     request<E extends 'admin/accounts/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -44,7 +45,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:account*
      */
     request<E extends 'admin/accounts/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -55,7 +56,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:account*
      */
     request<E extends 'admin/accounts/find-by-email', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -66,7 +67,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
     request<E extends 'admin/ad/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -77,7 +78,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
     request<E extends 'admin/ad/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -88,7 +89,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:ad*
      */
     request<E extends 'admin/ad/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -99,7 +100,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
     request<E extends 'admin/ad/update', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -110,7 +111,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
     request<E extends 'admin/announcements/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -121,7 +122,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
     request<E extends 'admin/announcements/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -132,7 +133,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:announcements*
      */
     request<E extends 'admin/announcements/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -143,7 +144,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
     request<E extends 'admin/announcements/update', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -154,7 +155,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
     request<E extends 'admin/avatar-decorations/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -165,7 +166,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
     request<E extends 'admin/avatar-decorations/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -176,7 +177,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:avatar-decorations*
      */
     request<E extends 'admin/avatar-decorations/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -187,7 +188,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
     request<E extends 'admin/avatar-decorations/update', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -198,7 +199,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:delete-all-files-of-a-user*
      */
     request<E extends 'admin/delete-all-files-of-a-user', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -209,7 +210,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-avatar*
      */
     request<E extends 'admin/unset-user-avatar', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -220,7 +221,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-banner*
      */
     request<E extends 'admin/unset-user-banner', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -231,7 +232,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
      */
     request<E extends 'admin/drive/clean-remote-files', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -242,7 +243,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
      */
     request<E extends 'admin/drive/cleanup', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -253,7 +254,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
      */
     request<E extends 'admin/drive/files', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -264,7 +265,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
      */
     request<E extends 'admin/drive/show-file', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -275,7 +276,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/add-aliases-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -286,7 +287,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/add', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -297,7 +298,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/copy', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -308,7 +309,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/delete-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -319,7 +320,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -330,7 +331,8 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
      */
     request<E extends 'admin/emoji/import-zip', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -341,7 +343,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
     request<E extends 'admin/emoji/list-remote', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -352,7 +354,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
     request<E extends 'admin/emoji/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -363,7 +365,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/remove-aliases-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -374,7 +376,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/set-aliases-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -385,7 +387,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/set-category-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -396,7 +398,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/set-license-bulk', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -407,7 +409,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/update', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -418,7 +420,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     request<E extends 'admin/federation/delete-all-files', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -429,7 +431,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     request<E extends 'admin/federation/refresh-remote-instance-metadata', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -440,7 +442,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     request<E extends 'admin/federation/remove-all-following', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -451,7 +453,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     request<E extends 'admin/federation/update-instance', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -462,7 +464,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:index-stats*
      */
     request<E extends 'admin/get-index-stats', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -473,7 +475,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:table-stats*
      */
     request<E extends 'admin/get-table-stats', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -484,7 +486,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:user-ips*
      */
     request<E extends 'admin/get-user-ips', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -495,7 +497,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:invite-codes*
      */
     request<E extends 'admin/invite/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -506,7 +508,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:invite-codes*
      */
     request<E extends 'admin/invite/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -517,7 +519,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:promo*
      */
     request<E extends 'admin/promo/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -528,7 +530,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
      */
     request<E extends 'admin/queue/clear', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -539,7 +541,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
      */
     request<E extends 'admin/queue/deliver-delayed', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -550,7 +552,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
      */
     request<E extends 'admin/queue/inbox-delayed', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -561,7 +563,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
      */
     request<E extends 'admin/queue/promote', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -572,7 +574,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
     request<E extends 'admin/queue/stats', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -583,7 +585,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
      */
     request<E extends 'admin/relays/add', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -594,7 +596,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:relays*
      */
     request<E extends 'admin/relays/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -605,7 +607,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
      */
     request<E extends 'admin/relays/remove', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -616,7 +618,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:reset-password*
      */
     request<E extends 'admin/reset-password', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -627,7 +629,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
      */
     request<E extends 'admin/resolve-abuse-user-report', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -638,7 +640,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:send-email*
      */
     request<E extends 'admin/send-email', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -649,7 +651,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:server-info*
      */
     request<E extends 'admin/server-info', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -660,7 +662,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:show-moderation-log*
      */
     request<E extends 'admin/show-moderation-logs', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -671,7 +673,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
      */
     request<E extends 'admin/show-user', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -682,7 +684,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
      */
     request<E extends 'admin/show-users', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -693,7 +695,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:suspend-user*
      */
     request<E extends 'admin/suspend-user', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -704,7 +706,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:unsuspend-user*
      */
     request<E extends 'admin/unsuspend-user', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -715,7 +717,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:meta*
      */
     request<E extends 'admin/update-meta', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -726,7 +728,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:delete-account*
      */
     request<E extends 'admin/delete-account', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -737,7 +739,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:user-note*
      */
     request<E extends 'admin/update-user-note', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -748,7 +750,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -759,7 +761,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -770,7 +772,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
      */
     request<E extends 'admin/roles/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -781,7 +783,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
      */
     request<E extends 'admin/roles/show', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -792,7 +794,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/update', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -803,7 +805,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/assign', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -814,7 +816,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/unassign', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -825,7 +827,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/update-default-policies', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -836,7 +838,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *No* / **Permission**: *read:admin*
+     * **Credential required**: *No* / **Permission**: *read:admin:roles*
      */
     request<E extends 'admin/roles/users', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -924,7 +926,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:federation*
      */
     request<E extends 'ap/get', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -935,7 +937,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'ap/show', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -1729,7 +1731,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *No*
      */
     request<E extends 'federation/update-remote-user', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2037,7 +2039,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2168,7 +2170,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/claim-achievement', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2469,7 +2471,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/registry/get-all', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2480,7 +2482,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/registry/get-detail', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2491,7 +2493,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/registry/get', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2502,7 +2504,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/registry/keys-with-type', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2513,7 +2515,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/registry/keys', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2524,7 +2526,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/registry/remove', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2547,7 +2549,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/registry/set', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2683,7 +2685,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
      */
     request<E extends 'invite/create', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2694,7 +2696,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
      */
     request<E extends 'invite/delete', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2705,7 +2707,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
      */
     request<E extends 'invite/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2716,7 +2718,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
      */
     request<E extends 'invite/limit', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2838,7 +2840,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'my/apps', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2959,7 +2961,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/hybrid-timeline', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2981,7 +2983,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/mentions', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -2992,7 +2994,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/polls/recommendation', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3102,7 +3104,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/state', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3135,7 +3137,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/timeline', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3146,7 +3148,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/translate', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3168,7 +3170,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/user-list-timeline', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3422,7 +3424,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'promo/read', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3433,7 +3435,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'roles/list', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3466,7 +3468,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'roles/notes', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3532,6 +3534,7 @@ declare module '../api.js' {
     /**
      * Check push notification registration exists.
      * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
     request<E extends 'sw/show-registration', P extends Endpoints[E]['req']>(
@@ -3543,6 +3546,7 @@ declare module '../api.js' {
     /**
      * Update push notification registration.
      * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
     request<E extends 'sw/update-registration', P extends Endpoints[E]['req']>(
@@ -3554,6 +3558,7 @@ declare module '../api.js' {
     /**
      * Register to receive push notifications.
      * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
     request<E extends 'sw/register', P extends Endpoints[E]['req']>(
@@ -3741,7 +3746,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/favorite', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3752,7 +3757,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/unfavorite', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3774,7 +3779,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/create-from-public', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3862,7 +3867,7 @@ declare module '../api.js' {
     /**
      * Show the different kinds of relations between the authenticated user and the specified user(s).
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'users/relation', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3873,7 +3878,7 @@ declare module '../api.js' {
     /**
      * File a report.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:report-abuse*
      */
     request<E extends 'users/report-abuse', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3917,7 +3922,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
-     * **Credential required**: *Yes*
+     * **Credential required**: *No*
      */
     request<E extends 'users/achievements', P extends Endpoints[E]['req']>(
       endpoint: E,
@@ -3950,6 +3955,7 @@ declare module '../api.js' {
     /**
      * No description provided.
      * 
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
     request<E extends 'fetch-external-resources', P extends Endpoints[E]['req']>(
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index 5e05759047..2ed76a22f9 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0
- * generatedAt: 2023-12-25T03:48:32.001Z
+ * generatedAt: 2023-12-26T23:35:09.491Z
  */
 
 import type {
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index ceb2f242ac..c857e8e370 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0
- * generatedAt: 2023-12-25T03:48:31.996Z
+ * generatedAt: 2023-12-26T23:35:09.489Z
  */
 
 import { operations } from './types.js';
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index a7fde6c1a3..c5b81a6b41 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.0
- * generatedAt: 2023-12-25T03:48:31.993Z
+ * generatedAt: 2023-12-26T23:35:09.485Z
  */
 
 import { components } from './types.js';
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 28fe5654e6..94bb263980 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -3,7 +3,7 @@
 
 /*
  * version: 2023.12.0
- * generatedAt: 2023-12-25T03:48:31.850Z
+ * generatedAt: 2023-12-26T23:35:09.389Z
  */
 
 /**
@@ -22,7 +22,7 @@ export type paths = {
      * admin/meta
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:meta*
      */
     post: operations['admin/meta'];
   };
@@ -31,7 +31,7 @@ export type paths = {
      * admin/abuse-user-reports
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-user-reports*
      */
     post: operations['admin/abuse-user-reports'];
   };
@@ -40,7 +40,8 @@ export type paths = {
      * admin/accounts/create
      * @description No description provided.
      *
-     * **Credential required**: *No* / **Permission**: *write:admin*
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *No*
      */
     post: operations['admin/accounts/create'];
   };
@@ -49,7 +50,7 @@ export type paths = {
      * admin/accounts/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:account*
      */
     post: operations['admin/accounts/delete'];
   };
@@ -58,7 +59,7 @@ export type paths = {
      * admin/accounts/find-by-email
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:account*
      */
     post: operations['admin/accounts/find-by-email'];
   };
@@ -67,7 +68,7 @@ export type paths = {
      * admin/ad/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
     post: operations['admin/ad/create'];
   };
@@ -76,7 +77,7 @@ export type paths = {
      * admin/ad/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
     post: operations['admin/ad/delete'];
   };
@@ -85,7 +86,7 @@ export type paths = {
      * admin/ad/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:ad*
      */
     post: operations['admin/ad/list'];
   };
@@ -94,7 +95,7 @@ export type paths = {
      * admin/ad/update
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
     post: operations['admin/ad/update'];
   };
@@ -103,7 +104,7 @@ export type paths = {
      * admin/announcements/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
     post: operations['admin/announcements/create'];
   };
@@ -112,7 +113,7 @@ export type paths = {
      * admin/announcements/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
     post: operations['admin/announcements/delete'];
   };
@@ -121,7 +122,7 @@ export type paths = {
      * admin/announcements/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:announcements*
      */
     post: operations['admin/announcements/list'];
   };
@@ -130,7 +131,7 @@ export type paths = {
      * admin/announcements/update
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
     post: operations['admin/announcements/update'];
   };
@@ -139,7 +140,7 @@ export type paths = {
      * admin/avatar-decorations/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
     post: operations['admin/avatar-decorations/create'];
   };
@@ -148,7 +149,7 @@ export type paths = {
      * admin/avatar-decorations/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
     post: operations['admin/avatar-decorations/delete'];
   };
@@ -157,7 +158,7 @@ export type paths = {
      * admin/avatar-decorations/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:avatar-decorations*
      */
     post: operations['admin/avatar-decorations/list'];
   };
@@ -166,7 +167,7 @@ export type paths = {
      * admin/avatar-decorations/update
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
     post: operations['admin/avatar-decorations/update'];
   };
@@ -175,7 +176,7 @@ export type paths = {
      * admin/delete-all-files-of-a-user
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:delete-all-files-of-a-user*
      */
     post: operations['admin/delete-all-files-of-a-user'];
   };
@@ -184,7 +185,7 @@ export type paths = {
      * admin/unset-user-avatar
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-avatar*
      */
     post: operations['admin/unset-user-avatar'];
   };
@@ -193,7 +194,7 @@ export type paths = {
      * admin/unset-user-banner
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-banner*
      */
     post: operations['admin/unset-user-banner'];
   };
@@ -202,7 +203,7 @@ export type paths = {
      * admin/drive/clean-remote-files
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
      */
     post: operations['admin/drive/clean-remote-files'];
   };
@@ -211,7 +212,7 @@ export type paths = {
      * admin/drive/cleanup
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
      */
     post: operations['admin/drive/cleanup'];
   };
@@ -220,7 +221,7 @@ export type paths = {
      * admin/drive/files
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
      */
     post: operations['admin/drive/files'];
   };
@@ -229,7 +230,7 @@ export type paths = {
      * admin/drive/show-file
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
      */
     post: operations['admin/drive/show-file'];
   };
@@ -238,7 +239,7 @@ export type paths = {
      * admin/emoji/add-aliases-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     post: operations['admin/emoji/add-aliases-bulk'];
   };
@@ -247,7 +248,7 @@ export type paths = {
      * admin/emoji/add
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     post: operations['admin/emoji/add'];
   };
@@ -256,7 +257,7 @@ export type paths = {
      * admin/emoji/copy
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     post: operations['admin/emoji/copy'];
   };
@@ -265,7 +266,7 @@ export type paths = {
      * admin/emoji/delete-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     post: operations['admin/emoji/delete-bulk'];
   };
@@ -274,7 +275,7 @@ export type paths = {
      * admin/emoji/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     post: operations['admin/emoji/delete'];
   };
@@ -283,7 +284,8 @@ export type paths = {
      * admin/emoji/import-zip
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+     * **Credential required**: *Yes*
      */
     post: operations['admin/emoji/import-zip'];
   };
@@ -292,7 +294,7 @@ export type paths = {
      * admin/emoji/list-remote
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
     post: operations['admin/emoji/list-remote'];
   };
@@ -301,7 +303,7 @@ export type paths = {
      * admin/emoji/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
     post: operations['admin/emoji/list'];
   };
@@ -310,7 +312,7 @@ export type paths = {
      * admin/emoji/remove-aliases-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     post: operations['admin/emoji/remove-aliases-bulk'];
   };
@@ -319,7 +321,7 @@ export type paths = {
      * admin/emoji/set-aliases-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     post: operations['admin/emoji/set-aliases-bulk'];
   };
@@ -328,7 +330,7 @@ export type paths = {
      * admin/emoji/set-category-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     post: operations['admin/emoji/set-category-bulk'];
   };
@@ -337,7 +339,7 @@ export type paths = {
      * admin/emoji/set-license-bulk
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     post: operations['admin/emoji/set-license-bulk'];
   };
@@ -346,7 +348,7 @@ export type paths = {
      * admin/emoji/update
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     post: operations['admin/emoji/update'];
   };
@@ -355,7 +357,7 @@ export type paths = {
      * admin/federation/delete-all-files
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     post: operations['admin/federation/delete-all-files'];
   };
@@ -364,7 +366,7 @@ export type paths = {
      * admin/federation/refresh-remote-instance-metadata
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     post: operations['admin/federation/refresh-remote-instance-metadata'];
   };
@@ -373,7 +375,7 @@ export type paths = {
      * admin/federation/remove-all-following
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     post: operations['admin/federation/remove-all-following'];
   };
@@ -382,7 +384,7 @@ export type paths = {
      * admin/federation/update-instance
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     post: operations['admin/federation/update-instance'];
   };
@@ -391,7 +393,7 @@ export type paths = {
      * admin/get-index-stats
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:index-stats*
      */
     post: operations['admin/get-index-stats'];
   };
@@ -400,7 +402,7 @@ export type paths = {
      * admin/get-table-stats
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:table-stats*
      */
     post: operations['admin/get-table-stats'];
   };
@@ -409,7 +411,7 @@ export type paths = {
      * admin/get-user-ips
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:user-ips*
      */
     post: operations['admin/get-user-ips'];
   };
@@ -418,7 +420,7 @@ export type paths = {
      * admin/invite/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:invite-codes*
      */
     post: operations['admin/invite/create'];
   };
@@ -427,7 +429,7 @@ export type paths = {
      * admin/invite/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:invite-codes*
      */
     post: operations['admin/invite/list'];
   };
@@ -436,7 +438,7 @@ export type paths = {
      * admin/promo/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:promo*
      */
     post: operations['admin/promo/create'];
   };
@@ -445,7 +447,7 @@ export type paths = {
      * admin/queue/clear
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
      */
     post: operations['admin/queue/clear'];
   };
@@ -454,7 +456,7 @@ export type paths = {
      * admin/queue/deliver-delayed
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
      */
     post: operations['admin/queue/deliver-delayed'];
   };
@@ -463,7 +465,7 @@ export type paths = {
      * admin/queue/inbox-delayed
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
      */
     post: operations['admin/queue/inbox-delayed'];
   };
@@ -472,7 +474,7 @@ export type paths = {
      * admin/queue/promote
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
      */
     post: operations['admin/queue/promote'];
   };
@@ -481,7 +483,7 @@ export type paths = {
      * admin/queue/stats
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
     post: operations['admin/queue/stats'];
   };
@@ -490,7 +492,7 @@ export type paths = {
      * admin/relays/add
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
      */
     post: operations['admin/relays/add'];
   };
@@ -499,7 +501,7 @@ export type paths = {
      * admin/relays/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:relays*
      */
     post: operations['admin/relays/list'];
   };
@@ -508,7 +510,7 @@ export type paths = {
      * admin/relays/remove
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
      */
     post: operations['admin/relays/remove'];
   };
@@ -517,7 +519,7 @@ export type paths = {
      * admin/reset-password
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:reset-password*
      */
     post: operations['admin/reset-password'];
   };
@@ -526,7 +528,7 @@ export type paths = {
      * admin/resolve-abuse-user-report
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
      */
     post: operations['admin/resolve-abuse-user-report'];
   };
@@ -535,7 +537,7 @@ export type paths = {
      * admin/send-email
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:send-email*
      */
     post: operations['admin/send-email'];
   };
@@ -544,7 +546,7 @@ export type paths = {
      * admin/server-info
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:server-info*
      */
     post: operations['admin/server-info'];
   };
@@ -553,7 +555,7 @@ export type paths = {
      * admin/show-moderation-logs
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:show-moderation-log*
      */
     post: operations['admin/show-moderation-logs'];
   };
@@ -562,7 +564,7 @@ export type paths = {
      * admin/show-user
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
      */
     post: operations['admin/show-user'];
   };
@@ -571,7 +573,7 @@ export type paths = {
      * admin/show-users
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
      */
     post: operations['admin/show-users'];
   };
@@ -580,7 +582,7 @@ export type paths = {
      * admin/suspend-user
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:suspend-user*
      */
     post: operations['admin/suspend-user'];
   };
@@ -589,7 +591,7 @@ export type paths = {
      * admin/unsuspend-user
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:unsuspend-user*
      */
     post: operations['admin/unsuspend-user'];
   };
@@ -598,7 +600,7 @@ export type paths = {
      * admin/update-meta
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:meta*
      */
     post: operations['admin/update-meta'];
   };
@@ -607,7 +609,7 @@ export type paths = {
      * admin/delete-account
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:delete-account*
      */
     post: operations['admin/delete-account'];
   };
@@ -616,7 +618,7 @@ export type paths = {
      * admin/update-user-note
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:user-note*
      */
     post: operations['admin/update-user-note'];
   };
@@ -625,7 +627,7 @@ export type paths = {
      * admin/roles/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     post: operations['admin/roles/create'];
   };
@@ -634,7 +636,7 @@ export type paths = {
      * admin/roles/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     post: operations['admin/roles/delete'];
   };
@@ -643,7 +645,7 @@ export type paths = {
      * admin/roles/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
      */
     post: operations['admin/roles/list'];
   };
@@ -652,7 +654,7 @@ export type paths = {
      * admin/roles/show
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *read:admin*
+     * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
      */
     post: operations['admin/roles/show'];
   };
@@ -661,7 +663,7 @@ export type paths = {
      * admin/roles/update
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     post: operations['admin/roles/update'];
   };
@@ -670,7 +672,7 @@ export type paths = {
      * admin/roles/assign
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     post: operations['admin/roles/assign'];
   };
@@ -679,7 +681,7 @@ export type paths = {
      * admin/roles/unassign
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     post: operations['admin/roles/unassign'];
   };
@@ -688,7 +690,7 @@ export type paths = {
      * admin/roles/update-default-policies
      * @description No description provided.
      *
-     * **Credential required**: *Yes* / **Permission**: *write:admin*
+     * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     post: operations['admin/roles/update-default-policies'];
   };
@@ -697,7 +699,7 @@ export type paths = {
      * admin/roles/users
      * @description No description provided.
      *
-     * **Credential required**: *No* / **Permission**: *read:admin*
+     * **Credential required**: *No* / **Permission**: *read:admin:roles*
      */
     post: operations['admin/roles/users'];
   };
@@ -769,7 +771,7 @@ export type paths = {
      * ap/get
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:federation*
      */
     post: operations['ap/get'];
   };
@@ -778,7 +780,7 @@ export type paths = {
      * ap/show
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['ap/show'];
   };
@@ -1519,7 +1521,7 @@ export type paths = {
      * federation/update-remote-user
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *No*
      */
     post: operations['federation/update-remote-user'];
   };
@@ -1792,7 +1794,7 @@ export type paths = {
      * i
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['i'];
   };
@@ -1901,7 +1903,7 @@ export type paths = {
      * i/claim-achievement
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     post: operations['i/claim-achievement'];
   };
@@ -2150,7 +2152,7 @@ export type paths = {
      * i/registry/get-all
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['i/registry/get-all'];
   };
@@ -2159,7 +2161,7 @@ export type paths = {
      * i/registry/get-detail
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['i/registry/get-detail'];
   };
@@ -2168,7 +2170,7 @@ export type paths = {
      * i/registry/get
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['i/registry/get'];
   };
@@ -2177,7 +2179,7 @@ export type paths = {
      * i/registry/keys-with-type
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['i/registry/keys-with-type'];
   };
@@ -2186,7 +2188,7 @@ export type paths = {
      * i/registry/keys
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['i/registry/keys'];
   };
@@ -2195,7 +2197,7 @@ export type paths = {
      * i/registry/remove
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     post: operations['i/registry/remove'];
   };
@@ -2214,7 +2216,7 @@ export type paths = {
      * i/registry/set
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     post: operations['i/registry/set'];
   };
@@ -2326,7 +2328,7 @@ export type paths = {
      * invite/create
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
      */
     post: operations['invite/create'];
   };
@@ -2335,7 +2337,7 @@ export type paths = {
      * invite/delete
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
      */
     post: operations['invite/delete'];
   };
@@ -2344,7 +2346,7 @@ export type paths = {
      * invite/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
      */
     post: operations['invite/list'];
   };
@@ -2353,7 +2355,7 @@ export type paths = {
      * invite/limit
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
      */
     post: operations['invite/limit'];
   };
@@ -2467,7 +2469,7 @@ export type paths = {
      * my/apps
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['my/apps'];
   };
@@ -2573,7 +2575,7 @@ export type paths = {
      * notes/hybrid-timeline
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['notes/hybrid-timeline'];
   };
@@ -2591,7 +2593,7 @@ export type paths = {
      * notes/mentions
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['notes/mentions'];
   };
@@ -2600,7 +2602,7 @@ export type paths = {
      * notes/polls/recommendation
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['notes/polls/recommendation'];
   };
@@ -2697,7 +2699,7 @@ export type paths = {
      * notes/state
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['notes/state'];
   };
@@ -2724,7 +2726,7 @@ export type paths = {
      * notes/timeline
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['notes/timeline'];
   };
@@ -2733,7 +2735,7 @@ export type paths = {
      * notes/translate
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['notes/translate'];
   };
@@ -2751,7 +2753,7 @@ export type paths = {
      * notes/user-list-timeline
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['notes/user-list-timeline'];
   };
@@ -2959,7 +2961,7 @@ export type paths = {
      * promo/read
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     post: operations['promo/read'];
   };
@@ -2968,7 +2970,7 @@ export type paths = {
      * roles/list
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['roles/list'];
   };
@@ -2995,7 +2997,7 @@ export type paths = {
      * roles/notes
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['roles/notes'];
   };
@@ -3056,6 +3058,7 @@ export type paths = {
      * sw/show-registration
      * @description Check push notification registration exists.
      *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
     post: operations['sw/show-registration'];
@@ -3065,6 +3068,7 @@ export type paths = {
      * sw/update-registration
      * @description Update push notification registration.
      *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
     post: operations['sw/update-registration'];
@@ -3074,6 +3078,7 @@ export type paths = {
      * sw/register
      * @description Register to receive push notifications.
      *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
     post: operations['sw/register'];
@@ -3234,7 +3239,7 @@ export type paths = {
      * users/lists/favorite
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     post: operations['users/lists/favorite'];
   };
@@ -3243,7 +3248,7 @@ export type paths = {
      * users/lists/unfavorite
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     post: operations['users/lists/unfavorite'];
   };
@@ -3261,7 +3266,7 @@ export type paths = {
      * users/lists/create-from-public
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     post: operations['users/lists/create-from-public'];
   };
@@ -3333,7 +3338,7 @@ export type paths = {
      * users/relation
      * @description Show the different kinds of relations between the authenticated user and the specified user(s).
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     post: operations['users/relation'];
   };
@@ -3342,7 +3347,7 @@ export type paths = {
      * users/report-abuse
      * @description File a report.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *Yes* / **Permission**: *write:report-abuse*
      */
     post: operations['users/report-abuse'];
   };
@@ -3378,7 +3383,7 @@ export type paths = {
      * users/achievements
      * @description No description provided.
      *
-     * **Credential required**: *Yes*
+     * **Credential required**: *No*
      */
     post: operations['users/achievements'];
   };
@@ -3412,6 +3417,7 @@ export type paths = {
      * fetch-external-resources
      * @description No description provided.
      *
+     * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
     post: operations['fetch-external-resources'];
@@ -4381,7 +4387,7 @@ export type operations = {
    * admin/meta
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:meta*
    */
   'admin/meta': {
     responses: {
@@ -4522,7 +4528,7 @@ export type operations = {
    * admin/abuse-user-reports
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-user-reports*
    */
   'admin/abuse-user-reports': {
     requestBody: {
@@ -4614,7 +4620,8 @@ export type operations = {
    * admin/accounts/create
    * @description No description provided.
    *
-   * **Credential required**: *No* / **Permission**: *write:admin*
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *No*
    */
   'admin/accounts/create': {
     requestBody: {
@@ -4668,7 +4675,7 @@ export type operations = {
    * admin/accounts/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:account*
    */
   'admin/accounts/delete': {
     requestBody: {
@@ -4720,7 +4727,7 @@ export type operations = {
    * admin/accounts/find-by-email
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:account*
    */
   'admin/accounts/find-by-email': {
     requestBody: {
@@ -4773,7 +4780,7 @@ export type operations = {
    * admin/ad/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
    */
   'admin/ad/create': {
     requestBody: {
@@ -4834,7 +4841,7 @@ export type operations = {
    * admin/ad/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
    */
   'admin/ad/delete': {
     requestBody: {
@@ -4886,7 +4893,7 @@ export type operations = {
    * admin/ad/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:ad*
    */
   'admin/ad/list': {
     requestBody: {
@@ -4946,7 +4953,7 @@ export type operations = {
    * admin/ad/update
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
    */
   'admin/ad/update': {
     requestBody: {
@@ -5007,7 +5014,7 @@ export type operations = {
    * admin/announcements/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
    */
   'admin/announcements/create': {
     requestBody: {
@@ -5096,7 +5103,7 @@ export type operations = {
    * admin/announcements/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
    */
   'admin/announcements/delete': {
     requestBody: {
@@ -5148,7 +5155,7 @@ export type operations = {
    * admin/announcements/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:announcements*
    */
   'admin/announcements/list': {
     requestBody: {
@@ -5222,7 +5229,7 @@ export type operations = {
    * admin/announcements/update
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
    */
   'admin/announcements/update': {
     requestBody: {
@@ -5285,7 +5292,7 @@ export type operations = {
    * admin/avatar-decorations/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
    */
   'admin/avatar-decorations/create': {
     requestBody: {
@@ -5339,7 +5346,7 @@ export type operations = {
    * admin/avatar-decorations/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
    */
   'admin/avatar-decorations/delete': {
     requestBody: {
@@ -5391,7 +5398,7 @@ export type operations = {
    * admin/avatar-decorations/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:avatar-decorations*
    */
   'admin/avatar-decorations/list': {
     requestBody: {
@@ -5465,7 +5472,7 @@ export type operations = {
    * admin/avatar-decorations/update
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
    */
   'admin/avatar-decorations/update': {
     requestBody: {
@@ -5521,7 +5528,7 @@ export type operations = {
    * admin/delete-all-files-of-a-user
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:delete-all-files-of-a-user*
    */
   'admin/delete-all-files-of-a-user': {
     requestBody: {
@@ -5573,7 +5580,7 @@ export type operations = {
    * admin/unset-user-avatar
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-avatar*
    */
   'admin/unset-user-avatar': {
     requestBody: {
@@ -5625,7 +5632,7 @@ export type operations = {
    * admin/unset-user-banner
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-banner*
    */
   'admin/unset-user-banner': {
     requestBody: {
@@ -5677,7 +5684,7 @@ export type operations = {
    * admin/drive/clean-remote-files
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
    */
   'admin/drive/clean-remote-files': {
     responses: {
@@ -5721,7 +5728,7 @@ export type operations = {
    * admin/drive/cleanup
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
    */
   'admin/drive/cleanup': {
     responses: {
@@ -5765,7 +5772,7 @@ export type operations = {
    * admin/drive/files
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
    */
   'admin/drive/files': {
     requestBody: {
@@ -5836,7 +5843,7 @@ export type operations = {
    * admin/drive/show-file
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
    */
   'admin/drive/show-file': {
     requestBody: {
@@ -5940,7 +5947,7 @@ export type operations = {
    * admin/emoji/add-aliases-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
   'admin/emoji/add-aliases-bulk': {
     requestBody: {
@@ -5992,7 +5999,7 @@ export type operations = {
    * admin/emoji/add
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
   'admin/emoji/add': {
     requestBody: {
@@ -6052,7 +6059,7 @@ export type operations = {
    * admin/emoji/copy
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
   'admin/emoji/copy': {
     requestBody: {
@@ -6109,7 +6116,7 @@ export type operations = {
    * admin/emoji/delete-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
   'admin/emoji/delete-bulk': {
     requestBody: {
@@ -6160,7 +6167,7 @@ export type operations = {
    * admin/emoji/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
   'admin/emoji/delete': {
     requestBody: {
@@ -6212,7 +6219,8 @@ export type operations = {
    * admin/emoji/import-zip
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
+   * **Credential required**: *Yes*
    */
   'admin/emoji/import-zip': {
     requestBody: {
@@ -6264,7 +6272,7 @@ export type operations = {
    * admin/emoji/list-remote
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
    */
   'admin/emoji/list-remote': {
     requestBody: {
@@ -6338,7 +6346,7 @@ export type operations = {
    * admin/emoji/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
    */
   'admin/emoji/list': {
     requestBody: {
@@ -6407,7 +6415,7 @@ export type operations = {
    * admin/emoji/remove-aliases-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
   'admin/emoji/remove-aliases-bulk': {
     requestBody: {
@@ -6459,7 +6467,7 @@ export type operations = {
    * admin/emoji/set-aliases-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
   'admin/emoji/set-aliases-bulk': {
     requestBody: {
@@ -6511,7 +6519,7 @@ export type operations = {
    * admin/emoji/set-category-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
   'admin/emoji/set-category-bulk': {
     requestBody: {
@@ -6564,7 +6572,7 @@ export type operations = {
    * admin/emoji/set-license-bulk
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
   'admin/emoji/set-license-bulk': {
     requestBody: {
@@ -6617,7 +6625,7 @@ export type operations = {
    * admin/emoji/update
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
    */
   'admin/emoji/update': {
     requestBody: {
@@ -6679,7 +6687,7 @@ export type operations = {
    * admin/federation/delete-all-files
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
    */
   'admin/federation/delete-all-files': {
     requestBody: {
@@ -6730,7 +6738,7 @@ export type operations = {
    * admin/federation/refresh-remote-instance-metadata
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
    */
   'admin/federation/refresh-remote-instance-metadata': {
     requestBody: {
@@ -6781,7 +6789,7 @@ export type operations = {
    * admin/federation/remove-all-following
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
    */
   'admin/federation/remove-all-following': {
     requestBody: {
@@ -6832,7 +6840,7 @@ export type operations = {
    * admin/federation/update-instance
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
    */
   'admin/federation/update-instance': {
     requestBody: {
@@ -6884,7 +6892,7 @@ export type operations = {
    * admin/get-index-stats
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:index-stats*
    */
   'admin/get-index-stats': {
     responses: {
@@ -6933,7 +6941,7 @@ export type operations = {
    * admin/get-table-stats
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:table-stats*
    */
   'admin/get-table-stats': {
     responses: {
@@ -6979,7 +6987,7 @@ export type operations = {
    * admin/get-user-ips
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:user-ips*
    */
   'admin/get-user-ips': {
     requestBody: {
@@ -7037,7 +7045,7 @@ export type operations = {
    * admin/invite/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:invite-codes*
    */
   'admin/invite/create': {
     requestBody: {
@@ -7092,7 +7100,7 @@ export type operations = {
    * admin/invite/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:invite-codes*
    */
   'admin/invite/list': {
     requestBody: {
@@ -7155,7 +7163,7 @@ export type operations = {
    * admin/promo/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:promo*
    */
   'admin/promo/create': {
     requestBody: {
@@ -7208,7 +7216,7 @@ export type operations = {
    * admin/queue/clear
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
    */
   'admin/queue/clear': {
     responses: {
@@ -7252,7 +7260,7 @@ export type operations = {
    * admin/queue/deliver-delayed
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
    */
   'admin/queue/deliver-delayed': {
     responses: {
@@ -7298,7 +7306,7 @@ export type operations = {
    * admin/queue/inbox-delayed
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
    */
   'admin/queue/inbox-delayed': {
     responses: {
@@ -7344,7 +7352,7 @@ export type operations = {
    * admin/queue/promote
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
    */
   'admin/queue/promote': {
     requestBody: {
@@ -7396,7 +7404,7 @@ export type operations = {
    * admin/queue/stats
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
    */
   'admin/queue/stats': {
     responses: {
@@ -7447,7 +7455,7 @@ export type operations = {
    * admin/relays/add
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
    */
   'admin/relays/add': {
     requestBody: {
@@ -7510,7 +7518,7 @@ export type operations = {
    * admin/relays/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:relays*
    */
   'admin/relays/list': {
     responses: {
@@ -7566,7 +7574,7 @@ export type operations = {
    * admin/relays/remove
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
    */
   'admin/relays/remove': {
     requestBody: {
@@ -7617,7 +7625,7 @@ export type operations = {
    * admin/reset-password
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:reset-password*
    */
   'admin/reset-password': {
     requestBody: {
@@ -7673,7 +7681,7 @@ export type operations = {
    * admin/resolve-abuse-user-report
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
    */
   'admin/resolve-abuse-user-report': {
     requestBody: {
@@ -7727,7 +7735,7 @@ export type operations = {
    * admin/send-email
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:send-email*
    */
   'admin/send-email': {
     requestBody: {
@@ -7780,7 +7788,7 @@ export type operations = {
    * admin/server-info
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:server-info*
    */
   'admin/server-info': {
     responses: {
@@ -7850,7 +7858,7 @@ export type operations = {
    * admin/show-moderation-logs
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:show-moderation-log*
    */
   'admin/show-moderation-logs': {
     requestBody: {
@@ -7921,7 +7929,7 @@ export type operations = {
    * admin/show-user
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
    */
   'admin/show-user': {
     requestBody: {
@@ -7975,7 +7983,7 @@ export type operations = {
    * admin/show-users
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
    */
   'admin/show-users': {
     requestBody: {
@@ -8050,7 +8058,7 @@ export type operations = {
    * admin/suspend-user
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:suspend-user*
    */
   'admin/suspend-user': {
     requestBody: {
@@ -8102,7 +8110,7 @@ export type operations = {
    * admin/unsuspend-user
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:unsuspend-user*
    */
   'admin/unsuspend-user': {
     requestBody: {
@@ -8154,7 +8162,7 @@ export type operations = {
    * admin/update-meta
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:meta*
    */
   'admin/update-meta': {
     requestBody: {
@@ -8299,7 +8307,7 @@ export type operations = {
    * admin/delete-account
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:delete-account*
    */
   'admin/delete-account': {
     requestBody: {
@@ -8353,7 +8361,7 @@ export type operations = {
    * admin/update-user-note
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:user-note*
    */
   'admin/update-user-note': {
     requestBody: {
@@ -8406,7 +8414,7 @@ export type operations = {
    * admin/roles/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
   'admin/roles/create': {
     requestBody: {
@@ -8474,7 +8482,7 @@ export type operations = {
    * admin/roles/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
   'admin/roles/delete': {
     requestBody: {
@@ -8526,7 +8534,7 @@ export type operations = {
    * admin/roles/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
    */
   'admin/roles/list': {
     responses: {
@@ -8572,7 +8580,7 @@ export type operations = {
    * admin/roles/show
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *read:admin*
+   * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
    */
   'admin/roles/show': {
     requestBody: {
@@ -8626,7 +8634,7 @@ export type operations = {
    * admin/roles/update
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
   'admin/roles/update': {
     requestBody: {
@@ -8693,7 +8701,7 @@ export type operations = {
    * admin/roles/assign
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
   'admin/roles/assign': {
     requestBody: {
@@ -8748,7 +8756,7 @@ export type operations = {
    * admin/roles/unassign
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
   'admin/roles/unassign': {
     requestBody: {
@@ -8802,7 +8810,7 @@ export type operations = {
    * admin/roles/update-default-policies
    * @description No description provided.
    *
-   * **Credential required**: *Yes* / **Permission**: *write:admin*
+   * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
    */
   'admin/roles/update-default-policies': {
     requestBody: {
@@ -8853,7 +8861,7 @@ export type operations = {
    * admin/roles/users
    * @description No description provided.
    *
-   * **Credential required**: *No* / **Permission**: *read:admin*
+   * **Credential required**: *No* / **Permission**: *read:admin:roles*
    */
   'admin/roles/users': {
     requestBody: {
@@ -9327,7 +9335,7 @@ export type operations = {
    * ap/get
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:federation*
    */
   'ap/get': {
     requestBody: {
@@ -9386,7 +9394,7 @@ export type operations = {
    * ap/show
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'ap/show': {
     requestBody: {
@@ -13615,7 +13623,7 @@ export type operations = {
    * federation/update-remote-user
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *No*
    */
   'federation/update-remote-user': {
     requestBody: {
@@ -15200,7 +15208,7 @@ export type operations = {
    * i
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   i: {
     responses: {
@@ -15853,7 +15861,7 @@ export type operations = {
    * i/claim-achievement
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:account*
    */
   'i/claim-achievement': {
     requestBody: {
@@ -17311,7 +17319,7 @@ export type operations = {
    * i/registry/get-all
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'i/registry/get-all': {
     requestBody: {
@@ -17366,7 +17374,7 @@ export type operations = {
    * i/registry/get-detail
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'i/registry/get-detail': {
     requestBody: {
@@ -17422,7 +17430,7 @@ export type operations = {
    * i/registry/get
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'i/registry/get': {
     requestBody: {
@@ -17478,7 +17486,7 @@ export type operations = {
    * i/registry/keys-with-type
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'i/registry/keys-with-type': {
     requestBody: {
@@ -17533,7 +17541,7 @@ export type operations = {
    * i/registry/keys
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'i/registry/keys': {
     requestBody: {
@@ -17586,7 +17594,7 @@ export type operations = {
    * i/registry/remove
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:account*
    */
   'i/registry/remove': {
     requestBody: {
@@ -17690,7 +17698,7 @@ export type operations = {
    * i/registry/set
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:account*
    */
   'i/registry/set': {
     requestBody: {
@@ -18446,7 +18454,7 @@ export type operations = {
    * invite/create
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
    */
   'invite/create': {
     responses: {
@@ -18492,7 +18500,7 @@ export type operations = {
    * invite/delete
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
    */
   'invite/delete': {
     requestBody: {
@@ -18544,7 +18552,7 @@ export type operations = {
    * invite/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
    */
   'invite/list': {
     requestBody: {
@@ -18602,7 +18610,7 @@ export type operations = {
    * invite/limit
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
    */
   'invite/limit': {
     responses: {
@@ -19282,7 +19290,7 @@ export type operations = {
    * my/apps
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'my/apps': {
     requestBody: {
@@ -19959,7 +19967,7 @@ export type operations = {
    * notes/hybrid-timeline
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'notes/hybrid-timeline': {
     requestBody: {
@@ -20101,7 +20109,7 @@ export type operations = {
    * notes/mentions
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'notes/mentions': {
     requestBody: {
@@ -20162,7 +20170,7 @@ export type operations = {
    * notes/polls/recommendation
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'notes/polls/recommendation': {
     requestBody: {
@@ -20762,7 +20770,7 @@ export type operations = {
    * notes/state
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'notes/state': {
     requestBody: {
@@ -20929,7 +20937,7 @@ export type operations = {
    * notes/timeline
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'notes/timeline': {
     requestBody: {
@@ -21001,7 +21009,7 @@ export type operations = {
    * notes/translate
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'notes/translate': {
     requestBody: {
@@ -21117,7 +21125,7 @@ export type operations = {
    * notes/user-list-timeline
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'notes/user-list-timeline': {
     requestBody: {
@@ -22409,7 +22417,7 @@ export type operations = {
    * promo/read
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:account*
    */
   'promo/read': {
     requestBody: {
@@ -22461,7 +22469,7 @@ export type operations = {
    * roles/list
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'roles/list': {
     responses: {
@@ -22625,7 +22633,7 @@ export type operations = {
    * roles/notes
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'roles/notes': {
     requestBody: {
@@ -22954,6 +22962,7 @@ export type operations = {
    * sw/show-registration
    * @description Check push notification registration exists.
    *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
   'sw/show-registration': {
@@ -23015,6 +23024,7 @@ export type operations = {
    * sw/update-registration
    * @description Update push notification registration.
    *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
   'sw/update-registration': {
@@ -23073,6 +23083,7 @@ export type operations = {
    * sw/register
    * @description Register to receive push notifications.
    *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
   'sw/register': {
@@ -24077,7 +24088,7 @@ export type operations = {
    * users/lists/favorite
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:account*
    */
   'users/lists/favorite': {
     requestBody: {
@@ -24129,7 +24140,7 @@ export type operations = {
    * users/lists/unfavorite
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:account*
    */
   'users/lists/unfavorite': {
     requestBody: {
@@ -24237,7 +24248,7 @@ export type operations = {
    * users/lists/create-from-public
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:account*
    */
   'users/lists/create-from-public': {
     requestBody: {
@@ -24728,7 +24739,7 @@ export type operations = {
    * users/relation
    * @description Show the different kinds of relations between the authenticated user and the specified user(s).
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *read:account*
    */
   'users/relation': {
     requestBody: {
@@ -24803,7 +24814,7 @@ export type operations = {
    * users/report-abuse
    * @description File a report.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *Yes* / **Permission**: *write:report-abuse*
    */
   'users/report-abuse': {
     requestBody: {
@@ -25036,7 +25047,7 @@ export type operations = {
    * users/achievements
    * @description No description provided.
    *
-   * **Credential required**: *Yes*
+   * **Credential required**: *No*
    */
   'users/achievements': {
     requestBody: {
@@ -25202,6 +25213,7 @@ export type operations = {
    * fetch-external-resources
    * @description No description provided.
    *
+   * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
    * **Credential required**: *Yes*
    */
   'fetch-external-resources': {
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 069f5f9499..9b25e2af74 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -45,7 +45,55 @@ export const permissions = [
 	'write:flash',
 	'read:flash-likes',
 	'write:flash-likes',
-];
+	'read:admin:abuse-user-reports',
+	'write:admin:delete-account',
+	'write:admin:delete-all-files-of-a-user',
+	'read:admin:index-stats',
+	'read:admin:table-stats',
+	'read:admin:user-ips',
+	'read:admin:meta',
+	'write:admin:reset-password',
+	'write:admin:resolve-abuse-user-report',
+	'write:admin:send-email',
+	'read:admin:server-info',
+	'read:admin:show-moderation-log',
+	'read:admin:show-user',
+	'read:admin:show-users',
+	'write:admin:suspend-user',
+	'write:admin:unset-user-avatar',
+	'write:admin:unset-user-banner',
+	'write:admin:unsuspend-user',
+	'write:admin:meta',
+	'write:admin:user-note',
+	'write:admin:roles',
+	'read:admin:roles',
+	'write:admin:relays',
+	'read:admin:relays',
+	'write:admin:invite-codes',
+	'read:admin:invite-codes',
+	'write:admin:announcements',
+	'read:admin:announcements',
+	'write:admin:avatar-decorations',
+	'read:admin:avatar-decorations',
+	'write:admin:federation',
+	'write:admin:account',
+	'read:admin:account',
+	'write:admin:emoji',
+	'read:admin:emoji',
+	'write:admin:queue',
+	'read:admin:queue',
+	'write:admin:promo',
+	'write:admin:drive',
+	'read:admin:drive',
+	'write:admin:ad',
+	'read:admin:ad',
+	'write:invite-codes',
+	'read:invite-codes',
+	'write:clip-favorite',
+	'read:clip-favorite',
+	'read:federation',
+	'write:report-abuse',
+] as const;
 
 export const moderationLogTypes = [
 	'updateServerSettings',

From 544b8106b2e85f57ca76ccf53c1ff998abeeef1a Mon Sep 17 00:00:00 2001
From: Kagami Sascha Rosylight <saschanaz@outlook.com>
Date: Wed, 27 Dec 2023 07:10:24 +0100
Subject: [PATCH 377/435] feat(backend/oauth): allow CORS for token endpoint
 (#12814)

* feat(backend/oauth): allow CORS for token endpoint

* no need to explicitly set origin to `*`

* Update CHANGELOG.md
---
 CHANGELOG.md                                  |  11 ++
 packages/backend/package.json                 |   2 +-
 packages/backend/src/server/ServerService.ts  |   3 +-
 .../src/server/WellKnownServerService.ts      |   6 +
 .../src/server/oauth/OAuth2ProviderService.ts |  29 +++++
 packages/backend/test/e2e/nodeinfo.ts         |  40 +++++++
 packages/backend/test/e2e/oauth.ts            |  20 ++++
 packages/backend/test/e2e/well-known.ts       | 111 ++++++++++++++++++
 packages/backend/test/utils.ts                |   2 +
 pnpm-lock.yaml                                |  23 ++--
 10 files changed, 238 insertions(+), 9 deletions(-)
 create mode 100644 packages/backend/test/e2e/nodeinfo.ts
 create mode 100644 packages/backend/test/e2e/well-known.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8b71f6540d..53931b44d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,17 @@
 
 -->
 
+## 2023.x.x (unreleased)
+
+### General
+-
+
+### Client
+- 
+
+### Server
+- Enhance: `oauth/token`エンドポイントのCORS対応
+
 ## 2023.12.1
 
 ### General
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 14243b9ba2..224700a77b 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -66,7 +66,7 @@
 		"@discordapp/twemoji": "15.0.2",
 		"@fastify/accepts": "4.3.0",
 		"@fastify/cookie": "9.2.0",
-		"@fastify/cors": "8.4.2",
+		"@fastify/cors": "8.5.0",
 		"@fastify/express": "2.3.0",
 		"@fastify/http-proxy": "9.3.0",
 		"@fastify/multipart": "8.0.0",
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index b6d4175c0c..cf8de4ea56 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -110,7 +110,8 @@ export class ServerService implements OnApplicationShutdown {
 		fastify.register(this.activityPubServerService.createServer);
 		fastify.register(this.nodeinfoServerService.createServer);
 		fastify.register(this.wellKnownServerService.createServer);
-		fastify.register(this.oauth2ProviderService.createServer);
+		fastify.register(this.oauth2ProviderService.createServer, { prefix: '/oauth' });
+		fastify.register(this.oauth2ProviderService.createTokenServer, { prefix: '/oauth/token' });
 
 		fastify.get<{ Params: { path: string }; Querystring: { static?: any; badge?: any; }; }>('/emoji/:path(.*)', async (request, reply) => {
 			const path = request.params.path;
diff --git a/packages/backend/src/server/WellKnownServerService.ts b/packages/backend/src/server/WellKnownServerService.ts
index 8fc3c96de6..c3eaf53a14 100644
--- a/packages/backend/src/server/WellKnownServerService.ts
+++ b/packages/backend/src/server/WellKnownServerService.ts
@@ -16,6 +16,7 @@ import * as Acct from '@/misc/acct.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { NodeinfoServerService } from './NodeinfoServerService.js';
+import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js';
 import type { FindOptionsWhere } from 'typeorm';
 import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
 
@@ -30,6 +31,7 @@ export class WellKnownServerService {
 
 		private nodeinfoServerService: NodeinfoServerService,
 		private userEntityService: UserEntityService,
+		private oauth2ProviderService: OAuth2ProviderService,
 	) {
 		//this.createServer = this.createServer.bind(this);
 	}
@@ -87,6 +89,10 @@ export class WellKnownServerService {
 			return { links: this.nodeinfoServerService.getLinks() };
 		});
 
+		fastify.get('/.well-known/oauth-authorization-server', async () => {
+			return this.oauth2ProviderService.generateRFC8414();
+		});
+
 		/* TODO
 fastify.get('/.well-known/change-password', async (request, reply) => {
 });
diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts
index 7ccf3a297e..52505ac5bb 100644
--- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts
+++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts
@@ -31,6 +31,22 @@ export class OAuth2ProviderService {
 		private config: Config,
 	) { }
 
+	// https://datatracker.ietf.org/doc/html/rfc8414.html
+	// https://indieauth.spec.indieweb.org/#indieauth-server-metadata
+	public generateRFC8414() {
+		return {
+			issuer: this.config.url,
+			authorization_endpoint: new URL('/oauth/authorize', this.config.url),
+			token_endpoint: new URL('/oauth/token', this.config.url),
+			scopes_supported: kinds,
+			response_types_supported: ['code'],
+			grant_types_supported: ['authorization_code'],
+			service_documentation: 'https://misskey-hub.net',
+			code_challenge_methods_supported: ['S256'],
+			authorization_response_iss_parameter_supported: true,
+		};
+	}
+
 	@bindThis
 	public async createServer(fastify: FastifyInstance): Promise<void> {
 		// https://datatracker.ietf.org/doc/html/rfc8414.html
@@ -151,4 +167,17 @@ export class OAuth2ProviderService {
 			}
 		});
 	}
+
+	@bindThis
+	public async createTokenServer(fastify: FastifyInstance): Promise<void> {
+		fastify.register(fastifyCors);
+		fastify.post('', async () => { });
+
+		await fastify.register(fastifyExpress);
+		// Clients may use JSON or urlencoded
+		fastify.use('', bodyParser.urlencoded({ extended: false }));
+		fastify.use('', bodyParser.json({ strict: true }));
+		fastify.use('', this.#server.token());
+		fastify.use('', this.#server.errorHandler());
+	}
 }
diff --git a/packages/backend/test/e2e/nodeinfo.ts b/packages/backend/test/e2e/nodeinfo.ts
new file mode 100644
index 0000000000..7eed39c5ed
--- /dev/null
+++ b/packages/backend/test/e2e/nodeinfo.ts
@@ -0,0 +1,40 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+process.env.NODE_ENV = 'test';
+
+import * as assert from 'assert';
+import { relativeFetch, startServer } from '../utils.js';
+import type { INestApplicationContext } from '@nestjs/common';
+
+describe('nodeinfo', () => {
+	let app: INestApplicationContext;
+
+	beforeAll(async () => {
+		app = await startServer();
+	}, 1000 * 60 * 2);
+
+	afterAll(async () => {
+		await app.close();
+	});
+
+	test('nodeinfo 2.1', async () => {
+		const res = await relativeFetch('nodeinfo/2.1');
+		assert.ok(res.ok);
+		assert.strictEqual(res.headers.get('Access-Control-Allow-Origin'), '*');
+
+		const nodeInfo = await res.json() as any;
+		assert.strictEqual(nodeInfo.software.name, 'misskey');
+	});
+
+	test('nodeinfo 2.0', async () => {
+		const res = await relativeFetch('nodeinfo/2.0');
+		assert.ok(res.ok);
+		assert.strictEqual(res.headers.get('Access-Control-Allow-Origin'), '*');
+
+		const nodeInfo = await res.json() as any;
+		assert.strictEqual(nodeInfo.software.name, 'misskey');
+	});
+});
diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts
index a029a0d4be..3a5e4ebdae 100644
--- a/packages/backend/test/e2e/oauth.ts
+++ b/packages/backend/test/e2e/oauth.ts
@@ -941,4 +941,24 @@ describe('OAuth', () => {
 		const response = await fetch(new URL('/oauth/foo', host));
 		assert.strictEqual(response.status, 404);
 	});
+
+	describe('CORS', () => {
+		test('Token endpoint should support CORS', async () => {
+			const response = await fetch(new URL('/oauth/token', host), { method: 'POST' });
+			assert.ok(!response.ok);
+			assert.strictEqual(response.headers.get('Access-Control-Allow-Origin'), '*');
+		});
+
+		test('Authorize endpoint should not support CORS', async () => {
+			const response = await fetch(new URL('/oauth/authorize', host), { method: 'GET' });
+			assert.ok(!response.ok);
+			assert.ok(!response.headers.has('Access-Control-Allow-Origin'));
+		});
+
+		test('Decision endpoint should not support CORS', async () => {
+			const response = await fetch(new URL('/oauth/decision', host), { method: 'POST' });
+			assert.ok(!response.ok);
+			assert.ok(!response.headers.has('Access-Control-Allow-Origin'));
+		});
+	});
 });
diff --git a/packages/backend/test/e2e/well-known.ts b/packages/backend/test/e2e/well-known.ts
new file mode 100644
index 0000000000..14e32e1627
--- /dev/null
+++ b/packages/backend/test/e2e/well-known.ts
@@ -0,0 +1,111 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+process.env.NODE_ENV = 'test';
+
+import * as assert from 'assert';
+import { host, origin, relativeFetch, signup, startServer } from '../utils.js';
+import type { INestApplicationContext } from '@nestjs/common';
+import type * as misskey from 'misskey-js';
+
+describe('.well-known', () => {
+	let app: INestApplicationContext;
+	let alice: misskey.entities.User;
+
+	beforeAll(async () => {
+		app = await startServer();
+
+		alice = await signup({ username: 'alice' });
+	}, 1000 * 60 * 2);
+
+	afterAll(async () => {
+		await app.close();
+	});
+
+	test('nodeinfo', async () => {
+		const res = await relativeFetch('.well-known/nodeinfo');
+		assert.ok(res.ok);
+		assert.strictEqual(res.headers.get('Access-Control-Allow-Origin'), '*');
+
+		const nodeInfo = await res.json();
+		assert.deepStrictEqual(nodeInfo, {
+			links: [{
+				rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
+				href: `${origin}/nodeinfo/2.1`,
+			}, {
+				rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
+				href: `${origin}/nodeinfo/2.0`,
+			}],
+		});
+	});
+
+	test('webfinger', async () => {
+		const preflight = await relativeFetch(`.well-known/webfinger?resource=acct:alice@${host}`, {
+			method: 'options',
+			headers: {
+				'Access-Control-Request-Method': 'GET',
+				Origin: 'http://example.com',
+			},
+		});
+		assert.ok(preflight.ok);
+		assert.strictEqual(preflight.headers.get('Access-Control-Allow-Headers'), 'Accept');
+
+		const res = await relativeFetch(`.well-known/webfinger?resource=acct:alice@${host}`);
+		assert.ok(res.ok);
+		assert.strictEqual(res.headers.get('Access-Control-Allow-Origin'), '*');
+		assert.strictEqual(res.headers.get('Access-Control-Expose-Headers'), 'Vary');
+		assert.strictEqual(res.headers.get('Vary'), 'Accept');
+
+		const webfinger = await res.json();
+
+		assert.deepStrictEqual(webfinger, {
+			subject: `acct:alice@${host}`,
+			links: [{
+				rel: 'self',
+				type: 'application/activity+json',
+				href: `${origin}/users/${alice.id}`,
+			}, {
+				rel: 'http://webfinger.net/rel/profile-page',
+				type: 'text/html',
+				href: `${origin}/@alice`,
+			}, {
+				rel: 'http://ostatus.org/schema/1.0/subscribe',
+				template: `${origin}/authorize-follow?acct={uri}`,
+			}],
+		});
+	});
+
+	test('host-meta', async () => {
+		const res = await relativeFetch('.well-known/host-meta');
+		assert.ok(res.ok);
+		assert.strictEqual(res.headers.get('Access-Control-Allow-Origin'), '*');
+	});
+
+	test('host-meta.json', async () => {
+		const res = await relativeFetch('.well-known/host-meta.json');
+		assert.ok(res.ok);
+		assert.strictEqual(res.headers.get('Access-Control-Allow-Origin'), '*');
+
+		const hostMeta = await res.json();
+		assert.deepStrictEqual(hostMeta, {
+			links: [{
+				rel: 'lrdd',
+				type: 'application/jrd+json',
+				template: `${origin}/.well-known/webfinger?resource={uri}`,
+			}],
+		});
+	});
+
+	test('oauth-authorization-server', async () => {
+		const res = await relativeFetch('.well-known/oauth-authorization-server');
+		assert.ok(res.ok);
+		assert.strictEqual(res.headers.get('Access-Control-Allow-Origin'), '*');
+
+		const serverInfo = await res.json() as any;
+		assert.strictEqual(serverInfo.issuer, origin);
+		assert.strictEqual(serverInfo.authorization_endpoint, `${origin}/oauth/authorize`);
+		assert.strictEqual(serverInfo.token_endpoint, `${origin}/oauth/token`);
+	});
+});
diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts
index db7629d2c4..46b8ea9cdd 100644
--- a/packages/backend/test/utils.ts
+++ b/packages/backend/test/utils.ts
@@ -26,6 +26,8 @@ interface UserToken {
 
 const config = loadConfig();
 export const port = config.port;
+export const origin = config.url;
+export const host = new URL(config.url).host;
 
 export const cookie = (me: UserToken): string => {
 	return `token=${me.token};`;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5f77a0081c..2db1e628fd 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -80,8 +80,8 @@ importers:
         specifier: 9.2.0
         version: 9.2.0
       '@fastify/cors':
-        specifier: 8.4.2
-        version: 8.4.2
+        specifier: 8.5.0
+        version: 8.5.0
       '@fastify/express':
         specifier: 2.3.0
         version: 2.3.0
@@ -4230,11 +4230,11 @@ packages:
       fastify-plugin: 4.5.0
     dev: false
 
-  /@fastify/cors@8.4.2:
-    resolution: {integrity: sha512-IVynbcPG9eWiJ0P/A1B+KynmiU/yTYbu3ooBUSIeHfca/N1XLb9nIJVCws+YTr2q63MA8Y6QLeXQczEv4npM9g==}
+  /@fastify/cors@8.5.0:
+    resolution: {integrity: sha512-/oZ1QSb02XjP0IK1U0IXktEsw/dUBTxJOW7IpIeO8c/tNalw/KjoNSJv1Sf6eqoBPO+TDGkifq6ynFK3v68HFQ==}
     dependencies:
       fastify-plugin: 4.5.0
-      mnemonist: 0.39.5
+      mnemonist: 0.39.6
     dev: false
 
   /@fastify/deepmerge@1.3.0:
@@ -7221,7 +7221,11 @@ packages:
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.3.12(typescript@5.3.3)
+<<<<<<< HEAD
       vue-component-type-helpers: 1.8.26
+=======
+      vue-component-type-helpers: 1.8.27
+>>>>>>> ad346b6f3 (feat(backend/oauth): allow CORS for token endpoint (#12814))
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -15138,8 +15142,8 @@ packages:
       ufo: 1.1.2
     dev: true
 
-  /mnemonist@0.39.5:
-    resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==}
+  /mnemonist@0.39.6:
+    resolution: {integrity: sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==}
     dependencies:
       obliterator: 2.0.4
     dev: false
@@ -19628,8 +19632,13 @@ packages:
   /vscode-textmate@8.0.0:
     resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
 
+<<<<<<< HEAD
   /vue-component-type-helpers@1.8.26:
     resolution: {integrity: sha512-CIwb7s8cqUuPpHDk+0DY8EJ/x8tzdzqw8ycX8hhw1GnbngTgSsIceHAqrrLjmv8zXi+j5XaiqYRQMw8sKyyjkw==}
+=======
+  /vue-component-type-helpers@1.8.27:
+    resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==}
+>>>>>>> ad346b6f3 (feat(backend/oauth): allow CORS for token endpoint (#12814))
     dev: true
 
   /vue-component-type-helpers@1.8.4:

From 31d32d4bf50e5f311452284ed2a9fa6d520a1383 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 27 Dec 2023 15:12:43 +0900
Subject: [PATCH 378/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 53931b44d0..b7f37d747d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,17 +12,6 @@
 
 -->
 
-## 2023.x.x (unreleased)
-
-### General
--
-
-### Client
-- 
-
-### Server
-- Enhance: `oauth/token`エンドポイントのCORS対応
-
 ## 2023.12.1
 
 ### General
@@ -35,6 +24,7 @@
 
 ### Server
 - Enhance: センシティブワードの設定がハッシュタグトレンドにも適用されるようになりました
+- Enhance: `oauth/token`エンドポイントのCORS対応
 - Fix: 1702718871541-ffVisibility.jsのdownが壊れている
 - Fix:「非センシティブのみ(リモートはいいねのみ)」を設定していても、センシティブに設定されたカスタム絵文字をリアクションできる問題を修正
 - Fix: ロールアサイン時の通知で,ロールアイコンが縮小されずに表示される問題を修正

From 08cd5ef8f584bc35e98b93bd67d5933850d1fd70 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 27 Dec 2023 15:15:08 +0900
Subject: [PATCH 379/435] :art:

---
 .../frontend/src/components/MkReactionsViewer.reaction.vue     | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index e7901316a2..09e864e497 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -198,7 +198,8 @@ if (!mock) {
 }
 
 .limitWidth {
-	max-width: 150px;
+	max-width: 70px;
+	object-fit: contain;
 }
 
 .count {

From 5e0eb76d3b0cb0a6d21cc08462b83b8cb9489216 Mon Sep 17 00:00:00 2001
From: GrapeApple0 <84321396+GrapeApple0@users.noreply.github.com>
Date: Wed, 27 Dec 2023 15:55:09 +0900
Subject: [PATCH 380/435] =?UTF-8?q?Revert=20"refactor:=20pagination?=
 =?UTF-8?q?=E3=81=AE=E5=9E=8B=E3=82=92=E6=98=8E=E7=A4=BA=E3=81=99=E3=82=8B?=
 =?UTF-8?q?=20(#12809)"=20(#12810)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This reverts commit 6855079811401be883167476726644e5730ea792.
---
 .../frontend/src/components/MkFileListForAdmin.vue   |  4 ++--
 packages/frontend/src/components/MkNoteDetailed.vue  |  6 +++---
 .../src/components/MkUserSetupDialog.Follow.vue      |  6 +++---
 packages/frontend/src/pages/about.federation.vue     |  2 +-
 packages/frontend/src/pages/admin-user.vue           |  4 ++--
 packages/frontend/src/pages/admin/abuses.vue         |  4 ++--
 packages/frontend/src/pages/admin/federation.vue     |  4 ++--
 packages/frontend/src/pages/admin/invites.vue        |  8 ++++----
 packages/frontend/src/pages/admin/modlog.vue         |  4 ++--
 packages/frontend/src/pages/admin/roles.role.vue     |  4 ++--
 packages/frontend/src/pages/admin/users.vue          |  4 ++--
 packages/frontend/src/pages/announcements.vue        |  6 +++---
 packages/frontend/src/pages/channels.vue             | 10 +++++-----
 .../frontend/src/pages/custom-emojis-manager.vue     |  6 +++---
 packages/frontend/src/pages/favorites.vue            |  4 ++--
 packages/frontend/src/pages/flash/flash-index.vue    |  8 ++++----
 packages/frontend/src/pages/follow-requests.vue      |  4 ++--
 packages/frontend/src/pages/gallery/index.vue        | 12 ++++++------
 packages/frontend/src/pages/gallery/post.vue         |  4 ++--
 packages/frontend/src/pages/instance-info.vue        |  4 ++--
 packages/frontend/src/pages/invite.vue               |  4 ++--
 packages/frontend/src/pages/my-clips/index.vue       |  4 ++--
 packages/frontend/src/pages/my-lists/list.vue        |  4 ++--
 packages/frontend/src/pages/page.vue                 |  4 ++--
 packages/frontend/src/pages/pages.vue                |  8 ++++----
 packages/frontend/src/pages/settings/apps.vue        |  4 ++--
 .../frontend/src/pages/settings/drive-cleaner.vue    |  4 ++--
 packages/frontend/src/pages/settings/mute-block.vue  |  8 ++++----
 packages/frontend/src/pages/settings/security.vue    |  4 ++--
 packages/frontend/src/pages/settings/webhook.vue     |  4 ++--
 packages/frontend/src/pages/user/clips.vue           |  4 ++--
 packages/frontend/src/pages/user/flashs.vue          |  4 ++--
 packages/frontend/src/pages/user/follow-list.vue     |  6 +++---
 packages/frontend/src/pages/user/gallery.vue         |  4 ++--
 packages/frontend/src/pages/user/lists.vue           |  4 ++--
 packages/frontend/src/pages/user/pages.vue           |  4 ++--
 packages/frontend/src/pages/user/reactions.vue       |  4 ++--
 37 files changed, 93 insertions(+), 93 deletions(-)

diff --git a/packages/frontend/src/components/MkFileListForAdmin.vue b/packages/frontend/src/components/MkFileListForAdmin.vue
index b60f058df0..eb0d4d61ac 100644
--- a/packages/frontend/src/components/MkFileListForAdmin.vue
+++ b/packages/frontend/src/components/MkFileListForAdmin.vue
@@ -38,14 +38,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
 import bytes from '@/filters/bytes.js';
 import { i18n } from '@/i18n.js';
 import { dateString } from '@/filters/date.js';
 
 const props = defineProps<{
-	pagination: Paging;
+	pagination: any;
 	viewMode: 'grid' | 'list';
 }>();
 </script>
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index a3fc3058d9..e287890e2c 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -253,7 +253,7 @@ import { checkAnimationFromMfm } from '@/scripts/check-animated-mfm.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 import MkButton from '@/components/MkButton.vue';
 
@@ -361,7 +361,7 @@ const renotesPagination = computed(() => ({
 	params: {
 		noteId: appearNote.value.id,
 	},
-} satisfies Paging));
+}));
 
 const reactionsPagination = computed(() => ({
 	endpoint: 'notes/reactions',
@@ -370,7 +370,7 @@ const reactionsPagination = computed(() => ({
 		noteId: appearNote.value.id,
 		type: reactionTabType.value,
 	},
-} satisfies Paging));
+}));
 
 async function addReplyTo(replyNote: Misskey.entities.Note) {
 		replies.value.unshift(replyNote);
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
index d924a54ffb..5f3f5b81dd 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
@@ -37,15 +37,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { i18n } from '@/i18n.js';
 import MkFolder from '@/components/MkFolder.vue';
 import XUser from '@/components/MkUserSetupDialog.User.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 
-const pinnedUsers = { endpoint: 'pinned-users', noPaging: true } satisfies Paging;
+const pinnedUsers = { endpoint: 'pinned-users', noPaging: true };
 
 const popularUsers = { endpoint: 'users', limit: 10, noPaging: true, params: {
 	state: 'alive',
 	origin: 'local',
 	sort: '+follower',
-} } satisfies Paging;
+} };
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue
index ed4e3bfe7a..0de000ee3e 100644
--- a/packages/frontend/src/pages/about.federation.vue
+++ b/packages/frontend/src/pages/about.federation.vue
@@ -82,7 +82,7 @@ const pagination = {
 			state.value === 'nsfw' ? { nsfw: true } :
 			{}),
 	})),
-} satisfies Paging;
+} as Paging;
 
 function getStatus(instance) {
 	if (instance.isSuspended) return 'Suspended';
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 094d6454f2..c87ec22ef6 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -212,7 +212,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 import { iAmAdmin, $i } from '@/account.js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 
 const props = withDefaults(defineProps<{
 	userId: string;
@@ -247,7 +247,7 @@ const announcementsPagination = {
 	params: computed(() => ({
 		userId: props.userId,
 	})),
-} satisfies Paging;
+};
 const expandedRoles = ref([]);
 
 function createFetcher() {
diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue
index 03dd6560ec..92688989d2 100644
--- a/packages/frontend/src/pages/admin/abuses.vue
+++ b/packages/frontend/src/pages/admin/abuses.vue
@@ -56,7 +56,7 @@ import { computed, shallowRef, ref } from 'vue';
 
 import XHeader from './_header_.vue';
 import MkSelect from '@/components/MkSelect.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import XAbuseReport from '@/components/MkAbuseReport.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -77,7 +77,7 @@ const pagination = {
 		reporterOrigin: reporterOrigin.value,
 		targetUserOrigin: targetUserOrigin.value,
 	})),
-} satisfies Paging;
+};
 
 function resolved(reportId) {
 	reports.value.removeItem(reportId);
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index 55ba0becce..1888a0eb16 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -63,7 +63,7 @@ import { computed, ref } from 'vue';
 import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
 import FormSplit from '@/components/form/split.vue';
 import { i18n } from '@/i18n.js';
@@ -90,7 +90,7 @@ const pagination = {
 			state.value === 'nsfw' ? { nsfw: true } :
 			{}),
 	})),
-} satisfies Paging;
+};
 
 function getStatus(instance) {
 	if (instance.isSuspended) return 'Suspended';
diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue
index fe2924d735..6314d0ce4e 100644
--- a/packages/frontend/src/pages/admin/invites.vue
+++ b/packages/frontend/src/pages/admin/invites.vue
@@ -73,7 +73,7 @@ const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
 const type = ref('all');
 const sort = ref('+createdAt');
 
-const pagination = {
+const pagination: Paging = {
 	endpoint: 'admin/invite/list' as const,
 	limit: 10,
 	params: computed(() => ({
@@ -81,7 +81,7 @@ const pagination = {
 		sort: sort.value,
 	})),
 	offsetMode: true,
-} satisfies Paging;
+};
 
 const expiresAt = ref('');
 const noExpirationDate = ref(true);
@@ -97,10 +97,10 @@ async function createWithOptions() {
 	os.alert({
 		type: 'success',
 		title: i18n.ts.inviteCodeCreated,
-		text: tickets.map(x => x.code).join('\n'),
+		text: tickets?.map(x => x.code).join('\n'),
 	});
 
-	tickets.forEach(ticket => pagingComponent.value?.prepend(ticket));
+	tickets?.forEach(ticket => pagingComponent.value?.prepend(ticket));
 }
 
 function deleted(id: string) {
diff --git a/packages/frontend/src/pages/admin/modlog.vue b/packages/frontend/src/pages/admin/modlog.vue
index ecd0d5c09c..acb0336491 100644
--- a/packages/frontend/src/pages/admin/modlog.vue
+++ b/packages/frontend/src/pages/admin/modlog.vue
@@ -36,7 +36,7 @@ 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, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
@@ -52,7 +52,7 @@ const pagination = {
 		type: type.value,
 		userId: moderatorId.value === '' ? null : moderatorId.value,
 	})),
-} satisfies Paging;
+};
 
 console.log(Misskey);
 
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 77155bdecd..92818cc3de 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -73,7 +73,7 @@ import { useRouter } from '@/router.js';
 import MkButton from '@/components/MkButton.vue';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import { infoImageUrl } from '@/instance.js';
 
 const router = useRouter();
@@ -88,7 +88,7 @@ const usersPagination = {
 	params: computed(() => ({
 		roleId: props.id,
 	})),
-} satisfies Paging;
+};
 
 const expandedItems = ref([]);
 
diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue
index a5cb0afdae..1bc4eb4089 100644
--- a/packages/frontend/src/pages/admin/users.vue
+++ b/packages/frontend/src/pages/admin/users.vue
@@ -62,7 +62,7 @@ import { computed, shallowRef, ref } from 'vue';
 import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import * as os from '@/os.js';
 import { lookupUser } from '@/scripts/lookup-user.js';
 import { i18n } from '@/i18n.js';
@@ -88,7 +88,7 @@ const pagination = {
 		hostname: searchHost.value,
 	})),
 	offsetMode: true,
-} satisfies Paging;
+};
 
 function searchUser() {
 	os.selectUser().then(user => {
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index 42af188608..705115abb0 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, computed } from 'vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import * as os from '@/os.js';
@@ -55,7 +55,7 @@ const paginationCurrent = {
 	params: {
 		isActive: true,
 	},
-} satisfies Paging;
+};
 
 const paginationPast = {
 	endpoint: 'announcements' as const,
@@ -63,7 +63,7 @@ const paginationPast = {
 	params: {
 		isActive: false,
 	},
-} satisfies Paging;
+};
 
 const paginationEl = ref<InstanceType<typeof MkPagination>>();
 
diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue
index 43be0bce47..182703f9da 100644
--- a/packages/frontend/src/pages/channels.vue
+++ b/packages/frontend/src/pages/channels.vue
@@ -53,7 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, onMounted, ref } from 'vue';
 import MkChannelPreview from '@/components/MkChannelPreview.vue';
 import MkChannelList from '@/components/MkChannelList.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkButton from '@/components/MkButton.vue';
@@ -83,20 +83,20 @@ onMounted(() => {
 const featuredPagination = {
 	endpoint: 'channels/featured' as const,
 	noPaging: true,
-} satisfies Paging;
+};
 const favoritesPagination = {
 	endpoint: 'channels/my-favorites' as const,
 	limit: 100,
 	noPaging: true,
-} satisfies Paging;
+};
 const followingPagination = {
 	endpoint: 'channels/followed' as const,
 	limit: 10,
-} satisfies Paging;
+};
 const ownedPagination = {
 	endpoint: 'channels/owned' as const,
 	limit: 10,
-} satisfies Paging;
+};
 
 async function search() {
 	const query = searchQuery.value.toString().trim();
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index e70d6143a4..bc2a268f34 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -77,7 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, defineAsyncComponent, ref, shallowRef } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import FormSplit from '@/components/form/split.vue';
 import { selectFile } from '@/scripts/select-file.js';
@@ -100,7 +100,7 @@ const pagination = {
 	params: computed(() => ({
 		query: (query.value && query.value !== '') ? query.value : null,
 	})),
-} satisfies Paging;
+};
 
 const remotePagination = {
 	endpoint: 'admin/emoji/list-remote' as const,
@@ -109,7 +109,7 @@ const remotePagination = {
 		query: (queryRemote.value && queryRemote.value !== '') ? queryRemote.value : null,
 		host: (host.value && host.value !== '') ? host.value : null,
 	})),
-} satisfies Paging;
+};
 
 const selectAll = () => {
 	if (selectedEmojis.value.length > 0) {
diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue
index 256dfcbc88..10f4a96a98 100644
--- a/packages/frontend/src/pages/favorites.vue
+++ b/packages/frontend/src/pages/favorites.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkNote from '@/components/MkNote.vue';
 import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
 import { i18n } from '@/i18n.js';
@@ -36,7 +36,7 @@ import { infoImageUrl } from '@/instance.js';
 const pagination = {
 	endpoint: 'i/favorites' as const,
 	limit: 10,
-} satisfies Paging;
+};
 
 definePageMetadata({
 	title: i18n.ts.favorites,
diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue
index e4e43b2828..2b9346fcac 100644
--- a/packages/frontend/src/pages/flash/flash-index.vue
+++ b/packages/frontend/src/pages/flash/flash-index.vue
@@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
 import MkFlashPreview from '@/components/MkFlashPreview.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import { useRouter } from '@/router.js';
 import { i18n } from '@/i18n.js';
@@ -53,15 +53,15 @@ const tab = ref('featured');
 const featuredFlashsPagination = {
 	endpoint: 'flash/featured' as const,
 	noPaging: true,
-} satisfies Paging;
+};
 const myFlashsPagination = {
 	endpoint: 'flash/my' as const,
 	limit: 5,
-} satisfies Paging;
+};
 const likedFlashsPagination = {
 	endpoint: 'flash/my-likes' as const,
 	limit: 5,
-} satisfies Paging;
+};
 
 function create() {
 	router.push('/play/new');
diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue
index 62c185df99..d750664221 100644
--- a/packages/frontend/src/pages/follow-requests.vue
+++ b/packages/frontend/src/pages/follow-requests.vue
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { shallowRef, computed } from 'vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import { userPage, acct } from '@/filters/user.js';
 import * as os from '@/os.js';
@@ -51,7 +51,7 @@ const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
 const pagination = {
 	endpoint: 'following/requests/list' as const,
 	limit: 10,
-} satisfies Paging;
+};
 
 function accept(user) {
 	os.api('following/requests/accept', { userId: user.id }).then(() => {
diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue
index 06896d05ca..936d9b8393 100644
--- a/packages/frontend/src/pages/gallery/index.vue
+++ b/packages/frontend/src/pages/gallery/index.vue
@@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { watch, ref, computed } from 'vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
@@ -68,19 +68,19 @@ const tagsRef = ref();
 const recentPostsPagination = {
 	endpoint: 'gallery/posts' as const,
 	limit: 6,
-} satisfies Paging;
+};
 const popularPostsPagination = {
 	endpoint: 'gallery/featured' as const,
 	noPaging: true,
-} satisfies Paging;
+};
 const myPostsPagination = {
 	endpoint: 'i/gallery/posts' as const,
 	limit: 5,
-} satisfies Paging;
+};
 const likedPostsPagination = {
 	endpoint: 'i/gallery/likes' as const,
 	limit: 5,
-} satisfies Paging;
+};
 
 const tagUsersPagination = computed(() => ({
 	endpoint: 'hashtags/users' as const,
@@ -90,7 +90,7 @@ const tagUsersPagination = computed(() => ({
 		origin: 'combined',
 		sort: '+follower',
 	},
-} satisfies Paging));
+}));
 
 watch(() => props.tag, () => {
 	if (tagsRef.value) tagsRef.value.tags.toggleContent(props.tag == null);
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 5c3fbb0d56..54a8790ef9 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -67,7 +67,7 @@ import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
 import { url } from '@/config.js';
@@ -93,7 +93,7 @@ const otherPostsPagination = {
 	params: computed(() => ({
 		userId: post.value.user.id,
 	})),
-} satisfies Paging;
+};
 
 function fetchPost() {
 	post.value = null;
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 87816a8552..683a31c36d 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -135,7 +135,7 @@ import { iAmModerator, iAmAdmin } from '@/account.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
 import { dateString } from '@/filters/date.js';
 
@@ -162,7 +162,7 @@ const usersPagination = {
 		hostname: props.host,
 	},
 	offsetMode: true,
-} satisfies Paging;
+};
 
 async function fetch(): Promise<void> {
 	if (iAmAdmin) {
diff --git a/packages/frontend/src/pages/invite.vue b/packages/frontend/src/pages/invite.vue
index 390f98ec32..6ac78a2068 100644
--- a/packages/frontend/src/pages/invite.vue
+++ b/packages/frontend/src/pages/invite.vue
@@ -52,10 +52,10 @@ const currentInviteLimit = ref<null | number>(null);
 const inviteLimit = (($i != null && $i.policies.inviteLimit) || (($i == null && instance.policies.inviteLimit))) as number;
 const inviteLimitCycle = (($i != null && $i.policies.inviteLimitCycle) || ($i == null && instance.policies.inviteLimitCycle)) as number;
 
-const pagination = {
+const pagination: Paging = {
 	endpoint: 'invite/list' as const,
 	limit: 10,
-} satisfies Paging;
+};
 
 const resetCycle = computed<null | string>(() => {
 	if (!inviteLimitCycle) return null;
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index ef6d2ef43a..d787e53bb0 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { watch, ref, shallowRef, computed } from 'vue';
 import * as Misskey from 'misskey-js';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkClipPreview from '@/components/MkClipPreview.vue';
 import * as os from '@/os.js';
@@ -40,7 +40,7 @@ const pagination = {
 	endpoint: 'clips/list' as const,
 	noPaging: true,
 	limit: 10,
-} satisfies Paging;
+};
 
 const tab = ref('my');
 const favorites = ref<Misskey.entities.Clip[] | null>(null);
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index 02dd1be477..df9cdb0fce 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -68,7 +68,7 @@ import MkInput from '@/components/MkInput.vue';
 import { userListsCache } from '@/cache.js';
 import { $i } from '@/account.js';
 import { defaultStore } from '@/store.js';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 
 const {
 	enableInfiniteScroll,
@@ -88,7 +88,7 @@ const membershipsPagination = {
 	params: computed(() => ({
 		listId: props.listId,
 	})),
-} satisfies Paging;
+};
 
 function fetchList() {
 	os.api('users/lists/show', {
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 0154724aec..6b06da9a24 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -85,7 +85,7 @@ import { url } from '@/config.js';
 import MkMediaImage from '@/components/MkMediaImage.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
 import MkContainer from '@/components/MkContainer.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkPagePreview from '@/components/MkPagePreview.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -108,7 +108,7 @@ const otherPostsPagination = {
 	params: computed(() => ({
 		userId: page.value.user.id,
 	})),
-} satisfies Paging;
+};
 const path = computed(() => props.username + '/' + props.pageName);
 
 function fetchPage() {
diff --git a/packages/frontend/src/pages/pages.vue b/packages/frontend/src/pages/pages.vue
index ea77913b7f..a7ca433ed3 100644
--- a/packages/frontend/src/pages/pages.vue
+++ b/packages/frontend/src/pages/pages.vue
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
 import MkPagePreview from '@/components/MkPagePreview.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import { useRouter } from '@/router.js';
 import { i18n } from '@/i18n.js';
@@ -51,15 +51,15 @@ const tab = ref('featured');
 const featuredPagesPagination = {
 	endpoint: 'pages/featured' as const,
 	noPaging: true,
-} satisfies Paging;
+};
 const myPagesPagination = {
 	endpoint: 'i/pages' as const,
 	limit: 5,
-} satisfies Paging;
+};
 const likedPagesPagination = {
 	endpoint: 'i/page-likes' as const,
 	limit: 5,
-} satisfies Paging;
+};
 
 function create() {
 	router.push('/pages/new');
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index a0f195b5a0..f492dc6d31 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, computed } from 'vue';
-import FormPagination, { Paging } from '@/components/MkPagination.vue';
+import FormPagination from '@/components/MkPagination.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -63,7 +63,7 @@ const pagination = {
 	params: {
 		sort: '+lastUsedAt',
 	},
-} satisfies Paging;
+};
 
 function revoke(token) {
 	os.api('i/revoke-token', { tokenId: token.id }).then(() => {
diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue
index 012c682d06..601479b73c 100644
--- a/packages/frontend/src/pages/settings/drive-cleaner.vue
+++ b/packages/frontend/src/pages/settings/drive-cleaner.vue
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, ref, watch } from 'vue';
 import tinycolor from 'tinycolor2';
 import * as os from '@/os.js';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
 import { i18n } from '@/i18n.js';
 import bytes from '@/filters/bytes.js';
@@ -64,7 +64,7 @@ const pagination = {
 	endpoint: 'drive/files' as const,
 	limit: 10,
 	params: computed(() => ({ sort: sortMode.value })),
-} satisfies Paging;
+};
 
 const sortOptions = [
 	{ value: 'sizeDesc', displayName: i18n.ts._drivecleaner.orderBySizeDesc },
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index d7bfb0c431..a996a03cce 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -129,7 +129,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, computed } from 'vue';
 import XInstanceMute from './mute-block.instance-mute.vue';
 import XWordMute from './mute-block.word-mute.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import { userPage } from '@/filters/user.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -142,17 +142,17 @@ import MkFolder from '@/components/MkFolder.vue';
 const renoteMutingPagination = {
 	endpoint: 'renote-mute/list' as const,
 	limit: 10,
-} satisfies Paging;
+};
 
 const mutingPagination = {
 	endpoint: 'mute/list' as const,
 	limit: 10,
-} satisfies Paging;
+};
 
 const blockingPagination = {
 	endpoint: 'blocking/list' as const,
 	limit: 10,
-} satisfies Paging;
+};
 
 const expandedRenoteMuteItems = ref([]);
 const expandedMuteItems = ref([]);
diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue
index 7fbcb2c8af..9ae479e6e4 100644
--- a/packages/frontend/src/pages/settings/security.vue
+++ b/packages/frontend/src/pages/settings/security.vue
@@ -45,7 +45,7 @@ import X2fa from './2fa.vue';
 import FormSection from '@/components/form/section.vue';
 import FormSlot from '@/components/form/slot.vue';
 import MkButton from '@/components/MkButton.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -53,7 +53,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 const pagination = {
 	endpoint: 'i/signin-history' as const,
 	limit: 5,
-} satisfies Paging;
+};
 
 async function change() {
 	const { canceled: canceled2, result: newPassword } = await os.inputText({
diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue
index da51520323..c391458274 100644
--- a/packages/frontend/src/pages/settings/webhook.vue
+++ b/packages/frontend/src/pages/settings/webhook.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed } from 'vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import FormSection from '@/components/form/section.vue';
 import FormLink from '@/components/form/link.vue';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -44,7 +44,7 @@ const pagination = {
 	endpoint: 'i/webhooks/list' as const,
 	limit: 100,
 	noPaging: true,
-} satisfies Paging;
+};
 
 const headerActions = computed(() => []);
 
diff --git a/packages/frontend/src/pages/user/clips.vue b/packages/frontend/src/pages/user/clips.vue
index da17975fdf..eaae472516 100644
--- a/packages/frontend/src/pages/user/clips.vue
+++ b/packages/frontend/src/pages/user/clips.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 
 const props = defineProps<{
 	user: Misskey.entities.User;
@@ -31,7 +31,7 @@ const pagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-} satisfies Paging;
+};
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/user/flashs.vue b/packages/frontend/src/pages/user/flashs.vue
index 267b1a2b87..5e93a0b04c 100644
--- a/packages/frontend/src/pages/user/flashs.vue
+++ b/packages/frontend/src/pages/user/flashs.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkFlashPreview from '@/components/MkFlashPreview.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 
 const props = defineProps<{
 	user: Misskey.entities.User;
@@ -27,5 +27,5 @@ const pagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-} satisfies Paging;
+};
 </script>
diff --git a/packages/frontend/src/pages/user/follow-list.vue b/packages/frontend/src/pages/user/follow-list.vue
index 7c0cb72067..19b7290353 100644
--- a/packages/frontend/src/pages/user/follow-list.vue
+++ b/packages/frontend/src/pages/user/follow-list.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkUserInfo from '@/components/MkUserInfo.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 
 const props = defineProps<{
 	user: Misskey.entities.User;
@@ -30,7 +30,7 @@ const followingPagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-} satisfies Paging;
+};
 
 const followersPagination = {
 	endpoint: 'users/followers' as const,
@@ -38,7 +38,7 @@ const followersPagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-} satisfies Paging;
+};
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/user/gallery.vue b/packages/frontend/src/pages/user/gallery.vue
index f7ec850965..0d806100d9 100644
--- a/packages/frontend/src/pages/user/gallery.vue
+++ b/packages/frontend/src/pages/user/gallery.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 
 const props = withDefaults(defineProps<{
 	user: Misskey.entities.User;
@@ -30,7 +30,7 @@ const pagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-} satisfies Paging;
+};
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/user/lists.vue b/packages/frontend/src/pages/user/lists.vue
index 545cc3b691..c58a8abdfb 100644
--- a/packages/frontend/src/pages/user/lists.vue
+++ b/packages/frontend/src/pages/user/lists.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import {} from 'vue';
 import * as Misskey from 'misskey-js';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkStickyContainer from '@/components/global/MkStickyContainer.vue';
 import MkSpacer from '@/components/global/MkSpacer.vue';
 import MkAvatars from '@/components/MkAvatars.vue';
@@ -37,7 +37,7 @@ const pagination = {
 	params: {
 		userId: props.user.id,
 	},
-} satisfies Paging;
+};
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/pages/user/pages.vue b/packages/frontend/src/pages/user/pages.vue
index 03373c3dfe..94ec80d05e 100644
--- a/packages/frontend/src/pages/user/pages.vue
+++ b/packages/frontend/src/pages/user/pages.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkPagePreview from '@/components/MkPagePreview.vue';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 
 const props = defineProps<{
 	user: Misskey.entities.User;
@@ -27,5 +27,5 @@ const pagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-} satisfies Paging;
+};
 </script>
diff --git a/packages/frontend/src/pages/user/reactions.vue b/packages/frontend/src/pages/user/reactions.vue
index dea66c6a17..916b6615d5 100644
--- a/packages/frontend/src/pages/user/reactions.vue
+++ b/packages/frontend/src/pages/user/reactions.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
-import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import MkPagination from '@/components/MkPagination.vue';
 import MkNote from '@/components/MkNote.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 
@@ -35,7 +35,7 @@ const pagination = {
 	params: computed(() => ({
 		userId: props.user.id,
 	})),
-} satisfies Paging;
+};
 </script>
 
 <style lang="scss" module>

From 6ae6ad6c9b563094985ce582b429149e4dab07fe Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 27 Dec 2023 15:55:27 +0900
Subject: [PATCH 381/435] New Crowdin updates (#12789)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (French)

* New translations ja-jp.yml (English)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Spanish)

* New translations ja-jp.yml (Russian)
---
 locales/en-US.yml |   8 +++
 locales/es-ES.yml |  20 ++++++++
 locales/fr-FR.yml |   8 +++
 locales/ko-GS.yml | 128 +++++++++++++++++++++++++++++++++++++++++++++-
 locales/ru-RU.yml |   8 +++
 5 files changed, 171 insertions(+), 1 deletion(-)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 888e26adbc..c1e39778b7 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -125,6 +125,8 @@ sensitive: "Sensitive"
 add: "Add"
 reaction: "Reactions"
 reactions: "Reactions"
+emojiPicker: "Emoji picker"
+emojiPickerDisplay: "Emoji picker display"
 reactionSettingDescription2: "Drag to reorder, click to delete, press \"+\" to add."
 rememberNoteVisibility: "Remember note visibility settings"
 attachCancel: "Remove attachment"
@@ -270,6 +272,7 @@ removed: "Successfully deleted"
 removeAreYouSure: "Are you sure that you want to remove \"{x}\"?"
 deleteAreYouSure: "Are you sure that you want to delete \"{x}\"?"
 resetAreYouSure: "Really reset?"
+areYouSure: "Are you sure?"
 saved: "Saved"
 messaging: "Chat"
 upload: "Upload"
@@ -891,6 +894,8 @@ makeReactionsPublicDescription: "This will make the list of all your past reacti
 classic: "Classic"
 muteThread: "Mute thread"
 unmuteThread: "Unmute thread"
+followingVisibility: "Visibility of follows"
+followersVisibility: "Visibility of followers"
 continueThread: "View thread continuation"
 deleteAccountConfirm: "This will irreversibly delete your account. Proceed?"
 incorrectPassword: "Incorrect password."
@@ -2014,6 +2019,7 @@ _widgets:
     chooseList: "Select a list"
   clicker: "Clicker"
   search: "Search"
+  birthdayFollowings: "Users who celebrate their birthday today"
 _cw:
   hide: "Hide"
   show: "Show content"
@@ -2461,6 +2467,8 @@ _dataSaver:
   _avatar:
     title: "Avatar image"
     description: "Stop avatar image animation. Animated images can be larger in file size than normal  images, potentially leading to further reductions in data traffic."
+  _urlPreview:
+    title: "URL preview thumbnails"
   _code:
     title: "Code highlighting"
     description: "If code highlighting notations are used in MFM, etc., they will not load until tapped. Syntax highlighting requires downloading the highlight definition files for each programming language. Therefore, disabling the automatic loading of these files is expected to reduce the amount of communication data."
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index 25b31ce428..c269cc4d75 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -1289,6 +1289,8 @@ _serverSettings:
   shortName: "Nombre corto"
   shortNameDescription: "Forma corta del nombre de la instancia que puede mostrarse si el nombre completo es demasiado largo."
   fanoutTimelineDescription: "Incrementa el rendimiento de forma significativa cuando se obtienen las líneas de tiempo y reduce la carga en la base de datos. A cambio, el uso de la memoria en Redis incrementará. Considera desactivar esta opción en caso de que tu servidor tenga poca memoria o detectes inestabilidad."
+  fanoutTimelineDbFallback: "Cargar desde la base de datos"
+  fanoutTimelineDbFallbackDescription: "Cuando esta opción está habilitada, la carga de peticiones adicionales de la línea de tiempo se hará desde la base de datos cuando éstas no se encuentren en la caché. Al deshabilitar esta opción se reduce la carga del servidor, pero limita el número de líneas de tiempo que pueden obtenerse."
 _accountMigration:
   moveFrom: "Trasladar de otra cuenta a ésta"
   moveFromSub: "Crear un alias para otra cuenta."
@@ -1833,6 +1835,14 @@ _sfx:
   notification: "Notificaciones"
   antenna: "Antena receptora"
   channel: "Notificaciones del canal"
+  reaction: "Al seleccionar una reacción"
+_soundSettings:
+  driveFile: "Usar un archivo de audio en Drive"
+  driveFileWarn: "Selecciona un archivo de audio en Drive."
+  driveFileTypeWarn: "Este archivo es incompatible"
+  driveFileTypeWarnDescription: "Selecciona un archivo de audio"
+  driveFileDurationWarn: "La duración del audio es demasiado larga."
+  driveFileDurationWarnDescription: "Usar un audio de larga duración puede llegar a molestar mientras usas Misskey. ¿Quieres continuar?"
 _ago:
   future: "Futuro"
   justNow: "Justo ahora"
@@ -2334,3 +2344,13 @@ _externalResourceInstaller:
 _dataSaver:
   _media:
     title: "Cargando Multimedia"
+    description: "Desactiva la carga automática de imágenes y vídeos. Tendrás que tocar en las imágenes y vídeos ocultos para cargarlos."
+  _avatar:
+    title: "Avatares animados"
+    description: "Desactiva la animación de los avatares. Las imágenes animadas pueden llegar a ser de mayor tamaño que las normales, por lo que al desactivarlas puedes reducir el consumo de datos."
+  _urlPreview:
+    title: "Vista previa de URLs"
+    description: "Desactiva la carga de vistas previas de las URLs."
+  _code:
+    title: "Resaltar código"
+    description: "Si se usa resaltado de código en MFM, etc., no se cargará hasta pulsar en ello. El resaltado de sintaxis requiere la descarga de archivos de definición para cada lenguaje de programación. Debido a esto, al deshabilitar la carga automática de estos archivos reducirás el consumo de datos."
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index f5b65f4b77..ac9e94a01a 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -162,6 +162,7 @@ addEmoji: "Ajouter un émoji"
 settingGuide: "Configuration proposée"
 cacheRemoteFiles: "Mise en cache des fichiers distants"
 cacheRemoteFilesDescription: "Lorsque cette option est désactivée, les fichiers distants sont chargés directement depuis l’instance distante. La désactiver diminuera certes l’utilisation de l’espace de stockage local mais augmentera le trafic réseau puisque les miniatures ne seront plus générées."
+youCanCleanRemoteFilesCache: "Vous pouvez supprimer tous les caches en cliquant le bouton 🗑️ dans la gestion des fichiers."
 cacheRemoteSensitiveFiles: "Mettre en cache les fichiers distants sensibles"
 cacheRemoteSensitiveFilesDescription: "Si vous désactivez ce paramètre, les fichiers sensibles distants ne seront pas mis en cache et un lien direct sera utilisé à la place"
 flagAsBot: "Ce compte est un robot"
@@ -726,6 +727,7 @@ lockedAccountInfo: "À moins que vous ne définissiez la visibilité de votre no
 alwaysMarkSensitive: "Marquer les médias comme contenu sensible par défaut"
 loadRawImages: "Affichage complet des images jointes au lieu des vignettes"
 disableShowingAnimatedImages: "Désactiver l'animation des images"
+highlightSensitiveMedia: "Mettre en évidence les médias sensibles"
 verificationEmailSent: "Un e-mail de vérification a été envoyé. Veuillez accéder au lien pour compléter la vérification."
 notSet: "Non défini"
 emailVerified: "Votre adresse e-mail a été vérifiée."
@@ -979,6 +981,7 @@ show: "Affichage"
 neverShow: "Ne plus afficher"
 remindMeLater: "Peut-être plus tard"
 didYouLikeMisskey: "Avez-vous aimé Misskey ?"
+pleaseDonate: "Misskey est le logiciel libre utilisé par {host}. Merci de faire un don pour que nous puissions continuer à le développer !"
 roles: "Rôles"
 role: "Rôles"
 noRole: "Aucun rôle"
@@ -991,8 +994,10 @@ manageCustomEmojis: "Gestion des émojis personnalisés"
 manageAvatarDecorations: "Gérer les décorations d'avatar"
 youCannotCreateAnymore: "Vous avez atteint la limite de création."
 cannotPerformTemporary: "Temporairement indisponible"
+cannotPerformTemporaryDescription: "Temporairement indisponible puisque le nombre d'opérations dépasse la limite. Veuillez patienter un peu, puis réessayer."
 invalidParamError: "Paramètres invalides"
 permissionDeniedError: "Opération refusée"
+permissionDeniedErrorDescription: "Ce compte n'a pas la permission d'effectuer cette opération."
 preset: "Préréglage"
 selectFromPresets: "Sélectionner à partir des préréglages"
 achievements: "Accomplissements"
@@ -1021,6 +1026,7 @@ likeOnlyForRemote: "Toutes (mentions j'aime seulement pour les instances distant
 nonSensitiveOnly: "Non sensibles seulement"
 nonSensitiveOnlyForLocalLikeOnlyForRemote: "Non sensibles seulement (mentions j'aime seulement pour les instances distantes)"
 rolesAssignedToMe: "Rôles attribués à moi"
+resetPasswordConfirm: "Souhaitez-vous réinitialiser votre mot de passe ?"
 sensitiveWords: "Mots sensibles"
 hiddenTags: "Hashtags cachés"
 hiddenTagsDescription: "Les hashtags définis ne s'afficheront pas dans les tendances. Vous pouvez définir plusieurs hashtags en faisant un saut de ligne."
@@ -1082,6 +1088,7 @@ installed: "Installé"
 branding: "Image de marque"
 expirationDate: "Date d’expiration"
 waitingForMailAuth: "En attente de la vérification de l'adresse courriel"
+inviteCodeCreator: "Créateur·rice de ce code d'invitation"
 usedAt: "Utilisé le"
 unused: "Non-utilisé"
 used: "Utilisé"
@@ -1765,6 +1772,7 @@ _visibility:
   followersDescription: "Publier à vos abonné·e·s uniquement"
   specified: "Direct"
   specifiedDescription: "Publier uniquement aux utilisateur·rice·s mentionné·e·s"
+  disableFederation: "Défédérer"
 _postForm:
   replyPlaceholder: "Répondre à cette note ..."
   quotePlaceholder: "Citez cette note ..."
diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml
index 9b113ad1b9..37bdf1e577 100644
--- a/locales/ko-GS.yml
+++ b/locales/ko-GS.yml
@@ -298,7 +298,7 @@ light: "볽엄"
 dark: "어덥엄"
 lightThemes: "볽언 테마"
 darkThemes: "어덥언 테마"
-syncDeviceDarkMode: "드라이브으 어덥엄 모드하고 같구로 마추기"
+syncDeviceDarkMode: "디바이스 쪽 어덥엄 모드하고 같구로 마추기"
 drive: "드라이브"
 fileName: "파일 이럼"
 selectFile: "파일 개리기"
@@ -425,20 +425,145 @@ moderationLogs: "중재 일지"
 nUsersMentioned: "{n}멩이 이바구하고 잇어예"
 securityKeyAndPasskey: "보안키·패스키"
 securityKey: "보안키"
+lastUsed: "마지막 쓰임"
+lastUsedAt: "마지막 쓰임: {t}"
 unregister: "맨걸기 무루기"
+passwordLessLogin: "비밀번호 없시 로그인"
+passwordLessLoginDescription: "비밀번호 말고 보안키나 패스키 같은 것만 써 가 로그인합니다."
+resetPassword: "비밀번호 재설정"
+newPasswordIs: "새 비밀번호는 \"{password}\" 입니다"
+reduceUiAnimation: "화면 움직임 효과들을 수ᇚ후기"
 share: "노누기"
 notFound: "몬 찾앗십니다"
+notFoundDescription: "고런 주소로 들어가는 하멘은 없십니다."
+uploadFolder: "기본 업로드 위치"
+markAsReadAllNotifications: "모든 알림 이럿다고 표시"
+markAsReadAllUnreadNotes: "모든 글 이럿다고 표시"
+markAsReadAllTalkMessages: "모든 대화 이럿다고 표시"
 help: "도움말"
+inputMessageHere: "여따가 메시지를 입력해주이소"
+close: "닫기"
 invites: "초대하기"
+members: "멤버"
+transfer: "양도"
+title: "제목"
+text: "글"
+enable: "키기"
+next: "다음"
 retype: "다시 서기"
 noteOf: "{user}님으 노트"
+quoteAttached: "따옴"
+quoteQuestion: "따와가 작성하겠십니까?"
+noMessagesYet: "아직 대화가 없십니다"
+newMessageExists: "새 메시지가 있십니다"
+onlyOneFileCanBeAttached: "메시지엔 파일 하나까제밖에 몬 넣십니다"
 invitations: "초대하기"
 checking: "학인하고 잇십니다"
 passwordMatched: "맞십니다"
 passwordNotMatched: "안 맞십니다"
+signinFailed: "로그인 몬 했십니다. 고 이름이랑 비밀번호 제대로 썼는가 확인해 주이소."
+or: "아니면"
 language: "언어"
+uiLanguage: "UI 표시 언어"
+aboutX: "{x}에 대해서"
+emojiStyle: "이모지 모양"
+native: "기본"
+disableDrawer: "드로어 메뉴 쓰지 않기"
+showNoteActionsOnlyHover: "마우스 올맀을 때만 노트 액션 버턴 보이기"
+noHistory: "기록이 없십니다"
+signinHistory: "로그인 기록"
+enableAdvancedMfm: "복잡한 MFM 키기"
+enableAnimatedMfm: "정신사나운 MFM 키기"
+doing: "잠만예"
+category: "카테고리"
+tags: "태그"
+docSource: "요 문서의 원본"
+createAccount: "게정 맨걸기"
+existingAccount: "원래 게정"
+regenerate: "엎고 다시 맨걸기"
+fontSize: "글자 크기"
+mediaListWithOneImageAppearance: "사진 하나짜리 미디어 목록의 높이"
+limitTo: "{x}로 제한"
+noFollowRequests: "지둘리는 팔로우 요청이 없십니다"
+openImageInNewTab: "새 탭서 사진 열기"
+dashboard: "대시보드"
+local: "로컬"
 remote: "웬겍"
+total: "합계"
+weekOverWeekChanges: "저번주보다"
+dayOverDayChanges: "어제보다"
+appearance: "모냥"
+clientSettings: "클라이언트 설정"
+accountSettings: "게정 설정"
+promotion: "선전"
+promote: "선전하기"
+numberOfDays: "며칠동안"
+hideThisNote: "요 노트를 수ᇚ후기"
+showFeaturedNotesInTimeline: "타임라인에다 추천 노트 보이기"
+objectStorage: "오브젝트 스토리지"
+useObjectStorage: "오브젝트 스토리지 키기"
+objectStorageBaseUrl: "Base URL"
+objectStorageBaseUrlDesc: "오브젝트 (미디어) 참조 링크 만들 때 쓰는 URL임다. CDN 내지 프락시를 쓴다 카멘은 그 URL을 갖다 늫고, 아이면 써먹을 서비스네 가이드를 봐봐가 공개적으로 접근할 수 있는 주소를 여 넣어 주이소. 그니께, 내가 AWS S3을 쓴다 카면은 'https://<bucket>.s3.amazonaws.com', GCS를 쓴다 카면 'https://storage.googleapis.com/<bucket>' 처럼 쓰믄 되입니더."
+objectStorageBucket: "Bucket"
+objectStorageBucketDesc: "써먹을 서비스의 바께쓰 이름을 여 써 주이소."
+objectStoragePrefix: "Prefix"
+objectStoragePrefixDesc: "요 Prefix 디렉토리 안에다가 파일이 들어감다."
+objectStorageEndpoint: "Endpoint"
+objectStorageEndpointDesc: "AWS S3을 쓸라멘 요는 비워두고, 아이멘은 그 서비스 가이드에 맞게 endpoint를 넣어 주이소. '<host>' 내지 '<host>:<port>'처럼 넣십니다."
+objectStorageRegion: "Region"
+objectStorageRegionDesc: "'xx-east-1' 같은 region 이름을 옇어 주이소. 써먹을 서비스에 region 개념 같은 게 읎다! 카면은 대신에 'us-east-1'을 옇어 놓으이소. AWS 설정 파일이나 환경 변수를 갖다 끌어다 쓸 거면은 요는 비워 두이소."
+objectStorageUseSSL: "SSL 쓰기"
+objectStorageUseSSLDesc: "API 호출할 때 HTTPS 안 쓸거면은 꺼 두이소"
+objectStorageUseProxy: "연결에 프락시 사용"
+objectStorageUseProxyDesc: "오브젝트 스토리지 API 호출에 프락시 안 쓸 거면 꺼 두이소"
+objectStorageSetPublicRead: "업로드할 때 'public-read' 설정하기"
+s3ForcePathStyleDesc: "s3ForcePathStyle을 키면, 바께쓰 이름을 URL의 호스트명 말고 경로의 일부로써 취급합니다. 셀프 호스트 Minio 같은 걸 굴릴라믄 켜놔야 될 수도 있십니다."
+serverLogs: "서버 로그"
+deleteAll: "말캉 뭉캐기"
+showFixedPostForm: "타임라인 우에 글 작성 칸 박기"
+showFixedPostFormInChannel: "채널 타임라인 우에 글 작성 칸 박기"
+withRepliesByDefaultForNewlyFollowed: "팔로우 할 때 기본적으로 답걸도 타임라인에 나오게 하기"
+newNoteRecived: "새 노트 있어예"
+sounds: "소리"
+sound: "소리"
+listen: "듣기"
+none: "없음"
+showInPage: "바닥서 보기"
+popout: "새 창 열기"
+volume: "음량"
+masterVolume: "대빵 음량"
+notUseSound: "음소거하기"
+useSoundOnlyWhenActive: "Misskey가 활성화되어 있을 때만 소리 내기"
+details: "좀 더"
+chooseEmoji: "이모지 선택"
+unableToProcess: "작업 다 몬 했십니다"
+recentUsed: "최근 쓴 놈"
+install: "설치"
+uninstall: "삭제"
+installedApps: "설치된 애플리케이션"
+nothing: "뭣도 없어예"
+installedDate: "설치한 날"
+lastUsedDate: "마지막 사용"
+state: "상태"
+sort: "정렬하기"
+ascendingOrder: "작은 순"
+descendingOrder: "큰 순"
+scratchpad: "스크래치 패드"
+scratchpadDescription: "스크래치 패드는 AiScript를 끼적거리는 창입니더. Misskey랑 갖다 이리저리 상호작용하는 코드를 서가 굴리멘은 그 결과도 바로 확인할 수 있십니다."
+output: "출력"
 script: "스크립트"
+disablePagesScript: "온갖 바닥서 AiScript를 쓰지 않음"
+updateRemoteUser: "원겍 사용자 근황 알아오기"
+unsetUserAvatar: "아바타 치우기"
+unsetUserAvatarConfirm: "아바타 갖다 치울까예?"
+unsetUserBanner: "배너 치우기"
+unsetUserBannerConfirm: "배너 갖다 치울까예?"
+deleteAllFiles: "파일 말캉 뭉캐기"
+deleteAllFilesConfirm: "파일을 싸그리 다 뭉캐삐릴까예?"
+removeAllFollowing: "팔로잉 말캉 무루기"
+removeAllFollowingDescription: "{host} 서버랑 걸어놓은 모든 팔로잉을 무룹니다. 고 서버가 아예 없어지삐맀든가, 그런 경우에 하이소."
+userSuspended: "요 게정은... 얼어 있십니다."
+userSilenced: "요 게정은... 수ᇚ혀 있십니다."
 manage: "간리"
 emailServer: "전자우펜 서버"
 email: "전자우펜"
@@ -581,4 +706,5 @@ _moderationLogTypes:
   suspend: "얼우기"
   deleteNote: "노트 뭉캐기"
   deleteUserAnnouncement: "사용자 공지 걸 뭉캐기"
+  resetPassword: "비밀번호 재설정"
   resolveAbuseReport: "신고 해겔하기"
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index b8095d7256..25f409df92 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -120,6 +120,12 @@ sensitive: "Содержимое не для всех"
 add: "Добавить"
 reaction: "Реакции"
 reactions: "Реакции"
+emojiPicker: "Палитра эмодзи"
+pinnedEmojisForReactionSettingDescription: "Здесь можно закрепить эмодзи для реакций"
+pinnedEmojisSettingDescription: "Здесь можно закрепить эмодзи в общей палитре"
+emojiPickerDisplay: "Внешний вид палитры"
+overwriteFromPinnedEmojisForReaction: "Заменить на эмодзи из списка реакций"
+overwriteFromPinnedEmojis: "Заменить на эмодзи из общего списка закреплённых"
 reactionSettingDescription2: "Расставляйте перетаскиванием, удаляйте нажатием, добавляйте кнопкой «+»."
 rememberNoteVisibility: "Запоминать видимость заметок"
 attachCancel: "Удалить вложение"
@@ -1053,6 +1059,8 @@ options: "Настройки ролей"
 specifyUser: "Указанный пользователь"
 failedToPreviewUrl: "Предварительный просмотр недоступен"
 update: "Обновить"
+rolesThatCanBeUsedThisEmojiAsReaction: "Роли тех, кому можно использовать эти эмодзи как реакцию"
+rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "Если здесь ничего не указать, в качестве реакции эту эмодзи сможет использовать каждый."
 later: "Позже"
 goToMisskey: "К Misskey"
 additionalEmojiDictionary: "Дополнительные словари эмодзи"

From 3f60d7c44b9c33f785f2d6565be61b34af7068f5 Mon Sep 17 00:00:00 2001
From: MomentQYC <62551256+MomentQYC@users.noreply.github.com>
Date: Wed, 27 Dec 2023 14:55:56 +0800
Subject: [PATCH 382/435] Add a prompt for Tor Browser users (#12776)

* perf: Add a prompt for Tor Browser users

* typo
---
 packages/backend/src/server/web/boot.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index e1aaee1d49..aac6689e12 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -199,6 +199,7 @@
 			<p>Clear the browser cache / ブラウザのキャッシュをクリアする</p>
 			<p>Update your os and browser / ブラウザおよびOSを最新バージョンに更新する</p>
 			<p>Disable an adblocker / アドブロッカーを無効にする</p>
+	 		<p>&#40;Tor Browser&#41; Set dom.webaudio.enabled to true / dom.webaudio.enabledをtrueに設定する</p>
 			<details style="color: #86b300;">
 				<summary>Other options / その他のオプション</summary>
 				<a href="/flush">

From 530a2825249b841a18dabed4bf17c75981470d70 Mon Sep 17 00:00:00 2001
From: Chocolate Pie <106949016+chocolate-pie@users.noreply.github.com>
Date: Wed, 27 Dec 2023 17:36:38 +0900
Subject: [PATCH 383/435] =?UTF-8?q?fix(test):=20CI=E3=81=8C=E8=90=BD?=
 =?UTF-8?q?=E3=81=A1=E3=81=A6=E3=81=84=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92?=
 =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20(#12816)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix(test): CIが落ちているのを修正

* fix(ci)?: CIの`typecheck`が落ちる問題を修正

* fix(ci): コンフィグファイルのタイポを修正
---
 .forgejo/workflows/lint.yml                                 | 2 ++
 CHANGELOG.md                                                | 2 ++
 .../src/server/api/endpoints/admin/accounts/create.ts       | 6 ++----
 packages/misskey-js/etc/misskey-js.api.md                   | 2 +-
 4 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/.forgejo/workflows/lint.yml b/.forgejo/workflows/lint.yml
index 132c6c455d..0a773d5fb0 100644
--- a/.forgejo/workflows/lint.yml
+++ b/.forgejo/workflows/lint.yml
@@ -80,4 +80,6 @@ jobs:
         cache: 'pnpm'
     - run: corepack enable
     - run: pnpm i --frozen-lockfile
+    - run: pnpm --filter misskey-js run build
+      if: ${{ matrix.workspace == 'backend' }}
     - run: pnpm --filter ${{ matrix.workspace }} run typecheck
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b7f37d747d..95fcbbd881 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,8 @@
 - Fix: 1702718871541-ffVisibility.jsのdownが壊れている
 - Fix:「非センシティブのみ(リモートはいいねのみ)」を設定していても、センシティブに設定されたカスタム絵文字をリアクションできる問題を修正
 - Fix: ロールアサイン時の通知で,ロールアイコンが縮小されずに表示される問題を修正
+- Fix: サードパーティアプリケーションがWebsocket APIに無条件にアクセスできる問題を修正
+- Fix: サードパーティアプリケーションがユーザーの許可なしに非公開の情報を見ることができる問題を修正
 
 ## 2023.12.0
 
diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
index a2f9bf6945..f54d567fff 100644
--- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts
@@ -15,8 +15,6 @@ import { DI } from '@/di-symbols.js';
 export const meta = {
 	tags: ['admin'],
 
-	secure: true,
-
 	res: {
 		type: 'object',
 		optional: false, nullable: false,
@@ -48,12 +46,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private userEntityService: UserEntityService,
 		private signupService: SignupService,
 	) {
-		super(meta, paramDef, async (ps, _me) => {
+		super(meta, paramDef, async (ps, _me, token) => {
 			const me = _me ? await this.usersRepository.findOneByOrFail({ id: _me.id }) : null;
 			const noUsers = (await this.usersRepository.countBy({
 				host: IsNull(),
 			})) === 0;
-			if (!noUsers && !me?.isRoot) throw new Error('access denied');
+			if ((!noUsers && !me?.isRoot) || token !== null) throw new Error('access denied');
 
 			const { account, secret } = await this.signupService.signup({
 				username: ps.username,
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 653372ba2c..d4c43f207c 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -2505,7 +2505,7 @@ type PagesUpdateRequest = operations['pages/update']['requestBody']['content']['
 function parse(acct: string): Acct;
 
 // @public (undocumented)
-export const permissions: string[];
+export const permissions: readonly ["read:account", "write:account", "read:blocks", "write:blocks", "read:drive", "write:drive", "read:favorites", "write:favorites", "read:following", "write:following", "read:messaging", "write:messaging", "read:mutes", "write:mutes", "write:notes", "read:notifications", "write:notifications", "read:reactions", "write:reactions", "write:votes", "read:pages", "write:pages", "write:page-likes", "read:page-likes", "read:user-groups", "write:user-groups", "read:channels", "write:channels", "read:gallery", "write:gallery", "read:gallery-likes", "write:gallery-likes", "read:flash", "write:flash", "read:flash-likes", "write:flash-likes", "read:admin:abuse-user-reports", "write:admin:delete-account", "write:admin:delete-all-files-of-a-user", "read:admin:index-stats", "read:admin:table-stats", "read:admin:user-ips", "read:admin:meta", "write:admin:reset-password", "write:admin:resolve-abuse-user-report", "write:admin:send-email", "read:admin:server-info", "read:admin:show-moderation-log", "read:admin:show-user", "read:admin:show-users", "write:admin:suspend-user", "write:admin:unset-user-avatar", "write:admin:unset-user-banner", "write:admin:unsuspend-user", "write:admin:meta", "write:admin:user-note", "write:admin:roles", "read:admin:roles", "write:admin:relays", "read:admin:relays", "write:admin:invite-codes", "read:admin:invite-codes", "write:admin:announcements", "read:admin:announcements", "write:admin:avatar-decorations", "read:admin:avatar-decorations", "write:admin:federation", "write:admin:account", "read:admin:account", "write:admin:emoji", "read:admin:emoji", "write:admin:queue", "read:admin:queue", "write:admin:promo", "write:admin:drive", "read:admin:drive", "write:admin:ad", "read:admin:ad", "write:invite-codes", "read:invite-codes", "write:clip-favorite", "read:clip-favorite", "read:federation", "write:report-abuse"];
 
 // @public (undocumented)
 type PingResponse = operations['ping']['responses']['200']['content']['application/json'];

From 24ca9ac5efbece864adccd3c3494eefb5376b3dc Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 27 Dec 2023 20:35:52 +0900
Subject: [PATCH 384/435] refactor

---
 packages/frontend/src/pages/channel-editor.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index bd38da7b63..912d02c7fc 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -99,7 +99,7 @@ const bannerId = ref<string | null>(null);
 const color = ref('#000');
 const isSensitive = ref(false);
 const allowRenoteToExternal = ref(true);
-const pinnedNotes = ref<Partial<Misskey.entities.Note>[]>([]);
+const pinnedNotes = ref<{ id: Misskey.entities.Note['id'] }[]>([]);
 
 watch(() => bannerId.value, async () => {
 	if (bannerId.value == null) {

From 49e2eb87e9727899d66dfed1e33ae2b43fd3a963 Mon Sep 17 00:00:00 2001
From: 1Step621 <86859447+1STEP621@users.noreply.github.com>
Date: Wed, 27 Dec 2023 20:41:01 +0900
Subject: [PATCH 385/435] =?UTF-8?q?Fix(frontend):=20MFM=E3=81=A7fg?=
 =?UTF-8?q?=E3=81=A8bg=E3=81=AB=E9=95=B7=E3=81=84=E5=8D=98=E8=AA=9E?=
 =?UTF-8?q?=E3=82=92=E4=BD=BF=E3=81=86=E3=81=A8=E6=94=B9=E8=A1=8C=E3=81=95?=
 =?UTF-8?q?=E3=82=8C=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE?=
 =?UTF-8?q?=E6=AD=A3=20(#12819)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* MFMでfgとbgに長い単語を使うと改行されない問題を修正

* update CHANGELOG.md
---
 CHANGELOG.md                                                  | 1 +
 .../src/components/global/MkMisskeyFlavoredMarkdown.ts        | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 95fcbbd881..1f65f62788 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@
 ### Client
 - Fix: 一部のモデログ(logYellowでの表示対象)について、表示の色が変わらない問題を修正
 - Feat: AiScript専用のMFM構文`$[clickable.ev=EVENTNAME ...]`を追加。`Mk:C:mfm`のオプション`onClickEv`に関数を渡すと、クリック時に`EVENTNAME`を引数にして呼び出す
+- Fix: `fg`/`bg`MFMに長い単語を指定すると、オーバーフローされずはみ出る問題を修正
 
 ### Server
 - Enhance: センシティブワードの設定がハッシュタグトレンドにも適用されるようになりました
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index 33d5786fb1..5bac440a81 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -251,13 +251,13 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) {
 					case 'fg': {
 						let color = token.props.args.color;
 						if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
-						style = `color: #${color};`;
+						style = `color: #${color}; overflow-wrap: anywhere;`;
 						break;
 					}
 					case 'bg': {
 						let color = token.props.args.color;
 						if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
-						style = `background-color: #${color};`;
+						style = `background-color: #${color}; overflow-wrap: anywhere;`;
 						break;
 					}
 					case 'ruby': {

From b0301dd2fbd48ada9c48be398e9b41865e6fef1f Mon Sep 17 00:00:00 2001
From: YAVIIGI <118232419+YAVIIGI@users.noreply.github.com>
Date: Wed, 27 Dec 2023 20:57:43 +0900
Subject: [PATCH 386/435] =?UTF-8?q?feat(frontend):=20=E6=8A=95=E7=A8=BF?=
 =?UTF-8?q?=E3=82=A6=E3=82=A4=E3=83=B3=E3=83=89=E3=82=A6=E3=81=ABMFM?=
 =?UTF-8?q?=E8=A6=81=E7=B4=A0=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B?=
 =?UTF-8?q?=E3=83=9C=E3=82=BF=E3=83=B3=E3=81=AE=E8=BF=BD=E5=8A=A0=20(#1278?=
 =?UTF-8?q?8)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* functionPicker の追加

* Update CHANGELOG.md

* fix lint errors

* Add addMfmFunction

* add enableQuickAddMfmFunction setting

* Update CHANGELOG.md

issue 番号を追加

* Update index.d.ts

* change 'functionPicker' to 'mfmFunctionPicker'

* Change indent from 4 space to 1 tab

---------

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
---
 CHANGELOG.md                                  |  3 +-
 locales/index.d.ts                            |  2 +
 locales/ja-JP.yml                             |  2 +
 .../frontend/src/components/MkPostForm.vue    | 12 ++++
 .../frontend/src/pages/settings/general.vue   |  2 +
 .../src/scripts/mfm-function-picker.ts        | 61 +++++++++++++++++++
 packages/frontend/src/store.ts                |  4 ++
 7 files changed, 85 insertions(+), 1 deletion(-)
 create mode 100644 packages/frontend/src/scripts/mfm-function-picker.ts

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1f65f62788..199a420f7b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,8 +19,9 @@
 - Fix: 自分のdirect noteがuser list timelineに追加されない
 
 ### Client
-- Fix: 一部のモデログ(logYellowでの表示対象)について、表示の色が変わらない問題を修正
 - Feat: AiScript専用のMFM構文`$[clickable.ev=EVENTNAME ...]`を追加。`Mk:C:mfm`のオプション`onClickEv`に関数を渡すと、クリック時に`EVENTNAME`を引数にして呼び出す
+- Enhance: MFM入力補助ボタンを投稿フォームに表示できるように #12787
+- Fix: 一部のモデログ(logYellowでの表示対象)について、表示の色が変わらない問題を修正
 - Fix: `fg`/`bg`MFMに長い単語を指定すると、オーバーフローされずはみ出る問題を修正
 
 ### Server
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 157b8f44d5..dd2f34a69a 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -1220,6 +1220,8 @@ export interface Locale {
     "overwriteContentConfirm": string;
     "seasonalScreenEffect": string;
     "decorate": string;
+    "addMfmFunction": string;
+    "enableQuickAddMfmFunction": string;
     "_announcement": {
         "forExistingUsers": string;
         "forExistingUsersDescription": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 7cb678b5f3..b632fbad63 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1217,6 +1217,8 @@ remainingN: "残り: {n}"
 overwriteContentConfirm: "現在の内容に上書きされますがよろしいですか?"
 seasonalScreenEffect: "季節に応じた画面の演出"
 decorate: "デコる"
+addMfmFunction: "装飾を追加"
+enableQuickAddMfmFunction: "高度なMFMのピッカーを表示する"
 
 _announcement:
   forExistingUsers: "既存ユーザーのみ"
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 8838da15a9..aa37cef6c2 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -86,6 +86,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 			<button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ph-hash ph-bold ph-lg"></i></button>
 			<button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugin" class="_button" :class="$style.footerButton" @click="showActions"><i class="ph-plug ph-bold ph-lg"></i></button>
 			<button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ph-smiley ph-bold ph-lg"></i></button>
+			<button v-if="showAddMfmFunction" v-tooltip="i18n.ts.addMfmFunction" :class="['_button', $style.footerButton]" @click="insertMfmFunction"><i class="ph-palette ph-bold ph-lg"></i></button>
 		</div>
 		<div :class="$style.footerRight">
 			<button v-tooltip="i18n.ts.previewNoteText" class="_button" :class="[$style.footerButton, { [$style.previewButtonActive]: showPreview }]" @click="showPreview = !showPreview"><i class="ph-eye ph-bold ph-lg"></i></button>
@@ -127,6 +128,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue';
 import { miLocalStorage } from '@/local-storage.js';
 import { claimAchievement } from '@/scripts/achievements.js';
 import { emojiPicker } from '@/scripts/emoji-picker.js';
+import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js';
 
 const modal = inject('modal');
 
@@ -184,6 +186,8 @@ const poll = ref<{
 const useCw = ref<boolean>(!!props.initialCw);
 const showPreview = ref(defaultStore.state.showPreview);
 watch(showPreview, () => defaultStore.set('showPreview', showPreview.value));
+const showAddMfmFunction = ref(defaultStore.state.enableQuickAddMfmFunction);
+watch(showAddMfmFunction, () => defaultStore.set('enableQuickAddMfmFunction', showAddMfmFunction.value));
 const cw = ref<string | null>(props.initialCw ?? null);
 const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
 const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
@@ -870,6 +874,14 @@ async function insertEmoji(ev: MouseEvent) {
 	);
 }
 
+async function insertMfmFunction(ev: MouseEvent) {
+	mfmFunctionPicker(
+		ev.currentTarget ?? ev.target,
+		textareaEl.value,
+		text,
+	);
+}
+
 function showActions(ev) {
 	os.popupMenu(postFormActions.map(action => ({
 		text: action.title,
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 0839a65ebb..8eacdd32e6 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -52,6 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 				<MkSwitch v-model="expandLongNote">Always expand long notes</MkSwitch>
 				<MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch>
 				<MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
+				<MkSwitch v-if="advancedMfm" v-model="enableQuickAddMfmFunction">{{ i18n.ts.enableQuickAddMfmFunction }}</MkSwitch>
 				<MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
 				<MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch>
 				<MkSwitch v-model="showTickerOnReplies">Show instance ticker on replies</MkSwitch>
@@ -296,6 +297,7 @@ const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect'));
 const showGapBetweenNotesInTimeline = computed(defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'));
 const animatedMfm = computed(defaultStore.makeGetterSetter('animatedMfm'));
 const advancedMfm = computed(defaultStore.makeGetterSetter('advancedMfm'));
+const enableQuickAddMfmFunction = computed(defaultStore.makeGetterSetter('enableQuickAddMfmFunction'));
 const emojiStyle = computed(defaultStore.makeGetterSetter('emojiStyle'));
 const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer'));
 const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages'));
diff --git a/packages/frontend/src/scripts/mfm-function-picker.ts b/packages/frontend/src/scripts/mfm-function-picker.ts
new file mode 100644
index 0000000000..465926fe04
--- /dev/null
+++ b/packages/frontend/src/scripts/mfm-function-picker.ts
@@ -0,0 +1,61 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Ref, nextTick } from 'vue';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { MFM_TAGS } from '@/const.js';
+
+/**
+ * MFMの装飾のリストを表示する
+ */
+export function mfmFunctionPicker(src: any, textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) {
+	return new Promise((res, rej) => {
+		os.popupMenu([{
+			text: i18n.ts.addMfmFunction,
+			type: 'label',
+		}, ...getFunctionList(textArea, textRef)], src);
+	});
+}
+
+function getFunctionList(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>) : object[] {
+	const ret: object[] = [];
+	MFM_TAGS.forEach(tag => {
+		ret.push({
+			text: tag,
+			icon: 'ti ti-icons',
+			action: () => add(textArea, textRef, tag),
+		});
+	});
+	return ret;
+}
+
+function add(textArea: HTMLInputElement | HTMLTextAreaElement, textRef: Ref<string>, type: string) {
+	const caretStart: number = textArea.selectionStart as number;
+	const caretEnd: number = textArea.selectionEnd as number;
+
+	MFM_TAGS.forEach(tag => {
+		if (type === tag) {
+			if (caretStart === caretEnd) {
+				// 単純にFunctionを追加
+				const trimmedText = `${textRef.value.substring(0, caretStart)}$[${type} ]${textRef.value.substring(caretEnd)}`;
+				textRef.value = trimmedText;
+			} else {
+				// 選択範囲を囲むようにFunctionを追加
+				const trimmedText = `${textRef.value.substring(0, caretStart)}$[${type} ${textRef.value.substring(caretStart, caretEnd)}]${textRef.value.substring(caretEnd)}`;
+				textRef.value = trimmedText;
+			}
+		}
+	});
+
+	const nextCaretStart: number = caretStart + 3 + type.length;
+	const nextCaretEnd: number = caretEnd + 3 + type.length;
+
+	// キャレットを戻す
+	nextTick(() => {
+		textArea.focus();
+		textArea.setSelectionRange(nextCaretStart, nextCaretEnd);
+	});
+}
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index c86c3d01ad..18cfad2102 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -239,6 +239,10 @@ export const defaultStore = markRaw(new Storage('base', {
 		where: 'device',
 		default: true,
 	},
+	enableQuickAddMfmFunction: {
+		where: 'device',
+		default: false,
+	},
 	loadRawImages: {
 		where: 'device',
 		default: false,

From 30f7611229696c8a8eb1d85a67ce8fbea687148d Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 27 Dec 2023 21:28:15 +0900
Subject: [PATCH 387/435] 2023.12.1

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

diff --git a/package.json b/package.json
index 6fad6f8bf8..834747792f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "sharkey",
-	"version": "2023.12.0.beta2",
+	"version": "2023.12.0.beta3",
 	"codename": "shonk",
 	"repository": {
 		"type": "git",

From de3bd2fcaef7e5acd9240b8e06961a782d1f9102 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 28 Dec 2023 00:04:22 +0900
Subject: [PATCH 388/435] Update CHANGELOG.md (#12826)

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 199a420f7b..1ddd779429 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,10 @@
 
 ## 2023.12.1
 
+### Note
+- アクセストークンの権限が再整理されたため、一部のAPIが古いAPIトークンでは動作しなくなりました。\
+  権限不足になる場合には権限を再設定して再生成してください。
+
 ### General
 - Enhance: ローカリゼーションの更新
 - Fix: 自分のdirect noteがuser list timelineに追加されない

From 67259695108965e47e340bb3888fca771a074657 Mon Sep 17 00:00:00 2001
From: anatawa12 <anatawa12@icloud.com>
Date: Thu, 28 Dec 2023 07:52:08 +0900
Subject: [PATCH 389/435] fix: running from docker is broken (#12824)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: running from docker is broken

* fix: dependencies of misskey-js not found from backend

* docs(changelog): Dockerでサーバーを起動できない問題を修正

* Update CHANGELOG.md

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>

---------

Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
---
 CHANGELOG.md | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1ddd779429..69f30f9232 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,11 @@
 
 -->
 
+## 2023.12.2
+
+### General
+- v2023.12.1でDockerを利用してサーバーを起動できない問題を修正
+
 ## 2023.12.1
 
 ### Note

From 16f401304475039512b1e06633751d0bdcd4398a Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Thu, 28 Dec 2023 07:58:15 +0900
Subject: [PATCH 390/435] New Crowdin updates (#12820)

* New translations ja-jp.yml (Korean)

* New translations ja-jp.yml (Korean (Gyeongsang))

* New translations ja-jp.yml (Korean)

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

* New translations ja-jp.yml (Indonesian)

* New translations ja-jp.yml (Bengali)
---
 locales/bn-BD.yml | 18 ++++++++++
 locales/id-ID.yml |  8 +++++
 locales/ko-GS.yml | 13 ++++++++
 locales/ko-KR.yml | 83 ++++++++++++++++++++++++++++++++++++++---------
 locales/zh-TW.yml |  2 ++
 5 files changed, 108 insertions(+), 16 deletions(-)

diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index c659e13250..77ba3f0306 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -2,6 +2,7 @@
 _lang_: "বাংলা"
 headlineMisskey: "নোট ব্যাবহার করে সংযুক্ত নেটওয়ার্ক"
 introMisskey: "স্বাগতম! মিসকি একটি ওপেন সোর্স, ডিসেন্ট্রালাইজড মাইক্রোব্লগিং পরিষেবা। \n\"নোট\" তৈরির মাধ্যমে যা ঘটছে তা সবার সাথে শেয়ার করুন 📡\n\"রিঅ্যাকশন\" গুলির মাধ্যমে যেকোনো নোট সম্পর্কে আপনার অনুভূতি ব্যাক্ত করতে পারেন 👍\nএকটি নতুন দুনিয়া ঘুরে দেখুন 🚀\n"
+poweredByMisskeyDescription: "{name} হল ওপেন সোর্স প্ল্যাটফর্ম <b>Misskey</b>-এর সার্ভারগুলির একটি৷"
 monthAndDay: "{day}/{month}"
 search: "খুঁজুন"
 notifications: "বিজ্ঞপ্তি"
@@ -12,12 +13,14 @@ fetchingAsApObject: "ফেডিভার্স থেকে খবর আন
 ok: "ঠিক"
 gotIt: "বুঝেছি"
 cancel: "বাতিল"
+noThankYou: "না, ধন্যবাদ"
 enterUsername: "ইউজারনেম লিখুন"
 renotedBy: "{user} রিনোট করেছেন"
 noNotes: "কোন নোট নেই"
 noNotifications: "কোনো বিজ্ঞপ্তি নেই"
 instance: "ইন্সট্যান্স"
 settings: "সেটিংস"
+notificationSettings: "বিজ্ঞপ্তির সেটিংস"
 basicSettings: "সাধারণ সেটিংস"
 otherSettings: "অন্যান্য সেটিংস"
 openInWindow: "নতুন উইন্ডোতে খুলা"
@@ -42,12 +45,20 @@ pin: "পিন করা"
 unpin: "পিন সরান"
 copyContent: "বিষয়বস্তু কপি করুন"
 copyLink: "লিঙ্ক কপি করুন"
+copyLinkRenote: "রিনোট লিঙ্ক কপি করুন"
 delete: "মুছুন"
 deleteAndEdit: "মুছুন এবং সম্পাদনা করুন"
 deleteAndEditConfirm: "আপনি কি এই নোটটি মুছে এটি সম্পাদনা করার বিষয়ে নিশ্চিত? আপনি এটির সমস্ত রিঅ্যাকশন, রিনোট এবং জবাব হারাবেন।"
 addToList: "লিস্ট এ যোগ করুন"
+addToAntenna: "অ্যান্টেনা এ যোগ করুন"
 sendMessage: "একটি বার্তা পাঠান"
+copyRSS: "RSS কপি করুন"
 copyUsername: "ব্যবহারকারীর নাম কপি করুন"
+copyUserId: "ব্যবহারকারীর ID কপি করুন"
+copyNoteId: "নোটের ID কপি করুন"
+copyFileId: "ফাইল ID কপি করুন"
+copyFolderId: "ফোল্ডার ID কপি করুন"
+copyProfileUrl: "প্রোফাইল URL কপি করুন"
 searchUser: "ব্যবহারকারী খুঁজুন..."
 reply: "জবাব"
 loadMore: "আরও দেখুন"
@@ -100,6 +111,8 @@ renoted: "রিনোট করা হয়েছে"
 cantRenote: "এই নোটটি রিনোট করা যাবে না।"
 cantReRenote: "রিনোটকে রিনোট করা যাবে না।"
 quote: "উদ্ধৃতি"
+inChannelRenote: "চ্যানেলে রিনোট"
+inChannelQuote: "চ্যানেলে উদ্ধৃতি"
 pinnedNote: "পিন করা নোট"
 pinned: "পিন করা"
 you: "আপনি"
@@ -108,6 +121,10 @@ sensitive: "সংবেদনশীল বিষয়বস্তু"
 add: "যুক্ত করুন"
 reaction: "প্রতিক্রিয়া"
 reactions: "প্রতিক্রিয়া"
+emojiPicker: "ইমোজি পিকার"
+pinnedEmojisForReactionSettingDescription: "রিঅ্যাকশন দেয়ার সময় আপনি ইমোজিটিকে পিন করা এবং প্রদর্শিত হওয়ার জন্য সেট করতে পারেন।"
+pinnedEmojisSettingDescription: "ইমোজি ইনপুট দেয়ার সময় আপনি ইমোজিটিকে পিন করা এবং প্রদর্শিত হওয়ার জন্য সেট করতে পারেন।"
+emojiPickerDisplay: "পিকার ডিসপ্লে"
 reactionSettingDescription2: "পুনরায় সাজাতে টেনে আনুন, মুছতে ক্লিক করুন, যোগ করতে + টিপুন।"
 rememberNoteVisibility: "নোটের দৃশ্যমান্যতার সেটিংস মনে রাখুন"
 attachCancel: "অ্যাটাচমেন্ট সরান "
@@ -1034,6 +1051,7 @@ _2fa:
   step3: "অ্যাপে প্রদর্শিত টোকেনটি লিখুন এবং আপনার কাজ শেষ।"
   step4: "আপনাকে এখন থেকে লগ ইন করার সময়, এইভাবে টোকেন লিখতে হবে।"
   securityKeyInfo: "আপনি একটি হার্ডওয়্যার সিকিউরিটি কী ব্যবহার করে লগ ইন করতে পারেন যা FIDO2 বা ডিভাইসের ফিঙ্গারপ্রিন্ট সেন্সর বা পিন সমর্থন করে৷"
+  renewTOTPCancel: "না, ধন্যবাদ"
 _permissions:
   "read:account": "অ্যাকাউন্টের তথ্য দেখুন"
   "write:account": "অ্যাকাউন্টের তথ্য সম্পাদন করুন"
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index 458202ac12..00844550fd 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -121,6 +121,10 @@ sensitive: "Konten sensitif"
 add: "Tambahkan"
 reaction: "Reaksi"
 reactions: "Reaksi"
+emojiPicker: "Emoji Picker"
+pinnedEmojisForReactionSettingDescription: "Atur sematan emoji pada reaksi"
+pinnedEmojisSettingDescription: "Atur sematan emoji pada masukan emoji"
+emojiPickerDisplay: "Tampilan Emoji Picker"
 reactionSettingDescription2: "Geser untuk memindah urutan emoji, klik untuk menghapus, tekan \"+\" untuk menambahkan"
 rememberNoteVisibility: "Ingat pengaturan visibilitas catatan"
 attachCancel: "Hapus lampiran"
@@ -641,6 +645,7 @@ smtpSecure: "Gunakan SSL/TLS implisit untuk koneksi SMTP"
 smtpSecureInfo: "Matikan ini ketika menggunakan STARTTLS"
 testEmail: "Tes pengiriman surel"
 wordMute: "Bisukan kata"
+hardWordMute: "Pembisuan kata keras"
 regexpError: "Kesalahan ekspresi reguler"
 regexpErrorDescription: "Galat terjadi pada baris {line} ekspresi reguler dari {tab} kata yang dibisukan:"
 instanceMute: "Bisukan instansi"
@@ -1154,6 +1159,7 @@ tosAndPrivacyPolicy: "Syarat dan Ketentuan serta Kebijakan Privasi"
 avatarDecorations: "Dekorasi avatar"
 attach: "Lampirkan"
 detach: "Hapus"
+detachAll: "Lepas Semua"
 angle: "Sudut"
 flip: "Balik"
 showAvatarDecorations: "Tampilkan dekorasi avatar"
@@ -1168,6 +1174,7 @@ doReaction: "Tambahkan reaksi"
 code: "Kode"
 reloadRequiredToApplySettings: "Muat ulang diperlukan untuk menerapkan pengaturan."
 remainingN: "Sisa : {n}"
+decorate: "Dekor"
 _announcement:
   forExistingUsers: "Hanya pengguna yang telah ada"
   forExistingUsersDescription: "Pengumuman ini akan dimunculkan ke pengguna yang sudah ada dari titik waktu publikasi jika dinyalakan. Apabila dimatikan, mereka yang baru mendaftar setelah publikasi ini akan juga melihatnya."
@@ -1215,6 +1222,7 @@ _initialTutorial:
       followers: "Perlihatkan ke pengikut saja. Hanya pengikut yang dapat melihat postinganmu dan tidak dapat direnote oleh siapapun."
       direct: "Hanya perlihatkan ke pengguna spesifik dan penerima akan diberi tahu. Dapat juga digunakan sebagai alternatif dari pesan langsung."
     _cw:
+      title: "Peringatan Konten (CW)"
       _exampleNote:
         cw: "Peringatan: Bikin Lapar!"
         note: "Baru aja makan donat berlapis coklat 🍩😋"
diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml
index 37bdf1e577..566667ba79 100644
--- a/locales/ko-GS.yml
+++ b/locales/ko-GS.yml
@@ -260,6 +260,7 @@ removed: "뭉캣십니다"
 removeAreYouSure: "‘{x}’(얼)럴 뭉캡니꺼?"
 deleteAreYouSure: "‘{x}’(얼)럴 뭉캡니꺼?"
 resetAreYouSure: "아시로 데돌립니꺼?"
+areYouSure: "갠찮십니꺼?"
 saved: "저장햇십니다"
 messaging: "대화"
 upload: "올리기"
@@ -458,6 +459,7 @@ noMessagesYet: "아직 대화가 없십니다"
 newMessageExists: "새 메시지가 있십니다"
 onlyOneFileCanBeAttached: "메시지엔 파일 하나까제밖에 몬 넣십니다"
 invitations: "초대하기"
+invitationCode: "초대장"
 checking: "학인하고 잇십니다"
 passwordMatched: "맞십니다"
 passwordNotMatched: "안 맞십니다"
@@ -564,6 +566,11 @@ removeAllFollowing: "팔로잉 말캉 무루기"
 removeAllFollowingDescription: "{host} 서버랑 걸어놓은 모든 팔로잉을 무룹니다. 고 서버가 아예 없어지삐맀든가, 그런 경우에 하이소."
 userSuspended: "요 게정은... 얼어 있십니다."
 userSilenced: "요 게정은... 수ᇚ혀 있십니다."
+relays: "릴레이"
+addRelay: "릴레이 옇기"
+addedRelays: "옇은 릴레이"
+enableInfiniteScroll: "알아서 더 보기"
+author: "맨던 사람"
 manage: "간리"
 emailServer: "전자우펜 서버"
 email: "전자우펜"
@@ -572,6 +579,8 @@ smtpHost: "호스트 이럼"
 smtpPort: "포트"
 smtpUser: "사용자 이럼"
 smtpPass: "비밀번호"
+display: "보기"
+create: "맨걸기"
 abuseReports: "신고하기"
 reportAbuse: "신고하기"
 reportAbuseRenote: "리노트 신고하기"
@@ -583,6 +592,7 @@ forwardReport: "웬겍 서버에 신고 보내기"
 random: "무작이"
 system: "시스템"
 clip: "클립 맨걸기"
+createNew: "새로 맨걸기"
 notesCount: "노트 수"
 renotesCount: "리노트한 수"
 renotedCount: "리노트덴 수"
@@ -608,6 +618,7 @@ tools: "도구"
 like: "좋네예!"
 unlike: "좋네예 무루기"
 numberOfLikes: "좋네예 수"
+show: "보기"
 roles: "옉할"
 role: "옉할"
 noRole: "옉할이 없십니다"
@@ -637,6 +648,8 @@ _gallery:
 _email:
   _follow:
     title: "새 팔로워가 잇십니다"
+_serverDisconnectedBehavior:
+  reload: "알아서 새로곤침"
 _channel:
   removeBanner: "배너 뭉캐기"
 _theme:
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 63d0812e93..4a13012eed 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -425,9 +425,9 @@ setupOf2fa: "2단계 인증 설정"
 totp: "인증 앱"
 totpDescription: "인증 앱을 사용하여 일회성 비밀번호 입력"
 moderator: "모더레이터"
-moderation: "모더레이션"
-moderationNote: "모더레이션 노트"
-addModerationNote: "모더레이션 노트 추가하기"
+moderation: "조정"
+moderationNote: "조정 기록"
+addModerationNote: "조정 기록 추가하기"
 moderationLogs: "모더레이션 로그"
 nUsersMentioned: "{n}명이 언급함"
 securityKeyAndPasskey: "보안 키 또는 패스 키"
@@ -513,7 +513,7 @@ dayOverDayChanges: "어제보다"
 appearance: "모양"
 clientSettings: "클라이언트 설정"
 accountSettings: "계정 설정"
-promotion: "프로모션"
+promotion: "홍보"
 promote: "프로모션하기"
 numberOfDays: "며칠동안"
 hideThisNote: "이 노트를 숨기기"
@@ -863,8 +863,8 @@ devMode: "개발자 모드"
 keepCw: "CW 유지하기"
 pubSub: "Pub/Sub 계정"
 lastCommunication: "마지막 통신"
-resolved: "해결됨"
-unresolved: "해결되지 않음"
+resolved: "처리함"
+unresolved: "처리되지 않음"
 breakFollow: "팔로워 해제"
 breakFollowConfirm: "팔로우를 해제하시겠습니까?"
 itsOn: "켜져 있습니다"
@@ -1181,6 +1181,8 @@ remainingN: "나머지: {n}"
 overwriteContentConfirm: "현재 내용을 덮어쓰기 합니다. 계속 진행하시겠습니까?"
 seasonalScreenEffect: "계절에 따른 효과 보이기"
 decorate: "장식하기"
+addMfmFunction: "장식 추가하기"
+enableQuickAddMfmFunction: "상급자용 MFM 선택기 표시하기"
 _announcement:
   forExistingUsers: "기존 유저에게만 알림"
   forExistingUsersDescription: "활성화하면 이 공지사항을 게시한 시점에서 이미 가입한 유저에게만 표시합니다. 비활성화하면 게시 후에 가입한 유저에게도 표시합니다."
@@ -1557,7 +1559,7 @@ _role:
   name: "역할 이름"
   description: "역할 설명"
   permission: "역할 권한"
-  descriptionOfPermission: "<b>모더레이터</b>는 기본적인 중재와 관련된 작업을 수행할 수 있습니다.\n<b>관리자</b>는 서버의 모든 설정을 변경할 수 있습니다."
+  descriptionOfPermission: "<b>조정자</b>는 기본적인 조정 작업을 진행할 수 있습니다.\n<b>관리자</b>는 서버의 모든 설정을 변경할 수 있습니다."
   assignTarget: "할당 대상"
   descriptionOfAssignTarget: "<b>수동</b>을 선택하면 누가 이 역할에 포함되는지를 수동으로 관리할 수 있습니다.\n<b>조건부</b>를 선택하면 조건을 설정해 일치하는 사용자를 자동으로 포함되게 할 수 있습니다."
   manual: "수동"
@@ -1628,7 +1630,7 @@ _role:
     or: "다음을 하나라도 만족"
     not: "다음을 만족하지 않음"
 _sensitiveMediaDetection:
-  description: "기계학습을 통해 자동으로 민감한 미디어를 탐지하여, 모더레이션에 참고할 수 있도록 합니다. 서버의 부하를 약간 증가시킵니다."
+  description: "기계 학습으로 민감한 미디어를 알아서 찾아내어 조정에 참고하도록 합니다. 서버가 부하를 다소 받습니다."
   sensitivity: "탐지 민감도"
   sensitivityDescription: "민감도가 낮을수록 안전한 미디어가 잘못 탐지될 확률이 줄어들며, 높을수록 민감한 미디어가 탐지되지 않을 확률이 줄어듭니다."
   setSensitiveFlagAutomatically: "자동으로 NSFW로 설정하기"
@@ -1933,6 +1935,55 @@ _permissions:
   "write:flash": "Play를 조작합니다"
   "read:flash-likes": "Play의 좋아요를 봅니다"
   "write:flash-likes": "Play의 좋아요를 조작합니다"
+  "read:admin:abuse-user-reports": "사용자 신고 보기"
+  "write:admin:delete-account": "사용자 계정 삭제하기"
+  "write:admin:delete-all-files-of-a-user": "모든 사용자 파일 삭제하기"
+  "read:admin:index-stats": "데이터베이스 색인 정보 보기"
+  "read:admin:table-stats": "데이터베이스 테이블 정보 보기"
+  "read:admin:user-ips": "사용자 IP 주소 보기"
+  "read:admin:meta": "인스턴스 메타데이터 보기"
+  "write:admin:reset-password": "사용자 비밀번호 재설정하기"
+  "write:admin:resolve-abuse-user-report": "사용자 신고 처리하기"
+  "write:admin:send-email": "이메일 보내기"
+  "read:admin:server-info": "서버 정보 보기"
+  "read:admin:show-moderation-log": "조정 기록 보기"
+  "read:admin:show-user": "사용자 개인정보 보기"
+  "read:admin:show-users": "사용자 개인정보 보기"
+  "write:admin:suspend-user": "사용자 정지하기"
+  "write:admin:unset-user-avatar": "사용자 아바타 삭제하기"
+  "write:admin:unset-user-banner": "사용자 배너 삭제하기"
+  "write:admin:unsuspend-user": "사용자 정지 해제하기"
+  "write:admin:meta": "인스턴스 메타데이터 수정하기"
+  "write:admin:user-note": "조정 기록 수정하기"
+  "write:admin:roles": "역할 수정하기"
+  "read:admin:roles": "역할 보기"
+  "write:admin:relays": "릴레이 수정하기"
+  "read:admin:relays": "릴레이 보기"
+  "write:admin:invite-codes": "초대 코드 수정하기"
+  "read:admin:invite-codes": "초대 코드 보기"
+  "write:admin:announcements": "공지사항 수정하기"
+  "read:admin:announcements": "공지사항 보기"
+  "write:admin:avatar-decorations": "아바타 꾸미기 수정하기"
+  "read:admin:avatar-decorations": "아바타 꾸미기 보기"
+  "write:admin:federation": "연합 정보 수정하기"
+  "write:admin:account": "사용자 계정 수정하기"
+  "read:admin:account": "사용자 정보 보기"
+  "write:admin:emoji": "이모지 수정하기"
+  "read:admin:emoji": "이모지 보기"
+  "write:admin:queue": "작업 대기열 수정하기"
+  "read:admin:queue": "작업 대기열 정보 보기"
+  "write:admin:promo": "홍보 기록 수정하기"
+  "write:admin:drive": "사용자 드라이브 수정하기"
+  "read:admin:drive": "사용자 드라이브 정보 보기"
+  "read:admin:stream": "관리자용 Websocket API 사용하기"
+  "write:admin:ad": "광고 수정하기"
+  "read:admin:ad": "광고 보기"
+  "write:invite-codes": "초대 코드 만들기"
+  "read:invite-codes": "초대 코드 불러오기"
+  "write:clip-favorite": "클립의 좋아요 수정하기"
+  "read:clip-favorite": "클립의 좋아요 보기"
+  "read:federation": "연합 정보 불러오기"
+  "write:report-abuse": "위반 내용 신고하기"
 _auth:
   shareAccessTitle: "어플리케이션의 접근 허가"
   shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?"
@@ -2267,21 +2318,21 @@ _moderationLogTypes:
   updateCustomEmoji: "커스텀 이모지 수정"
   deleteCustomEmoji: "커스텀 이모지 삭제"
   updateServerSettings: "서버 설정 갱신"
-  updateUserNote: "모더레이션 노트 갱신"
+  updateUserNote: "조정 기록 갱신"
   deleteDriveFile: "파일 삭제"
   deleteNote: "노트 삭제"
-  createGlobalAnnouncement: "전역 공지사항 생성"
-  createUserAnnouncement: "유저 공지사항 생성"
-  updateGlobalAnnouncement: "전역 공지사항 수정"
-  updateUserAnnouncement: "유저 공지사항 수정"
-  deleteGlobalAnnouncement: "전역 공지사항 삭제"
-  deleteUserAnnouncement: "유저 공지사항 삭제"
+  createGlobalAnnouncement: "모든 공지사항 만들기"
+  createUserAnnouncement: "사용자 공지사항 만들기"
+  updateGlobalAnnouncement: "모든 공지사항 수정"
+  updateUserAnnouncement: "사용자 공지사항 수정"
+  deleteGlobalAnnouncement: "모든 공지사항 삭제"
+  deleteUserAnnouncement: "사용자 공지사항 삭제"
   resetPassword: "비밀번호 재설정"
   suspendRemoteInstance: "리모트 서버를 정지"
   unsuspendRemoteInstance: "리모트 서버의 정지를 해제"
   markSensitiveDriveFile: "파일에 열람주의를 설정"
   unmarkSensitiveDriveFile: "파일에 열람주의를 해제"
-  resolveAbuseReport: "신고 해결"
+  resolveAbuseReport: "신고 처리"
   createInvitation: "초대 코드 생성"
   createAd: "광고 생성"
   deleteAd: "광고 삭제"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index 782f871b1e..36b6e77e9b 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -1181,6 +1181,8 @@ remainingN: "剩餘:{n}"
 overwriteContentConfirm: "確定要覆蓋目前的內容嗎?"
 seasonalScreenEffect: "隨季節變換畫面的呈現"
 decorate: "設置頭像裝飾"
+addMfmFunction: "插入MFM功能語法"
+enableQuickAddMfmFunction: "顯示高級MFM選擇器"
 _announcement:
   forExistingUsers: "僅限既有的使用者"
   forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。"

From 3c3f7fd5a66730684e7645709d6bd1c3eb582794 Mon Sep 17 00:00:00 2001
From: Korange <korange753+r@gmail.com>
Date: Thu, 28 Dec 2023 07:58:32 +0900
Subject: [PATCH 391/435] =?UTF-8?q?enhance(frontend):=20=E6=A4=9C=E7=B4=A2?=
 =?UTF-8?q?=E7=94=BB=E9=9D=A2=E3=81=AB=E3=81=8A=E3=81=84=E3=81=A6Enter?=
 =?UTF-8?q?=E3=82=AD=E3=83=BC=E6=8A=BC=E4=B8=8B=E3=81=A7=E6=A4=9C=E7=B4=A2?=
 =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1275?=
 =?UTF-8?q?2)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* enhance: 検索画面においてEnterキー押下で検索できるように

* enterイベントを使用するように
---
 CHANGELOG.md                                | 1 +
 packages/frontend/src/pages/channel.vue     | 2 +-
 packages/frontend/src/pages/channels.vue    | 2 +-
 packages/frontend/src/pages/search.note.vue | 2 +-
 packages/frontend/src/pages/search.user.vue | 2 +-
 5 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 69f30f9232..106cf3ce15 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -124,6 +124,7 @@
 - Fix: WebKitブラウザー上でも「デバイスの画面を常にオンにする」機能が効くように
 - Fix: ページ一覧ページの表示がモバイル環境において崩れているのを修正
 - Fix: MFMでルビの中のテキストがnyaizeされない問題を修正
+- Enhance: 検索画面においてEnterキー押下で検索できるように
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index a50965131f..b0873ea336 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div v-else-if="tab === 'search'">
 			<div class="_gaps">
 				<div>
-					<MkInput v-model="searchQuery">
+					<MkInput v-model="searchQuery" @enter="search()">
 						<template #prefix><i class="ph-magnifying-glass ph-bold ph-lg"></i></template>
 					</MkInput>
 					<MkButton primary rounded style="margin-top: 8px;" @click="search()">{{ i18n.ts.search }}</MkButton>
diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue
index 182703f9da..63d1e454a2 100644
--- a/packages/frontend/src/pages/channels.vue
+++ b/packages/frontend/src/pages/channels.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<MkSpacer :contentMax="700">
 		<div v-if="tab === 'search'">
 			<div class="_gaps">
-				<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search">
+				<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
 					<template #prefix><i class="ph-magnifying-glass ph-bold ph-lg"></i></template>
 				</MkInput>
 				<MkRadios v-model="searchType" @update:modelValue="search()">
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index f824d9e0a0..405db06758 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div class="_gaps">
 	<div class="_gaps">
-		<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search">
+		<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
 			<template #prefix><i class="ph-magnifying-glass ph-bold ph-lg"></i></template>
 		</MkInput>
 		<MkFolder>
diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue
index 0485012fdb..d9853e7700 100644
--- a/packages/frontend/src/pages/search.user.vue
+++ b/packages/frontend/src/pages/search.user.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div class="_gaps">
 	<div class="_gaps">
-		<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search">
+		<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
 			<template #prefix><i class="ph-magnifying-glass ph-bold ph-lg"></i></template>
 		</MkInput>
 		<MkRadios v-model="searchOrigin" @update:modelValue="search()">

From f40f969ec336474b4df50189dc0f94284fe2d5e4 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: Thu, 28 Dec 2023 10:27:12 +0900
Subject: [PATCH 392/435] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 106cf3ce15..30e2e57b7d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,9 @@
 ### General
 - v2023.12.1でDockerを利用してサーバーを起動できない問題を修正
 
+### Client
+- Enhance: 検索画面においてEnterキー押下で検索できるように
+
 ## 2023.12.1
 
 ### Note
@@ -124,7 +127,6 @@
 - Fix: WebKitブラウザー上でも「デバイスの画面を常にオンにする」機能が効くように
 - Fix: ページ一覧ページの表示がモバイル環境において崩れているのを修正
 - Fix: MFMでルビの中のテキストがnyaizeされない問題を修正
-- Enhance: 検索画面においてEnterキー押下で検索できるように
 
 ### Server
 - Enhance: MFM `$[ruby ]` が他ソフトウェアと連合されるように

From c4534b20bb51635d0c5da9e4a096d2acaa9855b5 Mon Sep 17 00:00:00 2001
From: zyoshoka <107108195+zyoshoka@users.noreply.github.com>
Date: Thu, 28 Dec 2023 13:40:57 +0900
Subject: [PATCH 393/435] =?UTF-8?q?chore(misskey-js):=20`build-misskey-js-?=
 =?UTF-8?q?with-types`=E6=99=82=E3=81=AB`api-extractor`=E3=82=92=E8=B5=B0?=
 =?UTF-8?q?=E3=82=89=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#1283?=
 =?UTF-8?q?0)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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

diff --git a/package.json b/package.json
index 834747792f..ec9e98175d 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
 		"build-assets": "node ./scripts/build-assets.mjs",
 		"build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
 		"build-storybook": "pnpm --filter frontend build-storybook",
-		"build-misskey-js-with-types": "pnpm --filter backend build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build",
+		"build-misskey-js-with-types": "pnpm --filter backend build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
 		"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
 		"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
 		"init": "pnpm migrate",

From 592027cf688bd60ae235e39ddd534a6f4851c463 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Thu, 28 Dec 2023 09:54:32 +0100
Subject: [PATCH 394/435] merge: upstream

---
 .../backend/src/core/NoteCreateService.ts     |  2 +-
 packages/backend/src/server/ServerService.ts  |  1 -
 .../src/server/oauth/OAuth2ProviderService.ts | 48 ++++++++++++++-----
 packages/frontend/src/pages/about-sharkey.vue |  8 ++++
 pnpm-lock.yaml                                |  9 ----
 5 files changed, 44 insertions(+), 24 deletions(-)

diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 6309313f11..3bc4a29b99 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -253,7 +253,7 @@ export class NoteCreateService implements OnApplicationShutdown {
 
 		if (data.visibility === 'public' && data.channel == null) {
 			const sensitiveWords = meta.sensitiveWords;
-			if (this.isSensitive(data, sensitiveWords)) {
+			if (this.utilityService.isSensitiveWordIncluded(data.cw ?? data.text ?? '', sensitiveWords)) {
 				data.visibility = 'home';
 			} else if ((await this.roleService.getUserPolicies(user.id)).canPublicNote === false) {
 				data.visibility = 'home';
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index cf8de4ea56..3b43b931ae 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -111,7 +111,6 @@ export class ServerService implements OnApplicationShutdown {
 		fastify.register(this.nodeinfoServerService.createServer);
 		fastify.register(this.wellKnownServerService.createServer);
 		fastify.register(this.oauth2ProviderService.createServer, { prefix: '/oauth' });
-		fastify.register(this.oauth2ProviderService.createTokenServer, { prefix: '/oauth/token' });
 
 		fastify.get<{ Params: { path: string }; Querystring: { static?: any; badge?: any; }; }>('/emoji/:path(.*)', async (request, reply) => {
 			const path = request.params.path;
diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts
index 52505ac5bb..d857f0bdd4 100644
--- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts
+++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts
@@ -16,6 +16,41 @@ import type { Config } from '@/config.js';
 import { DI } from '@/di-symbols.js';
 import type { FastifyInstance } from 'fastify';
 
+const kinds = [
+	'read:account',
+	'write:account',
+	'read:blocks',
+	'write:blocks',
+	'read:drive',
+	'write:drive',
+	'read:favorites',
+	'write:favorites',
+	'read:following',
+	'write:following',
+	'read:messaging',
+	'write:messaging',
+	'read:mutes',
+	'write:mutes',
+	'write:notes',
+	'read:notifications',
+	'write:notifications',
+	'read:reactions',
+	'write:reactions',
+	'write:votes',
+	'read:pages',
+	'write:pages',
+	'write:page-likes',
+	'read:page-likes',
+	'read:user-groups',
+	'write:user-groups',
+	'read:channels',
+	'write:channels',
+	'read:gallery',
+	'write:gallery',
+	'read:gallery-likes',
+	'write:gallery-likes',
+];
+
 function getClient(BASE_URL: string, authorization: string | undefined): MegalodonInterface {
 	const accessTokenArr = authorization?.split(' ') ?? [null];
 	const accessToken = accessTokenArr[accessTokenArr.length - 1];
@@ -167,17 +202,4 @@ export class OAuth2ProviderService {
 			}
 		});
 	}
-
-	@bindThis
-	public async createTokenServer(fastify: FastifyInstance): Promise<void> {
-		fastify.register(fastifyCors);
-		fastify.post('', async () => { });
-
-		await fastify.register(fastifyExpress);
-		// Clients may use JSON or urlencoded
-		fastify.use('', bodyParser.urlencoded({ extended: false }));
-		fastify.use('', bodyParser.json({ strict: true }));
-		fastify.use('', this.#server.token());
-		fastify.use('', this.#server.errorHandler());
-	}
 }
diff --git a/packages/frontend/src/pages/about-sharkey.vue b/packages/frontend/src/pages/about-sharkey.vue
index 4160dcb09c..2e4ff5d041 100644
--- a/packages/frontend/src/pages/about-sharkey.vue
+++ b/packages/frontend/src/pages/about-sharkey.vue
@@ -78,6 +78,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<img src="https://avatars.githubusercontent.com/u/40626578?v=4" :class="$style.contributorAvatar">
 							<span :class="$style.contributorUsername">@tai-cha</span>
 						</a>
+						<a href="https://github.com/samunohito" target="_blank" :class="$style.contributor">
+							<img src="https://avatars.githubusercontent.com/u/46447427?v=4" :class="$style.contributorAvatar">
+							<span :class="$style.contributorUsername">@samunohito</span>
+						</a>
+						<a href="https://github.com/anatawa12" target="_blank" :class="$style.contributor">
+							<img src="https://avatars.githubusercontent.com/u/22656849?v=4" :class="$style.contributorAvatar">
+							<span :class="$style.contributorUsername">@anatawa12</span>
+						</a>
 					</div>
 				</FormSection>
 				<FormSection>
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2db1e628fd..62cf9ef9ad 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -7221,11 +7221,7 @@ packages:
       ts-dedent: 2.2.0
       type-fest: 2.19.0
       vue: 3.3.12(typescript@5.3.3)
-<<<<<<< HEAD
-      vue-component-type-helpers: 1.8.26
-=======
       vue-component-type-helpers: 1.8.27
->>>>>>> ad346b6f3 (feat(backend/oauth): allow CORS for token endpoint (#12814))
     transitivePeerDependencies:
       - encoding
       - supports-color
@@ -19632,13 +19628,8 @@ packages:
   /vscode-textmate@8.0.0:
     resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
 
-<<<<<<< HEAD
-  /vue-component-type-helpers@1.8.26:
-    resolution: {integrity: sha512-CIwb7s8cqUuPpHDk+0DY8EJ/x8tzdzqw8ycX8hhw1GnbngTgSsIceHAqrrLjmv8zXi+j5XaiqYRQMw8sKyyjkw==}
-=======
   /vue-component-type-helpers@1.8.27:
     resolution: {integrity: sha512-0vOfAtI67UjeO1G6UiX5Kd76CqaQ67wrRZiOe7UAb9Jm6GzlUr/fC7CV90XfwapJRjpCMaZFhv1V0ajWRmE9Dg==}
->>>>>>> ad346b6f3 (feat(backend/oauth): allow CORS for token endpoint (#12814))
     dev: true
 
   /vue-component-type-helpers@1.8.4:

From 9a9f61a6c05926f0ba5c4d9243dbcf0d2bf7b36d Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Thu, 28 Dec 2023 12:52:12 +0100
Subject: [PATCH 395/435] fix: typecheck

---
 .../backend/src/server/api/endpoints/admin/approve-user.ts | 1 +
 .../backend/src/server/api/endpoints/admin/nsfw-user.ts    | 1 +
 .../backend/src/server/api/endpoints/admin/silence-user.ts | 1 +
 .../backend/src/server/api/endpoints/admin/unnsfw-user.ts  | 1 +
 .../src/server/api/endpoints/admin/unsilence-user.ts       | 1 +
 .../src/server/api/endpoints/i/registry/get-unsecure.ts    | 1 +
 .../src/server/api/stream/channels/bubble-timeline.ts      | 7 ++++---
 packages/misskey-js/src/consts.ts                          | 5 +++++
 8 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/admin/approve-user.ts b/packages/backend/src/server/api/endpoints/admin/approve-user.ts
index 0ea656ddaf..53002a71fd 100644
--- a/packages/backend/src/server/api/endpoints/admin/approve-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/approve-user.ts
@@ -10,6 +10,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:approve-user',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/nsfw-user.ts b/packages/backend/src/server/api/endpoints/admin/nsfw-user.ts
index 2dff0e8d09..2a47abe03c 100644
--- a/packages/backend/src/server/api/endpoints/admin/nsfw-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/nsfw-user.ts
@@ -8,6 +8,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:nsfw-user',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/silence-user.ts b/packages/backend/src/server/api/endpoints/admin/silence-user.ts
index ed1141da43..007bed5c03 100644
--- a/packages/backend/src/server/api/endpoints/admin/silence-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/silence-user.ts
@@ -9,6 +9,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:silence-user',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/unnsfw-user.ts b/packages/backend/src/server/api/endpoints/admin/unnsfw-user.ts
index 9c414ed55c..013e7771ba 100644
--- a/packages/backend/src/server/api/endpoints/admin/unnsfw-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unnsfw-user.ts
@@ -8,6 +8,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:unnsfw-user',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts
index 7cfedca7de..5e514ccda6 100644
--- a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts
+++ b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts
@@ -8,6 +8,7 @@ export const meta = {
 
 	requireCredential: true,
 	requireModerator: true,
+	kind: 'write:admin:unsilence-user',
 } as const;
 
 export const paramDef = {
diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts b/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts
index 74d896e650..bb471284c9 100644
--- a/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts
+++ b/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts
@@ -6,6 +6,7 @@ import { ApiError } from '../../../error.js';
 
 export const meta = {
 	requireCredential: true,
+	kind: 'read:account',
 
 	secure: false,
 
diff --git a/packages/backend/src/server/api/stream/channels/bubble-timeline.ts b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts
index 1a3fcede62..4f8809edbe 100644
--- a/packages/backend/src/server/api/stream/channels/bubble-timeline.ts
+++ b/packages/backend/src/server/api/stream/channels/bubble-timeline.ts
@@ -13,12 +13,12 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { bindThis } from '@/decorators.js';
 import { RoleService } from '@/core/RoleService.js';
 import type { MiMeta } from '@/models/Meta.js';
-import Channel from '../channel.js';
+import Channel, { MiChannelService } from '../channel.js';
 
 class BubbleTimelineChannel extends Channel {
 	public readonly chName = 'bubbleTimeline';
 	public static shouldShare = false;
-	public static requireCredential = false;
+	public static requireCredential = false as const;
 	private withRenotes: boolean;
 	private withFiles: boolean;
 	private withBots: boolean;
@@ -100,9 +100,10 @@ class BubbleTimelineChannel extends Channel {
 }
 
 @Injectable()
-export class BubbleTimelineChannelService {
+export class BubbleTimelineChannelService implements MiChannelService<false> {
 	public readonly shouldShare = BubbleTimelineChannel.shouldShare;
 	public readonly requireCredential = BubbleTimelineChannel.requireCredential;
+	public readonly kind = BubbleTimelineChannel.kind;
 
 	constructor(
 		private metaService: MetaService,
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 9b25e2af74..0748d9863e 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -60,6 +60,11 @@ export const permissions = [
 	'read:admin:show-user',
 	'read:admin:show-users',
 	'write:admin:suspend-user',
+	'write:admin:approve-user',
+	'write:admin:nsfw-user',
+	'write:admin:unnsfw-user',
+	'write:admin:silence-user',
+	'write:admin:unsilence-user',
 	'write:admin:unset-user-avatar',
 	'write:admin:unset-user-banner',
 	'write:admin:unsuspend-user',

From 870f70a6833f4dd9744fd1e6c356262d803c0d0c Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Thu, 28 Dec 2023 13:06:11 +0100
Subject: [PATCH 396/435] upd: up sfm.js version

---
 packages/backend/package.json  |  2 +-
 packages/frontend/package.json |  2 +-
 pnpm-lock.yaml                 | 12 ++++++------
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 224700a77b..303e6f5b56 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -76,7 +76,7 @@
 		"@nestjs/core": "10.2.10",
 		"@nestjs/testing": "10.2.10",
 		"@peertube/http-signature": "1.7.0",
-		"@sharkey/sfm-js": "0.24.1",
+		"@sharkey/sfm-js": "0.24.2",
 		"@simplewebauthn/server": "8.3.5",
 		"@sinonjs/fake-timers": "11.2.2",
 		"@smithy/node-http-handler": "2.1.10",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 496a083aa9..4451f138a5 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -22,7 +22,7 @@
 		"@rollup/plugin-json": "6.1.0",
 		"@rollup/plugin-replace": "5.0.5",
 		"@rollup/pluginutils": "5.1.0",
-		"@sharkey/sfm-js": "0.24.1",
+		"@sharkey/sfm-js": "0.24.2",
 		"@syuilo/aiscript": "0.16.0",
 		"@phosphor-icons/web": "^2.0.3",
 		"@twemoji/parser": "15.0.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 62cf9ef9ad..22a9c7d7e9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -110,8 +110,8 @@ importers:
         specifier: 1.7.0
         version: 1.7.0
       '@sharkey/sfm-js':
-        specifier: 0.24.1
-        version: 0.24.1
+        specifier: 0.24.2
+        version: 0.24.2
       '@simplewebauthn/server':
         specifier: 8.3.5
         version: 8.3.5
@@ -673,8 +673,8 @@ importers:
         specifier: 5.1.0
         version: 5.1.0(rollup@4.9.1)
       '@sharkey/sfm-js':
-        specifier: 0.24.1
-        version: 0.24.1
+        specifier: 0.24.2
+        version: 0.24.2
       '@syuilo/aiscript':
         specifier: 0.16.0
         version: 0.16.0
@@ -5853,8 +5853,8 @@ packages:
       string-argv: 0.3.1
     dev: true
 
-  /@sharkey/sfm-js@0.24.1:
-    resolution: {integrity: sha512-STBMI34OEXjS94+/uUk9MtJLoKzF6TqZbS6BZRZ8bo4NEq2rTH330R6Q90xSJI1FY6RIV7kxepIG8cjUumY4kA==, tarball: https://git.joinsharkey.org/api/packages/Sharkey/npm/%40sharkey%2Fsfm-js/-/0.24.1/sfm-js-0.24.1.tgz}
+  /@sharkey/sfm-js@0.24.2:
+    resolution: {integrity: sha512-xaOIyy+NGlb6TjzB3nB+80UU5ASHjyl2JE/RcKrHQJxkhjPtvSWvnoyKlKrG2pUEBCMyLrrpYViHECdJ9jJteA==, tarball: https://git.joinsharkey.org/api/packages/Sharkey/npm/%40sharkey%2Fsfm-js/-/0.24.2/sfm-js-0.24.2.tgz}
     dependencies:
       '@twemoji/parser': 15.0.0
     dev: false

From 2e55c292bfe4b742df11cb7f3222da2d479963f6 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Thu, 28 Dec 2023 17:21:02 +0000
Subject: [PATCH 397/435] special-case full usernames is search #264

this should be enough "merging" of lookup&search:

* partial usernames are searched as part of notes from the widget, and
as part of known usernames in "search users"
* tags are searched as part of notes from the widget and the "search
notes" page
* full usernames always navigate to the profile page of that
user (which will fetch the profile if possible)

as an extra nicety, if "search notes" is disabled, the search widget
handles hashtags like the lookup function does
---
 packages/frontend/src/pages/search.user.vue    |  5 +++++
 packages/frontend/src/widgets/WidgetSearch.vue | 16 ++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue
index d9853e7700..596f4da711 100644
--- a/packages/frontend/src/pages/search.user.vue
+++ b/packages/frontend/src/pages/search.user.vue
@@ -65,6 +65,11 @@ async function search() {
 		return;
 	}
 
+	if (query.match(/^@[a-z0-9_.-]+@[a-z0-9_.-]+$/i)) {
+		router.push(`/${query}`);
+		return;
+	}
+
 	userPagination.value = {
 		endpoint: 'users/search',
 		limit: 10,
diff --git a/packages/frontend/src/widgets/WidgetSearch.vue b/packages/frontend/src/widgets/WidgetSearch.vue
index c114707b23..0106df9c49 100644
--- a/packages/frontend/src/widgets/WidgetSearch.vue
+++ b/packages/frontend/src/widgets/WidgetSearch.vue
@@ -23,6 +23,8 @@ import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
 import { useRouter } from '@/router.js';
 import { GetFormResultType } from '@/scripts/form.js';
+import { $i } from '@/account.js';
+import { instance } from '@/instance.js';
 
 const name = 'search';
 
@@ -60,6 +62,7 @@ let notePagination = ref();
 let isLocalOnly = ref(false);
 let order = ref(true);
 let filetype = ref<null | string>(null);
+const notesSearchAvailable = (($i == null && instance.policies.canSearchNotes) || ($i != null && $i.policies.canSearchNotes));
 
 function options(ev) {
 	os.popupMenu([{
@@ -117,6 +120,19 @@ async function search() {
 		return;
 	}
 
+	if (query.match(/^@[a-z0-9_.-]+@[a-z0-9_.-]+$/i)) {
+		router.push(`/${query}`);
+		return;
+	}
+
+	if (!notesSearchAvailable && query.startsWith('#')) {
+		// can't really search, at least try handling hashtags
+		router.push(`/tags/${encodeURIComponent(query.substring(1))}`);
+		return;
+	}
+
+	// TODO: if !notesSearchAvailable pop up an error message
+
 	notePagination.value = {
 		endpoint: 'notes/search',
 		limit: 10,

From 18e82c0627b21971aa8132420a1d7975c46a91f3 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Thu, 28 Dec 2023 19:37:22 +0100
Subject: [PATCH 398/435] fix: frontend not being able to build

---
 packages/backend/package.json  |  2 +-
 packages/frontend/package.json |  2 +-
 pnpm-lock.yaml                 | 12 ++++++------
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 303e6f5b56..c1ac4649d8 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -76,7 +76,7 @@
 		"@nestjs/core": "10.2.10",
 		"@nestjs/testing": "10.2.10",
 		"@peertube/http-signature": "1.7.0",
-		"@sharkey/sfm-js": "0.24.2",
+		"@sharkey/sfm-js": "0.24.3",
 		"@simplewebauthn/server": "8.3.5",
 		"@sinonjs/fake-timers": "11.2.2",
 		"@smithy/node-http-handler": "2.1.10",
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 4451f138a5..68aa501c84 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -22,7 +22,7 @@
 		"@rollup/plugin-json": "6.1.0",
 		"@rollup/plugin-replace": "5.0.5",
 		"@rollup/pluginutils": "5.1.0",
-		"@sharkey/sfm-js": "0.24.2",
+		"@sharkey/sfm-js": "0.24.3",
 		"@syuilo/aiscript": "0.16.0",
 		"@phosphor-icons/web": "^2.0.3",
 		"@twemoji/parser": "15.0.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 22a9c7d7e9..fac96a0e62 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -110,8 +110,8 @@ importers:
         specifier: 1.7.0
         version: 1.7.0
       '@sharkey/sfm-js':
-        specifier: 0.24.2
-        version: 0.24.2
+        specifier: 0.24.3
+        version: 0.24.3
       '@simplewebauthn/server':
         specifier: 8.3.5
         version: 8.3.5
@@ -673,8 +673,8 @@ importers:
         specifier: 5.1.0
         version: 5.1.0(rollup@4.9.1)
       '@sharkey/sfm-js':
-        specifier: 0.24.2
-        version: 0.24.2
+        specifier: 0.24.3
+        version: 0.24.3
       '@syuilo/aiscript':
         specifier: 0.16.0
         version: 0.16.0
@@ -5853,8 +5853,8 @@ packages:
       string-argv: 0.3.1
     dev: true
 
-  /@sharkey/sfm-js@0.24.2:
-    resolution: {integrity: sha512-xaOIyy+NGlb6TjzB3nB+80UU5ASHjyl2JE/RcKrHQJxkhjPtvSWvnoyKlKrG2pUEBCMyLrrpYViHECdJ9jJteA==, tarball: https://git.joinsharkey.org/api/packages/Sharkey/npm/%40sharkey%2Fsfm-js/-/0.24.2/sfm-js-0.24.2.tgz}
+  /@sharkey/sfm-js@0.24.3:
+    resolution: {integrity: sha512-Fd2LWPYNVmnTg9AKdJm3MLMvYdxQafq/0eQlmJhUnQheRVm3f1xHrFFY12+yUWIq7rS0uxrKEmrVLnPzRqYG6Q==, tarball: https://git.joinsharkey.org/api/packages/Sharkey/npm/%40sharkey%2Fsfm-js/-/0.24.3/sfm-js-0.24.3.tgz}
     dependencies:
       '@twemoji/parser': 15.0.0
     dev: false

From cd8ba4b634cbaf1656a2631eaf02f0e5dcdf397d Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Fri, 29 Dec 2023 14:54:01 +0000
Subject: [PATCH 399/435] always go to tag page from widget #264

---
 packages/frontend/src/widgets/WidgetSearch.vue | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/packages/frontend/src/widgets/WidgetSearch.vue b/packages/frontend/src/widgets/WidgetSearch.vue
index 0106df9c49..9999139776 100644
--- a/packages/frontend/src/widgets/WidgetSearch.vue
+++ b/packages/frontend/src/widgets/WidgetSearch.vue
@@ -23,8 +23,6 @@ import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
 import { useRouter } from '@/router.js';
 import { GetFormResultType } from '@/scripts/form.js';
-import { $i } from '@/account.js';
-import { instance } from '@/instance.js';
 
 const name = 'search';
 
@@ -62,7 +60,6 @@ let notePagination = ref();
 let isLocalOnly = ref(false);
 let order = ref(true);
 let filetype = ref<null | string>(null);
-const notesSearchAvailable = (($i == null && instance.policies.canSearchNotes) || ($i != null && $i.policies.canSearchNotes));
 
 function options(ev) {
 	os.popupMenu([{
@@ -125,14 +122,11 @@ async function search() {
 		return;
 	}
 
-	if (!notesSearchAvailable && query.startsWith('#')) {
-		// can't really search, at least try handling hashtags
+	if (query.startsWith('#')) {
 		router.push(`/tags/${encodeURIComponent(query.substring(1))}`);
 		return;
 	}
 
-	// TODO: if !notesSearchAvailable pop up an error message
-
 	notePagination.value = {
 		endpoint: 'notes/search',
 		limit: 10,

From f2d452271456ba8a7a25dff623e175fd80e1cc97 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Fri, 29 Dec 2023 15:03:32 +0000
Subject: [PATCH 400/435] change all "renote" to "boost" #272 #273 #274

---
 locales/en-US.yml | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index c1e39778b7..6d7a2b9b84 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -46,7 +46,7 @@ pin: "Pin to profile"
 unpin: "Unpin from profile"
 copyContent: "Copy contents"
 copyLink: "Copy link"
-copyLinkRenote: "Copy renote link"
+copyLinkRenote: "Copy boost link"
 delete: "Delete"
 deleteAndEdit: "Delete and edit"
 deleteAndEditConfirm: "Are you sure you want to redraft this note? This means you will lose all reactions, boosts, and replies to it."
@@ -115,7 +115,7 @@ rmboost: "Unboosted."
 cantRenote: "This post can't be boosted."
 cantReRenote: "A boost can't be boosted."
 quote: "Quote"
-inChannelRenote: "Channel-only Renote"
+inChannelRenote: "Channel-only Boost"
 inChannelQuote: "Channel-only Quote"
 pinnedNote: "Pinned note"
 pinned: "Pin to profile"
@@ -135,8 +135,8 @@ unmarkAsSensitive: "Unmark as sensitive"
 enterFileName: "Enter filename"
 mute: "Mute"
 unmute: "Unmute"
-renoteMute: "Mute Renotes"
-renoteUnmute: "Unmute Renotes"
+renoteMute: "Mute Boosts"
+renoteUnmute: "Unmute Boosts"
 block: "Block"
 unblock: "Unblock"
 markAsNSFW: "Mark all media from user as NSFW"
@@ -685,7 +685,7 @@ behavior: "Behavior"
 sample: "Sample"
 abuseReports: "Reports"
 reportAbuse: "Report"
-reportAbuseRenote: "Report renote"
+reportAbuseRenote: "Report boost"
 reportAbuseOf: "Report {name}"
 fillAbuseReportDescription: "Please fill in details regarding this report. If it is about a specific note, please include its URL."
 abuseReported: "Your report has been sent. Thank you very much."
@@ -1275,8 +1275,8 @@ _initialTutorial:
     _visibility:
       description: "You can limit who can view your note."
       public: "Your note will be visible for all users."
-      home: "Public only on the Home timeline. People visiting your profile, via followers, and through renotes can see it."
-      followers: "Visible to followers only. Only followers can see it and no one else, and it cannot be renoted by others."
+      home: "Public only on the Home timeline. People visiting your profile, via followers, and through boostss can see it."
+      followers: "Visible to followers only. Only followers can see it and no one else, and it cannot be boosted by others."
       direct: "Visible only to specified users, and the recipient will be notified. It can be used as an alternative to direct messaging."
       doNotSendConfidencialOnDirect1: "Be careful when sending sensitive information!"
       doNotSendConfidencialOnDirect2: "Administrators of the server can see what you write. Be careful with sensitive information when sending direct notes to users on untrusted servers."
@@ -1774,7 +1774,7 @@ _channel:
   notesCount: "{n} Notes"
   nameAndDescription: "Name and description"
   nameOnly: "Name only"
-  allowRenoteToExternal: "Allow renote and quote outside the channel"
+  allowRenoteToExternal: "Allow boosts and quote outside the channel"
 _menuDisplay:
   sideFull: "Side"
   sideIcon: "Side (Icons)"
@@ -1837,7 +1837,7 @@ _theme:
     hashtag: "Hashtag"
     mention: "Mention"
     mentionMe: "Mentions (Me)"
-    renote: "Renote"
+    renote: "Boost"
     modalBg: "Modal background"
     divider: "Divider"
     scrollbarHandle: "Scrollbar handle"
@@ -2221,7 +2221,7 @@ _notification:
     follow: "New followers"
     mention: "Mentions"
     reply: "Replies"
-    renote: "Renotes"
+    renote: "Boosts"
     quote: "Quotes"
     reaction: "Reactions"
     pollEnded: "Polls ending"
@@ -2233,7 +2233,7 @@ _notification:
   _actions:
     followBack: "followed you back"
     reply: "Reply"
-    renote: "Renote"
+    renote: "Boost"
 _deck:
   alwaysShowMainColumn: "Always show main column"
   columnAlign: "Align columns"

From 70433db9d9079f5da3f7075416875a2cf402be46 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Fri, 29 Dec 2023 18:36:15 +0100
Subject: [PATCH 401/435] fix: button effect not lining up on sub notes|

Closes #277
---
 packages/frontend/src/components/MkNoteSub.vue |  2 +-
 packages/frontend/src/components/SkNoteSub.vue | 16 ++++++++--------
 2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 9c25ce3452..d96785a2d9 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -228,7 +228,7 @@ function like(): void {
 		noteId: props.note.id,
 		override: defaultLike.value,
 	});
-	const el = reactButton.value as HTMLElement | null | undefined;
+	const el = likeButton.value as HTMLElement | null | undefined;
 	if (el) {
 		const rect = el.getBoundingClientRect();
 		const x = rect.left + (el.offsetWidth / 2);
diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index 46f0838bf1..0dc4b0b31b 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -152,16 +152,16 @@ const isRenote = (
 );
 
 async function addReplyTo(replyNote: Misskey.entities.Note) {
-		replies.value.unshift(replyNote);
-		appearNote.value.repliesCount += 1;
+	replies.value.unshift(replyNote);
+	appearNote.value.repliesCount += 1;
 }
 
 async function removeReply(id: Misskey.entities.Note['id']) {
-		const replyIdx = replies.value.findIndex(note => note.id === id);
-		if (replyIdx >= 0) {
-			replies.value.splice(replyIdx, 1);
-			appearNote.value.repliesCount -= 1;
-		}
+	const replyIdx = replies.value.findIndex(note => note.id === id);
+	if (replyIdx >= 0) {
+		replies.value.splice(replyIdx, 1);
+		appearNote.value.repliesCount -= 1;
+	}
 }
 
 useNoteCapture({
@@ -237,7 +237,7 @@ function like(): void {
 		noteId: props.note.id,
 		override: defaultLike.value,
 	});
-	const el = reactButton.value as HTMLElement | null | undefined;
+	const el = likeButton.value as HTMLElement | null | undefined;
 	if (el) {
 		const rect = el.getBoundingClientRect();
 		const x = rect.left + (el.offsetWidth / 2);

From 3f36b1474d0b313ba112c0cfdf70c4348fe09612 Mon Sep 17 00:00:00 2001
From: trivernis <trivernis@protonmail.com>
Date: Fri, 29 Dec 2023 15:00:21 +0100
Subject: [PATCH 402/435] Revert toplevel package.json changes

---
 package.json | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/package.json b/package.json
index f267db967c..ec9e98175d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "sharkey",
-	"version": "2023.12.0.beta2",
+	"version": "2023.12.0.beta3",
 	"codename": "shonk",
 	"repository": {
 		"type": "git",
@@ -18,7 +18,7 @@
 		"build-assets": "node ./scripts/build-assets.mjs",
 		"build": "pnpm build-pre && pnpm -r build && pnpm build-assets",
 		"build-storybook": "pnpm --filter frontend build-storybook",
-		"build-misskey-js-with-types": "pnpm --filter backend build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build",
+		"build-misskey-js-with-types": "pnpm --filter backend build && pnpm --filter backend generate-api-json && ncp packages/backend/built/api.json packages/misskey-js/generator/api.json && pnpm --filter misskey-js update-autogen-code && pnpm --filter misskey-js build && pnpm --filter misskey-js api",
 		"start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js",
 		"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js",
 		"init": "pnpm migrate",
@@ -45,8 +45,8 @@
 		"lodash": "4.17.21"
 	},
 	"dependencies": {
-		"cssnano": "6.0.2",
 		"execa": "8.0.1",
+		"cssnano": "6.0.2",
 		"js-yaml": "4.1.0",
 		"postcss": "8.4.32",
 		"terser": "5.26.0",
@@ -58,7 +58,7 @@
 		"cross-env": "7.0.3",
 		"cypress": "13.6.1",
 		"eslint": "8.56.0",
-		"ncp": "2.0.0",
-		"start-server-and-test": "2.0.3"
+		"start-server-and-test": "2.0.3",
+		"ncp": "2.0.0"
 	}
 }

From 5f2e07d81fdc8810b8b1cfc3248f8eace9f37e74 Mon Sep 17 00:00:00 2001
From: trivernis <trivernis@protonmail.com>
Date: Fri, 29 Dec 2023 20:05:07 +0100
Subject: [PATCH 403/435] Revert unnecessary changes to backend package.json

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

diff --git a/packages/backend/package.json b/packages/backend/package.json
index 34c713f697..2aa10b1c96 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -123,8 +123,8 @@
 		"json5": "2.2.3",
 		"jsonld": "8.3.2",
 		"jsrsasign": "10.9.0",
-		"megalodon": "workspace:*",
 		"meilisearch": "0.36.0",
+		"megalodon": "workspace:*",
 		"microformats-parser": "2.0.2",
 		"mime-types": "2.1.35",
 		"misskey-js": "workspace:*",

From adf4e9b0b8619d2a325393f0285852dda73f0f84 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 30 Dec 2023 04:33:14 +0100
Subject: [PATCH 404/435] chore: remove extra letter on "boosts"

---
 locales/en-US.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 6d7a2b9b84..1ac2f4b280 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -1275,7 +1275,7 @@ _initialTutorial:
     _visibility:
       description: "You can limit who can view your note."
       public: "Your note will be visible for all users."
-      home: "Public only on the Home timeline. People visiting your profile, via followers, and through boostss can see it."
+      home: "Public only on the Home timeline. People visiting your profile, via followers, and through boosts can see it."
       followers: "Visible to followers only. Only followers can see it and no one else, and it cannot be boosted by others."
       direct: "Visible only to specified users, and the recipient will be notified. It can be used as an alternative to direct messaging."
       doNotSendConfidencialOnDirect1: "Be careful when sending sensitive information!"

From 9653a6acea0d3628f8062139a35996d899bc3b76 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 30 Dec 2023 04:41:56 +0100
Subject: [PATCH 405/435] chore: change feature request to yml

Closes #271
---
 .gitea/ISSUE_TEMPLATE/02_feature-request.md  | 10 ---------
 .gitea/ISSUE_TEMPLATE/02_feature-request.yml | 22 ++++++++++++++++++++
 2 files changed, 22 insertions(+), 10 deletions(-)
 delete mode 100644 .gitea/ISSUE_TEMPLATE/02_feature-request.md
 create mode 100644 .gitea/ISSUE_TEMPLATE/02_feature-request.yml

diff --git a/.gitea/ISSUE_TEMPLATE/02_feature-request.md b/.gitea/ISSUE_TEMPLATE/02_feature-request.md
deleted file mode 100644
index db73f0dc7f..0000000000
--- a/.gitea/ISSUE_TEMPLATE/02_feature-request.md
+++ /dev/null
@@ -1,10 +0,0 @@
----
-name: "Feature Request"
-about: "Suggest an idea for this project"
-title: "feat: "
----
-## Summary
-<!-- Tell us what the suggestion is -->
-
-## Purpose
-<!-- Describe the specific problem or need you think this feature will solve, and who it will help. -->
\ No newline at end of file
diff --git a/.gitea/ISSUE_TEMPLATE/02_feature-request.yml b/.gitea/ISSUE_TEMPLATE/02_feature-request.yml
new file mode 100644
index 0000000000..d9395b49c3
--- /dev/null
+++ b/.gitea/ISSUE_TEMPLATE/02_feature-request.yml
@@ -0,0 +1,22 @@
+name: ✨ Feature Request
+description: Suggest an idea for this project
+title: 'bug: '
+
+body:
+  - type: textarea
+    attributes:
+      label: Summary
+      description: Tell us what the suggestion is
+    validations:
+      required: true
+  - type: textarea
+    attributes:
+      label: Purpose
+      description: Describe the specific problem or need you think this feature will solve, and who it will help.
+    validations:
+      required: true
+  - type: checkboxes
+    attributes:
+      label: Do you want to implement this feature yourself?
+      options:
+        - label: Yes, I will implement this by myself and send a pull request
\ No newline at end of file

From 4a729a7f083fc3627ee414eefe7c95c1972b8e0f Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 30 Dec 2023 04:42:28 +0100
Subject: [PATCH 406/435] chore: add title to bug report template

---
 .gitea/ISSUE_TEMPLATE/01_bug-report.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitea/ISSUE_TEMPLATE/01_bug-report.yml b/.gitea/ISSUE_TEMPLATE/01_bug-report.yml
index a2cdebe549..6282cc43f9 100644
--- a/.gitea/ISSUE_TEMPLATE/01_bug-report.yml
+++ b/.gitea/ISSUE_TEMPLATE/01_bug-report.yml
@@ -1,5 +1,6 @@
 name: 🐛 Bug Report
 description: Create a report to help us improve
+title: 'bug: '
 
 body:
   - type: markdown

From 276f4967bce5822e6ef65f911a1b2cd09bc873c4 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 30 Dec 2023 04:42:42 +0100
Subject: [PATCH 407/435] chore: fix title on feature issue template

---
 .gitea/ISSUE_TEMPLATE/02_feature-request.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitea/ISSUE_TEMPLATE/02_feature-request.yml b/.gitea/ISSUE_TEMPLATE/02_feature-request.yml
index d9395b49c3..d3bf64d869 100644
--- a/.gitea/ISSUE_TEMPLATE/02_feature-request.yml
+++ b/.gitea/ISSUE_TEMPLATE/02_feature-request.yml
@@ -1,6 +1,6 @@
 name: ✨ Feature Request
 description: Suggest an idea for this project
-title: 'bug: '
+title: 'feat: '
 
 body:
   - type: textarea

From 4856b019ceacc2116c24a7c9354d37f24081685f Mon Sep 17 00:00:00 2001
From: Vavency <vavency@gmail.com>
Date: Sat, 30 Dec 2023 16:09:46 +0200
Subject: [PATCH 408/435] enhance (frontend): hide module player pattern
 display

---
 .../frontend/src/components/MkModPlayer.vue   | 59 ++++++++++++++++++-
 1 file changed, 56 insertions(+), 3 deletions(-)

diff --git a/packages/frontend/src/components/MkModPlayer.vue b/packages/frontend/src/components/MkModPlayer.vue
index 055522d466..530b9c34d2 100644
--- a/packages/frontend/src/components/MkModPlayer.vue
+++ b/packages/frontend/src/components/MkModPlayer.vue
@@ -7,7 +7,11 @@
 </div>
 
 <div v-else class="mod-player-enabled">
-	<div class="pattern-display">
+	<div class="pattern-display" @click="togglePattern()">
+		<div class="pattern-hide" v-if="patternHide">
+			<b><i class="ph-eye ph-bold ph-lg"></i> Pattern Hidden</b>
+			<span>{{ i18n.ts.clickToShow }}</span>
+		</div>
 		<canvas ref="displayCanvas" class="pattern-canvas"></canvas>
 	</div>
 	<div class="controls">
@@ -74,6 +78,8 @@ const props = defineProps<{
 const isSensitive = computed(() => { return props.module.isSensitive; });
 const url = computed(() => { return props.module.url; });
 let hide = ref((defaultStore.state.nsfw === 'force') ? true : isSensitive.value && (defaultStore.state.nsfw !== 'ignore'));
+let patternHide = ref(false);
+let firstFrame = ref(true);
 let playing = ref(false);
 let displayCanvas = ref<HTMLCanvasElement>();
 let progress = ref<HTMLProgressElement>();
@@ -156,15 +162,42 @@ function performSeek() {
 
 function toggleVisible() {
 	hide.value = !hide.value;
-	nextTick(() => { stop(hide.value); });
+	if (!hide.value && patternHide.value) {
+		firstFrame.value = true;
+		patternHide.value = false;
+	}
+	nextTick(() => {
+		stop(hide.value);
+	});
 }
 
+function togglePattern() {
+	patternHide.value = !patternHide.value;
+	if (!patternHide.value) {
+		if (player.value.getRow() == 0) {
+			try {
+				player.value.play(buffer);
+				display();
+			} catch (e) {
+				console.warn(e);
+			}
+			player.value.stop();
+		}
+	}
+}
 function display() {
 	if (!displayCanvas.value) {
 		stop();
 		return;
 	}
 
+	if (patternHide.value) { return; }
+
+	if (firstFrame.value) {
+		firstFrame.value = false;
+		patternHide.value = true;
+	}
+
 	const canvas = displayCanvas.value;
 
 	const pattern = player.value.getPattern();
@@ -211,7 +244,7 @@ function display() {
 				const instr = part.substring(4, 6);
 				ctx.fillStyle = colours.instr[active];
 				ctx.fillText(instr, baseOffset + CHAR_WIDTH * 5, baseRowOffset);
-				
+
 				const volume = part.substring(6, 9);
 				ctx.fillStyle = colours.volume[active];
 				ctx.fillText(volume, baseOffset + CHAR_WIDTH * 7, baseRowOffset);
@@ -271,12 +304,32 @@ function display() {
 			background-color: black;
 			height: 100%;
 		}
+		.pattern-hide {
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			align-items: center;
+			background: rgba(64, 64, 64, 0.3);
+			backdrop-filter: blur(2em);
+			color: #fff;
+			font-size: 12px;
+
+			position: absolute;
+			z-index: 0;
+			width: 100%;
+			height: 100%;
+
+			> span {
+				display: block;
+			}
+		}
 	}
 
 	> .controls {
 		display: flex;
 		width: 100%;
 		background-color: var(--bg);
+		z-index: 1;
 
 		> * {
 			padding: 4px 8px;

From 8d28c16ee10dee1a389dcae095c8a089995fd134 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 30 Dec 2023 18:05:09 +0100
Subject: [PATCH 409/435] fix: bring back default like setting

Seems like due to misskey deleting reaction.vue and making it a new file called emoji-picker.vue caused the default like setting to vanish
---
 .../src/pages/settings/emoji-picker.vue       | 39 +++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue
index 1a07790e75..e0a622dc6c 100644
--- a/packages/frontend/src/pages/settings/emoji-picker.vue
+++ b/packages/frontend/src/pages/settings/emoji-picker.vue
@@ -85,6 +85,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</div>
 	</MkFolder>
 
+	<FromSlot>
+		<template #label>{{ i18n.ts.defaultLike }}</template>
+		<MkCustomEmoji v-if="like && like.startsWith(':')" style="max-height: 3em; font-size: 1.1em;" :useOriginalSize="false" :class="$style.reaction" :name="like" :normal="true" :noStyle="true"/>
+		<MkEmoji v-else-if="like && !like.startsWith(':')" :emoji="like" style="max-height: 3em; font-size: 1.1em;" :normal="true" :noStyle="true"/>
+		<span v-else-if="!like">{{ i18n.ts.notSet }}</span>
+		<div class="_buttons" style="padding-top: 8px;">
+			<MkButton rounded :small="true" inline @click="chooseNewLike"><i class="ph-smiley ph-bold ph-lg"></i> Change</MkButton>
+			<MkButton rounded :small="true" inline @click="resetLike"><i class="ph-arrow-clockwise ph-bold ph-lg"></i> Reset</MkButton>
+		</div>
+	</FromSlot>
+
 	<FormSection>
 		<template #label>{{ i18n.ts.emojiPickerDisplay }}</template>
 
@@ -128,6 +139,7 @@ import Sortable from 'vuedraggable';
 import MkRadios from '@/components/MkRadios.vue';
 import MkButton from '@/components/MkButton.vue';
 import FormSection from '@/components/form/section.vue';
+import FromSlot from '@/components/form/slot.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import * as os from '@/os.js';
 import { defaultStore } from '@/store.js';
@@ -136,6 +148,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { deepClone } from '@/scripts/clone.js';
 import { reactionPicker } from '@/scripts/reaction-picker.js';
 import { emojiPicker } from '@/scripts/emoji-picker.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
 import MkCustomEmoji from '@/components/global/MkCustomEmoji.vue';
 import MkEmoji from '@/components/global/MkEmoji.vue';
 import MkFolder from '@/components/MkFolder.vue';
@@ -152,6 +165,8 @@ const removeReaction = (reaction: string, ev: MouseEvent) => remove(pinnedEmojis
 const chooseReaction = (ev: MouseEvent) => pickEmoji(pinnedEmojisForReaction, ev);
 const setDefaultReaction = () => setDefault(pinnedEmojisForReaction);
 
+const like = computed(defaultStore.makeGetterSetter('like'));
+
 const removeEmoji = (reaction: string, ev: MouseEvent) => remove(pinnedEmojis, reaction, ev);
 const chooseEmoji = (ev: MouseEvent) => pickEmoji(pinnedEmojis, ev);
 const setDefaultEmoji = () => setDefault(pinnedEmojis);
@@ -220,6 +235,30 @@ async function pickEmoji(itemsRef: Ref<string[]>, ev: MouseEvent) {
 	});
 }
 
+async function reloadAsk() {
+	const { canceled } = await os.confirm({
+		type: 'info',
+		text: i18n.ts.reloadToApplySetting,
+	});
+	if (canceled) return;
+
+	unisonReload();
+}
+
+function chooseNewLike(ev: MouseEvent) {
+	os.pickEmoji(getHTMLElement(ev), {
+		showPinned: false,
+	}).then(async emoji => {
+		defaultStore.set('like', emoji as string);
+		await reloadAsk();
+	});
+}
+
+async function resetLike() {
+	defaultStore.set('like', null);
+	await reloadAsk();
+}
+
 function getHTMLElement(ev: MouseEvent): HTMLElement {
 	const target = ev.currentTarget ?? ev.target;
 	return target as HTMLElement;

From 386e4f2665730b0b134756b6e52a204a25fed52f Mon Sep 17 00:00:00 2001
From: Vavency <vavency@gmail.com>
Date: Sat, 30 Dec 2023 21:07:24 +0200
Subject: [PATCH 410/435] fix: lint MkModPlayer

---
 packages/frontend/src/components/MkModPlayer.vue | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/packages/frontend/src/components/MkModPlayer.vue b/packages/frontend/src/components/MkModPlayer.vue
index 530b9c34d2..f61144cbca 100644
--- a/packages/frontend/src/components/MkModPlayer.vue
+++ b/packages/frontend/src/components/MkModPlayer.vue
@@ -8,7 +8,7 @@
 
 <div v-else class="mod-player-enabled">
 	<div class="pattern-display" @click="togglePattern()">
-		<div class="pattern-hide" v-if="patternHide">
+		<div v-if="patternHide" class="pattern-hide">
 			<b><i class="ph-eye ph-bold ph-lg"></i> Pattern Hidden</b>
 			<span>{{ i18n.ts.clickToShow }}</span>
 		</div>
@@ -166,32 +166,31 @@ function toggleVisible() {
 		firstFrame.value = true;
 		patternHide.value = false;
 	}
-	nextTick(() => {
-		stop(hide.value);
-	});
+	nextTick(() => { stop(hide.value); });
 }
 
 function togglePattern() {
 	patternHide.value = !patternHide.value;
 	if (!patternHide.value) {
-		if (player.value.getRow() == 0) {
+		if (player.value.getRow() === 0) {
 			try {
 				player.value.play(buffer);
 				display();
-			} catch (e) {
-				console.warn(e);
+			} catch (err) {
+				console.warn(err);
 			}
 			player.value.stop();
 		}
 	}
 }
+
 function display() {
 	if (!displayCanvas.value) {
 		stop();
 		return;
 	}
 
-	if (patternHide.value) { return; }
+	if (patternHide.value) return;
 
 	if (firstFrame.value) {
 		firstFrame.value = false;

From 265bcf54b08c8943e8589ab28e9ad8efec3512e4 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sat, 30 Dec 2023 23:14:00 +0100
Subject: [PATCH 411/435] upd: remove collapsed mentions

Glitchy/Broken CSS - Won't get any fixes by free so it is best to just undo the change

Closes #279
Closes #278
---
 .../frontend/src/components/MkMention.vue     | 42 -------------------
 .../global/MkMisskeyFlavoredMarkdown.ts       |  8 +---
 2 files changed, 1 insertion(+), 49 deletions(-)

diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue
index 3aaf73683f..4d42053657 100644
--- a/packages/frontend/src/components/MkMention.vue
+++ b/packages/frontend/src/components/MkMention.vue
@@ -57,48 +57,6 @@ const avatarUrl = computed(() => defaultStore.state.disableShowingAnimatedImages
 	}
 }
 
-.root + .root {
-  position: relative;
-  margin-inline: -20px 0;
-  box-shadow: -4px 0 0 var(--panel), -15px 0 15px var(--panel);
-  overflow: clip;
-  isolation: isolate;
-
-  &::before {
-    content: "";
-    position: absolute;
-    inset: 0;
-    background: var(--panel);
-    z-index: -1;
-  }
-
-  &::after {
-    content: "";
-    position: absolute;
-    inset: 0;
-    background: var(--panel);
-    z-index: -1;
-    background: inherit;
-  }
-
-  span {
-    display: inline-block;
-    white-space: nowrap;
-    max-width: 3em;
-    mask: linear-gradient(to right, #000 20%, rgba(0, 0, 0, 0.4));
-  }
-
-  + .root {
-    margin-inline: -10px 0;
-    padding-inline-end: 0;
-    box-shadow: -4px 0 0 var(--panel);
-
-    span {
-      display: none;
-    }
-  }
-}
-
 .icon {
 	width: 1.5em;
 	height: 1.5em;
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index 5bac440a81..a3bfdf0bb4 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -88,13 +88,7 @@ export default function(props: MfmProps, context: SetupContext<MfmEvents>) {
 						res.push(t);
 					}
 					res.shift();
-
-					// Don't wrap whitespaces in a span
-					if (text === ' ') {
-						return res;
-					}
-
-					return h('span', res);
+					return res;
 				} else {
 					return [text.replace(/\n/g, ' ')];
 				}

From 0bb0d69543c8bc393d28e25f25019cd545be040a Mon Sep 17 00:00:00 2001
From: smitten <everything-cozy@pm.me>
Date: Sat, 30 Dec 2023 20:44:31 -0500
Subject: [PATCH 412/435] Use hashed filename for exists check

---
 .../src/queue/processors/ImportNotesProcessorService.ts    | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
index 552b69d92d..67078adaf6 100644
--- a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
@@ -1,5 +1,6 @@
 import * as fs from 'node:fs';
 import * as vm from 'node:vm';
+import * as crypto from 'node:crypto';
 import { Inject, Injectable } from '@nestjs/common';
 import { ZipReader } from 'slacc';
 import { DI } from '@/di-symbols.js';
@@ -469,7 +470,10 @@ export class ImportNotesProcessorService {
 
 			for await (const file of post.object.attachment) {
 				const slashdex = file.url.lastIndexOf('/');
-				const name = file.url.substring(slashdex + 1);
+				const filename = file.url.substring(slashdex + 1);
+				const hash = crypto.createHash('md5').setEncoding('hex');
+				const urlHash = hash.update(file.url).digest('base64');
+				const name = `${urlHash}-${filename}`;
 				const [filePath, cleanup] = await createTemp();
 
 				const exists = await this.driveFilesRepository.findOneBy({ name: name, userId: user.id }) ?? await this.driveFilesRepository.findOneBy({ name: name, userId: user.id, folderId: pleroFolder?.id });
@@ -484,6 +488,7 @@ export class ImportNotesProcessorService {
 						user: user,
 						path: filePath,
 						name: name,
+						comment: file.name,
 						folderId: pleroFolder?.id,
 					});
 					files.push(driveFile);

From 667daebb79c09f7ab80f97e5d3dd30f5b5add4a5 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 03:48:51 +0100
Subject: [PATCH 413/435] upd: prevent vanilla mastodon imports from importing
 DMs

Also adds the visibility function to mastodon imports
---
 .../src/queue/processors/ImportNotesProcessorService.ts  | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
index 552b69d92d..942870dfc7 100644
--- a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
@@ -72,7 +72,6 @@ export class ImportNotesProcessorService {
 		}
 	}
 
-	// Function was taken from Firefish and modified for our needs
 	@bindThis
 	private async recreateChain(idFieldPath: string[], replyFieldPath: string[], arr: any[], includeOrphans: boolean): Promise<any[]> {
 		type NotesMap = {
@@ -378,7 +377,11 @@ export class ImportNotesProcessorService {
 			return;
 		}
 
-		if (toot.directMessage) return;
+		const followers = toot.to.some((str: string) => str.includes('/followers'));
+
+		if (toot.directMessage || (!toot.to.includes('https://www.w3.org/ns/activitystreams#Public') || !followers)) return;
+
+		const visibility = followers ? 'home' : 'public';
 
 		const date = new Date(toot.object.published);
 		let text = undefined;
@@ -417,7 +420,7 @@ export class ImportNotesProcessorService {
 			}
 		}
 
-		const createdNote = await this.noteCreateService.import(user, { createdAt: date, text: text, files: files, apMentions: new Array(0), cw: toot.object.sensitive ? toot.object.summary : null, reply: reply });
+		const createdNote = await this.noteCreateService.import(user, { createdAt: date, text: text, files: files, visibility: visibility, apMentions: new Array(0), cw: toot.object.sensitive ? toot.object.summary : null, reply: reply });
 		if (toot.childNotes) this.queueService.createImportMastoToDbJob(user, toot.childNotes, createdNote.id);
 	}
 

From fc6581b94850502e4dac258e32265f9b1fe72617 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 03:50:05 +0100
Subject: [PATCH 414/435] fix: correct followers visibility on import

---
 .../backend/src/queue/processors/ImportNotesProcessorService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
index 942870dfc7..072567d05c 100644
--- a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
@@ -381,7 +381,7 @@ export class ImportNotesProcessorService {
 
 		if (toot.directMessage || (!toot.to.includes('https://www.w3.org/ns/activitystreams#Public') || !followers)) return;
 
-		const visibility = followers ? 'home' : 'public';
+		const visibility = followers ? 'followers' : 'public';
 
 		const date = new Date(toot.object.published);
 		let text = undefined;

From 07f06d7ed6948c949aa273df29ff0ef11551d568 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 04:09:44 +0100
Subject: [PATCH 415/435] fix: if condition

---
 .../backend/src/queue/processors/ImportNotesProcessorService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
index 072567d05c..9344467b58 100644
--- a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
@@ -379,7 +379,7 @@ export class ImportNotesProcessorService {
 
 		const followers = toot.to.some((str: string) => str.includes('/followers'));
 
-		if (toot.directMessage || (!toot.to.includes('https://www.w3.org/ns/activitystreams#Public') || !followers)) return;
+		if (toot.directMessage || !toot.to.includes('https://www.w3.org/ns/activitystreams#Public') && !followers) return;
 
 		const visibility = followers ? 'followers' : 'public';
 

From b700fadbe32cc15912a072d638af55b4da849576 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 06:32:39 +0100
Subject: [PATCH 416/435] upd: add home as a visibility for mastodon imports

---
 .../backend/src/queue/processors/ImportNotesProcessorService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
index 9344467b58..6749001205 100644
--- a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
@@ -381,7 +381,7 @@ export class ImportNotesProcessorService {
 
 		if (toot.directMessage || !toot.to.includes('https://www.w3.org/ns/activitystreams#Public') && !followers) return;
 
-		const visibility = followers ? 'followers' : 'public';
+		const visibility = followers ? 'followers' : toot.cc.includes('https://www.w3.org/ns/activitystreams#Public') ? 'home' : 'public';
 
 		const date = new Date(toot.object.published);
 		let text = undefined;

From 6d5d3d9ea18ba348d67af9af7e60d268ebe981e0 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Sun, 31 Dec 2023 13:27:38 +0000
Subject: [PATCH 417/435] auth-fetch: ask to never cache responses
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

I could have factored out all the lines that set cache headers, but
that would have made future merges even more complicated ☹

thanks ShittyCopper for reporting the problem!
---
 .../src/server/ActivityPubServerService.ts    | 55 +++++++++++++------
 1 file changed, 39 insertions(+), 16 deletions(-)

diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index 68de738238..c3992eac80 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -129,6 +129,13 @@ export class ActivityPubServerService {
 			 this is also inspired by FireFish's `checkFetch`
 		*/
 
+		/* tell any caching proxy that they should not cache these
+		   responses: we wouldn't want the proxy to return a 403 to
+		   someone presenting a valid signature, or return a cached
+		   response body to someone we've blocked!
+		 */
+		reply.header('Cache-Control', 'private, max-age=0, must-revalidate');
+
 		/* we always allow requests about our instance actor, because when
 			 a remote instance needs to check our signature on a request we
 			 sent, it will need to fetch information about the user that
@@ -322,11 +329,13 @@ export class ActivityPubServerService {
 
 		if (profile.followersVisibility === 'private') {
 			reply.code(403);
-			reply.header('Cache-Control', 'public, max-age=30');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=30');
 			return;
 		} else if (profile.followersVisibility === 'followers') {
 			reply.code(403);
-			reply.header('Cache-Control', 'public, max-age=30');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=30');
 			return;
 		}
 		//#endregion
@@ -378,7 +387,8 @@ export class ActivityPubServerService {
 				user.followersCount,
 				`${partOf}?page=true`,
 			);
-			reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(rendered));
 		}
@@ -416,11 +426,13 @@ export class ActivityPubServerService {
 
 		if (profile.followingVisibility === 'private') {
 			reply.code(403);
-			reply.header('Cache-Control', 'public, max-age=30');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=30');
 			return;
 		} else if (profile.followingVisibility === 'followers') {
 			reply.code(403);
-			reply.header('Cache-Control', 'public, max-age=30');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=30');
 			return;
 		}
 		//#endregion
@@ -472,7 +484,8 @@ export class ActivityPubServerService {
 				user.followingCount,
 				`${partOf}?page=true`,
 			);
-			reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(rendered));
 		}
@@ -513,7 +526,8 @@ export class ActivityPubServerService {
 			renderedNotes,
 		);
 
-		reply.header('Cache-Control', 'public, max-age=180');
+		if (!this.config.checkActivityPubGetSignature)
+			reply.header('Cache-Control', 'public, max-age=180');
 		this.setResponseType(request, reply);
 		return (this.apRendererService.addContext(rendered));
 	}
@@ -604,7 +618,8 @@ export class ActivityPubServerService {
 				`${partOf}?page=true`,
 				`${partOf}?page=true&since_id=000000000000000000000000`,
 			);
-			reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(rendered));
 		}
@@ -617,7 +632,8 @@ export class ActivityPubServerService {
 			return;
 		}
 
-		reply.header('Cache-Control', 'public, max-age=180');
+		if (!this.config.checkActivityPubGetSignature)
+			reply.header('Cache-Control', 'public, max-age=180');
 		this.setResponseType(request, reply);
 		return (this.apRendererService.addContext(await this.apRendererService.renderPerson(user as MiLocalUser)));
 	}
@@ -707,7 +723,8 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return this.apRendererService.addContext(await this.apRendererService.renderNote(note, false));
 		});
@@ -730,7 +747,8 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(await this.packActivity(note)));
 		});
@@ -775,7 +793,8 @@ export class ActivityPubServerService {
 			const keypair = await this.userKeypairService.getUserKeypair(user.id);
 
 			if (this.userEntityService.isLocalUser(user)) {
-				reply.header('Cache-Control', 'public, max-age=180');
+				if (!this.config.checkActivityPubGetSignature)
+					reply.header('Cache-Control', 'public, max-age=180');
 				this.setResponseType(request, reply);
 				return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair)));
 			} else {
@@ -825,7 +844,8 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(await this.apRendererService.renderEmoji(emoji)));
 		});
@@ -848,7 +868,8 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, note)));
 		});
@@ -876,7 +897,8 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee)));
 		});
@@ -913,7 +935,8 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature)
+				reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee)));
 		});

From e9428a5a0542479a4b45e3b905c677d4f1da6e9a Mon Sep 17 00:00:00 2001
From: smitten <everything-cozy@pm.me>
Date: Sun, 31 Dec 2023 09:03:46 -0500
Subject: [PATCH 418/435] Use hex digest

---
 .../src/queue/processors/ImportNotesProcessorService.ts       | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
index 67078adaf6..769f4d1db7 100644
--- a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
@@ -471,8 +471,8 @@ export class ImportNotesProcessorService {
 			for await (const file of post.object.attachment) {
 				const slashdex = file.url.lastIndexOf('/');
 				const filename = file.url.substring(slashdex + 1);
-				const hash = crypto.createHash('md5').setEncoding('hex');
-				const urlHash = hash.update(file.url).digest('base64');
+				const hash = crypto.createHash('md5');
+				const urlHash = hash.update(file.url).digest('hex');
 				const name = `${urlHash}-${filename}`;
 				const [filePath, cleanup] = await createTemp();
 

From 327694d4cff00f64f5529d017524c27e848db562 Mon Sep 17 00:00:00 2001
From: smitten <everything-cozy@pm.me>
Date: Sun, 31 Dec 2023 09:13:51 -0500
Subject: [PATCH 419/435] Use base64url digest

---
 .../backend/src/queue/processors/ImportNotesProcessorService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
index 769f4d1db7..40aa7ffc18 100644
--- a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
@@ -472,7 +472,7 @@ export class ImportNotesProcessorService {
 				const slashdex = file.url.lastIndexOf('/');
 				const filename = file.url.substring(slashdex + 1);
 				const hash = crypto.createHash('md5');
-				const urlHash = hash.update(file.url).digest('hex');
+				const urlHash = hash.update(file.url).digest('base64url');
 				const name = `${urlHash}-${filename}`;
 				const [filePath, cleanup] = await createTemp();
 

From 2b06b822ac655f42b7e27c5a5fb92faaa13b766a Mon Sep 17 00:00:00 2001
From: Insert5StarName <anime@shourai.de>
Date: Sun, 31 Dec 2023 15:55:00 +0100
Subject: [PATCH 420/435] fix: detailed view being dashed

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

diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue
index df0259a2c7..f850adba1b 100644
--- a/packages/frontend/src/components/SkNoteDetailed.vue
+++ b/packages/frontend/src/components/SkNoteDetailed.vue
@@ -970,7 +970,7 @@ function animatedMFM() {
 
 .quoteNote {
 	padding: 16px;
-	border: dashed 1px var(--renote);
+	border: solid 1px var(--renote);
 	border-radius: var(--radius-sm);
 	overflow: clip;
 }

From 8d6d5923daff95b484a4d9ad92d0c43e6ac9ec96 Mon Sep 17 00:00:00 2001
From: smitten <everything-cozy@pm.me>
Date: Sun, 31 Dec 2023 11:14:41 -0500
Subject: [PATCH 421/435] Simplify hash steps

---
 .../src/queue/processors/ImportNotesProcessorService.ts      | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
index 40aa7ffc18..a49e0d4dde 100644
--- a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
@@ -471,9 +471,8 @@ export class ImportNotesProcessorService {
 			for await (const file of post.object.attachment) {
 				const slashdex = file.url.lastIndexOf('/');
 				const filename = file.url.substring(slashdex + 1);
-				const hash = crypto.createHash('md5');
-				const urlHash = hash.update(file.url).digest('base64url');
-				const name = `${urlHash}-${filename}`;
+				const hash = crypto.createHash('md5').update(file.url).digest('base64url');
+				const name = `${hash}-${filename}`;
 				const [filePath, cleanup] = await createTemp();
 
 				const exists = await this.driveFilesRepository.findOneBy({ name: name, userId: user.id }) ?? await this.driveFilesRepository.findOneBy({ name: name, userId: user.id, folderId: pleroFolder?.id });

From 61c193c08f17c00b643f3d7087858b5a2f7693ce Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Sun, 31 Dec 2023 16:17:45 +0000
Subject: [PATCH 422/435] lint

---
 .../src/server/ActivityPubServerService.ts    | 48 +++++++------------
 1 file changed, 16 insertions(+), 32 deletions(-)

diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts
index c3992eac80..8fa8320c8c 100644
--- a/packages/backend/src/server/ActivityPubServerService.ts
+++ b/packages/backend/src/server/ActivityPubServerService.ts
@@ -329,13 +329,11 @@ export class ActivityPubServerService {
 
 		if (profile.followersVisibility === 'private') {
 			reply.code(403);
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=30');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=30');
 			return;
 		} else if (profile.followersVisibility === 'followers') {
 			reply.code(403);
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=30');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=30');
 			return;
 		}
 		//#endregion
@@ -387,8 +385,7 @@ export class ActivityPubServerService {
 				user.followersCount,
 				`${partOf}?page=true`,
 			);
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(rendered));
 		}
@@ -426,13 +423,11 @@ export class ActivityPubServerService {
 
 		if (profile.followingVisibility === 'private') {
 			reply.code(403);
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=30');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=30');
 			return;
 		} else if (profile.followingVisibility === 'followers') {
 			reply.code(403);
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=30');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=30');
 			return;
 		}
 		//#endregion
@@ -484,8 +479,7 @@ export class ActivityPubServerService {
 				user.followingCount,
 				`${partOf}?page=true`,
 			);
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(rendered));
 		}
@@ -526,8 +520,7 @@ export class ActivityPubServerService {
 			renderedNotes,
 		);
 
-		if (!this.config.checkActivityPubGetSignature)
-			reply.header('Cache-Control', 'public, max-age=180');
+		if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 		this.setResponseType(request, reply);
 		return (this.apRendererService.addContext(rendered));
 	}
@@ -618,8 +611,7 @@ export class ActivityPubServerService {
 				`${partOf}?page=true`,
 				`${partOf}?page=true&since_id=000000000000000000000000`,
 			);
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(rendered));
 		}
@@ -632,8 +624,7 @@ export class ActivityPubServerService {
 			return;
 		}
 
-		if (!this.config.checkActivityPubGetSignature)
-			reply.header('Cache-Control', 'public, max-age=180');
+		if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 		this.setResponseType(request, reply);
 		return (this.apRendererService.addContext(await this.apRendererService.renderPerson(user as MiLocalUser)));
 	}
@@ -723,8 +714,7 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return this.apRendererService.addContext(await this.apRendererService.renderNote(note, false));
 		});
@@ -747,8 +737,7 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(await this.packActivity(note)));
 		});
@@ -793,8 +782,7 @@ export class ActivityPubServerService {
 			const keypair = await this.userKeypairService.getUserKeypair(user.id);
 
 			if (this.userEntityService.isLocalUser(user)) {
-				if (!this.config.checkActivityPubGetSignature)
-					reply.header('Cache-Control', 'public, max-age=180');
+				if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 				this.setResponseType(request, reply);
 				return (this.apRendererService.addContext(this.apRendererService.renderKey(user, keypair)));
 			} else {
@@ -844,8 +832,7 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(await this.apRendererService.renderEmoji(emoji)));
 		});
@@ -868,8 +855,7 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(await this.apRendererService.renderLike(reaction, note)));
 		});
@@ -897,8 +883,7 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee)));
 		});
@@ -935,8 +920,7 @@ export class ActivityPubServerService {
 				return;
 			}
 
-			if (!this.config.checkActivityPubGetSignature)
-				reply.header('Cache-Control', 'public, max-age=180');
+			if (!this.config.checkActivityPubGetSignature) reply.header('Cache-Control', 'public, max-age=180');
 			this.setResponseType(request, reply);
 			return (this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee)));
 		});

From 031d748d0c196b69ba7bba0fd2629b0b9fc3412f Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 17:25:38 +0100
Subject: [PATCH 423/435] fix: /oauth/oauth to /oauth

---
 packages/backend/src/server/oauth/OAuth2ProviderService.ts | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/packages/backend/src/server/oauth/OAuth2ProviderService.ts b/packages/backend/src/server/oauth/OAuth2ProviderService.ts
index d857f0bdd4..6de9038620 100644
--- a/packages/backend/src/server/oauth/OAuth2ProviderService.ts
+++ b/packages/backend/src/server/oauth/OAuth2ProviderService.ts
@@ -131,7 +131,7 @@ export class OAuth2ProviderService {
 
 		fastify.register(multer.contentParser);
 
-		fastify.get('/oauth/authorize', async (request, reply) => {
+		fastify.get('/authorize', async (request, reply) => {
 			const query: any = request.query;
 			let param = "mastodon=true";
 			if (query.state) param += `&state=${query.state}`;
@@ -142,7 +142,7 @@ export class OAuth2ProviderService {
 			);
 		});
 
-		fastify.get('/oauth/authorize/', async (request, reply) => {
+		fastify.get('/authorize/', async (request, reply) => {
 			const query: any = request.query;
 			let param = "mastodon=true";
 			if (query.state) param += `&state=${query.state}`;
@@ -153,7 +153,7 @@ export class OAuth2ProviderService {
 			);
 		});
 
-		fastify.post('/oauth/token', { preHandler: upload.none() }, async (request, reply) => {
+		fastify.post('/token', { preHandler: upload.none() }, async (request, reply) => {
 			const body: any = request.body || request.query;
 			if (body.grant_type === "client_credentials") {
 				const ret = {

From b1c26201ca91140c7b28a768e829e185dc23f2d7 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 18:22:02 +0100
Subject: [PATCH 424/435] upd: Note Length customization

note length is now configurable through the config file

Closes #281

falls back to 3000 (misskey default) if not used/included in config
---
 .config/docker_example.yml                       |  5 +++++
 .config/example.yml                              |  3 +++
 packages/backend/src/config.ts                   |  5 ++++-
 .../backend/src/server/NodeinfoServerService.ts  |  3 +--
 .../backend/src/server/api/endpoints/meta.ts     |  3 +--
 .../src/server/api/endpoints/notes/create.ts     | 16 ++++++++++++++--
 .../src/server/api/endpoints/notes/edit.ts       | 16 +++++++++++++---
 .../src/server/api/mastodon/endpoints/meta.ts    |  4 ++--
 8 files changed, 43 insertions(+), 12 deletions(-)

diff --git a/.config/docker_example.yml b/.config/docker_example.yml
index b3a6d78520..c6c83a98bf 100644
--- a/.config/docker_example.yml
+++ b/.config/docker_example.yml
@@ -167,6 +167,9 @@ id: 'aidx'
 # IP address family used for outgoing request (ipv4, ipv6 or dual)
 #outgoingAddressFamily: ipv4
 
+# Amount of characters that can be used when writing notes (maximum: 8192, minimum: 1)
+maxNoteLength: 3000
+
 # Proxy for HTTP/HTTPS
 #proxy: http://127.0.0.1:3128
 
@@ -197,6 +200,8 @@ proxyRemoteFiles: true
 
 # Sign to ActivityPub GET request (default: true)
 signToActivityPubGet: true
+# check that inbound ActivityPub GET requests are signed ("authorized fetch")
+checkActivityPubGetSignature: false
 
 # For security reasons, uploading attachments from the intranet is prohibited,
 # but exceptions can be made from the following settings. Default value is "undefined". 
diff --git a/.config/example.yml b/.config/example.yml
index 28fe5b359d..4aa7757c61 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -179,6 +179,9 @@ id: 'aidx'
 # IP address family used for outgoing request (ipv4, ipv6 or dual)
 #outgoingAddressFamily: ipv4
 
+# Amount of characters that can be used when writing notes (maximum: 8192, minimum: 1)
+maxNoteLength: 3000
+
 # Proxy for HTTP/HTTPS
 #proxy: http://127.0.0.1:3128
 
diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index 24a0296aa9..dfaf186e03 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -7,8 +7,8 @@ import * as fs from 'node:fs';
 import { fileURLToPath } from 'node:url';
 import { dirname, resolve } from 'node:path';
 import * as yaml from 'js-yaml';
-import type { RedisOptions } from 'ioredis';
 import { globSync } from 'glob';
+import type { RedisOptions } from 'ioredis';
 
 type RedisOptionsSource = Partial<RedisOptions> & {
 	host: string;
@@ -65,6 +65,7 @@ type Source = {
 	allowedPrivateNetworks?: string[];
 
 	maxFileSize?: number;
+	maxNoteLength?: number;
 
 	clusterLimit?: number;
 
@@ -133,6 +134,7 @@ export type Config = {
 	proxyBypassHosts: string[] | undefined;
 	allowedPrivateNetworks: string[] | undefined;
 	maxFileSize: number | undefined;
+	maxNoteLength: number;
 	clusterLimit: number | undefined;
 	id: string;
 	outgoingAddress: string | undefined;
@@ -249,6 +251,7 @@ export function loadConfig(): Config {
 		proxyBypassHosts: config.proxyBypassHosts,
 		allowedPrivateNetworks: config.allowedPrivateNetworks,
 		maxFileSize: config.maxFileSize,
+		maxNoteLength: config.maxNoteLength ?? 3000,
 		clusterLimit: config.clusterLimit,
 		outgoingAddress: config.outgoingAddress,
 		outgoingAddressFamily: config.outgoingAddressFamily,
diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts
index 37a120a6f6..31479269b9 100644
--- a/packages/backend/src/server/NodeinfoServerService.ts
+++ b/packages/backend/src/server/NodeinfoServerService.ts
@@ -7,7 +7,6 @@ import { Inject, Injectable } from '@nestjs/common';
 import { DI } from '@/di-symbols.js';
 import type { Config } from '@/config.js';
 import { MetaService } from '@/core/MetaService.js';
-import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
 import { MemorySingleCache } from '@/misc/cache.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { bindThis } from '@/decorators.js';
@@ -118,7 +117,7 @@ export class NodeinfoServerService {
 					emailRequiredForSignup: meta.emailRequiredForSignup,
 					enableHcaptcha: meta.enableHcaptcha,
 					enableRecaptcha: meta.enableRecaptcha,
-					maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
+					maxNoteTextLength: this.config.maxNoteLength,
 					enableEmail: meta.enableEmail,
 					enableServiceWorker: meta.enableServiceWorker,
 					proxyAccountName: proxyAccount ? proxyAccount.username : null,
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index 9ba22f89b9..1d0c102c9d 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -7,7 +7,6 @@ import { IsNull, LessThanOrEqual, MoreThan, Brackets } from 'typeorm';
 import { Inject, Injectable } from '@nestjs/common';
 import JSON5 from 'json5';
 import type { AdsRepository, UsersRepository } from '@/models/_.js';
-import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { UserEntityService } from '@/core/entities/UserEntityService.js';
 import { MetaService } from '@/core/MetaService.js';
@@ -375,7 +374,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 				iconUrl: instance.iconUrl,
 				backgroundImageUrl: instance.backgroundImageUrl,
 				logoImageUrl: instance.logoImageUrl,
-				maxNoteTextLength: MAX_NOTE_TEXT_LENGTH,
+				maxNoteTextLength: this.config.maxNoteLength,
 				// クライアントの手間を減らすためあらかじめJSONに変換しておく
 				defaultLightTheme: instance.defaultLightTheme ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) : null,
 				defaultDarkTheme: instance.defaultDarkTheme ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) : null,
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 27743dfffa..ac0a7f3b51 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -11,7 +11,7 @@ import type { UsersRepository, NotesRepository, BlockingsRepository, DriveFilesR
 import type { MiDriveFile } from '@/models/DriveFile.js';
 import type { MiNote } from '@/models/Note.js';
 import type { MiChannel } from '@/models/Channel.js';
-import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
+import type { Config } from '@/config.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { NoteCreateService } from '@/core/NoteCreateService.js';
@@ -82,6 +82,12 @@ export const meta = {
 			id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15',
 		},
 
+		maxLength: {
+			message: 'You tried posting a note which is too long.',
+			code: 'MAX_LENGTH',
+			id: '3ac74a84-8fd5-4bb0-870f-01804f82ce16',
+		},
+
 		cannotCreateAlreadyExpiredPoll: {
 			message: 'Poll is already expired.',
 			code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL',
@@ -136,7 +142,6 @@ export const paramDef = {
 		text: {
 			type: 'string',
 			minLength: 1,
-			maxLength: MAX_NOTE_TEXT_LENGTH,
 			nullable: true,
 		},
 		fileIds: {
@@ -184,6 +189,9 @@ export const paramDef = {
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 	constructor(
+		@Inject(DI.config)
+		private config: Config,
+
 		@Inject(DI.usersRepository)
 		private usersRepository: UsersRepository,
 
@@ -203,6 +211,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private noteCreateService: NoteCreateService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			if (ps.text && (ps.text.length > this.config.maxNoteLength)) {
+				throw new ApiError(meta.errors.maxLength);
+			}
+
 			let visibleUsers: MiUser[] = [];
 			if (ps.visibleUserIds) {
 				visibleUsers = await this.usersRepository.findBy({
diff --git a/packages/backend/src/server/api/endpoints/notes/edit.ts b/packages/backend/src/server/api/endpoints/notes/edit.ts
index cfbc207853..0c9c0d3baf 100644
--- a/packages/backend/src/server/api/endpoints/notes/edit.ts
+++ b/packages/backend/src/server/api/endpoints/notes/edit.ts
@@ -6,7 +6,7 @@ import type { UsersRepository, NotesRepository, BlockingsRepository, DriveFilesR
 import type { MiDriveFile } from '@/models/DriveFile.js';
 import type { MiNote } from '@/models/Note.js';
 import type { MiChannel } from '@/models/Channel.js';
-import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
+import type { Config } from '@/config.js';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
 import { NoteEditService } from '@/core/NoteEditService.js';
@@ -135,6 +135,12 @@ export const meta = {
 			code: 'CANNOT_QUOTE_THE_CURRENT_NOTE',
 			id: '33510210-8452-094c-6227-4a6c05d99f02',
 		},
+
+		maxLength: {
+			message: 'You tried posting a note which is too long.',
+			code: 'MAX_LENGTH',
+			id: '3ac74a84-8fd5-4bb0-870f-01804f82ce16',
+		},
 	},
 } as const;
 
@@ -163,7 +169,6 @@ export const paramDef = {
 		text: {
 			type: 'string',
 			minLength: 1,
-			maxLength: MAX_NOTE_TEXT_LENGTH,
 			nullable: true,
 		},
 		fileIds: {
@@ -205,7 +210,6 @@ export const paramDef = {
 				text: {
 					type: 'string',
 					minLength: 1,
-					maxLength: MAX_NOTE_TEXT_LENGTH,
 					nullable: false,
 				},
 			},
@@ -236,6 +240,9 @@ export const paramDef = {
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 	constructor(
+		@Inject(DI.config)
+		private config: Config,
+
 		@Inject(DI.usersRepository)
 		private usersRepository: UsersRepository,
 
@@ -255,6 +262,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
 		private noteEditService: NoteEditService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
+			if (ps.text && (ps.text.length > this.config.maxNoteLength)) {
+				throw new ApiError(meta.errors.maxLength);
+			}
 			let visibleUsers: MiUser[] = [];
 			if (ps.visibleUserIds) {
 				visibleUsers = await this.usersRepository.findBy({
diff --git a/packages/backend/src/server/api/mastodon/endpoints/meta.ts b/packages/backend/src/server/api/mastodon/endpoints/meta.ts
index 61713b3415..efb39ef939 100644
--- a/packages/backend/src/server/api/mastodon/endpoints/meta.ts
+++ b/packages/backend/src/server/api/mastodon/endpoints/meta.ts
@@ -1,5 +1,5 @@
 import { Entity } from 'megalodon';
-import { MAX_NOTE_TEXT_LENGTH, FILE_TYPE_BROWSERSAFE } from '@/const.js';
+import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
 import type { Config } from '@/config.js';
 import type { MiMeta } from '@/models/Meta.js';
 
@@ -35,7 +35,7 @@ export async function getInstance(
 				max_featured_tags: 20,
 			},
 			statuses: {
-				max_characters: MAX_NOTE_TEXT_LENGTH,
+				max_characters: config.maxNoteLength,
 				max_media_attachments: 16,
 				characters_reserved_per_url: response.uri.length,
 			},

From 8336b6c6b47e08d1458a43e9e89bfbb7235cc769 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 18:41:29 +0100
Subject: [PATCH 425/435] upd: check for replies length

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

diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index 0dc4b0b31b..f5a3f08b91 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<!-- new avatar container with line (post section) -->
 		<div :class="$style.avatarContainer">
 			<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
-			<template v-if="note.repliesCount > 0">
+			<template v-if="note.repliesCount > 0 && replies.length > 0">
 				<div v-if="hideLine" :class="$style.threadLine"></div>
 			</template>
 		</div>

From 3ec00398a3302100648d9b8819e095391a8683f9 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 19:20:52 +0100
Subject: [PATCH 426/435] fix: security with notes/show endpoint

---
 .../src/server/api/endpoints/notes/show.ts    | 29 ++++++++++++++-----
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts
index 5bb8196543..b3107f6754 100644
--- a/packages/backend/src/server/api/endpoints/notes/show.ts
+++ b/packages/backend/src/server/api/endpoints/notes/show.ts
@@ -3,10 +3,12 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Injectable } from '@nestjs/common';
+import { Inject, Injectable } from '@nestjs/common';
 import { Endpoint } from '@/server/api/endpoint-base.js';
 import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
-import { GetterService } from '@/server/api/GetterService.js';
+import { DI } from '@/di-symbols.js';
+import type { NotesRepository } from '@/models/_.js';
+import { QueryService } from '@/core/QueryService.js';
 import { ApiError } from '../../error.js';
 
 export const meta = {
@@ -40,14 +42,27 @@ export const paramDef = {
 @Injectable()
 export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
 	constructor(
+		@Inject(DI.notesRepository)
+		private notesRepository: NotesRepository,
+		
 		private noteEntityService: NoteEntityService,
-		private getterService: GetterService,
+		private queryService: QueryService,
 	) {
 		super(meta, paramDef, async (ps, me) => {
-			const note = await this.getterService.getNote(ps.noteId).catch(err => {
-				if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
-				throw err;
-			});
+			const query = await this.notesRepository.createQueryBuilder('note')
+				.where('note.id = :noteId', { noteId: ps.noteId });
+
+			this.queryService.generateVisibilityQuery(query, me);
+			if (me) {
+				this.queryService.generateMutedUserQuery(query, me);
+				this.queryService.generateBlockedUserQuery(query, me);
+			}
+			
+			const note = await query.getOne();
+
+			if (note === null) {
+				throw new ApiError(meta.errors.noSuchNote);
+			}
 
 			return await this.noteEntityService.pack(note, me, {
 				detail: true,

From 7b04c6ade4dc11c604ccbf39b834988a16621e85 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 19:29:09 +0100
Subject: [PATCH 427/435] fix: make real-time update work with new notes/show
 changes

---
 .../frontend/src/scripts/use-note-capture.ts  | 35 +++++++++++--------
 1 file changed, 21 insertions(+), 14 deletions(-)

diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts
index 427bc6ff36..bcdba5455a 100644
--- a/packages/frontend/src/scripts/use-note-capture.ts
+++ b/packages/frontend/src/scripts/use-note-capture.ts
@@ -30,11 +30,15 @@ export function useNoteCapture(props: {
 			case 'replied': {
 				if (!props.onReplyCallback) break;
 
-				const replyNote = await os.api("notes/show", {
-					noteId: body.id,
-				});
+				// notes/show may throw if the current user can't see the note
+				try {
+					const replyNote = await os.api('notes/show', {
+						noteId: body.id,
+					});
 
-				await props.onReplyCallback(replyNote);
+					await props.onReplyCallback(replyNote);
+				} catch { /* empty */ }
+				
 				break;
 			}
 
@@ -95,17 +99,20 @@ export function useNoteCapture(props: {
 			}
 
 			case 'updated': {
-				const editedNote = await os.api("notes/show", {
-					noteId: id,
-				});
+				try {
+					const editedNote = await os.api('notes/show', {
+						noteId: id,
+					});
+					
+					const keys = new Set<string>();
+					Object.keys(editedNote)
+						.concat(Object.keys(note.value))
+						.forEach((key) => keys.add(key));
+					keys.forEach((key) => {
+						note.value[key] = editedNote[key];
+					});
+				} catch { /* empty */ }
 
-				const keys = new Set<string>();
-				Object.keys(editedNote)
-					.concat(Object.keys(note.value))
-					.forEach((key) => keys.add(key));
-				keys.forEach((key) => {
-					note.value[key] = editedNote[key];
-				});
 				break;
 			}
 		}

From 8bc77072cbe8946eb4aa922719cdf51c4e275a97 Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Sun, 31 Dec 2023 18:44:53 +0000
Subject: [PATCH 428/435] fix: sort multiple config files

`globSync` doesn't guarantee the order in which it returns the
matching paths, so without the `sort()`, the config files may be
merged differently each time the server is started
---
 packages/backend/src/config.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index dfaf186e03..a550fdc364 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -201,7 +201,7 @@ export function loadConfig(): Config {
 		JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_vite_/manifest.json`, 'utf-8'))
 		: { 'src/_boot_.ts': { file: 'src/_boot_.ts' } };
 
-	const config = globSync(path)
+	const config = globSync(path).sort()
 		.map(path => fs.readFileSync(path, 'utf-8'))
 		.map(contents => yaml.load(contents) as Source)
 		.reduce(

From 049129fabeed017498d996f9780e641e041a2e3d Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 20:02:31 +0100
Subject: [PATCH 429/435] chore: translate some japanese to english

---
 locales/en-US.yml | 64 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/locales/en-US.yml b/locales/en-US.yml
index 1ac2f4b280..64f5d568eb 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -126,7 +126,11 @@ add: "Add"
 reaction: "Reactions"
 reactions: "Reactions"
 emojiPicker: "Emoji picker"
+pinnedEmojisForReactionSettingDescription: "Set the emojis which should be pinned and displayed immediately when reacting."
+pinnedEmojisSettingDescription: "Set the emojis to be pinned and displayed when entering emojis"
 emojiPickerDisplay: "Emoji picker display"
+overwriteFromPinnedEmojisForReaction: "Override from reaction settings"
+overwriteFromPinnedEmojis: "Override from general settings"
 reactionSettingDescription2: "Drag to reorder, click to delete, press \"+\" to add."
 rememberNoteVisibility: "Remember note visibility settings"
 attachCancel: "Remove attachment"
@@ -1195,6 +1199,7 @@ tosAndPrivacyPolicy: "Terms of Service and Privacy Policy"
 avatarDecorations: "Avatar decorations"
 attach: "Attach"
 detach: "Remove"
+detachAll: "Remove all"
 angle: "Angle"
 flip: "Flip"
 showAvatarDecorations: "Show avatar decorations"
@@ -1208,7 +1213,12 @@ cwNotationRequired: "If \"Hide content\" is enabled, a description must be provi
 doReaction: "Add reaction"
 code: "Code"
 reloadRequiredToApplySettings: "Reloading is required to apply the settings."
+remainingN: "Remaining: {n}"
+overwriteContentConfirm: "Are you sure you want to overwrite the current content?"
+seasonalScreenEffect: "Seasonal screen effects"
 decorate: "Decorate"
+addMfmFunction: "Add MFM"
+enableQuickAddMfmFunction: "Show advanced MFM picker"
 _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."
@@ -1642,6 +1652,7 @@ _role:
     canHideAds: "Can hide ads"
     canSearchNotes: "Usage of note search"
     canUseTranslator: "Translator usage"
+    avatarDecorationLimit: "Maximum number of avatar decorations that can be applied"
   _condition:
     isLocal: "Local user"
     isRemote: "Remote user"
@@ -1670,6 +1681,7 @@ _emailUnavailable:
   disposable: "Disposable email addresses may not be used"
   mx: "This email server is invalid"
   smtp: "This email server is not responding"
+  banned: "This email address is banned"
 _ffVisibility:
   public: "Public"
   followers: "Visible to followers only"
@@ -1964,6 +1976,55 @@ _permissions:
   "write:flash": "Edit Plays"
   "read:flash-likes": "View list of liked Plays"
   "write:flash-likes": "Edit list of liked Plays"
+  "read:admin:abuse-user-reports": "View user reports"
+  "write:admin:delete-account": "Delete account"
+  "write:admin:delete-all-files-of-a-user": "Delete all files of a user"
+  "read:admin:index-stats": "View information about database indexes"
+  "read:admin:table-stats": "View information about database tables"
+  "read:admin:user-ips": "View user IP address"
+  "read:admin:meta": "View instance metadata"
+  "write:admin:reset-password": "Reset user passwords"
+  "write:admin:resolve-abuse-user-report": "Resolve user reports"
+  "write:admin:send-email": "Send Email"
+  "read:admin:server-info": "View server info"
+  "read:admin:show-moderation-log": "View moderation log"
+  "read:admin:show-user": "View user information"
+  "read:admin:show-users": "View users"
+  "write:admin:suspend-user": "Suspend user"
+  "write:admin:unset-user-avatar": "Remove avatar from user"
+  "write:admin:unset-user-banner": "Remove banner from user"
+  "write:admin:unsuspend-user": "Unsuspend user"
+  "write:admin:meta": "Edit instance metadata"
+  "write:admin:user-note": "Edit user note"
+  "write:admin:roles": "Edit roles"
+  "read:admin:roles": "View roles"
+  "write:admin:relays": "Edit relays"
+  "read:admin:relays": "View relays"
+  "write:admin:invite-codes": "Edit invite codes"
+  "read:admin:invite-codes": "View invite codes"
+  "write:admin:announcements": "Edit announcements"
+  "read:admin:announcements": "View announcements"
+  "write:admin:avatar-decorations": "Edit avatar decorations"
+  "read:admin:avatar-decorations": "View avatar decorations"
+  "write:admin:federation": "Edit remote instance information"
+  "write:admin:account": "Edit users"
+  "read:admin:account": "View information about user"
+  "write:admin:emoji": "Edit emojis"
+  "read:admin:emoji": "View emojis"
+  "write:admin:queue": "Edit queue"
+  "read:admin:queue": "View queue"
+  "write:admin:promo": "Edit promo"
+  "write:admin:drive": "Edit user drive"
+  "read:admin:drive": "View user drive"
+  "read:admin:stream": "Using the Websocket API for Admin"
+  "write:admin:ad": "Edit ads"
+  "read:admin:ad": "View ads"
+  "write:invite-codes": "Create Invitation Code"
+  "read:invite-codes": "View Invitation Code"
+  "write:clip-favorite": "Edit clips and likes"
+  "read:clip-favorite": "View clips and likes"
+  "read:federation": "View information about remote instance"
+  "write:report-abuse": "Report abuse"
 _auth:
   shareAccessTitle: "Granting application permissions"
   shareAccess: "Would you like to authorize \"{name}\" to access this account?"
@@ -2084,6 +2145,7 @@ _profile:
   changeBanner: "Change banner"
   changeBackground: "Change background"
   verifiedLinkDescription: "By entering an URL that contains a link to your profile here, an ownership verification icon can be displayed next to the field."
+  avatarDecorationMax: "You can add up to {max} decorations."
 _exportOrImport:
   allNotes: "All notes"
   favoritedNotes: "Favorite notes"
@@ -2464,11 +2526,13 @@ _dataRequest:
 _dataSaver:
   _media:
     title: "Loading Media"
+    description: "Prevents images/videos from being loaded automatically. Hidden images/videos will be loaded when tapped."
   _avatar:
     title: "Avatar image"
     description: "Stop avatar image animation. Animated images can be larger in file size than normal  images, potentially leading to further reductions in data traffic."
   _urlPreview:
     title: "URL preview thumbnails"
+    description: "URL preview thumbnail images will no longer be loaded."
   _code:
     title: "Code highlighting"
     description: "If code highlighting notations are used in MFM, etc., they will not load until tapped. Syntax highlighting requires downloading the highlight definition files for each programming language. Therefore, disabling the automatic loading of these files is expected to reduce the amount of communication data."

From 8e02d7f36496e3c15b533b612e19ff9ec978bcbb Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 20:14:22 +0100
Subject: [PATCH 430/435] upd: add rosepine and rosepine dawn as preinstalle
 themes

---
 packages/frontend/.storybook/preload-theme.ts |  2 +
 packages/frontend/src/scripts/theme.ts        |  2 +
 .../frontend/src/themes/rosepine-dawn.json5   | 89 +++++++++++++++++++
 packages/frontend/src/themes/rosepine.json5   | 86 ++++++++++++++++++
 4 files changed, 179 insertions(+)
 create mode 100644 packages/frontend/src/themes/rosepine-dawn.json5
 create mode 100644 packages/frontend/src/themes/rosepine.json5

diff --git a/packages/frontend/.storybook/preload-theme.ts b/packages/frontend/.storybook/preload-theme.ts
index ad2cf18a35..8f32c6e622 100644
--- a/packages/frontend/.storybook/preload-theme.ts
+++ b/packages/frontend/.storybook/preload-theme.ts
@@ -28,6 +28,8 @@ const keys = [
 	'd-cherry',
 	'd-ice',
 	'd-u0',
+	'rosepine',
+	'rosepine-dawn',
 ]
 
 await Promise.all(keys.map((key) => readFile(new URL(`../src/themes/${key}.json5`, import.meta.url), 'utf8'))).then((sources) => {
diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index 3bf6d5798c..a174f51756 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -44,6 +44,8 @@ export const getBuiltinThemes = () => Promise.all(
 		'd-cherry',
 		'd-ice',
 		'd-u0',
+		'rosepine',
+		'rosepine-dawn',
 	].map(name => import(`@/themes/${name}.json5`).then(({ default: _default }): Theme => _default)),
 );
 
diff --git a/packages/frontend/src/themes/rosepine-dawn.json5 b/packages/frontend/src/themes/rosepine-dawn.json5
new file mode 100644
index 0000000000..ff1ca0c996
--- /dev/null
+++ b/packages/frontend/src/themes/rosepine-dawn.json5
@@ -0,0 +1,89 @@
+{
+	id: '919c91ac-c6fa-43dc-a423-3cc84fd67d7c',
+	base: 'light',
+	name: ' Rosé Pine Dawn',
+	description: 'Soho vibes for Misskey, dawn edition',
+	props: {
+		accent: '#286983',
+		accentDarken: ':darken<10<@accent',
+		accentLighten: ':lighten<10<@accent',
+		accentedBg: ':alpha<0.15<@accent',
+		focus: ':alpha<0.3<@accent',
+		bg: '#faf4ed',
+		acrylicBg: ':alpha<0.5<@bg',
+		fg: '#575279',
+		fgTransparentWeak: ':alpha<0.75<@fg',
+		fgTransparent: ':alpha<0.5<@fg',
+		fgHighlighted: ':darken<3<@fg',
+		fgOnAccent: '#fffaf3',
+		divider: 'rgba(0, 0, 0, 0.1)',
+		indicator: '@accent',
+		panel: ':lighten<3<@bg',
+		panelHighlight: ':darken<3<@panel',
+		panelHeaderBg: ':lighten<3<@panel',
+		panelHeaderFg: '@fg',
+		panelHeaderDivider: 'rgba(0, 0, 0, 0)',
+		panelBorder: '" solid 1px var(--divider)',
+		acrylicPanel: ':alpha<0.5<@panel',
+		popup: ':lighten<3<@panel',
+		shadow: 'rgba(0, 0, 0, 0.1)',
+		header: ':alpha<0.7<@panel',
+		navBg: '@panel',
+		navFg: '@fg',
+		navHoverFg: ':darken<17<@fg',
+		navActive: '@accent',
+		navIndicator: '@indicator',
+		link: '#56949f',
+		hashtag: '#ea9d34',
+		mention: '@accent',
+		mentionMe: '@mention',
+		renote: '#56949f',
+		modalBg: 'rgba(0, 0, 0, 0.3)',
+		scrollbarHandle: 'rgba(0, 0, 0, 0.2)',
+		scrollbarHandleHover: 'rgba(0, 0, 0, 0.4)',
+		dateLabelFg: '@fg',
+		infoBg: '#f2e9e1',
+		infoFg: '#ea9d34',
+		infoWarnBg: '#f2e9e1',
+		infoWarnFg: '#b4637a',
+		switchBg: 'rgba(0, 0, 0, 0.15)',
+		cwBg: '#b4637a',
+		cwFg: '#faf4ed',
+		cwHoverBg: '#d7827e',
+		buttonBg: 'rgba(0, 0, 0, 0.05)',
+		buttonHoverBg: 'rgba(0, 0, 0, 0.1)',
+		buttonGradateA: '#d7827e',
+		buttonGradateB: ':hue<20<#d7827e',
+		inputBorder: 'rgba(0, 0, 0, 0.1)',
+		inputBorderHover: 'rgba(0, 0, 0, 0.2)',
+		listItemHoverBg: 'rgba(0, 0, 0, 0.03)',
+		driveFolderBg: ':alpha<0.3<@accent',
+		wallpaperOverlay: 'rgba(255, 255, 255, 0.5)',
+		badge: '#31b1ce',
+		messageBg: '@bg',
+		success: '#907aa9',
+		error: '#b4637a',
+		warn: '#ea9d34',
+		codeString: '#b98710',
+		codeNumber: '#0fbbbb',
+		codeBoolean: '#62b70c',
+		htmlThemeColor: '@bg',
+		X2: ':darken<2<@panel',
+		X3: 'rgba(0, 0, 0, 0.05)',
+		X4: 'rgba(0, 0, 0, 0.1)',
+		X5: 'rgba(0, 0, 0, 0.05)',
+		X6: 'rgba(0, 0, 0, 0.25)',
+		X7: 'rgba(0, 0, 0, 0.05)',
+		X8: ':lighten<5<@accent',
+		X9: ':darken<5<@accent',
+		X10: ':alpha<0.4<@accent',
+		X11: 'rgba(0, 0, 0, 0.1)',
+		X12: 'rgba(0, 0, 0, 0.1)',
+		X13: 'rgba(0, 0, 0, 0.15)',
+		X14: ':alpha<0.5<@navBg',
+		X15: ':alpha<0<@panel',
+		X16: ':alpha<0.7<@panel',
+		X17: ':alpha<0.8<@bg',
+	},
+	author: '@thatonecalculator@stop.voring.me',
+}
\ No newline at end of file
diff --git a/packages/frontend/src/themes/rosepine.json5 b/packages/frontend/src/themes/rosepine.json5
new file mode 100644
index 0000000000..06516f75fc
--- /dev/null
+++ b/packages/frontend/src/themes/rosepine.json5
@@ -0,0 +1,86 @@
+{
+	id: '3cdfd635-4d5e-4d06-9ba3-20f123f0999b',
+	base: 'dark',
+	desc: 'Soho vibes for Misskey',
+	name: 'Rosé Pine v3',
+	props: {
+		X2: ':darken<2<@panel',
+		X3: 'rgba(255, 255, 255, 0.05)',
+		X4: 'rgba(255, 255, 255, 0.1)',
+		X5: 'rgba(255, 255, 255, 0.05)',
+		X6: 'rgba(255, 255, 255, 0.15)',
+		X7: 'rgba(255, 255, 255, 0.05)',
+		X8: ':lighten<5<@accent',
+		X9: ':darken<5<@accent',
+		bg: '#191724',
+		fg: '#e0def4',
+		X10: ':alpha<0.4<@accent',
+		X11: 'rgba(0, 0, 0, 0.3)',
+		X12: 'rgba(255, 255, 255, 0.1)',
+		X13: 'rgba(255, 255, 255, 0.15)',
+		X14: ':alpha<0.5<@navBg',
+		X15: ':alpha<0<@panel',
+		X16: ':alpha<0.7<@panel',
+		X17: ':alpha<0.8<@bg',
+		cwBg: '#1f1d2e',
+		cwFg: '#f6c177',
+		link: '#9ccfd8',
+		warn: '#f6c177',
+		badge: '#ebbcba',
+		error: '#eb6f92',
+		focus: ':alpha<0.3<@accent',
+		navBg: '@panel',
+		navFg: '@fg',
+		panel: ':lighten<3<@bg',
+		popup: ':lighten<3<@panel',
+		accent: '#c4a7e7',
+		header: ':alpha<0.7<@panel',
+		infoBg: '#253142',
+		infoFg: '#fff',
+		renote: '#31748f',
+		shadow: 'rgba(0, 0, 0, 0.3)',
+		divider: 'rgba(255, 255, 255, 0.1)',
+		hashtag: '#ebbcba',
+		mention: '@accent',
+		modalBg: 'rgba(0, 0, 0, 0.5)',
+		success: '#ebbcba',
+		buttonBg: 'rgba(255, 255, 255, 0.05)',
+		switchBg: 'rgba(255, 255, 255, 0.15)',
+		acrylicBg: ':alpha<0.5<@bg',
+		cwHoverBg: '#26233a',
+		indicator: '@accent',
+		mentionMe: '@mention',
+		messageBg: '@bg',
+		navActive: '@accent',
+		accentedBg: ':alpha<0.15<@accent',
+		fgOnAccent: '#26233a',
+		infoWarnBg: '#26233a',
+		infoWarnFg: '#f6c177',
+		navHoverFg: ':lighten<17<@fg',
+		dateLabelFg: '@fg',
+		inputBorder: 'rgba(255, 255, 255, 0.1)',
+		panelBorder: '" solid 1px var(--divider)',
+		accentDarken: ':darken<10<@accent',
+		acrylicPanel: ':alpha<0.5<@panel',
+		navIndicator: '@indicator',
+		accentLighten: ':lighten<10<@accent',
+		buttonHoverBg: 'rgba(255, 255, 255, 0.1)',
+		driveFolderBg: ':alpha<0.3<@accent',
+		fgHighlighted: ':lighten<3<@fg',
+		fgTransparent: ':alpha<0.5<@fg',
+		panelHeaderBg: ':lighten<3<@panel',
+		panelHeaderFg: '@fg',
+		buttonGradateA: '@accent',
+		buttonGradateB: '#ebbcba',
+		htmlThemeColor: '@bg',
+		panelHighlight: ':lighten<3<@panel',
+		listItemHoverBg: 'rgba(255, 255, 255, 0.03)',
+		scrollbarHandle: 'rgba(255, 255, 255, 0.2)',
+		inputBorderHover: 'rgba(255, 255, 255, 0.2)',
+		wallpaperOverlay: 'rgba(0, 0, 0, 0.5)',
+		fgTransparentWeak: ':alpha<0.75<@fg',
+		panelHeaderDivider: 'rgba(0, 0, 0, 0)',
+		scrollbarHandleHover: 'rgba(255, 255, 255, 0.4)',
+	},
+	author: '@thatonecalculator@stop.voring.me',
+}
\ No newline at end of file

From f42e2bacd4e1413ec491e27d26b70d446e832cfc Mon Sep 17 00:00:00 2001
From: dakkar <dakkar@thenautilus.net>
Date: Sun, 31 Dec 2023 20:56:16 +0000
Subject: [PATCH 431/435] (probably) fix line length between note and 1st reply

---
 packages/frontend/src/components/SkNoteSub.vue | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue
index f5a3f08b91..bc482294b4 100644
--- a/packages/frontend/src/components/SkNoteSub.vue
+++ b/packages/frontend/src/components/SkNoteSub.vue
@@ -437,10 +437,11 @@ if (props.detail) {
 
 .line {
 	position: absolute;
-	height: 100%;
+	height: calc(100% - 58px); // 58px of avatar height (see SkNote)
 	left: 60px;
 	// using solid instead of dotted, stylelistic choice
 	border-left: 2.5px solid rgb(174, 174, 174);
+	top: 86px; // 28px of .root padding, plus 58px of avatar height (see SkNote)
 }
 
 .footer {

From ce6fadf767b1e0238b90f284f4f38712fbfe18dd Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 22:17:40 +0100
Subject: [PATCH 432/435] fix: broken icon in emoji picker

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

diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index ea9d7a0d26..dabc12237a 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <!-- フォルダの中にはカスタム絵文字やフォルダがある -->
 <section v-else v-panel style="border-radius: var(--radius-sm); border-bottom: 0.5px solid var(--divider);">
 	<header class="_acrylic" @click="shown = !shown">
-		<i class="toggle ti-fw" :class="shown ? 'ph-caret-down ph-bold ph-lg' : 'ph-caret-up ph-bold ph-lg'"></i> <slot></slot> (<i class="ph-folder ph-bold ph-lg"></i>:{{ customEmojiTree.length }} <i class="ti ti-icons ti-fw"></i>:{{ emojis.length }})
+		<i class="toggle ti-fw" :class="shown ? 'ph-caret-down ph-bold ph-lg' : 'ph-caret-up ph-bold ph-lg'"></i> <slot></slot> (<i class="ph-folder ph-bold ph-lg"></i>:{{ customEmojiTree.length }} <i class="ph-smiley-sticker ph-bold ph-lg ti-fw"></i>:{{ emojis.length }})
 	</header>
 	<div v-if="shown" style="padding-left: 9px;">
 		<MkEmojiPickerSection

From 6a46e30f673f8e544e52536429b904577e7c72a2 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 22:26:19 +0100
Subject: [PATCH 433/435] chore: fix icons

---
 packages/frontend/src/components/MkCode.vue   |  2 +-
 .../frontend/src/components/MkCodeEditor.vue  |  2 +-
 packages/frontend/src/pages/admin-user.vue    |  4 ++--
 .../frontend/src/pages/admin/security.vue     |  4 ++--
 .../settings/avatar-decoration.decoration.vue |  2 +-
 .../settings/avatar-decoration.dialog.vue     |  6 ++---
 .../src/pages/settings/avatar-decoration.vue  |  2 +-
 .../src/pages/settings/emoji-picker.vue       | 22 +++++++++----------
 packages/frontend/src/pages/timeline.vue      |  6 ++---
 .../src/scripts/mfm-function-picker.ts        |  2 +-
 .../src/widgets/WidgetBirthdayFollowings.vue  |  2 +-
 11 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/packages/frontend/src/components/MkCode.vue b/packages/frontend/src/components/MkCode.vue
index 7346d5782b..e0973b676a 100644
--- a/packages/frontend/src/components/MkCode.vue
+++ b/packages/frontend/src/components/MkCode.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<pre v-else-if="show" :class="$style.codeBlockFallbackRoot"><code :class="$style.codeBlockFallbackCode">{{ code }}</code></pre>
 	<button v-else :class="$style.codePlaceholderRoot" @click="show = true">
 		<div :class="$style.codePlaceholderContainer">
-			<div><i class="ti ti-code"></i> {{ i18n.ts.code }}</div>
+			<div><i class="ph-code ph-bold ph-lg"></i> {{ i18n.ts.code }}</div>
 			<div>{{ i18n.ts.clickToShow }}</div>
 		</div>
 	</button>
diff --git a/packages/frontend/src/components/MkCodeEditor.vue b/packages/frontend/src/components/MkCodeEditor.vue
index c1aaa7f1ff..0ec69a69af 100644
--- a/packages/frontend/src/components/MkCodeEditor.vue
+++ b/packages/frontend/src/components/MkCodeEditor.vue
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</div>
 	</div>
 	<div :class="$style.caption"><slot name="caption"></slot></div>
-	<MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
+	<MkButton v-if="manualSave && changed" primary :class="$style.save" @click="updated"><i class="ph-floppy-disk ph-bold ph-lg"></i> {{ i18n.ts.save }}</MkButton>
 </div>
 </template>
 
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index c87ec22ef6..741897b5f0 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -110,8 +110,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</MkFolder>
 
 						<div>
-							<MkButton v-if="iAmModerator" inline danger style="margin-right: 8px;" @click="unsetUserAvatar"><i class="ti ti-user-circle"></i> {{ i18n.ts.unsetUserAvatar }}</MkButton>
-							<MkButton v-if="iAmModerator" inline danger @click="unsetUserBanner"><i class="ti ti-photo"></i> {{ i18n.ts.unsetUserBanner }}</MkButton>
+							<MkButton v-if="iAmModerator" inline danger style="margin-right: 8px;" @click="unsetUserAvatar"><i class="ph-user-circle ph-bold ph-lg"></i> {{ i18n.ts.unsetUserAvatar }}</MkButton>
+							<MkButton v-if="iAmModerator" inline danger @click="unsetUserBanner"><i class="ph-photo ph-bold ph-lg"></i> {{ i18n.ts.unsetUserBanner }}</MkButton>
 						</div>
 						<MkButton v-if="$i.isAdmin" inline danger @click="deleteAccount">{{ i18n.ts.deleteAccount }}</MkButton>
 					</div>
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index 524b9b9a79..8ed3e20af3 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 							<template #label>Use Verifymail.io API</template>
 						</MkSwitch>
 						<MkInput v-model="verifymailAuthKey" @update:modelValue="save">
-							<template #prefix><i class="ti ti-key"></i></template>
+							<template #prefix><i class="ph-key ph-bold ph-lg"></i></template>
 							<template #label>Verifymail.io API Auth Key</template>
 						</MkInput>
 					</div>
@@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 						<MkTextarea v-model="bannedEmailDomains">
 							<template #label>Banned Email Domains List</template>
 						</MkTextarea>
-						<MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
+						<MkButton primary @click="save"><i class="ph-floppy-disk ph-bold ph-lg"></i> {{ i18n.ts.save }}</MkButton>
 					</div>
 				</MkFolder>
 
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
index fcd74002f2..2bf261abd9 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 >
 	<div :class="$style.name"><MkCondensedLine :minScale="0.5">{{ decoration.name }}</MkCondensedLine></div>
 	<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: decoration.url, angle, flipH, offsetX, offsetY }]" forceShowDecoration/>
-	<i v-if="decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.lock" class="ti ti-lock"></i>
+	<i v-if="decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.lock" class="ph-lock ph-bold ph-lg"></i>
 </div>
 </template>
 
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
index 329ab4d47a..a46a92d1c6 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
@@ -36,9 +36,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</MkSpacer>
 
 		<div :class="$style.footer" class="_buttonsCenter">
-			<MkButton v-if="usingIndex != null" primary rounded @click="update"><i class="ti ti-check"></i> {{ i18n.ts.update }}</MkButton>
-			<MkButton v-if="usingIndex != null" rounded @click="detach"><i class="ti ti-x"></i> {{ i18n.ts.detach }}</MkButton>
-			<MkButton v-else :disabled="exceeded" primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.attach }}</MkButton>
+			<MkButton v-if="usingIndex != null" primary rounded @click="update"><i class="ph-check ph-bold ph-lg"></i> {{ i18n.ts.update }}</MkButton>
+			<MkButton v-if="usingIndex != null" rounded @click="detach"><i class="ph-x ph-bold ph-lg"></i> {{ i18n.ts.detach }}</MkButton>
+			<MkButton v-else :disabled="exceeded" primary rounded @click="attach"><i class="ph-check ph-bold ph-lg"></i> {{ i18n.ts.attach }}</MkButton>
 		</div>
 	</div>
 </MkModalWindow>
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue
index 6551fc917e..976f6aa68c 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.vue
@@ -127,7 +127,7 @@ const headerTabs = computed(() => []);
 
 definePageMetadata({
 	title: i18n.ts.avatarDecorations,
-	icon: 'ti ti-sparkles',
+	icon: 'ph-sparkle ph-bold ph-lg',
 });
 </script>
 
diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue
index e0a622dc6c..40bb823ac6 100644
--- a/packages/frontend/src/pages/settings/emoji-picker.vue
+++ b/packages/frontend/src/pages/settings/emoji-picker.vue
@@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <template>
 <div class="_gaps_m">
 	<MkFolder :defaultOpen="true">
-		<template #icon><i class="ti ti-pin"></i></template>
+		<template #icon><i class="ph-pin ph-bold ph-lg"></i></template>
 		<template #label>{{ i18n.ts.pinned }} ({{ i18n.ts.reaction }})</template>
 		<template #caption>{{ i18n.ts.pinnedEmojisForReactionSettingDescription }}</template>
 
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</template>
 						<template #footer>
 							<button class="_button" :class="$style.emojisAdd" @click="chooseReaction">
-								<i class="ti ti-plus"></i>
+								<i class="ph-plus ph-bold ph-lg"></i>
 							</button>
 						</template>
 					</Sortable>
@@ -38,15 +38,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</div>
 
 			<div class="_buttons">
-				<MkButton inline @click="previewReaction"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton>
-				<MkButton inline danger @click="setDefaultReaction"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton>
-				<MkButton inline danger @click="overwriteFromPinnedEmojis"><i class="ti ti-copy"></i> {{ i18n.ts.overwriteFromPinnedEmojis }}</MkButton>
+				<MkButton inline @click="previewReaction"><i class="ph-eye ph-bold ph-lg"></i> {{ i18n.ts.preview }}</MkButton>
+				<MkButton inline danger @click="setDefaultReaction"><i class="ph-arrow-counter-clockwise ph-bold ph-lg"></i> {{ i18n.ts.default }}</MkButton>
+				<MkButton inline danger @click="overwriteFromPinnedEmojis"><i class="ph-copy ph-bold ph-lg"></i> {{ i18n.ts.overwriteFromPinnedEmojis }}</MkButton>
 			</div>
 		</div>
 	</MkFolder>
 
 	<MkFolder>
-		<template #icon><i class="ti ti-pin"></i></template>
+		<template #icon><i class="ph-pin ph-bold ph-lg"></i></template>
 		<template #label>{{ i18n.ts.pinned }} ({{ i18n.ts.general }})</template>
 		<template #caption>{{ i18n.ts.pinnedEmojisSettingDescription }}</template>
 
@@ -69,7 +69,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 						</template>
 						<template #footer>
 							<button class="_button" :class="$style.emojisAdd" @click="chooseEmoji">
-								<i class="ti ti-plus"></i>
+								<i class="ph-plus ph-bold ph-lg"></i>
 							</button>
 						</template>
 					</Sortable>
@@ -78,9 +78,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 			</div>
 
 			<div class="_buttons">
-				<MkButton inline @click="previewEmoji"><i class="ti ti-eye"></i> {{ i18n.ts.preview }}</MkButton>
-				<MkButton inline danger @click="setDefaultEmoji"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton>
-				<MkButton inline danger @click="overwriteFromPinnedEmojisForReaction"><i class="ti ti-copy"></i> {{ i18n.ts.overwriteFromPinnedEmojisForReaction }}</MkButton>
+				<MkButton inline @click="previewEmoji"><i class="ph-eye ph-bold ph-lg"></i> {{ i18n.ts.preview }}</MkButton>
+				<MkButton inline danger @click="setDefaultEmoji"><i class="ph-arrow-counter-clockwise ph-bold ph-lg"></i> {{ i18n.ts.default }}</MkButton>
+				<MkButton inline danger @click="overwriteFromPinnedEmojisForReaction"><i class="ph-copy ph-bold ph-lg"></i> {{ i18n.ts.overwriteFromPinnedEmojisForReaction }}</MkButton>
 			</div>
 		</div>
 	</MkFolder>
@@ -278,7 +278,7 @@ watch(pinnedEmojis, () => {
 
 definePageMetadata({
 	title: i18n.ts.emojiPicker,
-	icon: 'ti ti-mood-happy',
+	icon: 'ph-smiley ph-bold ph-lg',
 });
 </script>
 
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index f9adee94dc..f5cefeddb4 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -96,7 +96,7 @@ async function chooseList(ev: MouseEvent): Promise<void> {
 		(lists.length === 0 ? undefined : { type: 'divider' }),
 		{
 			type: 'link' as const,
-			icon: 'ti ti-plus',
+			icon: 'ph-plus ph-bold ph-lg',
 			text: i18n.ts.createNew,
 			to: '/my/lists',
 		},
@@ -116,7 +116,7 @@ async function chooseAntenna(ev: MouseEvent): Promise<void> {
 		(antennas.length === 0 ? undefined : { type: 'divider' }),
 		{
 			type: 'link' as const,
-			icon: 'ti ti-plus',
+			icon: 'ph-plus ph-bold ph-lg',
 			text: i18n.ts.createNew,
 			to: '/my/antennas',
 		},
@@ -143,7 +143,7 @@ async function chooseChannel(ev: MouseEvent): Promise<void> {
 		(channels.length === 0 ? undefined : { type: 'divider' }),
 		{
 			type: 'link' as const,
-			icon: 'ti ti-plus',
+			icon: 'ph-plus ph-bold ph-lg',
 			text: i18n.ts.createNew,
 			to: '/channels',
 		},
diff --git a/packages/frontend/src/scripts/mfm-function-picker.ts b/packages/frontend/src/scripts/mfm-function-picker.ts
index 465926fe04..6e25cc856c 100644
--- a/packages/frontend/src/scripts/mfm-function-picker.ts
+++ b/packages/frontend/src/scripts/mfm-function-picker.ts
@@ -25,7 +25,7 @@ function getFunctionList(textArea: HTMLInputElement | HTMLTextAreaElement, textR
 	MFM_TAGS.forEach(tag => {
 		ret.push({
 			text: tag,
-			icon: 'ti ti-icons',
+			icon: 'ph-brackets-curly ph-bold ph-lg',
 			action: () => add(textArea, textRef, tag),
 		});
 	});
diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
index 7c4455516d..0a83eba9c1 100644
--- a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
+++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <template>
 <MkContainer :showHeader="widgetProps.showHeader" class="mkw-bdayfollowings">
-	<template #icon><i class="ti ti-cake"></i></template>
+	<template #icon><i class="ph-cake ph-bold ph-lg"></i></template>
 	<template #header>{{ i18n.ts._widgets.birthdayFollowings }}</template>
 
 	<div :class="$style.bdayFRoot">

From 18051505332cd274acbe74ad9c4e1ec3e16d6629 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 22:41:35 +0100
Subject: [PATCH 434/435] fix: visibility check on masto import

Originally from PR #288
---
 .../backend/src/queue/processors/ImportNotesProcessorService.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
index 1dc22404bf..03a0e951b3 100644
--- a/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportNotesProcessorService.ts
@@ -382,7 +382,7 @@ export class ImportNotesProcessorService {
 
 		if (toot.directMessage || !toot.to.includes('https://www.w3.org/ns/activitystreams#Public') && !followers) return;
 
-		const visibility = followers ? 'followers' : toot.cc.includes('https://www.w3.org/ns/activitystreams#Public') ? 'home' : 'public';
+		const visibility = followers ? toot.cc.includes('https://www.w3.org/ns/activitystreams#Public') ? 'home' : 'followers' : 'public';
 
 		const date = new Date(toot.object.published);
 		let text = undefined;

From 7ba8fde9b942ce8f24dd926be8fb432e5bf68401 Mon Sep 17 00:00:00 2001
From: Marie <marie@kaifa.ch>
Date: Sun, 31 Dec 2023 22:49:43 +0100
Subject: [PATCH 435/435] chore: change version

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

diff --git a/package.json b/package.json
index ec9e98175d..3aae34b8bf 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "sharkey",
-	"version": "2023.12.0.beta3",
+	"version": "2023.12.0",
 	"codename": "shonk",
 	"repository": {
 		"type": "git",