From 7cb84dc30a9143efa225203bd5b45d774508949f Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 6 Jul 2022 06:36:14 +0900
Subject: [PATCH 01/12] revert: feat: styled error screen (#8930)

---
 packages/backend/src/server/web/boot.js       | 43 ++------
 packages/backend/src/server/web/error.css     | 98 -------------------
 .../backend/src/server/web/views/base.pug     |  2 +-
 .../backend/src/server/web/views/flush.pug    |  6 --
 4 files changed, 11 insertions(+), 138 deletions(-)
 delete mode 100644 packages/backend/src/server/web/error.css

diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js
index e82de8e14..9fc536555 100644
--- a/packages/backend/src/server/web/boot.js
+++ b/packages/backend/src/server/web/boot.js
@@ -106,39 +106,15 @@
 	function renderError(code, details) {
 		let errorsElement = document.getElementById('errors');
 		if (!errorsElement) {
-			document.getElementsByTagName("head")[0].insertAdjacentHTML(
-				"beforeend",
-				`<link rel="stylesheet" href="../error.css" />`);
 			document.documentElement.innerHTML = `
-			<svg class="icon-warning" xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-alert-triangle" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
-   			<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
-   			<path d="M12 9v2m0 4v.01"></path>
-   			<path d="M5 19h14a2 2 0 0 0 1.84 -2.75l-7.1 -12.25a2 2 0 0 0 -3.5 0l-7.1 12.25a2 2 0 0 0 1.75 2.75"></path>
-			</svg>
-			<h1>An error has occurred!</h1>
-			<button class="button-big" onclick="location.reload(true);">
-				<span class="button-label-big">Refresh</span>
-			</button>
-      <p class="dont-worry">Don't worry, it's (probably) not your fault.</p>
-			<p>If the problem persists after refreshing, please contact your instance's administrator.<br>You may also try the following options:</p>
-      <a href="/flush">
-      <button class="button-small">
-        <span class="button-label-small">Flush preferences and cache</span>
-      </button>
-      </a>
-			<br>
-      <a href="/cli">
-        <button class="button-small">
-          <span class="button-label-small">Start the simple client</span>
-        </button>
-      </a>
-			<br>
-      <a href="/bios">
-        <button class="button-small">
-          <span class="button-label-small">Attempt to repair in Repair Tool</span>
-        </button>
-      </a>
-			<br>
+			<h1>⚠ An error has occurred. ⚠</h1>
+			<p>If the problem persists, please contact the administrator. You may also try the following options:</p>
+			<ul>
+				<li>Start <a href="/cli">the simple client</a></li>
+				<li>Attempt to repair in <a href="/bios">BIOS</a></li>
+				<li><a href="/flush">Flush preferences and cache</a></li>
+			</ul>
+			<hr>
 			<div id="errors"></div>
 			`;
 
@@ -146,7 +122,8 @@
 		}
 
 		const detailsElement = document.createElement('details');
-		detailsElement.innerHTML = `<br><summary><code>ERROR CODE: ${code}</code></summary>${JSON.stringify(details)}`;
+		detailsElement.innerHTML = `<summary><code>ERROR CODE: ${code}</code></summary>${JSON.stringify(details)}`;
+
 		errorsElement.appendChild(detailsElement);
 	}
 
diff --git a/packages/backend/src/server/web/error.css b/packages/backend/src/server/web/error.css
deleted file mode 100644
index 9f8f90501..000000000
--- a/packages/backend/src/server/web/error.css
+++ /dev/null
@@ -1,98 +0,0 @@
-* {
-	font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif;
-}
-
-body,
-html {
-	background-color: #222;
-	color: #dfddcc;
-	justify-content: center;
-	margin: auto;
-	width: 80%;
-	padding: 10px;
-	text-align: center;
-}
-
-button {
-	border-radius: 999px;
-	padding: 0px 12px 0px 12px;
-	border: none;
-	cursor: pointer;
-	margin-bottom: 12px;
-}
-
-.button-big {
-	background: linear-gradient(90deg, rgb(134, 179, 0), rgb(74, 179, 0));
-	line-height: 50px;
-}
-
-.button-big:hover {
-	background: rgb(153, 204, 0);
-}
-
-.button-small {
-	background: #444;
-	line-height: 40px;
-}
-
-.button-small:hover {
-	background: #555;
-}
-
-.button-label-big {
-	color: #222;
-	font-weight: bold;
-	font-size: 20px;
-	padding: 12px;
-}
-
-.button-label-small {
-	color: rgb(153, 204, 0);
-	font-size: 16px;
-	padding: 12px;
-}
-
-a {
-	color: rgb(134, 179, 0);
-	text-decoration: none;
-}
-
-p,
-li {
-	font-size: 16px;
-}
-
-.dont-worry,
-#msg {
-	font-size: 18px;
-}
-
-.icon-warning {
-	color: #dec340;
-	height: 4rem;
-}
-
-h1 {
-	font-size: 32px;
-}
-
-code {
-	font-family: Fira, FiraCode, monospace;
-}
-
-details {
-	background: #333;
-	margin-bottom: 2rem;
-	padding: 0.5rem 1rem;
-	border-radius: 5px;
-	justify-content: center;
-	margin: auto;
-}
-
-summary {
-	cursor: pointer;
-}
-
-summary > * {
-	display: inline;
-}
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 32499aa45..5bb156f0f 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -53,7 +53,7 @@ html
 		block meta
 
 		block og
-			meta(property='og:title'       content= title || 'Misskey')
+			meta(property='og:title'       content= title || 'Misskey') 
 			meta(property='og:description' content= desc || '✨🌎✨ A interplanetary communication platform ✨🚀✨') 
 			meta(property='og:image'       content= img)
 
diff --git a/packages/backend/src/server/web/views/flush.pug b/packages/backend/src/server/web/views/flush.pug
index b08f61c73..ec585a34d 100644
--- a/packages/backend/src/server/web/views/flush.pug
+++ b/packages/backend/src/server/web/views/flush.pug
@@ -1,12 +1,6 @@
 doctype html
 
 html
-	head
-		meta(charset='utf-8')
-		meta(name='application-name' content='Misskey')
-		title Flushing Misskey
-		style
-			include ../error.css
 	#msg
 	script.
 		const msg = document.getElementById('msg');

From 1ceeec7cfa032adeb092f38e1f86d79883c49ae7 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 6 Jul 2022 07:08:45 +0900
Subject: [PATCH 02/12] =?UTF-8?q?fix(client):=20=E3=83=86=E3=83=BC?=
 =?UTF-8?q?=E3=83=9E=E3=82=92=E4=BD=9C=E6=88=90=E3=81=99=E3=82=8B=E3=81=A8?=
 =?UTF-8?q?=E3=82=AF=E3=83=A9=E3=82=A4=E3=82=A2=E3=83=B3=E3=83=88=E3=81=8C?=
 =?UTF-8?q?=E8=B5=B7=E5=8B=95=E3=81=97=E3=81=AA=E3=81=8F=E3=81=AA=E3=82=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 packages/client/src/pages/settings/theme.vue | 10 ++++++++--
 packages/client/src/pages/theme-editor.vue   |  2 +-
 packages/client/src/store.ts                 |  8 ++++++++
 3 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/packages/client/src/pages/settings/theme.vue b/packages/client/src/pages/settings/theme.vue
index 1bdad3e75..d330e1ba2 100644
--- a/packages/client/src/pages/settings/theme.vue
+++ b/packages/client/src/pages/settings/theme.vue
@@ -97,7 +97,10 @@ const darkThemeId = computed({
 		return darkTheme.value.id;
 	},
 	set(id) {
-		ColdDeviceStorage.set('darkTheme', themes.value.find(x => x.id === id));
+		const t = themes.value.find(x => x.id === id);
+		if (t) { // テーマエディタでテーマを作成したときなどは、themesに反映されないため undefined になる
+			ColdDeviceStorage.set('darkTheme', t);
+		}
 	},
 });
 const lightTheme = ColdDeviceStorage.ref('lightTheme');
@@ -106,7 +109,10 @@ const lightThemeId = computed({
 		return lightTheme.value.id;
 	},
 	set(id) {
-		ColdDeviceStorage.set('lightTheme', themes.value.find(x => x.id === id));
+		const t = themes.value.find(x => x.id === id);
+		if (t) { // テーマエディタでテーマを作成したときなどは、themesに反映されないため undefined になる
+			ColdDeviceStorage.set('lightTheme', t);
+		}
 	},
 });
 const darkMode = computed(defaultStore.makeGetterSetter('darkMode'));
diff --git a/packages/client/src/pages/theme-editor.vue b/packages/client/src/pages/theme-editor.vue
index 44b5a05f2..548e60614 100644
--- a/packages/client/src/pages/theme-editor.vue
+++ b/packages/client/src/pages/theme-editor.vue
@@ -192,7 +192,7 @@ async function saveAs() {
 	theme.name = name;
 	theme.author = `@${$i.username}@${toUnicode(host)}`;
 	if (description) theme.desc = description;
-	addTheme(theme);
+	await addTheme(theme);
 	applyTheme(theme);
 	if (defaultStore.state.darkMode) {
 		ColdDeviceStorage.set('darkTheme', theme);
diff --git a/packages/client/src/store.ts b/packages/client/src/store.ts
index d87e05a4d..503333331 100644
--- a/packages/client/src/store.ts
+++ b/packages/client/src/store.ts
@@ -304,6 +304,14 @@ export class ColdDeviceStorage {
 	}
 
 	public static set<T extends keyof typeof ColdDeviceStorage.default>(key: T, value: typeof ColdDeviceStorage.default[T]): void {
+		// 呼び出し側のバグ等で undefined が来ることがある
+		// undefined を文字列として localStorage に入れると参照する際の JSON.parse でコケて不具合の元になるため無視
+		// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+		if (value === undefined) {
+			console.error(`attempt to store undefined value for key '${key}'`);
+			return;
+		}
+
 		localStorage.setItem(PREFIX + key, JSON.stringify(value));
 
 		for (const watcher of this.watchers) {

From 7672369a7dae7816d993097514be8dc68c6d135a Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 6 Jul 2022 07:26:58 +0900
Subject: [PATCH 03/12] revert emoji picker changes

---
 .../src/components/emoji-picker.section.vue   |  7 ++-
 .../client/src/components/emoji-picker.vue    | 50 ++++++++++---------
 2 files changed, 29 insertions(+), 28 deletions(-)

diff --git a/packages/client/src/components/emoji-picker.section.vue b/packages/client/src/components/emoji-picker.section.vue
index e2a80d546..901bc76ac 100644
--- a/packages/client/src/components/emoji-picker.section.vue
+++ b/packages/client/src/components/emoji-picker.section.vue
@@ -1,17 +1,16 @@
 <template>
-<!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと -->
 <section>
 	<header class="_acrylic" @click="shown = !shown">
 		<i class="toggle fa-fw" :class="shown ? 'fas fa-chevron-down' : 'fas fa-chevron-up'"></i> <slot></slot> ({{ emojis.length }})
 	</header>
-	<div v-if="shown" class="body">
+	<div v-if="shown">
 		<button
 			v-for="emoji in emojis"
 			:key="emoji"
-			class="_button item"
+			class="_button"
 			@click="emit('chosen', emoji, $event)"
 		>
-			<MkEmoji class="emoji" :emoji="emoji" :normal="true"/>
+			<MkEmoji :emoji="emoji" :normal="true"/>
 		</button>
 	</div>
 </section>
diff --git a/packages/client/src/components/emoji-picker.vue b/packages/client/src/components/emoji-picker.vue
index 2f974cbda..46cf56230 100644
--- a/packages/client/src/components/emoji-picker.vue
+++ b/packages/client/src/components/emoji-picker.vue
@@ -3,67 +3,67 @@
 	<input ref="search" v-model.trim="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" @paste.stop="paste" @keyup.enter="done()">
 	<div ref="emojis" class="emojis">
 		<section class="result">
-			<div v-if="searchResultCustom.length > 0" class="body">
+			<div v-if="searchResultCustom.length > 0">
 				<button
 					v-for="emoji in searchResultCustom"
 					:key="emoji.id"
-					class="_button item"
+					class="_button"
 					:title="emoji.name"
 					tabindex="0"
 					@click="chosen(emoji, $event)"
 				>
 					<!--<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>-->
-					<img class="emoji" :src="disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
+					<img :src="disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
 				</button>
 			</div>
-			<div v-if="searchResultUnicode.length > 0" class="body">
+			<div v-if="searchResultUnicode.length > 0">
 				<button
 					v-for="emoji in searchResultUnicode"
 					:key="emoji.name"
-					class="_button item"
+					class="_button"
 					:title="emoji.name"
 					tabindex="0"
 					@click="chosen(emoji, $event)"
 				>
-					<MkEmoji class="emoji" :emoji="emoji.char"/>
+					<MkEmoji :emoji="emoji.char"/>
 				</button>
 			</div>
 		</section>
 
-		<div v-if="tab === 'index'" class="group index">
+		<div v-if="tab === 'index'" class="index">
 			<section v-if="showPinned">
-				<div class="body">
+				<div>
 					<button
 						v-for="emoji in pinned"
 						:key="emoji"
-						class="_button item"
+						class="_button"
 						tabindex="0"
 						@click="chosen(emoji, $event)"
 					>
-						<MkEmoji class="emoji" :emoji="emoji" :normal="true"/>
+						<MkEmoji :emoji="emoji" :normal="true"/>
 					</button>
 				</div>
 			</section>
 
 			<section>
 				<header class="_acrylic"><i class="far fa-clock fa-fw"></i> {{ i18n.ts.recentUsed }}</header>
-				<div class="body">
+				<div>
 					<button
 						v-for="emoji in recentlyUsedEmojis"
 						:key="emoji"
-						class="_button item"
+						class="_button"
 						@click="chosen(emoji, $event)"
 					>
-						<MkEmoji class="emoji" :emoji="emoji" :normal="true"/>
+						<MkEmoji :emoji="emoji" :normal="true"/>
 					</button>
 				</div>
 			</section>
 		</div>
-		<div class="group">
+		<div>
 			<header class="_acrylic">{{ i18n.ts.customEmojis }}</header>
 			<XSection v-for="category in customEmojiCategories" :key="'custom:' + category" :initial-shown="false" :emojis="customEmojis.filter(e => e.category === category).map(e => ':' + e.name + ':')" @chosen="chosen">{{ category || i18n.ts.other }}</XSection>
 		</div>
-		<div class="group">
+		<div>
 			<header class="_acrylic">{{ i18n.ts.emoji }}</header>
 			<XSection v-for="category in categories" :key="category" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)" @chosen="chosen">{{ category }}</XSection>
 		</div>
@@ -419,16 +419,19 @@ defineExpose({
 					font-size: 15px;
 				}
 
-				> .body {
+				> div {
 					display: grid;
 					grid-template-columns: var(--columns);
-					font-size: 30px;
 
-					> .item {
+					> button {
 						aspect-ratio: 1 / 1;
 						width: auto;
 						height: auto;
 						min-width: 0;
+
+						> * {
+							font-size: 30px;
+						}
 					}
 				}
 			}
@@ -479,7 +482,7 @@ defineExpose({
 			display: none;
 		}
 
-		> .group {
+		> div {
 			&:not(.index) {
 				padding: 4px 0 8px 0;
 				border-top: solid 0.5px var(--divider);
@@ -514,18 +517,16 @@ defineExpose({
 				}
 			}
 
-			> .body {
+			> div {
 				position: relative;
 				padding: $pad;
 
-				> .item {
+				> button {
 					position: relative;
 					padding: 0;
 					width: var(--eachSize);
 					height: var(--eachSize);
-					contain: strict;
 					border-radius: 4px;
-					font-size: 24px;
 
 					&:focus-visible {
 						outline: solid 2px var(--focus);
@@ -541,7 +542,8 @@ defineExpose({
 						box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
 					}
 
-					> .emoji {
+					> * {
+						font-size: 24px;
 						height: 1.25em;
 						vertical-align: -.25em;
 						pointer-events: none;

From fe534fc3b4791815105d1cfbacf747bb008ceb09 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 6 Jul 2022 07:27:09 +0900
Subject: [PATCH 04/12] 12.112.0-beta.19

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

diff --git a/package.json b/package.json
index d86750411..9ba79b3b5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "12.112.0-beta.18",
+	"version": "12.112.0-beta.19",
 	"codename": "indigo",
 	"repository": {
 		"type": "git",

From d7ed3c370e11bd53d6e441735c61b138dfc710a5 Mon Sep 17 00:00:00 2001
From: Johann150 <johann.galle@protonmail.com>
Date: Wed, 6 Jul 2022 00:29:51 +0200
Subject: [PATCH 05/12] fix prismjs import

fixes #8944
---
 packages/client/src/components/code-core.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/client/src/components/code-core.vue b/packages/client/src/components/code-core.vue
index a816f3480..b07402882 100644
--- a/packages/client/src/components/code-core.vue
+++ b/packages/client/src/components/code-core.vue
@@ -6,7 +6,7 @@
 
 <script lang="ts" setup>
 import { computed } from 'vue';
-import { Prism } from 'prismjs';
+import Prism from 'prismjs';
 import 'prismjs/themes/prism-okaidia.css';
 
 const props = defineProps<{

From 2015c81ce4af2367f3c9a99d804f59381511f265 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 6 Jul 2022 07:30:56 +0900
Subject: [PATCH 06/12] Revert "revert emoji picker changes"

This reverts commit 38d5303ccd067e67f11ae130b643713f6ca86eef.
---
 .../src/components/emoji-picker.section.vue   |  7 +--
 .../client/src/components/emoji-picker.vue    | 50 +++++++++----------
 2 files changed, 28 insertions(+), 29 deletions(-)

diff --git a/packages/client/src/components/emoji-picker.section.vue b/packages/client/src/components/emoji-picker.section.vue
index 901bc76ac..e2a80d546 100644
--- a/packages/client/src/components/emoji-picker.section.vue
+++ b/packages/client/src/components/emoji-picker.section.vue
@@ -1,16 +1,17 @@
 <template>
+<!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと -->
 <section>
 	<header class="_acrylic" @click="shown = !shown">
 		<i class="toggle fa-fw" :class="shown ? 'fas fa-chevron-down' : 'fas fa-chevron-up'"></i> <slot></slot> ({{ emojis.length }})
 	</header>
-	<div v-if="shown">
+	<div v-if="shown" class="body">
 		<button
 			v-for="emoji in emojis"
 			:key="emoji"
-			class="_button"
+			class="_button item"
 			@click="emit('chosen', emoji, $event)"
 		>
-			<MkEmoji :emoji="emoji" :normal="true"/>
+			<MkEmoji class="emoji" :emoji="emoji" :normal="true"/>
 		</button>
 	</div>
 </section>
diff --git a/packages/client/src/components/emoji-picker.vue b/packages/client/src/components/emoji-picker.vue
index 46cf56230..2f974cbda 100644
--- a/packages/client/src/components/emoji-picker.vue
+++ b/packages/client/src/components/emoji-picker.vue
@@ -3,67 +3,67 @@
 	<input ref="search" v-model.trim="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" @paste.stop="paste" @keyup.enter="done()">
 	<div ref="emojis" class="emojis">
 		<section class="result">
-			<div v-if="searchResultCustom.length > 0">
+			<div v-if="searchResultCustom.length > 0" class="body">
 				<button
 					v-for="emoji in searchResultCustom"
 					:key="emoji.id"
-					class="_button"
+					class="_button item"
 					:title="emoji.name"
 					tabindex="0"
 					@click="chosen(emoji, $event)"
 				>
 					<!--<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>-->
-					<img :src="disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
+					<img class="emoji" :src="disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
 				</button>
 			</div>
-			<div v-if="searchResultUnicode.length > 0">
+			<div v-if="searchResultUnicode.length > 0" class="body">
 				<button
 					v-for="emoji in searchResultUnicode"
 					:key="emoji.name"
-					class="_button"
+					class="_button item"
 					:title="emoji.name"
 					tabindex="0"
 					@click="chosen(emoji, $event)"
 				>
-					<MkEmoji :emoji="emoji.char"/>
+					<MkEmoji class="emoji" :emoji="emoji.char"/>
 				</button>
 			</div>
 		</section>
 
-		<div v-if="tab === 'index'" class="index">
+		<div v-if="tab === 'index'" class="group index">
 			<section v-if="showPinned">
-				<div>
+				<div class="body">
 					<button
 						v-for="emoji in pinned"
 						:key="emoji"
-						class="_button"
+						class="_button item"
 						tabindex="0"
 						@click="chosen(emoji, $event)"
 					>
-						<MkEmoji :emoji="emoji" :normal="true"/>
+						<MkEmoji class="emoji" :emoji="emoji" :normal="true"/>
 					</button>
 				</div>
 			</section>
 
 			<section>
 				<header class="_acrylic"><i class="far fa-clock fa-fw"></i> {{ i18n.ts.recentUsed }}</header>
-				<div>
+				<div class="body">
 					<button
 						v-for="emoji in recentlyUsedEmojis"
 						:key="emoji"
-						class="_button"
+						class="_button item"
 						@click="chosen(emoji, $event)"
 					>
-						<MkEmoji :emoji="emoji" :normal="true"/>
+						<MkEmoji class="emoji" :emoji="emoji" :normal="true"/>
 					</button>
 				</div>
 			</section>
 		</div>
-		<div>
+		<div class="group">
 			<header class="_acrylic">{{ i18n.ts.customEmojis }}</header>
 			<XSection v-for="category in customEmojiCategories" :key="'custom:' + category" :initial-shown="false" :emojis="customEmojis.filter(e => e.category === category).map(e => ':' + e.name + ':')" @chosen="chosen">{{ category || i18n.ts.other }}</XSection>
 		</div>
-		<div>
+		<div class="group">
 			<header class="_acrylic">{{ i18n.ts.emoji }}</header>
 			<XSection v-for="category in categories" :key="category" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)" @chosen="chosen">{{ category }}</XSection>
 		</div>
@@ -419,19 +419,16 @@ defineExpose({
 					font-size: 15px;
 				}
 
-				> div {
+				> .body {
 					display: grid;
 					grid-template-columns: var(--columns);
+					font-size: 30px;
 
-					> button {
+					> .item {
 						aspect-ratio: 1 / 1;
 						width: auto;
 						height: auto;
 						min-width: 0;
-
-						> * {
-							font-size: 30px;
-						}
 					}
 				}
 			}
@@ -482,7 +479,7 @@ defineExpose({
 			display: none;
 		}
 
-		> div {
+		> .group {
 			&:not(.index) {
 				padding: 4px 0 8px 0;
 				border-top: solid 0.5px var(--divider);
@@ -517,16 +514,18 @@ defineExpose({
 				}
 			}
 
-			> div {
+			> .body {
 				position: relative;
 				padding: $pad;
 
-				> button {
+				> .item {
 					position: relative;
 					padding: 0;
 					width: var(--eachSize);
 					height: var(--eachSize);
+					contain: strict;
 					border-radius: 4px;
+					font-size: 24px;
 
 					&:focus-visible {
 						outline: solid 2px var(--focus);
@@ -542,8 +541,7 @@ defineExpose({
 						box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15);
 					}
 
-					> * {
-						font-size: 24px;
+					> .emoji {
 						height: 1.25em;
 						vertical-align: -.25em;
 						pointer-events: none;

From 21cdbdc7c80dcaf139284247cd89376d6c296164 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 6 Jul 2022 07:54:04 +0900
Subject: [PATCH 07/12] perf(client): trying improve perf of emoji-picker

---
 packages/client/src/components/emoji-picker.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/client/src/components/emoji-picker.vue b/packages/client/src/components/emoji-picker.vue
index 2f974cbda..4a46e0ecf 100644
--- a/packages/client/src/components/emoji-picker.vue
+++ b/packages/client/src/components/emoji-picker.vue
@@ -59,11 +59,11 @@
 				</div>
 			</section>
 		</div>
-		<div class="group">
+		<div v-once class="group">
 			<header class="_acrylic">{{ i18n.ts.customEmojis }}</header>
 			<XSection v-for="category in customEmojiCategories" :key="'custom:' + category" :initial-shown="false" :emojis="customEmojis.filter(e => e.category === category).map(e => ':' + e.name + ':')" @chosen="chosen">{{ category || i18n.ts.other }}</XSection>
 		</div>
-		<div class="group">
+		<div v-once class="group">
 			<header class="_acrylic">{{ i18n.ts.emoji }}</header>
 			<XSection v-for="category in categories" :key="category" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)" @chosen="chosen">{{ category }}</XSection>
 		</div>

From ee40c20fa40edc146fb5dfbed91aba596e605f95 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 6 Jul 2022 07:54:35 +0900
Subject: [PATCH 08/12] 12.112.0-beta.20

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

diff --git a/package.json b/package.json
index 9ba79b3b5..ccdfa727f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "misskey",
-	"version": "12.112.0-beta.19",
+	"version": "12.112.0-beta.20",
 	"codename": "indigo",
 	"repository": {
 		"type": "git",

From 8fee1ec9f4988555db3b3829707ad461f359d019 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 6 Jul 2022 08:42:04 +0900
Subject: [PATCH 09/12] Update CHANGELOG.md

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

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 039a0ec08..e44ac75b6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@ You should also include the user name that made the change.
 - Client: Improve deck UI @syuilo
 - Client: Word mute also checks content warnings @Johann150
 - Client: メニューからページをリロードできるように @syuilo
+- Client: Improve emoji picker performance @syuilo
 - ユーザーにモデレーションメモを残せる機能 @syuilo
 - Make possible to delete an account by admin @syuilo
 - Improve player detection in URL preview @mei23

From 95ca3ab718dcf0a20b4859974b51779ef5525950 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 6 Jul 2022 18:20:32 +0900
Subject: [PATCH 10/12] chore(client): tweak style

---
 packages/client/src/components/ui/menu.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/client/src/components/ui/menu.vue b/packages/client/src/components/ui/menu.vue
index cb4ec7c34..1f3d50897 100644
--- a/packages/client/src/components/ui/menu.vue
+++ b/packages/client/src/components/ui/menu.vue
@@ -136,7 +136,7 @@ function focusDown() {
 	> .item {
 		display: block;
 		position: relative;
-		padding: 6px 18px;
+		padding: 6px 16px;
 		width: 100%;
 		box-sizing: border-box;
 		white-space: nowrap;

From 0af344b4e3c6aa09eaf7a5e879f421078db8d440 Mon Sep 17 00:00:00 2001
From: Johann150 <johann.galle@protonmail.com>
Date: Wed, 6 Jul 2022 18:23:05 +0200
Subject: [PATCH 11/12] refactor: use autofocus parameter

Using the `ref` seems to be broken but using the autofocus parameter
seems to fix it.
---
 .../client/src/components/user-select-dialog.vue  | 15 +--------------
 1 file changed, 1 insertion(+), 14 deletions(-)

diff --git a/packages/client/src/components/user-select-dialog.vue b/packages/client/src/components/user-select-dialog.vue
index b34d21af0..972d35348 100644
--- a/packages/client/src/components/user-select-dialog.vue
+++ b/packages/client/src/components/user-select-dialog.vue
@@ -11,7 +11,7 @@
 	<div class="tbhwbxda">
 		<div class="form">
 			<FormSplit :min-width="170">
-				<MkInput ref="usernameEl" v-model="username" @update:modelValue="search">
+				<MkInput v-model="username" :autofocus="true" @update:modelValue="search">
 					<template #label>{{ $ts.username }}</template>
 					<template #prefix>@</template>
 				</MkInput>
@@ -70,15 +70,8 @@ let host = $ref('');
 let users: misskey.entities.UserDetailed[] = $ref([]);
 let recentUsers: misskey.entities.UserDetailed[] = $ref([]);
 let selected: misskey.entities.UserDetailed | null = $ref(null);
-let usernameEl: HTMLElement = $ref();
 let dialogEl = $ref();
 
-const focus = () => {
-	if (usernameEl) {
-		usernameEl.focus();
-	}
-};
-
 const search = () => {
 	if (username === '' && host === '') {
 		users = [];
@@ -112,12 +105,6 @@ const cancel = () => {
 };
 
 onMounted(() => {
-	focus();
-
-	nextTick(() => {
-		focus();
-	});
-
 	os.api('users/show', {
 		userIds: defaultStore.state.recentlyUsedUsers,
 	}).then(users => {

From 1c0966726b9aa505e05f65b72101c0a61178869d Mon Sep 17 00:00:00 2001
From: Johann150 <johann.galle@protonmail.com>
Date: Wed, 6 Jul 2022 19:02:39 +0200
Subject: [PATCH 12/12] refactor: use overflow-y to determine scroll container

By using `overflow-y` instead of `overflow` using `endsWith` can be
avoided and represents the data we are actually interested in here
more accurately.
---
 packages/client/src/scripts/scroll.ts | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/packages/client/src/scripts/scroll.ts b/packages/client/src/scripts/scroll.ts
index 0643bad2f..f5bc6bf9c 100644
--- a/packages/client/src/scripts/scroll.ts
+++ b/packages/client/src/scripts/scroll.ts
@@ -2,12 +2,8 @@ type ScrollBehavior = 'auto' | 'smooth' | 'instant';
 
 export function getScrollContainer(el: HTMLElement | null): HTMLElement | null {
 	if (el == null || el.tagName === 'HTML') return null;
-	const overflow = window.getComputedStyle(el).getPropertyValue('overflow');
-	if (
-		// xとyを個別に指定している場合、`hidden scroll`みたいな値になる
-		overflow.endsWith('scroll') ||
-		overflow.endsWith('auto')
-	) {
+	const overflow = window.getComputedStyle(el).getPropertyValue('overflow-y');
+	if (overflow === 'scroll' || overflow === 'auto') {
 		return el;
 	} else {
 		return getScrollContainer(el.parentElement);