diff --git a/src/client/components/deck/column.vue b/src/client/components/deck/column.vue
index d19dc7eec3..ec5c09f6f0 100644
--- a/src/client/components/deck/column.vue
+++ b/src/client/components/deck/column.vue
@@ -210,6 +210,7 @@ export default defineComponent({
 		showMenu() {
 			os.menu({
 				items: this.getMenu(),
+			}, {
 				source: this.$refs.menu,
 			});
 		},
diff --git a/src/client/components/drive.file.vue b/src/client/components/drive.file.vue
index 6d1250d282..458919ed0e 100644
--- a/src/client/components/drive.file.vue
+++ b/src/client/components/drive.file.vue
@@ -108,6 +108,7 @@ export default defineComponent({
 						icon: faTrashAlt,
 						action: this.deleteFile
 					}],
+				}, {
 					source: ev.currentTarget || ev.target,
 				});
 			}
diff --git a/src/client/components/note.vue b/src/client/components/note.vue
index 926f8e0ccf..8629b5ed25 100644
--- a/src/client/components/note.vue
+++ b/src/client/components/note.vue
@@ -468,9 +468,10 @@ export default defineComponent({
 							renote: this.appearNote,
 						});
 					}
-				}]
-				source: this.$refs.renoteButton,
+				}],
 				viaKeyboard
+			}, {
+				source: this.$refs.renoteButton,
 			});
 		},
 
@@ -684,8 +685,9 @@ export default defineComponent({
 
 			os.menu({
 				items: menu,
-				source: this.$refs.menuButton,
 				viaKeyboard
+			}, {
+				source: this.$refs.menuButton,
 			}).then(this.focus);
 		},
 
@@ -702,8 +704,9 @@ export default defineComponent({
 						this.isDeleted = true;
 					}
 				}],
-				source: this.$refs.renoteTime,
 				viaKeyboard: viaKeyboard
+			}, {
+				source: this.$refs.renoteTime,
 			});
 		},
 
diff --git a/src/client/components/post-form-attaches.vue b/src/client/components/post-form-attaches.vue
index c57714545f..5ff4ebd5d4 100644
--- a/src/client/components/post-form-attaches.vue
+++ b/src/client/components/post-form-attaches.vue
@@ -94,7 +94,8 @@ export default defineComponent({
 					icon: faTimesCircle,
 					action: () => { this.detachMedia(file.id) }
 				}],
-				source: ev.currentTarget || ev.target
+			}, {
+				source: ev.currentTarget || ev.target,
 			}).then(() => this.menu = null);
 		}
 	}
diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue
index e29b75ff20..af810d2498 100644
--- a/src/client/components/post-form.vue
+++ b/src/client/components/post-form.vue
@@ -366,7 +366,8 @@ export default defineComponent({
 					icon: faLink,
 					action: () => { this.chooseFileFromUrl() }
 				}],
-				source: ev.currentTarget || ev.target
+			}, {
+				source: ev.currentTarget || ev.target,
 			});
 		},
 
@@ -625,6 +626,7 @@ export default defineComponent({
 						});
 					}
 				})),
+			}, {
 				source: ev.currentTarget || ev.target,
 			});
 		}
diff --git a/src/client/components/sidebar.vue b/src/client/components/sidebar.vue
index ab8f62916e..cd78b64ca2 100644
--- a/src/client/components/sidebar.vue
+++ b/src/client/components/sidebar.vue
@@ -176,6 +176,7 @@ export default defineComponent({
 							align: 'left',
 							fixed: true,
 							width: 240,
+						}, {
 							source: ev.currentTarget || ev.target,
 						});
 					},
@@ -183,6 +184,7 @@ export default defineComponent({
 				align: 'left',
 				fixed: true,
 				width: 240,
+			}, {
 				source: ev.currentTarget || ev.target,
 			});
 		},
@@ -238,6 +240,7 @@ export default defineComponent({
 				align: 'left',
 				fixed: true,
 				width: 200,
+			}, {
 				source: ev.currentTarget || ev.target,
 			});
 		},
@@ -271,6 +274,7 @@ export default defineComponent({
 				align: 'left',
 				fixed: true,
 				width: 200,
+			}, {
 				source: ev.currentTarget || ev.target,
 			});
 		},
diff --git a/src/client/os.ts b/src/client/os.ts
index 37fffc72ee..7a130a3408 100644
--- a/src/client/os.ts
+++ b/src/client/os.ts
@@ -113,12 +113,23 @@ export function modal(component: Component, props: Record<string, any>, events =
 }
 
 export function dialog(props: Record<string, any>, opts?: { cancelableByBgClick: boolean; }) {
-	return modal(defineAsyncComponent(() => import('@/components/dialog.vue')), props, {}, { cancelableByBgClick: opts?.cancelableByBgClick }).then(result => {
-		if (result) {
-			return result;
-		} else {
-			return { canceled: true };
-		}
+	return new PCancelable((resolve, reject, onCancel) => {
+		const dialog = modal(defineAsyncComponent(() => import('@/components/dialog.vue')), props, {}, { cancelableByBgClick: opts?.cancelableByBgClick });
+
+		dialog.then(result => {
+			if (result) {
+				resolve(result);
+			} else {
+				resolve({ canceled: true });
+			}
+		});
+
+		dialog.catch(reject);
+
+		onCancel.shouldReject = false;
+		onCancel(() => {
+			dialog.cancel();
+		});
 	});
 }
 
diff --git a/src/client/pages/channel-editor.vue b/src/client/pages/channel-editor.vue
index 21fa45f611..68cc8d9a0f 100644
--- a/src/client/pages/channel-editor.vue
+++ b/src/client/pages/channel-editor.vue
@@ -111,7 +111,7 @@ export default defineComponent({
 		},
 
 		setBannerImage(e) {
-			selectFile(this, e.currentTarget || e.target, null, false).then(file => {
+			selectFile(e.currentTarget || e.target, null, false).then(file => {
 				this.bannerId = file.id;
 			});
 		},
diff --git a/src/client/pages/drive.vue b/src/client/pages/drive.vue
index 55b5bd8431..5d5b8c1a1c 100644
--- a/src/client/pages/drive.vue
+++ b/src/client/pages/drive.vue
@@ -70,7 +70,8 @@ export default defineComponent({
 				}],
 				fixed: true,
 				noCenter: true,
-				source: ev.currentTarget || ev.target
+			}, {
+				source: ev.currentTarget || ev.target,
 			}).then(() => {
 				this.menuOpened = false;
 			});
diff --git a/src/client/pages/index.home.vue b/src/client/pages/index.home.vue
index 24ddf0ac3e..84fbfca802 100644
--- a/src/client/pages/index.home.vue
+++ b/src/client/pages/index.home.vue
@@ -192,7 +192,8 @@ export default defineComponent({
 				}, antennaItems.length > 0 ? null : undefined, ...antennaItems, listItems.length > 0 ? null : undefined, ...listItems, channelItems.length > 0 ? null : undefined, ...channelItems],
 				fixed: true,
 				noCenter: true,
-				source: ev.currentTarget || ev.target
+			}, {
+				source: ev.currentTarget || ev.target,
 			}).then(() => {
 				this.menuOpened = false;
 			});
diff --git a/src/client/pages/instance/emojis.vue b/src/client/pages/instance/emojis.vue
index df76389e17..46e57fcad9 100644
--- a/src/client/pages/instance/emojis.vue
+++ b/src/client/pages/instance/emojis.vue
@@ -127,7 +127,7 @@ export default defineComponent({
 
 	methods: {
 		async add(e) {
-			const files = await selectFile(this, e.currentTarget || e.target, null, true);
+			const files = await selectFile(e.currentTarget || e.target, null, true);
 
 			const dialog = os.dialog({
 				type: 'waiting',
diff --git a/src/client/pages/messaging/index.vue b/src/client/pages/messaging/index.vue
index 618f49182d..2549311fcb 100644
--- a/src/client/pages/messaging/index.vue
+++ b/src/client/pages/messaging/index.vue
@@ -127,6 +127,7 @@ export default defineComponent({
 					action: () => { this.startGroup() }
 				}],
 				noCenter: true,
+			}, {
 				source: ev.currentTarget || ev.target,
 			});
 		},
diff --git a/src/client/pages/messaging/messaging-room.form.vue b/src/client/pages/messaging/messaging-room.form.vue
index 81229b7d70..21a73cd40f 100644
--- a/src/client/pages/messaging/messaging-room.form.vue
+++ b/src/client/pages/messaging/messaging-room.form.vue
@@ -158,7 +158,7 @@ export default defineComponent({
 		},
 
 		chooseFile(e) {
-			selectFile(this, e.currentTarget || e.target, this.$t('selectFile'), false).then(file => {
+			selectFile(e.currentTarget || e.target, this.$t('selectFile'), false).then(file => {
 				this.file = file;
 			});
 		},
diff --git a/src/client/pages/my-settings/profile.vue b/src/client/pages/my-settings/profile.vue
index 7a401af105..f55491e12e 100644
--- a/src/client/pages/my-settings/profile.vue
+++ b/src/client/pages/my-settings/profile.vue
@@ -120,7 +120,7 @@ export default defineComponent({
 
 	methods: {
 		changeAvatar(e) {
-			selectFile(this, e.currentTarget || e.target, this.$t('avatar')).then(file => {
+			selectFile(e.currentTarget || e.target, this.$t('avatar')).then(file => {
 				os.api('i/update', {
 					avatarId: file.id,
 				});
@@ -128,7 +128,7 @@ export default defineComponent({
 		},
 
 		changeBanner(e) {
-			selectFile(this, e.currentTarget || e.target, this.$t('banner')).then(file => {
+			selectFile(e.currentTarget || e.target, this.$t('banner')).then(file => {
 				os.api('i/update', {
 					bannerId: file.id,
 				});
diff --git a/src/client/pages/preferences/theme.vue b/src/client/pages/preferences/theme.vue
index 64f411bdf1..afecc195cf 100644
--- a/src/client/pages/preferences/theme.vue
+++ b/src/client/pages/preferences/theme.vue
@@ -188,7 +188,7 @@ export default defineComponent({
 
 	methods: {
 		setWallpaper(e) {
-			selectFile(this, e.currentTarget || e.target, null, false).then(file => {
+			selectFile(e.currentTarget || e.target, null, false).then(file => {
 				this.wallpaper = file.url;
 			});
 		},
diff --git a/src/client/pages/room/room.vue b/src/client/pages/room/room.vue
index cd699c14a6..83831b43fa 100644
--- a/src/client/pages/room/room.vue
+++ b/src/client/pages/room/room.vue
@@ -224,7 +224,7 @@ export default defineComponent({
 		},
 
 		chooseImage(key, e) {
-			selectFile(this, e.currentTarget || e.target, null, false).then(file => {
+			selectFile(e.currentTarget || e.target, null, false).then(file => {
 				room.updateProp(key, `/proxy/?${urlQuery({ url: file.thumbnailUrl })}`);
 				this.$refs.preview.selected(room.getSelectedObject());
 				this.changed = true;
diff --git a/src/client/pages/theme-editor.vue b/src/client/pages/theme-editor.vue
index f8e3f8ad15..40a0e3a50b 100644
--- a/src/client/pages/theme-editor.vue
+++ b/src/client/pages/theme-editor.vue
@@ -279,6 +279,7 @@ export default defineComponent({
 							type: 'refConst', key: '',
 						}),
 					},],
+				}, {
 					source: e.currentTarget || e.target,
 				});
 			});
diff --git a/src/client/scripts/select-drive-file.ts b/src/client/scripts/select-drive-file.ts
index 9459bd604b..6a2d531cc4 100644
--- a/src/client/scripts/select-drive-file.ts
+++ b/src/client/scripts/select-drive-file.ts
@@ -1,4 +1,4 @@
-export function selectDriveFile($root: any, multiple) {
+export function selectDriveFile(multiple) {
 	return new Promise((res, rej) => {
 		import('@/components/drive-window.vue').then(dialog => {
 			const w = $root.new(dialog, {
diff --git a/src/client/scripts/select-drive-folder.ts b/src/client/scripts/select-drive-folder.ts
index ee6e5c1551..38e909bfa8 100644
--- a/src/client/scripts/select-drive-folder.ts
+++ b/src/client/scripts/select-drive-folder.ts
@@ -1,4 +1,4 @@
-export function selectDriveFolder($root: any, multiple) {
+export function selectDriveFolder(multiple) {
 	return new Promise((res, rej) => {
 		import('@/components/drive-window.vue').then(dialog => {
 			const w = $root.new(dialog, {
diff --git a/src/client/scripts/select-file.ts b/src/client/scripts/select-file.ts
index c56e39c313..09d49d35c7 100644
--- a/src/client/scripts/select-file.ts
+++ b/src/client/scripts/select-file.ts
@@ -1,18 +1,20 @@
 import { faUpload, faCloud } from '@fortawesome/free-solid-svg-icons';
 import { selectDriveFile } from './select-drive-file';
 import { apiUrl } from '@/config';
+import { store } from '@/store';
+import * as os from '@/os';
+import { locale } from '@/i18n';
 
-// TODO: component引数は消せる(各種操作がstoreに移動し、かつstoreが複数ファイルで共有されるようになったため)
-export function selectFile(component: any, src: any, label: string | null, multiple = false) {
+export function selectFile(src: any, label: string | null, multiple = false) {
 	return new Promise((res, rej) => {
 		const chooseFileFromPc = () => {
 			const input = document.createElement('input');
 			input.type = 'file';
 			input.multiple = multiple;
 			input.onchange = () => {
-				const dialog = component.os.dialog({
+				const dialog = os.dialog({
 					type: 'waiting',
-					text: component.$t('uploading') + '...',
+					text: locale['uploading'] + '...',
 					showOkButton: false,
 					showCancelButton: false,
 					cancelableByBgClick: false
@@ -21,7 +23,7 @@ export function selectFile(component: any, src: any, label: string | null, multi
 				const promises = Array.from(input.files).map(file => new Promise((ok, err) => {
 					const data = new FormData();
 					data.append('file', file);
-					data.append('i', component.$store.state.i.token);
+					data.append('i', store.state.i.token);
 
 					fetch(apiUrl + '/drive/files/create', {
 						method: 'POST',
@@ -35,12 +37,12 @@ export function selectFile(component: any, src: any, label: string | null, multi
 				Promise.all(promises).then(driveFiles => {
 					res(multiple ? driveFiles : driveFiles[0]);
 				}).catch(e => {
-					component.os.dialog({
+					os.dialog({
 						type: 'error',
 						text: e
 					});
 				}).finally(() => {
-					dialog.close();
+					dialog.cancel();
 				});
 
 				// 一応廃棄
@@ -55,7 +57,7 @@ export function selectFile(component: any, src: any, label: string | null, multi
 		};
 
 		const chooseFileFromDrive = () => {
-			selectDriveFile(component.$root, multiple).then(files => {
+			selectDriveFile(multiple).then(files => {
 				res(files);
 			});
 		};
@@ -65,24 +67,25 @@ export function selectFile(component: any, src: any, label: string | null, multi
 
 		};
 
-		component.os.menu({
+		os.menu({
 			items: [label ? {
 				text: label,
 				type: 'label'
 			} : undefined, {
-				text: component.$t('upload'),
+				text: locale['upload'],
 				icon: faUpload,
 				action: chooseFileFromPc
 			}, {
-				text: component.$t('fromDrive'),
+				text: locale['fromDrive'],
 				icon: faCloud,
 				action: chooseFileFromDrive
 			}, /*{
-				text: component.$t('fromUrl'),
+				text: locale('fromUrl'),
 				icon: faLink,
 				action: chooseFileFromUrl
 			}*/],
-			source: src
+		}, {
+			source: src,
 		});
 	});
 }
diff --git a/src/client/widgets/timeline.vue b/src/client/widgets/timeline.vue
index 2300b56e6d..6426b78432 100644
--- a/src/client/widgets/timeline.vue
+++ b/src/client/widgets/timeline.vue
@@ -108,7 +108,8 @@ export default defineComponent({
 					action: () => { this.setSrc('global') }
 				}, antennaItems.length > 0 ? null : undefined, ...antennaItems, listItems.length > 0 ? null : undefined, ...listItems],
 				noCenter: true,
-				source: ev.currentTarget || ev.target
+			}, {
+				source: ev.currentTarget || ev.target,
 			}).then(() => {
 				this.menuOpened = false;
 			});