diff --git a/src/client/components/deck/antenna-column.vue b/src/client/components/deck/antenna-column.vue
index 891f59e37d..3196f50a03 100644
--- a/src/client/components/deck/antenna-column.vue
+++ b/src/client/components/deck/antenna-column.vue
@@ -60,7 +60,7 @@ export default defineComponent({
 	methods: {
 		async setAntenna() {
 			const antennas = await this.$root.api('antennas/list');
-			const { canceled, result: antenna } = await this.$root.dialog({
+			const { canceled, result: antenna } = await this.$root.showDialog({
 				title: this.$t('selectAntenna'),
 				type: null,
 				select: {
diff --git a/src/client/components/deck/column.vue b/src/client/components/deck/column.vue
index 39b50912e2..e91b4d1169 100644
--- a/src/client/components/deck/column.vue
+++ b/src/client/components/deck/column.vue
@@ -137,7 +137,7 @@ export default defineComponent({
 				icon: faPencilAlt,
 				text: this.$t('rename'),
 				action: () => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						title: this.$t('rename'),
 						input: {
 							default: this.column.name,
diff --git a/src/client/components/deck/list-column.vue b/src/client/components/deck/list-column.vue
index 9dfceb4c78..9353f5b1b1 100644
--- a/src/client/components/deck/list-column.vue
+++ b/src/client/components/deck/list-column.vue
@@ -60,7 +60,7 @@ export default defineComponent({
 	methods: {
 		async setList() {
 			const lists = await this.$root.api('users/lists/list');
-			const { canceled, result: list } = await this.$root.dialog({
+			const { canceled, result: list } = await this.$root.showDialog({
 				title: this.$t('selectList'),
 				type: null,
 				select: {
diff --git a/src/client/components/deck/tl-column.vue b/src/client/components/deck/tl-column.vue
index 8ef379b09d..9500b7d5b6 100644
--- a/src/client/components/deck/tl-column.vue
+++ b/src/client/components/deck/tl-column.vue
@@ -78,7 +78,7 @@ export default defineComponent({
 
 	methods: {
 		async setType() {
-			const { canceled, result: src } = await this.$root.dialog({
+			const { canceled, result: src } = await this.$root.showDialog({
 				title: this.$t('timeline'),
 				type: null,
 				select: {
diff --git a/src/client/components/dialog.vue b/src/client/components/dialog.vue
index 6b4f1cfb82..e250b58472 100644
--- a/src/client/components/dialog.vue
+++ b/src/client/components/dialog.vue
@@ -1,10 +1,10 @@
 <template>
-<div class="mk-dialog" :class="{ iconOnly }">
+<div class="mk-dialog" :class="{ iconOnly }" :style="{ pointerEvents: closing ? 'none' : 'auto' }">
 	<transition :name="$store.state.device.animation ? 'bg-fade' : ''" appear>
-		<div class="bg _modalBg" ref="bg" @click="onBgClick" v-if="show"></div>
+		<div class="bg _modalBg" @click="onBgClick" v-if="!closing" :style="{ pointerEvents: closing ? 'none' : 'auto' }"></div>
 	</transition>
-	<transition :name="$store.state.device.animation ? 'dialog' : ''" appear @after-leave="() => { destroyDom(); }">
-		<div class="main" ref="main" v-if="show">
+	<transition :name="$store.state.device.animation ? 'dialog' : ''" appear @after-leave="$emit('closed')">
+		<div class="main" v-if="!closing" :style="{ pointerEvents: closing ? 'none' : 'auto' }">
 			<template v-if="type == 'signin'">
 				<mk-signin/>
 			</template>
@@ -114,12 +114,15 @@ export default defineComponent({
 		autoClose: {
 			type: Boolean,
 			default: false
-		}
+		},
+		closing: {
+			type: Boolean,
+			default: false
+		},
 	},
 
 	data() {
 		return {
-			show: true,
 			inputValue: this.input && this.input.default ? this.input.default : null,
 			userInputValue: null,
 			selectedValue: this.select ? this.select.default ? this.select.default : this.select.items ? this.select.items[0].value : this.select.groupedItems[0].items[0].value : null,
@@ -137,7 +140,7 @@ export default defineComponent({
 					this.canOk = false;
 				});
 			}
-		}
+		},
 	},
 
 	mounted() {
@@ -145,7 +148,7 @@ export default defineComponent({
 
 		if (this.autoClose) {
 			setTimeout(() => {
-				this.close();
+				this.$emit('ok');
 			}, 1000);
 		}
 
@@ -165,7 +168,6 @@ export default defineComponent({
 				const user = await this.$root.api('users/show', parseAcct(this.userInputValue));
 				if (user) {
 					this.$emit('ok', user);
-					this.close();
 				}
 			} else {
 				const result =
@@ -173,21 +175,11 @@ export default defineComponent({
 					this.select ? this.selectedValue :
 					true;
 				this.$emit('ok', result);
-				this.close();
 			}
 		},
 
 		cancel() {
 			this.$emit('cancel');
-			this.close();
-		},
-
-		close() {
-			if (!this.show) return;
-			this.show = false;
-			this.$el.style.pointerEvents = 'none';
-			(this.$refs.bg as any).style.pointerEvents = 'none';
-			(this.$refs.main as any).style.pointerEvents = 'none';
 		},
 
 		onBgClick() {
diff --git a/src/client/components/drive.file.vue b/src/client/components/drive.file.vue
index 571b751ef4..91fb7b81e0 100644
--- a/src/client/components/drive.file.vue
+++ b/src/client/components/drive.file.vue
@@ -128,7 +128,7 @@ export default defineComponent({
 		},
 
 		rename() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('renameFile'),
 				input: {
 					placeholder: this.$t('inputNewFileName'),
@@ -153,7 +153,7 @@ export default defineComponent({
 
 		copyUrl() {
 			copyToClipboard(this.file.url);
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
@@ -172,7 +172,7 @@ export default defineComponent({
 		},
 
 		async deleteFile() {
-			const { canceled } = await this.$root.dialog({
+			const { canceled } = await this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('driveFileDeleteConfirm', { name: this.file.name }),
 				showCancelButton: true
diff --git a/src/client/components/drive.folder.vue b/src/client/components/drive.folder.vue
index 1f9afefdbd..93fff52278 100644
--- a/src/client/components/drive.folder.vue
+++ b/src/client/components/drive.folder.vue
@@ -149,13 +149,13 @@ export default defineComponent({
 				}).catch(err => {
 					switch (err) {
 						case 'detected-circular-definition':
-							this.$root.dialog({
+							this.$root.showDialog({
 								title: this.$t('unableToProcess'),
 								text: this.$t('circularReferenceFolder')
 							});
 							break;
 						default:
-							this.$root.dialog({
+							this.$root.showDialog({
 								type: 'error',
 								text: this.$t('error')
 							});
@@ -189,7 +189,7 @@ export default defineComponent({
 		},
 
 		rename() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('renameFolder'),
 				input: {
 					placeholder: this.$t('inputNewFolderName'),
@@ -217,14 +217,14 @@ export default defineComponent({
 			}).catch(err => {
 				switch(err.id) {
 					case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
-						this.$root.dialog({
+						this.$root.showDialog({
 							type: 'error',
 							title: this.$t('unableToDelete'),
 							text: this.$t('hasChildFilesOrFolders')
 						});
 						break;
 					default:
-						this.$root.dialog({
+						this.$root.showDialog({
 							type: 'error',
 							text: this.$t('unableToDelete')
 						});
diff --git a/src/client/components/drive.vue b/src/client/components/drive.vue
index f351fd8b1e..a5d81620c2 100644
--- a/src/client/components/drive.vue
+++ b/src/client/components/drive.vue
@@ -282,13 +282,13 @@ export default defineComponent({
 				}).catch(err => {
 					switch (err) {
 						case 'detected-circular-definition':
-							this.$root.dialog({
+							this.$root.showDialog({
 								title: this.$t('unableToProcess'),
 								text: this.$t('circularReferenceFolder')
 							});
 							break;
 						default:
-							this.$root.dialog({
+							this.$root.showDialog({
 								type: 'error',
 								text: this.$t('error')
 							});
@@ -303,7 +303,7 @@ export default defineComponent({
 		},
 
 		urlUpload() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('uploadFromUrl'),
 				input: {
 					placeholder: this.$t('uploadFromUrlDescription')
@@ -315,7 +315,7 @@ export default defineComponent({
 					folderId: this.folder ? this.folder.id : undefined
 				});
 
-				this.$root.dialog({
+				this.$root.showDialog({
 					title: this.$t('uploadFromUrlRequested'),
 					text: this.$t('uploadFromUrlMayTakeTime')
 				});
@@ -323,7 +323,7 @@ export default defineComponent({
 		},
 
 		createFolder() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('createFolder'),
 				input: {
 					placeholder: this.$t('folderName')
@@ -340,7 +340,7 @@ export default defineComponent({
 		},
 
 		renameFolder(folder) {
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('renameFolder'),
 				input: {
 					placeholder: this.$t('inputNewFolderName'),
@@ -367,14 +367,14 @@ export default defineComponent({
 			}).catch(err => {
 				switch(err.id) {
 					case 'b0fc8a17-963c-405d-bfbc-859a487295e1':
-						this.$root.dialog({
+						this.$root.showDialog({
 							type: 'error',
 							title: this.$t('unableToDelete'),
 							text: this.$t('hasChildFilesOrFolders')
 						});
 						break;
 					default:
-						this.$root.dialog({
+						this.$root.showDialog({
 							type: 'error',
 							text: this.$t('unableToDelete')
 						});
diff --git a/src/client/components/follow-button.vue b/src/client/components/follow-button.vue
index 8ed4300206..f09e9780f8 100644
--- a/src/client/components/follow-button.vue
+++ b/src/client/components/follow-button.vue
@@ -91,7 +91,7 @@ export default defineComponent({
 
 			try {
 				if (this.isFollowing) {
-					const { canceled } = await this.$root.dialog({
+					const { canceled } = await this.$root.showDialog({
 						type: 'warning',
 						text: this.$t('unfollowConfirm', { name: this.user.name || this.user.username }),
 						showCancelButton: true
diff --git a/src/client/components/note.vue b/src/client/components/note.vue
index c2705be0ee..2fb5e58a86 100644
--- a/src/client/components/note.vue
+++ b/src/client/components/note.vue
@@ -519,7 +519,7 @@ export default defineComponent({
 			this.$root.api('notes/favorites/create', {
 				noteId: this.appearNote.id
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
@@ -527,7 +527,7 @@ export default defineComponent({
 		},
 
 		del() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('noteDeleteConfirm'),
 				showCancelButton: true
@@ -541,7 +541,7 @@ export default defineComponent({
 		},
 
 		delEdit() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('deleteAndEditConfirm'),
 				showCancelButton: true
@@ -560,7 +560,7 @@ export default defineComponent({
 			this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
 				noteId: this.appearNote.id
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
@@ -571,7 +571,7 @@ export default defineComponent({
 			this.$root.api(watch ? 'notes/watching/create' : 'notes/watching/delete', {
 				noteId: this.appearNote.id
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
@@ -716,7 +716,7 @@ export default defineComponent({
 
 		copyContent() {
 			copyToClipboard(this.appearNote.text);
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
@@ -724,7 +724,7 @@ export default defineComponent({
 
 		copyLink() {
 			copyToClipboard(`${url}/notes/${this.appearNote.id}`);
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
@@ -734,13 +734,13 @@ export default defineComponent({
 			this.$root.api(pin ? 'i/pin' : 'i/unpin', {
 				noteId: this.appearNote.id
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}).catch(e => {
 				if (e.id === '72dab508-c64d-498f-8740-a8eec1ba385a') {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						text: this.$t('pinLimitExceeded')
 					});
@@ -749,7 +749,7 @@ export default defineComponent({
 		},
 
 		async promote() {
-			const { canceled, result: days } = await this.$root.dialog({
+			const { canceled, result: days } = await this.$root.showDialog({
 				title: this.$t('numberOfDays'),
 				input: { type: 'number' }
 			});
@@ -760,12 +760,12 @@ export default defineComponent({
 				noteId: this.appearNote.id,
 				expiresAt: Date.now() + (86400000 * days)
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue
index 13780d7791..d2e5e22a0e 100644
--- a/src/client/components/notification.vue
+++ b/src/client/components/notification.vue
@@ -134,7 +134,7 @@ export default defineComponent({
 		acceptGroupInvitation() {
 			this.groupInviteDone = true;
 			this.$root.api('users/groups/invitations/accept', { invitationId: this.notification.invitation.id });
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
diff --git a/src/client/components/page/page.button.vue b/src/client/components/page/page.button.vue
index cea7ecb6fe..e2e6523428 100644
--- a/src/client/components/page/page.button.vue
+++ b/src/client/components/page/page.button.vue
@@ -24,7 +24,7 @@ export default defineComponent({
 		click() {
 			if (this.value.action === 'dialog') {
 				this.hpml.eval();
-				this.$root.dialog({
+				this.$root.showDialog({
 					text: this.hpml.interpolate(this.value.content)
 				});
 			} else if (this.value.action === 'resetRandom') {
@@ -39,7 +39,7 @@ export default defineComponent({
 					} : {})
 				});
 
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					text: this.hpml.interpolate(this.value.message)
 				});
diff --git a/src/client/components/page/page.post.vue b/src/client/components/page/page.post.vue
index d7cb753143..a2cdade8df 100644
--- a/src/client/components/page/page.post.vue
+++ b/src/client/components/page/page.post.vue
@@ -44,7 +44,7 @@ export default defineComponent({
 	methods: {
 		upload() {
 			return new Promise((ok) => {
-				const dialog = this.$root.dialog({
+				const dialog = this.$root.showDialog({
 					type: 'waiting',
 					text: this.$t('uploading') + '...',
 					showOkButton: false,
@@ -80,7 +80,7 @@ export default defineComponent({
 				fileIds: file ? [file.id] : undefined,
 			}).then(() => {
 				this.posted = true;
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
diff --git a/src/client/components/page/page.vue b/src/client/components/page/page.vue
index 752cd70fbf..11c0a6cfe9 100644
--- a/src/client/components/page/page.vue
+++ b/src/client/components/page/page.vue
@@ -49,7 +49,7 @@ export default defineComponent({
 					ast = parse(this.page.script);
 				} catch (e) {
 					console.error(e);
-					/*this.$root.dialog({
+					/*this.$root.showDialog({
 						type: 'error',
 						text: 'Syntax error :('
 					});*/
@@ -59,7 +59,7 @@ export default defineComponent({
 					this.hpml.eval();
 				}).catch(e => {
 					console.error(e);
-					/*this.$root.dialog({
+					/*this.$root.showDialog({
 						type: 'error',
 						text: e
 					});*/
diff --git a/src/client/components/post-form-attaches.vue b/src/client/components/post-form-attaches.vue
index f177dfe8af..078b7037d7 100644
--- a/src/client/components/post-form-attaches.vue
+++ b/src/client/components/post-form-attaches.vue
@@ -62,7 +62,7 @@ export default defineComponent({
 			});
 		},
 		async rename(file) {
-			const { canceled, result } = await this.$root.dialog({
+			const { canceled, result } = await this.$root.showDialog({
 				title: this.$t('enterFileName'),
 				input: {
 					default: file.name
diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue
index d0d1f99576..0e446e465f 100644
--- a/src/client/components/post-form.vue
+++ b/src/client/components/post-form.vue
@@ -469,7 +469,7 @@ export default defineComponent({
 			if (!this.renote && !this.quoteId && paste.startsWith(url + '/notes/')) {
 				e.preventDefault();
 
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'info',
 					text: this.$t('quoteQuestion'),
 					showCancelButton: true
diff --git a/src/client/components/sidebar.vue b/src/client/components/sidebar.vue
index 6e9c3864d4..98fb038a61 100644
--- a/src/client/components/sidebar.vue
+++ b/src/client/components/sidebar.vue
@@ -127,7 +127,7 @@ export default defineComponent({
 		search() {
 			if (this.searching) return;
 
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('search'),
 				input: true
 			}).then(async ({ canceled, result: query }) => {
@@ -277,7 +277,7 @@ export default defineComponent({
 		async addAcount() {
 			this.$root.new(await import('./signin-dialog.vue')).$once('login', res => {
 				this.$store.dispatch('addAcount', res);
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
@@ -297,7 +297,7 @@ export default defineComponent({
 		},
 
 		switchAccountWithToken(token: string) {
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'waiting',
 				iconOnly: true
 			});
diff --git a/src/client/components/signin.vue b/src/client/components/signin.vue
index 651b559c5f..2fa79103e2 100755
--- a/src/client/components/signin.vue
+++ b/src/client/components/signin.vue
@@ -145,7 +145,7 @@ export default defineComponent({
 				this.$emit('login', res);
 			}).catch(err => {
 				if (err === null) return;
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: this.$t('signinFailed')
 				});
@@ -166,7 +166,7 @@ export default defineComponent({
 						this.challengeData = res;
 						return this.queryKey();
 					}).catch(() => {
-						this.$root.dialog({
+						this.$root.showDialog({
 							type: 'error',
 							text: this.$t('signinFailed')
 						});
@@ -186,7 +186,7 @@ export default defineComponent({
 				}).then(res => {
 					this.$emit('login', res);
 				}).catch(() => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						text: this.$t('loginFailed')
 					});
diff --git a/src/client/components/signup.vue b/src/client/components/signup.vue
index 03d6190c33..6f99037443 100644
--- a/src/client/components/signup.vue
+++ b/src/client/components/signup.vue
@@ -193,7 +193,7 @@ export default defineComponent({
 				this.$refs.hcaptcha?.reset?.();
 				this.$refs.recaptcha?.reset?.();
 
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: this.$t('error')
 				});
diff --git a/src/client/components/ui/input.vue b/src/client/components/ui/input.vue
index 3763f780c3..f95582ff8b 100644
--- a/src/client/components/ui/input.vue
+++ b/src/client/components/ui/input.vue
@@ -206,11 +206,12 @@ export default defineComponent({
 			});
 		});
 
-		this.$on('keydown', (e: KeyboardEvent) => {
+		// TODO: vue3
+		/*this.$on('keydown', (e: KeyboardEvent) => {
 			if (e.code == 'Enter') {
 				this.$emit('enter');
 			}
-		});
+		});*/
 	},
 	methods: {
 		focus() {
diff --git a/src/client/components/user-menu.vue b/src/client/components/user-menu.vue
index f437c80ee0..65719431e9 100644
--- a/src/client/components/user-menu.vue
+++ b/src/client/components/user-menu.vue
@@ -100,13 +100,13 @@ export default defineComponent({
 			const t = this.$t('selectList'); // なぜか後で参照すると null になるので最初にメモリに確保しておく
 			const lists = await this.$root.api('users/lists/list');
 			if (lists.length === 0) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: this.$t('youHaveNoLists')
 				});
 				return;
 			}
-			const { canceled, result: listId } = await this.$root.dialog({
+			const { canceled, result: listId } = await this.$root.showDialog({
 				type: null,
 				title: t,
 				select: {
@@ -121,12 +121,12 @@ export default defineComponent({
 				listId: listId,
 				userId: this.user.id
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
@@ -136,13 +136,13 @@ export default defineComponent({
 		async inviteGroup() {
 			const groups = await this.$root.api('users/groups/owned');
 			if (groups.length === 0) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: this.$t('youHaveNoGroups')
 				});
 				return;
 			}
-			const { canceled, result: groupId } = await this.$root.dialog({
+			const { canceled, result: groupId } = await this.$root.showDialog({
 				type: null,
 				title: this.$t('group'),
 				select: {
@@ -157,12 +157,12 @@ export default defineComponent({
 				groupId: groupId,
 				userId: this.user.id
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
@@ -174,12 +174,12 @@ export default defineComponent({
 				userId: this.user.id
 			}).then(() => {
 				this.user.isMuted = !this.user.isMuted;
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}, e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
@@ -193,12 +193,12 @@ export default defineComponent({
 				userId: this.user.id
 			}).then(() => {
 				this.user.isBlocking = !this.user.isBlocking;
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}, e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
@@ -212,12 +212,12 @@ export default defineComponent({
 				userId: this.user.id
 			}).then(() => {
 				this.user.isSilenced = !this.user.isSilenced;
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}, e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
@@ -231,12 +231,12 @@ export default defineComponent({
 				userId: this.user.id
 			}).then(() => {
 				this.user.isSuspended = !this.user.isSuspended;
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}, e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
@@ -244,7 +244,7 @@ export default defineComponent({
 		},
 
 		async getConfirmed(text: string): Promise<Boolean> {
-			const confirm = await this.$root.dialog({
+			const confirm = await this.$root.showDialog({
 				type: 'warning',
 				showCancelButton: true,
 				title: 'confirm',
diff --git a/src/client/deck.vue b/src/client/deck.vue
index cb84214281..3585cd7264 100644
--- a/src/client/deck.vue
+++ b/src/client/deck.vue
@@ -141,7 +141,7 @@ export default defineComponent({
 		search() {
 			if (this.searching) return;
 
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('search'),
 				input: true
 			}).then(async ({ canceled, result: query }) => {
@@ -183,7 +183,7 @@ export default defineComponent({
 				'direct',
 			];
 
-			const { canceled, result: column } = await this.$root.dialog({
+			const { canceled, result: column } = await this.$root.showDialog({
 				title: this.$t('_deck.addColumn'),
 				type: null,
 				select: {
diff --git a/src/client/default.vue b/src/client/default.vue
index 84810945dd..d1f990d02f 100644
--- a/src/client/default.vue
+++ b/src/client/default.vue
@@ -301,7 +301,7 @@ export default defineComponent({
 		search() {
 			if (this.searching) return;
 
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('search'),
 				input: true
 			}).then(async ({ canceled, result: query }) => {
@@ -351,7 +351,7 @@ export default defineComponent({
 		},
 
 		async addWidget(place) {
-			const { canceled, result: widget } = await this.$root.dialog({
+			const { canceled, result: widget } = await this.$root.showDialog({
 				type: null,
 				title: this.$t('chooseWidget'),
 				select: {
diff --git a/src/client/pages/channel-editor.vue b/src/client/pages/channel-editor.vue
index e3e8edd853..608bfbae01 100644
--- a/src/client/pages/channel-editor.vue
+++ b/src/client/pages/channel-editor.vue
@@ -92,7 +92,7 @@ export default defineComponent({
 				params.channelId = this.channelId;
 				this.$root.api('channels/update', params)
 				.then(channel => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						iconOnly: true, autoClose: true
 					});
@@ -100,7 +100,7 @@ export default defineComponent({
 			} else {
 				this.$root.api('channels/create', params)
 				.then(channel => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						iconOnly: true, autoClose: true
 					});
diff --git a/src/client/pages/follow.vue b/src/client/pages/follow.vue
index 58bc638e7e..641f8758b9 100644
--- a/src/client/pages/follow.vue
+++ b/src/client/pages/follow.vue
@@ -11,7 +11,7 @@ export default defineComponent({
 		const acct = new URL(location.href).searchParams.get('acct');
 		if (acct == null) return;
 
-		const dialog = this.$root.dialog({
+		const dialog = this.$root.showDialog({
 			type: 'waiting',
 			text: this.$t('fetchingAsApObject') + '...',
 			showOkButton: false,
@@ -26,7 +26,7 @@ export default defineComponent({
 				if (res.type == 'User') {
 					this.follow(res.object);
 				} else {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						text: 'Not a user'
 					}).then(() => {
@@ -34,7 +34,7 @@ export default defineComponent({
 					});
 				}
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				}).then(() => {
@@ -47,7 +47,7 @@ export default defineComponent({
 			this.$root.api('users/show', parseAcct(acct)).then(user => {
 				this.follow(user);
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				}).then(() => {
@@ -61,7 +61,7 @@ export default defineComponent({
 
 	methods: {
 		async follow(user) {
-			const { canceled } = await this.$root.dialog({
+			const { canceled } = await this.$root.showDialog({
 				type: 'question',
 				text: this.$t('followConfirm', { name: user.name || user.username }),
 				showCancelButton: true
@@ -75,14 +75,14 @@ export default defineComponent({
 			this.$root.api('following/create', {
 				userId: user.id
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				}).then(() => {
 					window.close();
 				});
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				}).then(() => {
diff --git a/src/client/pages/index.welcome.setup.vue b/src/client/pages/index.welcome.setup.vue
index 2158c12072..a9d5ef6925 100644
--- a/src/client/pages/index.welcome.setup.vue
+++ b/src/client/pages/index.welcome.setup.vue
@@ -57,7 +57,7 @@ export default defineComponent({
 			}).catch(() => {
 				this.submitting = false;
 
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: this.$t('error')
 				});
diff --git a/src/client/pages/instance/announcements.vue b/src/client/pages/instance/announcements.vue
index 5c9b492d5a..2b5dbf9df6 100644
--- a/src/client/pages/instance/announcements.vue
+++ b/src/client/pages/instance/announcements.vue
@@ -68,7 +68,7 @@ export default defineComponent({
 		},
 
 		remove(announcement) {
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('removeAreYouSure', { x: announcement.title }),
 				showCancelButton: true
@@ -82,24 +82,24 @@ export default defineComponent({
 		save(announcement) {
 			if (announcement.id == null) {
 				this.$root.api('admin/announcements/create', announcement).then(() => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						text: this.$t('saved')
 					});
 				}).catch(e => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						text: e
 					});
 				});
 			} else {
 				this.$root.api('admin/announcements/update', announcement).then(() => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						text: this.$t('saved')
 					});
 				}).catch(e => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						text: e
 					});
diff --git a/src/client/pages/instance/emojis.vue b/src/client/pages/instance/emojis.vue
index e0754c8340..ea3273448e 100644
--- a/src/client/pages/instance/emojis.vue
+++ b/src/client/pages/instance/emojis.vue
@@ -128,7 +128,7 @@ export default defineComponent({
 		async add(e) {
 			const files = await selectFile(this, e.currentTarget || e.target, null, true);
 
-			const dialog = this.$root.dialog({
+			const dialog = this.$root.showDialog({
 				type: 'waiting',
 				text: this.$t('doing') + '...',
 				showOkButton: false,
@@ -141,7 +141,7 @@ export default defineComponent({
 			})))
 			.then(() => {
 				this.$refs.emojis.reload();
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
@@ -159,7 +159,7 @@ export default defineComponent({
 				aliases: this.aliases.split(' '),
 			});
 
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
@@ -168,7 +168,7 @@ export default defineComponent({
 		},
 
 		async del() {
-			const { canceled } = await this.$root.dialog({
+			const { canceled } = await this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('removeAreYouSure', { x: this.selected.name }),
 				showCancelButton: true
@@ -187,12 +187,12 @@ export default defineComponent({
 				emojiId: this.selectedRemote.id,
 			}).then(() => {
 				this.$refs.emojis.reload();
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
diff --git a/src/client/pages/instance/files.vue b/src/client/pages/instance/files.vue
index 5fcb7c807f..bddc6ee04f 100644
--- a/src/client/pages/instance/files.vue
+++ b/src/client/pages/instance/files.vue
@@ -34,7 +34,7 @@ export default defineComponent({
 
 	methods: {
 		clear() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('clearCachedFilesConfirm'),
 				showCancelButton: true
@@ -42,7 +42,7 @@ export default defineComponent({
 				if (canceled) return;
 
 				this.$root.api('admin/drive/clean-remote-files', {}).then(() => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						iconOnly: true, autoClose: true
 					});
diff --git a/src/client/pages/instance/index.vue b/src/client/pages/instance/index.vue
index 2edcc3a484..e678115651 100644
--- a/src/client/pages/instance/index.vue
+++ b/src/client/pages/instance/index.vue
@@ -578,7 +578,7 @@ export default defineComponent({
 
 		deleteAllLogs() {
 			this.$root.api('admin/delete-logs').then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
diff --git a/src/client/pages/instance/instance.vue b/src/client/pages/instance/instance.vue
index 615a5f9442..76476728ab 100644
--- a/src/client/pages/instance/instance.vue
+++ b/src/client/pages/instance/instance.vue
@@ -255,7 +255,7 @@ export default defineComponent({
 			this.$root.api('admin/federation/remove-all-following', {
 				host: this.instance.host
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
@@ -266,7 +266,7 @@ export default defineComponent({
 			this.$root.api('admin/federation/delete-all-files', {
 				host: this.instance.host
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
diff --git a/src/client/pages/instance/queue.vue b/src/client/pages/instance/queue.vue
index b6b82ed0d7..49cb960820 100644
--- a/src/client/pages/instance/queue.vue
+++ b/src/client/pages/instance/queue.vue
@@ -57,7 +57,7 @@ export default defineComponent({
 
 	methods: {
 		clear() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'warning',
 				title: this.$t('clearQueueConfirmTitle'),
 				text: this.$t('clearQueueConfirmText'),
@@ -66,7 +66,7 @@ export default defineComponent({
 				if (canceled) return;
 
 				this.$root.api('admin/queue/clear', {}).then(() => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						iconOnly: true, autoClose: true
 					});
diff --git a/src/client/pages/instance/relays.vue b/src/client/pages/instance/relays.vue
index 030ddedad0..157b28a552 100644
--- a/src/client/pages/instance/relays.vue
+++ b/src/client/pages/instance/relays.vue
@@ -61,7 +61,7 @@ export default defineComponent({
 			}).then((relay: any) => {
 				this.refresh();
 			}).catch((e: any) => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e.message || e
 				});
@@ -74,7 +74,7 @@ export default defineComponent({
 			}).then(() => {
 				this.refresh();
 			}).catch((e: any) => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e.message || e
 				});
diff --git a/src/client/pages/instance/settings.vue b/src/client/pages/instance/settings.vue
index 1956ed9919..3231a49538 100644
--- a/src/client/pages/instance/settings.vue
+++ b/src/client/pages/instance/settings.vue
@@ -402,7 +402,7 @@ export default defineComponent({
 	mounted() {
 		this.$refs.enableHcaptcha.$on('change', () => {
 			if (this.enableHcaptcha && this.enableRecaptcha) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'question', // warning だと間違って cancel するかもしれない
 					showCancelButton: true,
 					title: this.$t('settingGuide'),
@@ -419,7 +419,7 @@ export default defineComponent({
 
 		this.$refs.enableRecaptcha.$on('change', () => {
 			if (this.enableRecaptcha && this.enableHcaptcha) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'question', // warning だと間違って cancel するかもしれない
 					showCancelButton: true,
 					title: this.$t('settingGuide'),
@@ -438,12 +438,12 @@ export default defineComponent({
 	methods: {
 		invite() {
 			this.$root.api('admin/invite').then(x => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'info',
 					text: x.code
 				});
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
@@ -472,12 +472,12 @@ export default defineComponent({
 				subject: 'Test email',
 				text: 'Yo'
 			}).then(x => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					splash: true
 				});
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
@@ -546,13 +546,13 @@ export default defineComponent({
 			}).then(() => {
 				this.$store.dispatch('instance/fetch');
 				if (withDialog) {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						iconOnly: true, autoClose: true
 					});
 				}
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
diff --git a/src/client/pages/instance/users.user.vue b/src/client/pages/instance/users.user.vue
index 14c676b4f6..0f0eae107e 100644
--- a/src/client/pages/instance/users.user.vue
+++ b/src/client/pages/instance/users.user.vue
@@ -89,7 +89,7 @@ export default defineComponent({
 
 		async updateRemoteUser() {
 			await this.$root.api('admin/update-remote-user', { userId: this.user.id }).then(res => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
@@ -98,7 +98,7 @@ export default defineComponent({
 		},
 
 		async resetPassword() {
-			const dialog = this.$root.dialog({
+			const dialog = this.$root.showDialog({
 				type: 'waiting',
 				iconOnly: true
 			});
@@ -106,12 +106,12 @@ export default defineComponent({
 			this.$root.api('admin/reset-password', {
 				userId: this.user.id,
 			}).then(({ password }) => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					text: this.$t('newPasswordIs', { password })
 				});
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
@@ -121,7 +121,7 @@ export default defineComponent({
 		},
 
 		async toggleSilence() {
-			const confirm = await this.$root.dialog({
+			const confirm = await this.$root.showDialog({
 				type: 'warning',
 				showCancelButton: true,
 				text: this.silenced ? this.$t('silenceConfirm') : this.$t('unsilenceConfirm'),
@@ -135,7 +135,7 @@ export default defineComponent({
 		},
 
 		async toggleSuspend() {
-			const confirm = await this.$root.dialog({
+			const confirm = await this.$root.showDialog({
 				type: 'warning',
 				showCancelButton: true,
 				text: this.suspended ? this.$t('suspendConfirm') : this.$t('unsuspendConfirm'),
@@ -154,7 +154,7 @@ export default defineComponent({
 		},
 
 		async deleteAllFiles() {
-			const confirm = await this.$root.dialog({
+			const confirm = await this.$root.showDialog({
 				type: 'warning',
 				showCancelButton: true,
 				text: this.$t('deleteAllFilesConfirm'),
@@ -162,13 +162,13 @@ export default defineComponent({
 			if (confirm.canceled) return;
 			const process = async () => {
 				await this.$root.api('admin/delete-all-files-of-a-user', { userId: this.user.id });
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			};
 			await process().catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e.toString()
 				});
diff --git a/src/client/pages/instance/users.vue b/src/client/pages/instance/users.vue
index 7f5665655d..e064e3a8f6 100644
--- a/src/client/pages/instance/users.vue
+++ b/src/client/pages/instance/users.vue
@@ -152,7 +152,7 @@ export default defineComponent({
 				let _notFound = false;
 				const notFound = () => {
 					if (_notFound) {
-						this.$root.dialog({
+						this.$root.showDialog({
 							type: 'error',
 							text: this.$t('noSuchUser')
 						});
@@ -185,19 +185,19 @@ export default defineComponent({
 		},
 
 		async addUser() {
-			const { canceled: canceled1, result: username } = await this.$root.dialog({
+			const { canceled: canceled1, result: username } = await this.$root.showDialog({
 				title: this.$t('username'),
 				input: true
 			});
 			if (canceled1) return;
 
-			const { canceled: canceled2, result: password } = await this.$root.dialog({
+			const { canceled: canceled2, result: password } = await this.$root.showDialog({
 				title: this.$t('password'),
 				input: { type: 'password' }
 			});
 			if (canceled2) return;
 
-			const dialog = this.$root.dialog({
+			const dialog = this.$root.showDialog({
 				type: 'waiting',
 				iconOnly: true
 			});
@@ -207,12 +207,12 @@ export default defineComponent({
 				password: password,
 			}).then(res => {
 				this.$refs.users.reload();
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e.id
 				});
diff --git a/src/client/pages/messaging/index.vue b/src/client/pages/messaging/index.vue
index b54855f454..e5a4bdf18e 100644
--- a/src/client/pages/messaging/index.vue
+++ b/src/client/pages/messaging/index.vue
@@ -140,14 +140,14 @@ export default defineComponent({
 			const groups1 = await this.$root.api('users/groups/owned');
 			const groups2 = await this.$root.api('users/groups/joined');
 			if (groups1.length === 0 && groups2.length === 0) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'warning',
 					title: this.$t('youHaveNoGroups'),
 					text: this.$t('joinOrCreateGroup'),
 				});
 				return;
 			}
-			const { canceled, result: group } = await this.$root.dialog({
+			const { canceled, result: group } = await this.$root.showDialog({
 				type: null,
 				title: this.$t('group'),
 				select: {
diff --git a/src/client/pages/messaging/messaging-room.form.vue b/src/client/pages/messaging/messaging-room.form.vue
index 4103150a87..d8dbe5f64a 100644
--- a/src/client/pages/messaging/messaging-room.form.vue
+++ b/src/client/pages/messaging/messaging-room.form.vue
@@ -97,7 +97,7 @@ export default defineComponent({
 					const ext = lio >= 0 ? file.name.slice(lio) : '';
 					const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.settings.pastedFileName).replace(/{{number}}/g, '1')}${ext}`;
 					const name = this.$store.state.settings.pasteDialog
-						? await this.$root.dialog({
+						? await this.$root.showDialog({
 							title: this.$t('enterFileName'),
 							input: {
 								default: formatted
@@ -109,7 +109,7 @@ export default defineComponent({
 				}
 			} else {
 				if (items[0].kind == 'file') {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						text: this.$t('onlyOneFileCanBeAttached')
 					});
@@ -134,7 +134,7 @@ export default defineComponent({
 				return;
 			} else if (e.dataTransfer.files.length > 1) {
 				e.preventDefault();
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: this.$t('onlyOneFileCanBeAttached')
 				});
diff --git a/src/client/pages/messaging/messaging-room.vue b/src/client/pages/messaging/messaging-room.vue
index 427a869083..4f11bfcd30 100644
--- a/src/client/pages/messaging/messaging-room.vue
+++ b/src/client/pages/messaging/messaging-room.vue
@@ -144,7 +144,7 @@ export default defineComponent({
 				this.form.upload(e.dataTransfer.files[0]);
 				return;
 			} else if (e.dataTransfer.files.length > 1) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: this.$t('onlyOneFileCanBeAttached')
 				});
diff --git a/src/client/pages/my-antennas/index.antenna.vue b/src/client/pages/my-antennas/index.antenna.vue
index bfff685709..1be459139c 100644
--- a/src/client/pages/my-antennas/index.antenna.vue
+++ b/src/client/pages/my-antennas/index.antenna.vue
@@ -150,14 +150,14 @@ export default defineComponent({
 				});
 			}
 
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
 		},
 
 		async deleteAntenna() {
-			const { canceled } = await this.$root.dialog({
+			const { canceled } = await this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('removeAreYouSure', { x: this.antenna.name }),
 				showCancelButton: true
@@ -168,7 +168,7 @@ export default defineComponent({
 				antennaId: this.antenna.id,
 			});
 
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
diff --git a/src/client/pages/my-groups/group.vue b/src/client/pages/my-groups/group.vue
index 75e3538fc3..c47aaa4621 100644
--- a/src/client/pages/my-groups/group.vue
+++ b/src/client/pages/my-groups/group.vue
@@ -93,12 +93,12 @@ export default defineComponent({
 					groupId: this.group.id,
 					userId: user.id
 				}).then(() => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						iconOnly: true, autoClose: true
 					});
 				}).catch(e => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						text: e
 					});
@@ -116,7 +116,7 @@ export default defineComponent({
 		},
 
 		async renameGroup() {
-			const { canceled, result: name } = await this.$root.dialog({
+			const { canceled, result: name } = await this.$root.showDialog({
 				title: this.$t('groupName'),
 				input: {
 					default: this.group.name
@@ -138,12 +138,12 @@ export default defineComponent({
 					groupId: this.group.id,
 					userId: user.id
 				}).then(() => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						iconOnly: true, autoClose: true
 					});
 				}).catch(e => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						text: e
 					});
@@ -152,7 +152,7 @@ export default defineComponent({
 		},
 
 		async deleteGroup() {
-			const { canceled } = await this.$root.dialog({
+			const { canceled } = await this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('removeAreYouSure', { x: this.group.name }),
 				showCancelButton: true
@@ -162,7 +162,7 @@ export default defineComponent({
 			await this.$root.api('users/groups/delete', {
 				groupId: this.group.id
 			});
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
diff --git a/src/client/pages/my-groups/index.vue b/src/client/pages/my-groups/index.vue
index 14e81e9fb1..055586f160 100644
--- a/src/client/pages/my-groups/index.vue
+++ b/src/client/pages/my-groups/index.vue
@@ -82,14 +82,14 @@ export default defineComponent({
 
 	methods: {
 		async create() {
-			const { canceled, result: name } = await this.$root.dialog({
+			const { canceled, result: name } = await this.$root.showDialog({
 				title: this.$t('groupName'),
 				input: true
 			});
 			if (canceled) return;
 			await this.$root.api('users/groups/create', { name: name });
 			this.$refs.owned.reload();
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
@@ -98,7 +98,7 @@ export default defineComponent({
 			this.$root.api('users/groups/invitations/accept', {
 				invitationId: invitation.id
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
diff --git a/src/client/pages/my-lists/index.vue b/src/client/pages/my-lists/index.vue
index 73d002f626..1ec6e0fb61 100644
--- a/src/client/pages/my-lists/index.vue
+++ b/src/client/pages/my-lists/index.vue
@@ -42,14 +42,14 @@ export default defineComponent({
 
 	methods: {
 		async create() {
-			const { canceled, result: name } = await this.$root.dialog({
+			const { canceled, result: name } = await this.$root.showDialog({
 				title: this.$t('enterListName'),
 				input: true
 			});
 			if (canceled) return;
 			await this.$root.api('users/lists/create', { name: name });
 			this.$refs.list.reload();
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
diff --git a/src/client/pages/my-lists/list.vue b/src/client/pages/my-lists/list.vue
index 3c1bb56d79..06424115f4 100644
--- a/src/client/pages/my-lists/list.vue
+++ b/src/client/pages/my-lists/list.vue
@@ -93,12 +93,12 @@ export default defineComponent({
 					userId: user.id
 				}).then(() => {
 					this.users.push(user);
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						iconOnly: true, autoClose: true
 					});
 				}).catch(e => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						text: e
 					});
@@ -116,7 +116,7 @@ export default defineComponent({
 		},
 
 		async renameList() {
-			const { canceled, result: name } = await this.$root.dialog({
+			const { canceled, result: name } = await this.$root.showDialog({
 				title: this.$t('enterListName'),
 				input: {
 					default: this.list.name
@@ -133,7 +133,7 @@ export default defineComponent({
 		},
 
 		async deleteList() {
-			const { canceled } = await this.$root.dialog({
+			const { canceled } = await this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('removeAreYouSure', { x: this.list.name }),
 				showCancelButton: true
@@ -143,7 +143,7 @@ export default defineComponent({
 			await this.$root.api('users/lists/delete', {
 				listId: this.list.id
 			});
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
diff --git a/src/client/pages/my-settings/2fa.vue b/src/client/pages/my-settings/2fa.vue
index e2eca7e305..e7483c12a1 100644
--- a/src/client/pages/my-settings/2fa.vue
+++ b/src/client/pages/my-settings/2fa.vue
@@ -89,7 +89,7 @@ export default defineComponent({
 	},
 	methods: {
 		register() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('password'),
 				input: {
 					type: 'password'
@@ -105,7 +105,7 @@ export default defineComponent({
 		},
 
 		unregister() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('password'),
 				input: {
 					type: 'password'
@@ -118,7 +118,7 @@ export default defineComponent({
 					this.usePasswordLessLogin = false;
 					this.updatePasswordLessLogin();
 				}).then(() => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						iconOnly: true, autoClose: true
 					});
@@ -131,13 +131,13 @@ export default defineComponent({
 			this.$root.api('i/2fa/done', {
 				token: this.token
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 				this.$store.state.i.twoFactorEnabled = true;
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					iconOnly: true, autoClose: true
 				});
@@ -156,7 +156,7 @@ export default defineComponent({
 			}).then(key => {
 				this.registration = null;
 				key.lastUsed = new Date();
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
@@ -164,7 +164,7 @@ export default defineComponent({
 		},
 
 		unregisterKey(key) {
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('password'),
 				input: {
 					type: 'password'
@@ -178,7 +178,7 @@ export default defineComponent({
 					this.usePasswordLessLogin = false;
 					this.updatePasswordLessLogin();
 				}).then(() => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						iconOnly: true, autoClose: true
 					});
@@ -187,7 +187,7 @@ export default defineComponent({
 		},
 
 		addSecurityKey() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('password'),
 				input: {
 					type: 'password'
diff --git a/src/client/pages/my-settings/api.vue b/src/client/pages/my-settings/api.vue
index 35dac16758..d0fa8fab53 100644
--- a/src/client/pages/my-settings/api.vue
+++ b/src/client/pages/my-settings/api.vue
@@ -33,7 +33,7 @@ export default defineComponent({
 					permission: permissions,
 				});
 
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					title: this.$t('token'),
 					text: token
@@ -41,7 +41,7 @@ export default defineComponent({
 			});
 		},
 		regenerateToken() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				title: this.$t('password'),
 				input: {
 					type: 'password'
diff --git a/src/client/pages/my-settings/drive.vue b/src/client/pages/my-settings/drive.vue
index d01c8fb36a..4785d67a26 100644
--- a/src/client/pages/my-settings/drive.vue
+++ b/src/client/pages/my-settings/drive.vue
@@ -39,7 +39,7 @@ export default defineComponent({
 		chooseUploadFolder() {
 			selectDriveFolder(this.$root, false).then(async folder => {
 				await this.$store.dispatch('settings/set', { key: 'uploadFolder', value: folder ? folder.id : null });
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
diff --git a/src/client/pages/my-settings/import-export.vue b/src/client/pages/my-settings/import-export.vue
index 25be6a0b40..060d033c30 100644
--- a/src/client/pages/my-settings/import-export.vue
+++ b/src/client/pages/my-settings/import-export.vue
@@ -45,12 +45,12 @@ export default defineComponent({
 				this.exportTarget == 'user-lists' ? 'i/export-user-lists' :
 				null, {})
 			.then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'info',
 					text: this.$t('exportRequested')
 				});
 			}).catch((e: any) => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e.message
 				});
@@ -68,7 +68,7 @@ export default defineComponent({
 			data.append('file', file);
 			data.append('i', this.$store.state.i.token);
 
-			const dialog = this.$root.dialog({
+			const dialog = this.$root.showDialog({
 				type: 'waiting',
 				text: this.$t('uploading') + '...',
 				showOkButton: false,
@@ -85,7 +85,7 @@ export default defineComponent({
 				this.reqImport(f);
 			})
 			.catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
@@ -102,12 +102,12 @@ export default defineComponent({
 				null, {
 					fileId: file.id
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'info',
 					text: this.$t('importRequested')
 				});
 			}).catch((e: any) => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e.message
 				});
diff --git a/src/client/pages/my-settings/index.vue b/src/client/pages/my-settings/index.vue
index 6b1ef84d2d..bfbe19da7e 100644
--- a/src/client/pages/my-settings/index.vue
+++ b/src/client/pages/my-settings/index.vue
@@ -122,7 +122,7 @@ export default defineComponent({
 				}).then(i => {
 					this.$store.state.i.includingNotificationTypes = i.includingNotificationTypes;
 				}).catch(err => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						text: err.message
 					});
diff --git a/src/client/pages/my-settings/profile.vue b/src/client/pages/my-settings/profile.vue
index e6d140b772..037fe39663 100644
--- a/src/client/pages/my-settings/profile.vue
+++ b/src/client/pages/my-settings/profile.vue
@@ -160,14 +160,14 @@ export default defineComponent({
 				this.$store.state.i.bannerUrl = i.bannerUrl;
 
 				if (notify) {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						iconOnly: true, autoClose: true
 					});
 				}
 			}).catch(err => {
 				this.saving = false;
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: err.id
 				});
diff --git a/src/client/pages/my-settings/security.vue b/src/client/pages/my-settings/security.vue
index b750c35829..6c52f114ae 100644
--- a/src/client/pages/my-settings/security.vue
+++ b/src/client/pages/my-settings/security.vue
@@ -25,7 +25,7 @@ export default defineComponent({
 
 	methods: {
 		async change() {
-			const { canceled: canceled1, result: currentPassword } = await this.$root.dialog({
+			const { canceled: canceled1, result: currentPassword } = await this.$root.showDialog({
 				title: this.$t('currentPassword'),
 				input: {
 					type: 'password'
@@ -33,7 +33,7 @@ export default defineComponent({
 			});
 			if (canceled1) return;
 
-			const { canceled: canceled2, result: newPassword } = await this.$root.dialog({
+			const { canceled: canceled2, result: newPassword } = await this.$root.showDialog({
 				title: this.$t('newPassword'),
 				input: {
 					type: 'password'
@@ -41,7 +41,7 @@ export default defineComponent({
 			});
 			if (canceled2) return;
 
-			const { canceled: canceled3, result: newPassword2 } = await this.$root.dialog({
+			const { canceled: canceled3, result: newPassword2 } = await this.$root.showDialog({
 				title: this.$t('newPasswordRetype'),
 				input: {
 					type: 'password'
@@ -50,14 +50,14 @@ export default defineComponent({
 			if (canceled3) return;
 
 			if (newPassword !== newPassword2) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: this.$t('retypedNotMatch')
 				});
 				return;
 			}
 
-			const dialog = this.$root.dialog({
+			const dialog = this.$root.showDialog({
 				type: 'waiting',
 				iconOnly: true
 			});
@@ -66,12 +66,12 @@ export default defineComponent({
 				currentPassword,
 				newPassword
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}).catch(e => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
diff --git a/src/client/pages/page-editor/els/page-editor.el.if.vue b/src/client/pages/page-editor/els/page-editor.el.if.vue
index 84bd1026e9..84bd485cab 100644
--- a/src/client/pages/page-editor/els/page-editor.el.if.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.if.vue
@@ -64,7 +64,7 @@ export default defineComponent({
 
 	methods: {
 		async add() {
-			const { canceled, result: type } = await this.$root.dialog({
+			const { canceled, result: type } = await this.$root.showDialog({
 				type: null,
 				title: this.$t('_pages.chooseBlock'),
 				select: {
diff --git a/src/client/pages/page-editor/els/page-editor.el.section.vue b/src/client/pages/page-editor/els/page-editor.el.section.vue
index 41b9cf86e7..ad4f08c9fd 100644
--- a/src/client/pages/page-editor/els/page-editor.el.section.vue
+++ b/src/client/pages/page-editor/els/page-editor.el.section.vue
@@ -62,7 +62,7 @@ export default defineComponent({
 
 	methods: {
 		async rename() {
-			const { canceled, result: title } = await this.$root.dialog({
+			const { canceled, result: title } = await this.$root.showDialog({
 				title: 'Enter title',
 				input: {
 					type: 'text',
@@ -75,7 +75,7 @@ export default defineComponent({
 		},
 
 		async add() {
-			const { canceled, result: type } = await this.$root.dialog({
+			const { canceled, result: type } = await this.$root.showDialog({
 				type: null,
 				title: this.$t('_pages.chooseBlock'),
 				select: {
diff --git a/src/client/pages/page-editor/page-editor.script-block.vue b/src/client/pages/page-editor/page-editor.script-block.vue
index 5bbd3180b4..9dd84e4193 100644
--- a/src/client/pages/page-editor/page-editor.script-block.vue
+++ b/src/client/pages/page-editor/page-editor.script-block.vue
@@ -210,7 +210,7 @@ export default defineComponent({
 
 	methods: {
 		async changeType() {
-			const { canceled, result: type } = await this.$root.dialog({
+			const { canceled, result: type } = await this.$root.showDialog({
 				type: null,
 				title: this.$t('_pages.selectType'),
 				select: {
diff --git a/src/client/pages/page-editor/page-editor.vue b/src/client/pages/page-editor/page-editor.vue
index da6f925ebb..45054ed53e 100644
--- a/src/client/pages/page-editor/page-editor.vue
+++ b/src/client/pages/page-editor/page-editor.vue
@@ -239,14 +239,14 @@ export default defineComponent({
 			const onError = err => {
 				if (err.id == '3d81ceae-475f-4600-b2a8-2bc116157532') {
 					if (err.info.param == 'name') {
-						this.$root.dialog({
+						this.$root.showDialog({
 							type: 'error',
 							title: this.$t('_pages.invalidNameTitle'),
 							text: this.$t('_pages.invalidNameText')
 						});
 					}
 				} else if (err.code == 'NAME_ALREADY_EXISTS') {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						text: this.$t('_pages.nameAlreadyExists')
 					});
@@ -258,7 +258,7 @@ export default defineComponent({
 				this.$root.api('pages/update', options)
 				.then(page => {
 					this.currentName = this.name.trim();
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						text: this.$t('_pages.updated')
 					});
@@ -268,7 +268,7 @@ export default defineComponent({
 				.then(page => {
 					this.pageId = page.id;
 					this.currentName = this.name.trim();
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						text: this.$t('_pages.created')
 					});
@@ -278,7 +278,7 @@ export default defineComponent({
 		},
 
 		del() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('removeAreYouSure', { x: this.title.trim() }),
 				showCancelButton: true
@@ -287,7 +287,7 @@ export default defineComponent({
 				this.$root.api('pages/delete', {
 					pageId: this.pageId,
 				}).then(() => {
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'success',
 						text: this.$t('_pages.deleted')
 					});
@@ -297,7 +297,7 @@ export default defineComponent({
 		},
 
 		async add() {
-			const { canceled, result: type } = await this.$root.dialog({
+			const { canceled, result: type } = await this.$root.showDialog({
 				type: null,
 				title: this.$t('_pages.chooseBlock'),
 				select: {
@@ -312,7 +312,7 @@ export default defineComponent({
 		},
 
 		async addVariable() {
-			let { canceled, result: name } = await this.$root.dialog({
+			let { canceled, result: name } = await this.$root.showDialog({
 				title: this.$t('_pages.enterVariableName'),
 				input: {
 					type: 'text',
@@ -324,7 +324,7 @@ export default defineComponent({
 			name = name.trim();
 
 			if (this.hpml.isUsedName(name)) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: this.$t('_pages.variableNameIsAlreadyUsed')
 				});
diff --git a/src/client/pages/page.vue b/src/client/pages/page.vue
index 340d06ebcd..f1152ff475 100644
--- a/src/client/pages/page.vue
+++ b/src/client/pages/page.vue
@@ -105,7 +105,7 @@ export default defineComponent({
 			this.$root.api('i/update', {
 				pinnedPageId: pin ? this.page.id : null,
 			}).then(() => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
diff --git a/src/client/pages/preferences/index.vue b/src/client/pages/preferences/index.vue
index 44d7b79238..66ba80cc68 100644
--- a/src/client/pages/preferences/index.vue
+++ b/src/client/pages/preferences/index.vue
@@ -295,7 +295,7 @@ export default defineComponent({
 
 	watch: {
 		lang() {
-			const dialog = this.$root.dialog({
+			const dialog = this.$root.showDialog({
 				type: 'waiting',
 				iconOnly: true
 			});
@@ -306,7 +306,7 @@ export default defineComponent({
 				.then(() => location.reload())
 				.catch(() => {
 					dialog.close();
-					this.$root.dialog({
+					this.$root.showDialog({
 						type: 'error',
 						iconOnly: true,
 						autoClose: true
diff --git a/src/client/pages/preferences/plugins.vue b/src/client/pages/preferences/plugins.vue
index 5af517003e..dc87f29514 100644
--- a/src/client/pages/preferences/plugins.vue
+++ b/src/client/pages/preferences/plugins.vue
@@ -85,7 +85,7 @@ export default defineComponent({
 			try {
 				ast = parse(this.script);
 			} catch (e) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: 'Syntax error :('
 				});
@@ -93,7 +93,7 @@ export default defineComponent({
 			}
 			const meta = AiScript.collectMetadata(ast);
 			if (meta == null) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: 'No metadata found :('
 				});
@@ -101,7 +101,7 @@ export default defineComponent({
 			}
 			const data = meta.get(null);
 			if (data == null) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: 'No metadata found :('
 				});
@@ -109,7 +109,7 @@ export default defineComponent({
 			}
 			const { name, version, author, description, permissions, config } = data;
 			if (name == null || version == null || author == null) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: 'Required property not found :('
 				});
@@ -142,7 +142,7 @@ export default defineComponent({
 				ast: serialize(ast)
 			});
 
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
@@ -154,7 +154,7 @@ export default defineComponent({
 
 		uninstall() {
 			this.$store.commit('deviceUser/uninstallPlugin', this.selectedPluginId);
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
diff --git a/src/client/pages/preferences/sidebar.vue b/src/client/pages/preferences/sidebar.vue
index f64658920b..02d4129626 100644
--- a/src/client/pages/preferences/sidebar.vue
+++ b/src/client/pages/preferences/sidebar.vue
@@ -61,7 +61,7 @@ export default defineComponent({
 	methods: {
 		async addItem() {
 			const menu = Object.keys(this.menuDef).filter(k => !this.$store.state.deviceUser.menu.includes(k));
-			const { canceled, result: item } = await this.$root.dialog({
+			const { canceled, result: item } = await this.$root.showDialog({
 				type: null,
 				title: this.$t('addItem'),
 				select: {
diff --git a/src/client/pages/preferences/theme.vue b/src/client/pages/preferences/theme.vue
index d968950ed8..12f3273097 100644
--- a/src/client/pages/preferences/theme.vue
+++ b/src/client/pages/preferences/theme.vue
@@ -194,7 +194,7 @@ export default defineComponent({
 
 		copyThemeCode() {
 			copyToClipboard(this.selectedThemeCode);
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
@@ -206,21 +206,21 @@ export default defineComponent({
 			try {
 				theme = JSON5.parse(code);
 			} catch (e) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: this.$t('_theme.invalid')
 				});
 				return false;
 			}
 			if (!validateTheme(theme)) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: this.$t('_theme.invalid')
 				});
 				return false;
 			}
 			if (this.$store.state.device.themes.some(t => t.id === theme.id)) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'info',
 					text: this.$t('_theme.alreadyInstalled')
 				});
@@ -242,7 +242,7 @@ export default defineComponent({
 			this.$store.commit('device/set', {
 				key: 'themes', value: themes
 			});
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				text: this.$t('_theme.installed', { name: theme.name })
 			});
@@ -254,7 +254,7 @@ export default defineComponent({
 			this.$store.commit('device/set', {
 				key: 'themes', value: themes
 			});
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				iconOnly: true, autoClose: true
 			});
diff --git a/src/client/pages/room/room.vue b/src/client/pages/room/room.vue
index 6466f1aa26..f4c9e92c3c 100644
--- a/src/client/pages/room/room.vue
+++ b/src/client/pages/room/room.vue
@@ -141,7 +141,7 @@ export default defineComponent({
 
 	beforeRouteLeave(to, from, next) {
 		if (this.changed) {
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('leaveConfirm'),
 				showCancelButton: true
@@ -171,7 +171,7 @@ export default defineComponent({
 		},
 
 		async add() {
-			const { canceled, result: id } = await this.$root.dialog({
+			const { canceled, result: id } = await this.$root.showDialog({
 				type: null,
 				title: this.$t('_rooms.addFurniture'),
 				select: {
@@ -198,12 +198,12 @@ export default defineComponent({
 				room: room.getRoomInfo()
 			}).then(() => {
 				this.changed = false;
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
 			}).catch((e: any) => {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e.message
 				});
@@ -211,7 +211,7 @@ export default defineComponent({
 		},
 
 		clear() {
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('_rooms.clearConfirm'),
 				showCancelButton: true
diff --git a/src/client/pages/scratchpad.vue b/src/client/pages/scratchpad.vue
index db17d1a1a0..34a9d04d88 100644
--- a/src/client/pages/scratchpad.vue
+++ b/src/client/pages/scratchpad.vue
@@ -77,7 +77,7 @@ export default defineComponent({
 			}), {
 				in: (q) => {
 					return new Promise(ok => {
-						this.$root.dialog({
+						this.$root.showDialog({
 							title: q,
 							input: {}
 						}).then(({ canceled, result: a }) => {
@@ -108,7 +108,7 @@ export default defineComponent({
 			try {
 				ast = parse(this.code);
 			} catch (e) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: 'Syntax error :('
 				});
@@ -117,7 +117,7 @@ export default defineComponent({
 			try {
 				await aiscript.exec(ast);
 			} catch (e) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e
 				});
diff --git a/src/client/pages/share.vue b/src/client/pages/share.vue
index 5f1de9ceed..aeb1646e71 100644
--- a/src/client/pages/share.vue
+++ b/src/client/pages/share.vue
@@ -64,7 +64,7 @@ export default defineComponent({
 				initialText: text.trim()
 			}).$once('posted', () => {
 				this.posted = true;
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'success',
 					iconOnly: true, autoClose: true
 				});
diff --git a/src/client/pages/test.vue b/src/client/pages/test.vue
new file mode 100644
index 0000000000..98d9492615
--- /dev/null
+++ b/src/client/pages/test.vue
@@ -0,0 +1,50 @@
+<template>
+<div>
+	<portal to="header"><fa :icon="faExclamationTriangle"/>TEST</portal>
+
+	<div class="_card">
+		<div class="_title">Dialog</div>
+		<div class="_content">
+			<mk-input v-model:value="dialogTitle">
+				<span>Title</span>
+			</mk-input>
+			<mk-button @click="showDialog()">Show</mk-button>
+		</div>
+	</div>
+</div>
+</template>
+
+<script lang="ts">
+import { defineComponent } from 'vue';
+import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
+import MkButton from '../components/ui/button.vue';
+import MkInput from '../components/ui/input.vue';
+
+export default defineComponent({
+	components: {
+		MkButton,
+		MkInput,
+	},
+
+	metaInfo() {
+		return {
+			title: this.$t('notFound') as string
+		};
+	},
+
+	data() {
+		return {
+			dialogTitle: 'Title',
+			faExclamationTriangle
+		}
+	},
+
+	methods: {
+		showDialog() {
+			this.$root.showDialog({
+				title: this.dialogTitle,
+			})
+		}
+	}
+});
+</script>
diff --git a/src/client/pages/theme-editor.vue b/src/client/pages/theme-editor.vue
index e2ca7fb04c..ddffc28196 100644
--- a/src/client/pages/theme-editor.vue
+++ b/src/client/pages/theme-editor.vue
@@ -155,7 +155,7 @@ export default defineComponent({
 		},
 
 		async confirm(): Promise<boolean> {
-			const { canceled } = await this.$root.dialog({
+			const { canceled } = await this.$root.showDialog({
 				type: 'warning',
 				text: this.$t('leaveConfirm'),
 				showCancelButton: true
@@ -172,7 +172,7 @@ export default defineComponent({
 		},
 	
 		async del(i: number) {
-			const { canceled } = await this.$root.dialog({ 
+			const { canceled } = await this.$root.showDialog({ 
 				type: 'warning',
 				showCancelButton: true,
 				text: this.$t('_theme.deleteConstantConfirm', { const: this.theme[i][0] }),
@@ -182,7 +182,7 @@ export default defineComponent({
 		},
 	
 		async addConst() {
-			const { canceled, result } = await this.$root.dialog({
+			const { canceled, result } = await this.$root.showDialog({
 				title: this.$t('_theme.inputConstantName'),
 				input: true
 			});
@@ -196,7 +196,7 @@ export default defineComponent({
 			this.$store.commit('device/set', {
 				key: 'themes', value: themes
 			});
-			this.$root.dialog({
+			this.$root.showDialog({
 				type: 'success',
 				text: this.$t('_theme.installed', { name: theme.name })
 			});
@@ -208,7 +208,7 @@ export default defineComponent({
 			try {
 				applyTheme(theme, false);
 			} catch (e) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e.message
 				});
@@ -229,7 +229,7 @@ export default defineComponent({
 				this.theme = convertToViewModel(theme);
 				this.themeToImport = '';
 			} catch (e) {
-				this.$root.dialog({
+				this.$root.showDialog({
 					type: 'error',
 					text: e.message
 				});
diff --git a/src/client/root.vue b/src/client/root.vue
index bd7391a0e4..694f99a73c 100644
--- a/src/client/root.vue
+++ b/src/client/root.vue
@@ -1,11 +1,12 @@
 <template>
 <DeckUI v-if="deckmode"/>
 <DefaultUI v-else/>
-<!-- Render modals here -->
+
+<XDialog v-if="dialog" v-bind="dialog" :key="dialog.id" @ok="onDialogOk" @closed="onDialogClosed"/>
 </template>
 
 <script lang="ts">
-import { defineComponent } from 'vue';
+import { defineComponent, defineAsyncComponent } from 'vue';
 import DefaultUI from './default.vue';
 import DeckUI from './deck.vue';
 import { instanceName, deckmode } from './config';
@@ -14,6 +15,7 @@ export default defineComponent({
 	components: {
 		DefaultUI,
 		DeckUI,
+		XDialog: defineAsyncComponent(() => import('./components/dialog.vue')),
 	},
 
 	metaInfo: {
@@ -39,13 +41,31 @@ export default defineComponent({
 		};
 	},
 
+	computed: {
+		dialog() {
+			if (this.$store.state.dialogs.length === 0) return null;
+
+			// what: ダイアログが複数ある場合は、一番最後に追加されたダイアログを表示する
+			// why: ダイアログが一度に複数表示されるとユーザビリティが悪いため。
+			return this.$store.state.dialogs[this.$store.state.dialogs.length - 1];
+		}
+	},
+
 	methods: {
 		api(endpoint: string, data: { [x: string]: any } = {}, token?) {
 			return this.$store.dispatch('api', { endpoint, data, token });
 		},
 
-		dialog(opts) {
+		showDialog(opts) {
 			this.$store.commit('showDialog', opts);
+		},
+
+		onDialogOk(result) {
+			this.$store.commit('requestDialogClose', this.dialog.id);
+		},
+
+		onDialogClosed() {
+			this.$store.commit('removeDialog', this.dialog.id);
 		}
 	}
 });
diff --git a/src/client/router.ts b/src/client/router.ts
index 7a83592b1e..40f8d89c33 100644
--- a/src/client/router.ts
+++ b/src/client/router.ts
@@ -69,6 +69,7 @@ export const router = createRouter({
 		{ path: '/miauth/:session', component: page('miauth') },
 		{ path: '/authorize-follow', component: page('follow') },
 		{ path: '/share', component: page('share') },
+		{ path: '/test', component: page('test') },
 		{ path: '/:catchAll(.*)', component: page('not-found') }
 	],
 	// なんかHacky
diff --git a/src/client/scripts/aiscript/api.ts b/src/client/scripts/aiscript/api.ts
index c91b9578d5..dad28ec38e 100644
--- a/src/client/scripts/aiscript/api.ts
+++ b/src/client/scripts/aiscript/api.ts
@@ -9,14 +9,14 @@ export function createAiScriptEnv(vm, opts) {
 		USER_NAME: vm.$store.getters.isSignedIn ? values.STR(vm.$store.state.i.name) : values.NULL,
 		USER_USERNAME: vm.$store.getters.isSignedIn ? values.STR(vm.$store.state.i.username) : values.NULL,
 		'Mk:dialog': values.FN_NATIVE(async ([title, text, type]) => {
-			await vm.$root.dialog({
+			await vm.$root.showDialog({
 				type: type ? type.value : 'info',
 				title: title.value,
 				text: text.value,
 			});
 		}),
 		'Mk:confirm': values.FN_NATIVE(async ([title, text, type]) => {
-			const confirm = await vm.$root.dialog({
+			const confirm = await vm.$root.showDialog({
 				type: type ? type.value : 'question',
 				showCancelButton: true,
 				title: title.value,
diff --git a/src/client/scripts/hpml/evaluator.ts b/src/client/scripts/hpml/evaluator.ts
index a056884368..a914b15ef3 100644
--- a/src/client/scripts/hpml/evaluator.ts
+++ b/src/client/scripts/hpml/evaluator.ts
@@ -42,7 +42,7 @@ export class Hpml {
 			}), ...initLib(this)}, {
 				in: (q) => {
 					return new Promise(ok => {
-						vm.$root.dialog({
+						vm.$root.showDialog({
 							title: q,
 							input: {}
 						}).then(({ canceled, result: a }) => {
diff --git a/src/client/scripts/please-login.ts b/src/client/scripts/please-login.ts
index ebd7dd82ab..e4a9f40be7 100644
--- a/src/client/scripts/please-login.ts
+++ b/src/client/scripts/please-login.ts
@@ -1,7 +1,7 @@
 export default ($root: any) => {
 	if ($root.$store.getters.isSignedIn) return;
 
-	$root.dialog({
+	$root.showDialog({
 		title: $root.$t('signinRequired'),
 		text: null
 	});
diff --git a/src/client/scripts/search.ts b/src/client/scripts/search.ts
index 16057dfd34..f96702b5b0 100644
--- a/src/client/scripts/search.ts
+++ b/src/client/scripts/search.ts
@@ -26,7 +26,7 @@ export async function search(v: any, q: string) {
 		}
 
 		v.$root.$emit('warp', date);
-		v.$root.dialog({
+		v.$root.showDialog({
 			icon: faHistory,
 			iconOnly: true, autoClose: true
 		});
@@ -34,7 +34,7 @@ export async function search(v: any, q: string) {
 	}
 
 	if (q.startsWith('https://')) {
-		const dialog = v.$root.dialog({
+		const dialog = v.$root.showDialog({
 			type: 'waiting',
 			text: v.$t('fetchingAsApObject') + '...',
 			showOkButton: false,
diff --git a/src/client/scripts/select-file.ts b/src/client/scripts/select-file.ts
index 2ecbadd112..efff59d0ed 100644
--- a/src/client/scripts/select-file.ts
+++ b/src/client/scripts/select-file.ts
@@ -10,7 +10,7 @@ export function selectFile(component: any, src: any, label: string | null, multi
 			input.type = 'file';
 			input.multiple = multiple;
 			input.onchange = () => {
-				const dialog = component.$root.dialog({
+				const dialog = component.$root.showDialog({
 					type: 'waiting',
 					text: component.$t('uploading') + '...',
 					showOkButton: false,
@@ -35,7 +35,7 @@ export function selectFile(component: any, src: any, label: string | null, multi
 				Promise.all(promises).then(driveFiles => {
 					res(multiple ? driveFiles : driveFiles[0]);
 				}).catch(e => {
-					component.$root.dialog({
+					component.$root.showDialog({
 						type: 'error',
 						text: e
 					});
diff --git a/src/client/store.ts b/src/client/store.ts
index 5544edd122..61a9e8d00b 100644
--- a/src/client/store.ts
+++ b/src/client/store.ts
@@ -263,7 +263,18 @@ export const store = createStore({
 		},
 
 		showDialog(state, dialog) {
-			state.dialogs.push(dialog);
+			state.dialogs.push({
+				...dialog,
+				id: Math.random().toString() // TODO: uuidとか使う
+			});
+		},
+
+		requestDialogClose(state, dialogId) {
+			state.dialogs.find(d => d.id === dialogId).closing = true;
+		},
+
+		removeDialog(state, dialogId) {
+			state.dialogs = state.dialogs.filter(d => d.id !== dialogId);
 		},
 
 		setFullView(state, v) {