diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 62aade568d..74964e366a 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -743,7 +743,7 @@ online: "オンライン"
 active: "アクティブ"
 offline: "オフライン"
 notRecommended: "非推奨"
-botProtection: "Bot防御"
+botProtection: "Botプロテクション"
 instanceBlocking: "インスタンスブロック"
 selectAccount: "アカウントを選択"
 enabled: "有効"
@@ -754,7 +754,7 @@ administration: "管理"
 accounts: "アカウント"
 switch: "切り替え"
 noMaintainerInformationWarning: "管理者情報が設定されていません。"
-noBotProtectionWarning: "Bot防御が設定されていません。"
+noBotProtectionWarning: "Botプロテクションが設定されていません。"
 configure: "設定する"
 postToGallery: "ギャラリーへ投稿"
 gallery: "ギャラリー"
diff --git a/packages/client/@types/vue.d.ts b/packages/client/@types/vue.d.ts
index 8cb6130629..f6b66228f6 100644
--- a/packages/client/@types/vue.d.ts
+++ b/packages/client/@types/vue.d.ts
@@ -1,3 +1,5 @@
+/// <reference types="vue/macros-global" />
+
 declare module '*.vue' {
 	import type { DefineComponent } from 'vue';
 	const component: DefineComponent<{}, {}, any>;
diff --git a/packages/client/package.json b/packages/client/package.json
index 198bcbcef6..fe149890dd 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -116,7 +116,7 @@
 		"v-debounce": "0.1.2",
 		"vanilla-tilt": "1.7.2",
 		"vue": "3.2.26",
-		"vue-loader": "16.8.3",
+		"vue-loader": "17.0.0",
 		"vue-prism-editor": "2.0.0-alpha.2",
 		"vue-router": "4.0.5",
 		"vue-style-loader": "4.1.3",
diff --git a/packages/client/src/components/form/section.vue b/packages/client/src/components/form/section.vue
index bc2ab966b8..ab9fbe5fcd 100644
--- a/packages/client/src/components/form/section.vue
+++ b/packages/client/src/components/form/section.vue
@@ -7,12 +7,7 @@
 </div>
 </template>
 
-<script lang="ts">
-import { defineComponent } from 'vue';
-
-export default defineComponent({
-
-});
+<script lang="ts" setup>
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/client/src/components/form/split.vue b/packages/client/src/components/form/split.vue
new file mode 100644
index 0000000000..676b293967
--- /dev/null
+++ b/packages/client/src/components/form/split.vue
@@ -0,0 +1,27 @@
+<template>
+<div class="terlnhxf _formBlock">
+	<slot></slot>
+</div>
+</template>
+
+<script lang="ts" setup>
+const props = withDefaults(defineProps<{
+	minWidth: number;
+}>(), {
+  minWidth: 210,
+});
+
+const minWidth = props.minWidth + 'px';
+</script>
+
+<style lang="scss" scoped>
+.terlnhxf {
+	display: grid;
+	grid-template-columns: repeat(auto-fill, minmax(v-bind('minWidth'), 1fr));
+	grid-gap: 12px;
+
+	> ::v-deep(*) {
+		margin: 0 !important;
+	}
+}
+</style>
diff --git a/packages/client/src/components/form/switch.vue b/packages/client/src/components/form/switch.vue
index aa9b09215e..ac3284e7da 100644
--- a/packages/client/src/components/form/switch.vue
+++ b/packages/client/src/components/form/switch.vue
@@ -13,7 +13,8 @@
 		<i class="check fas fa-check"></i>
 	</span>
 	<span class="label">
-		<span @click="toggle"><slot></slot></span>
+		<!-- TODO: 無名slotの方は廃止 -->
+		<span @click="toggle"><slot name="label"></slot><slot></slot></span>
 		<p class="caption"><slot name="caption"></slot></p>
 	</span>
 </div>
diff --git a/packages/client/src/components/user-select-dialog.vue b/packages/client/src/components/user-select-dialog.vue
index ba2975478b..dbef34d547 100644
--- a/packages/client/src/components/user-select-dialog.vue
+++ b/packages/client/src/components/user-select-dialog.vue
@@ -1,5 +1,5 @@
 <template>
-<XModalWindow ref="dialog"
+<XModalWindow ref="dialogEl"
 	:with-ok-button="true"
 	:ok-button-disabled="selected == null"
 	@click="cancel()"
@@ -8,20 +8,20 @@
 	@closed="$emit('closed')"
 >
 	<template #header>{{ $ts.selectUser }}</template>
-	<div class="tbhwbxda _monolithic_">
-		<div class="_section">
-			<div class="_inputSplit">
-				<MkInput ref="username" v-model="username" class="input" @update:modelValue="search">
+	<div class="tbhwbxda">
+		<div class="form">
+			<FormSplit :min-width="170">
+				<MkInput ref="usernameEl" v-model="username" @update:modelValue="search">
 					<template #label>{{ $ts.username }}</template>
 					<template #prefix>@</template>
 				</MkInput>
-				<MkInput v-model="host" class="input" @update:modelValue="search">
+				<MkInput v-model="host" @update:modelValue="search">
 					<template #label>{{ $ts.host }}</template>
 					<template #prefix>@</template>
 				</MkInput>
-			</div>
+			</FormSplit>
 		</div>
-		<div v-if="username != '' || host != ''" class="_section result" :class="{ hit: users.length > 0 }">
+		<div v-if="username != '' || host != ''" class="result" :class="{ hit: users.length > 0 }">
 			<div v-if="users.length > 0" class="users">
 				<div v-for="user in users" :key="user.id" class="user" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()">
 					<MkAvatar :user="user" class="avatar" :show-indicator="true"/>
@@ -35,7 +35,7 @@
 				<span>{{ $ts.noUsers }}</span>
 			</div>
 		</div>
-		<div v-if="username == '' && host == ''" class="_section recent">
+		<div v-if="username == '' && host == ''" class="recent">
 			<div class="users">
 				<div v-for="user in recentUsers" :key="user.id" class="user" :class="{ selected: selected && selected.id === user.id }" @click="selected = user" @dblclick="ok()">
 					<MkAvatar :user="user" class="avatar" :show-indicator="true"/>
@@ -50,87 +50,89 @@
 </XModalWindow>
 </template>
 
-<script lang="ts">
-import { defineComponent } from 'vue';
-import MkInput from './form/input.vue';
+<script lang="ts" setup>
+import { nextTick, onMounted } from 'vue';
+import * as misskey from 'misskey-js';
+import MkInput from '@/components/form/input.vue';
+import FormSplit from '@/components/form/split.vue';
 import XModalWindow from '@/components/ui/modal-window.vue';
 import * as os from '@/os';
+import { defaultStore } from '@/store';
 
-export default defineComponent({
-	components: {
-		MkInput,
-		XModalWindow,
-	},
+const emit = defineEmits<{
+  (e: 'ok', selected: misskey.entities.UserDetailed): void;
+  (e: 'cancel'): void;
+	(e: 'closed'): void;
+}>();
 
-	props: {
-	},
+let username = $ref('');
+let host = $ref('');
+let users: misskey.entities.UserDetailed[] = $ref([]);
+let recentUsers: misskey.entities.UserDetailed[] = $ref([]);
+let selected: misskey.entities.UserDetailed | null = $ref(null);
+let usernameEl: HTMLElement = $ref();
+let dialogEl = $ref();
 
-	emits: ['ok', 'cancel', 'closed'],
-
-	data() {
-		return {
-			username: '',
-			host: '',
-			recentUsers: [],
-			users: [],
-			selected: null,
-		};
-	},
-
-	async mounted() {
-		this.focus();
-
-		this.$nextTick(() => {
-			this.focus();
-		});
-
-		this.recentUsers = await os.api('users/show', {
-			userIds: this.$store.state.recentlyUsedUsers
-		});
-	},
-
-	methods: {
-		search() {
-			if (this.username == '' && this.host == '') {
-				this.users = [];
-				return;
-			}
-			os.api('users/search-by-username-and-host', {
-				username: this.username,
-				host: this.host,
-				limit: 10,
-				detail: false
-			}).then(users => {
-				this.users = users;
-			});
-		},
-
-		focus() {
-			this.$refs.username.focus();
-		},
-
-		ok() {
-			this.$emit('ok', this.selected);
-			this.$refs.dialog.close();
-
-			// 最近使ったユーザー更新
-			let recents = this.$store.state.recentlyUsedUsers;
-			recents = recents.filter(x => x !== this.selected.id);
-			recents.unshift(this.selected.id);
-			this.$store.set('recentlyUsedUsers', recents.splice(0, 16));
-		},
-
-		cancel() {
-			this.$emit('cancel');
-			this.$refs.dialog.close();
-		},
+const focus = () => {
+	if (usernameEl) {
+		usernameEl.focus();
 	}
+};
+
+const search = () => {
+	if (username === '' && host === '') {
+		users = [];
+		return;
+	}
+	os.api('users/search-by-username-and-host', {
+		username: username,
+		host: host,
+		limit: 10,
+		detail: false
+	}).then(_users => {
+		users = _users;
+	});
+};
+
+const ok = () => {
+	if (selected == null) return;
+	emit('ok', selected);
+	dialogEl.close();
+
+	// 最近使ったユーザー更新
+	let recents = defaultStore.state.recentlyUsedUsers;
+	recents = recents.filter(x => x !== selected.id);
+	recents.unshift(selected.id);
+	defaultStore.set('recentlyUsedUsers', recents.splice(0, 16));
+};
+
+const cancel = () => {
+	emit('cancel');
+	dialogEl.close();
+};
+
+onMounted(() => {
+	focus();
+
+	nextTick(() => {
+		focus();
+	});
+
+	os.api('users/show', {
+		userIds: defaultStore.state.recentlyUsedUsers,
+	}).then(users => {
+		recentUsers = users;
+	});
 });
 </script>
 
 <style lang="scss" scoped>
 .tbhwbxda {
-	> ._section {
+	> .form {
+		padding: 0 var(--root-margin);
+	}
+
+	> .result, > .recent {
 		display: flex;
 		flex-direction: column;
 		overflow: auto;
diff --git a/packages/client/src/pages/about.vue b/packages/client/src/pages/about.vue
index 04f68b7201..618e569839 100644
--- a/packages/client/src/pages/about.vue
+++ b/packages/client/src/pages/about.vue
@@ -24,7 +24,7 @@
 		</FormSection>
 
 		<FormSection>
-			<div class="_inputSplit _formBlock">
+			<FormSplit>
 				<MkKeyValue class="_formBlock">
 					<template #key>{{ $ts.administrator }}</template>
 					<template #value>{{ $instance.maintainerName }}</template>
@@ -33,14 +33,14 @@
 					<template #key>{{ $ts.contact }}</template>
 					<template #value>{{ $instance.maintainerEmail }}</template>
 				</MkKeyValue>
-			</div>
+			</FormSplit>
 			<FormLink v-if="$instance.tosUrl" :to="$instance.tosUrl" class="_formBlock" external>{{ $ts.tos }}</FormLink>
 		</FormSection>
 
 		<FormSuspense :p="initStats">
 			<FormSection>
 				<template #label>{{ $ts.statistics }}</template>
-				<div class="_inputSplit">
+				<FormSplit>
 					<MkKeyValue class="_formBlock">
 						<template #key>{{ $ts.users }}</template>
 						<template #value>{{ number(stats.originalUsersCount) }}</template>
@@ -49,7 +49,7 @@
 						<template #key>{{ $ts.notes }}</template>
 						<template #value>{{ number(stats.originalNotesCount) }}</template>
 					</MkKeyValue>
-				</div>
+				</FormSplit>
 			</FormSection>
 		</FormSuspense>
 
@@ -73,6 +73,7 @@ import { version, instanceName } from '@/config';
 import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
 import FormSuspense from '@/components/form/suspense.vue';
+import FormSplit from '@/components/form/split.vue';
 import MkKeyValue from '@/components/key-value.vue';
 import * as os from '@/os';
 import number from '@/filters/number';
@@ -85,6 +86,7 @@ export default defineComponent({
 		FormSection,
 		FormLink,
 		FormSuspense,
+		FormSplit,
 	},
 
 	data() {
diff --git a/packages/client/src/pages/admin/ads.vue b/packages/client/src/pages/admin/ads.vue
index d12ed8563e..0396dae10c 100644
--- a/packages/client/src/pages/admin/ads.vue
+++ b/packages/client/src/pages/admin/ads.vue
@@ -23,14 +23,14 @@
 				<MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
 			</div>
 			-->
-			<div class="_inputSplit">
+			<FormSplit>
 				<MkInput v-model="ad.ratio" type="number">
 					<template #label>{{ $ts.ratio }}</template>
 				</MkInput>
 				<MkInput v-model="ad.expiresAt" type="date">
 					<template #label>{{ $ts.expiration }}</template>
 				</MkInput>
-			</div>
+			</FormSplit>
 			<MkTextarea v-model="ad.memo" class="_formBlock">
 				<template #label>{{ $ts.memo }}</template>
 			</MkTextarea>
@@ -49,6 +49,7 @@ import MkButton from '@/components/ui/button.vue';
 import MkInput from '@/components/form/input.vue';
 import MkTextarea from '@/components/form/textarea.vue';
 import FormRadios from '@/components/form/radios.vue';
+import FormSplit from '@/components/form/split.vue';
 import * as os from '@/os';
 import * as symbols from '@/symbols';
 
@@ -58,6 +59,7 @@ export default defineComponent({
 		MkInput,
 		MkTextarea,
 		FormRadios,
+		FormSplit,
 	},
 
 	emits: ['info'],
diff --git a/packages/client/src/pages/admin/email-settings.vue b/packages/client/src/pages/admin/email-settings.vue
index 873a853918..0799755a4d 100644
--- a/packages/client/src/pages/admin/email-settings.vue
+++ b/packages/client/src/pages/admin/email-settings.vue
@@ -1,50 +1,55 @@
 <template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
 	<FormSuspense :p="init">
-		<FormSwitch v-model="enableEmail">{{ $ts.enableEmail }}<template #desc>{{ $ts.emailConfigInfo }}</template></FormSwitch>
+		<div class="_formRoot">
+			<FormSwitch v-model="enableEmail" class="_formBlock">
+				<template #label>{{ $ts.enableEmail }}</template>
+				<template #caption>{{ $ts.emailConfigInfo }}</template>
+			</FormSwitch>
 
-		<template v-if="enableEmail">
-			<FormInput v-model="email" type="email">
-				<span>{{ $ts.emailAddress }}</span>
-			</FormInput>
+			<template v-if="enableEmail">
+				<FormInput v-model="email" type="email" class="_formBlock">
+					<template #label>{{ $ts.emailAddress }}</template>
+				</FormInput>
 
-			<div v-sticky-container class="_debobigegoItem _debobigegoNoConcat">
-				<div class="_debobigegoLabel">{{ $ts.smtpConfig }}</div>
-				<div class="main">
-					<FormInput v-model="smtpHost">
-						<span>{{ $ts.smtpHost }}</span>
-					</FormInput>
-					<FormInput v-model="smtpPort" type="number">
-						<span>{{ $ts.smtpPort }}</span>
-					</FormInput>
-					<FormInput v-model="smtpUser">
-						<span>{{ $ts.smtpUser }}</span>
-					</FormInput>
-					<FormInput v-model="smtpPass" type="password">
-						<span>{{ $ts.smtpPass }}</span>
-					</FormInput>
-					<FormInfo>{{ $ts.emptyToDisableSmtpAuth }}</FormInfo>
-					<FormSwitch v-model="smtpSecure">{{ $ts.smtpSecure }}<template #desc>{{ $ts.smtpSecureInfo }}</template></FormSwitch>
-				</div>
-			</div>
-
-			<FormButton @click="testEmail">{{ $ts.testEmail }}</FormButton>
-		</template>
-
-		<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+				<FormSection>
+					<template #label>{{ $ts.smtpConfig }}</template>
+					<FormSplit :min-width="280">
+						<FormInput v-model="smtpHost" class="_formBlock">
+							<template #label>{{ $ts.smtpHost }}</template>
+						</FormInput>
+						<FormInput v-model="smtpPort" type="number" class="_formBlock">
+							<template #label>{{ $ts.smtpPort }}</template>
+						</FormInput>
+					</FormSplit>
+					<FormSplit :min-width="280">
+						<FormInput v-model="smtpUser" class="_formBlock">
+							<template #label>{{ $ts.smtpUser }}</template>
+						</FormInput>
+						<FormInput v-model="smtpPass" type="password" class="_formBlock">
+							<template #label>{{ $ts.smtpPass }}</template>
+						</FormInput>
+					</FormSplit>
+					<FormInfo class="_formBlock">{{ $ts.emptyToDisableSmtpAuth }}</FormInfo>
+					<FormSwitch v-model="smtpSecure" class="_formBlock">
+						<template #label>{{ $ts.smtpSecure }}</template>
+						<template #caption>{{ $ts.smtpSecureInfo }}</template>
+					</FormSwitch>
+				</FormSection>
+			</template>
+		</div>
 	</FormSuspense>
-</FormBase>
+</MkSpacer>
 </template>
 
 <script lang="ts">
 import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInput from '@/components/form/input.vue';
+import FormInfo from '@/components/ui/info.vue';
+import FormSuspense from '@/components/form/suspense.vue';
+import FormSplit from '@/components/form/split.vue';
+import FormSection from '@/components/form/section.vue';
 import * as os from '@/os';
 import * as symbols from '@/symbols';
 import { fetchInstance } from '@/instance';
@@ -53,9 +58,8 @@ export default defineComponent({
 	components: {
 		FormSwitch,
 		FormInput,
-		FormBase,
-		FormGroup,
-		FormButton,
+		FormSplit,
+		FormSection,
 		FormInfo,
 		FormSuspense,
 	},
@@ -68,6 +72,16 @@ export default defineComponent({
 				title: this.$ts.emailServer,
 				icon: 'fas fa-envelope',
 				bg: 'var(--bg)',
+				actions: [{
+					asFullButton: true,
+					text: this.$ts.testEmail,
+					handler: this.testEmail,
+				}, {
+					asFullButton: true,
+					icon: 'fas fa-check',
+					text: this.$ts.save,
+					handler: this.save,
+				}],
 			},
 			enableEmail: false,
 			email: null,
diff --git a/packages/client/src/pages/admin/emojis.vue b/packages/client/src/pages/admin/emojis.vue
index 49277325a0..df5d234d6f 100644
--- a/packages/client/src/pages/admin/emojis.vue
+++ b/packages/client/src/pages/admin/emojis.vue
@@ -23,7 +23,7 @@
 		</div>
 
 		<div v-else-if="tab === 'remote'" class="remote">
-			<div class="_inputSplit">
+			<FormSplit>
 				<MkInput v-model="queryRemote" :debounce="true" type="search">
 					<template #prefix><i class="fas fa-search"></i></template>
 					<template #label>{{ $ts.search }}</template>
@@ -31,7 +31,7 @@
 				<MkInput v-model="host" :debounce="true">
 					<template #label>{{ $ts.host }}</template>
 				</MkInput>
-			</div>
+			</FormSplit>
 			<MkPagination ref="remoteEmojis" :pagination="remotePagination">
 				<template #empty><span>{{ $ts.noCustomEmojis }}</span></template>
 				<template v-slot="{items}">
@@ -57,6 +57,7 @@ import MkButton from '@/components/ui/button.vue';
 import MkInput from '@/components/form/input.vue';
 import MkPagination from '@/components/ui/pagination.vue';
 import MkTab from '@/components/tab.vue';
+import FormSplit from '@/components/form/split.vue';
 import { selectFiles } from '@/scripts/select-file';
 import * as os from '@/os';
 import * as symbols from '@/symbols';
@@ -67,6 +68,7 @@ export default defineComponent({
 		MkButton,
 		MkInput,
 		MkPagination,
+		FormSplit,
 	},
 
 	emits: ['info'],
diff --git a/packages/client/src/pages/admin/files-settings.vue b/packages/client/src/pages/admin/files-settings.vue
index df25bd0fb2..2ac81843f0 100644
--- a/packages/client/src/pages/admin/files-settings.vue
+++ b/packages/client/src/pages/admin/files-settings.vue
@@ -1,41 +1,41 @@
 <template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
 	<FormSuspense :p="init">
-		<FormSwitch v-model="cacheRemoteFiles">
-			{{ $ts.cacheRemoteFiles }}
-			<template #desc>{{ $ts.cacheRemoteFilesDescription }}</template>
-		</FormSwitch>
+		<div class="_formRoot">
+			<FormSwitch v-model="cacheRemoteFiles" class="_formBlock">
+				<template #label>{{ $ts.cacheRemoteFiles }}</template>
+				<template #caption>{{ $ts.cacheRemoteFilesDescription }}</template>
+			</FormSwitch>
 
-		<FormSwitch v-model="proxyRemoteFiles">
-			{{ $ts.proxyRemoteFiles }}
-			<template #desc>{{ $ts.proxyRemoteFilesDescription }}</template>
-		</FormSwitch>
+			<FormSwitch v-model="proxyRemoteFiles" class="_formBlock">
+				<template #label>{{ $ts.proxyRemoteFiles }}</template>
+				<template #caption>{{ $ts.proxyRemoteFilesDescription }}</template>
+			</FormSwitch>
 
-		<FormInput v-model="localDriveCapacityMb" type="number">
-			<span>{{ $ts.driveCapacityPerLocalAccount }}</span>
-			<template #suffix>MB</template>
-			<template #desc>{{ $ts.inMb }}</template>
-		</FormInput>
+			<FormSplit :min-width="280">
+				<FormInput v-model="localDriveCapacityMb" type="number" class="_formBlock">
+					<template #label>{{ $ts.driveCapacityPerLocalAccount }}</template>
+					<template #suffix>MB</template>
+					<template #caption>{{ $ts.inMb }}</template>
+				</FormInput>
 
-		<FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">
-			<span>{{ $ts.driveCapacityPerRemoteAccount }}</span>
-			<template #suffix>MB</template>
-			<template #desc>{{ $ts.inMb }}</template>
-		</FormInput>
-
-		<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+				<FormInput v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles" class="_formBlock">
+					<template #label>{{ $ts.driveCapacityPerRemoteAccount }}</template>
+					<template #suffix>MB</template>
+					<template #caption>{{ $ts.inMb }}</template>
+				</FormInput>
+			</FormSplit>
+		</div>
 	</FormSuspense>
-</FormBase>
+</MkSpacer>
 </template>
 
 <script lang="ts">
 import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInput from '@/components/form/input.vue';
+import FormSuspense from '@/components/form/suspense.vue';
+import FormSplit from '@/components/form/split.vue';
 import * as os from '@/os';
 import * as symbols from '@/symbols';
 import { fetchInstance } from '@/instance';
@@ -44,9 +44,7 @@ export default defineComponent({
 	components: {
 		FormSwitch,
 		FormInput,
-		FormBase,
-		FormGroup,
-		FormButton,
+		FormSplit,
 		FormSuspense,
 	},
 
@@ -58,6 +56,12 @@ export default defineComponent({
 				title: this.$ts.files,
 				icon: 'fas fa-cloud',
 				bg: 'var(--bg)',
+				actions: [{
+					asFullButton: true,
+					icon: 'fas fa-check',
+					text: this.$ts.save,
+					handler: this.save,
+				}],
 			},
 			cacheRemoteFiles: false,
 			proxyRemoteFiles: false,
diff --git a/packages/client/src/pages/admin/index.vue b/packages/client/src/pages/admin/index.vue
index e363d1bd03..e66a2f6c01 100644
--- a/packages/client/src/pages/admin/index.vue
+++ b/packages/client/src/pages/admin/index.vue
@@ -3,7 +3,7 @@
 	<div v-if="!narrow || page == null" class="nav">
 		<MkHeader :info="header"></MkHeader>
 	
-		<MkSpacer :content-max="700">
+		<MkSpacer :content-max="700" :margin-min="16">
 			<div class="lxpfedzu">
 				<div class="banner">
 					<img :src="$instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
diff --git a/packages/client/src/pages/admin/object-storage.vue b/packages/client/src/pages/admin/object-storage.vue
index 8984686b5e..0f74865b10 100644
--- a/packages/client/src/pages/admin/object-storage.vue
+++ b/packages/client/src/pages/admin/object-storage.vue
@@ -1,76 +1,78 @@
 <template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
 	<FormSuspense :p="init">
-		<FormSwitch v-model="useObjectStorage">{{ $ts.useObjectStorage }}</FormSwitch>
+		<div class="_formRoot">
+			<FormSwitch v-model="useObjectStorage" class="_formBlock">{{ $ts.useObjectStorage }}</FormSwitch>
 
-		<template v-if="useObjectStorage">
-			<FormInput v-model="objectStorageBaseUrl">
-				<span>{{ $ts.objectStorageBaseUrl }}</span>
-				<template #desc>{{ $ts.objectStorageBaseUrlDesc }}</template>
-			</FormInput>
+			<template v-if="useObjectStorage">
+				<FormInput v-model="objectStorageBaseUrl" class="_formBlock">
+					<template #label>{{ $ts.objectStorageBaseUrl }}</template>
+					<template #caption>{{ $ts.objectStorageBaseUrlDesc }}</template>
+				</FormInput>
 
-			<FormInput v-model="objectStorageBucket">
-				<span>{{ $ts.objectStorageBucket }}</span>
-				<template #desc>{{ $ts.objectStorageBucketDesc }}</template>
-			</FormInput>
+				<FormInput v-model="objectStorageBucket" class="_formBlock">
+					<template #label>{{ $ts.objectStorageBucket }}</template>
+					<template #caption>{{ $ts.objectStorageBucketDesc }}</template>
+				</FormInput>
 
-			<FormInput v-model="objectStoragePrefix">
-				<span>{{ $ts.objectStoragePrefix }}</span>
-				<template #desc>{{ $ts.objectStoragePrefixDesc }}</template>
-			</FormInput>
+				<FormInput v-model="objectStoragePrefix" class="_formBlock">
+					<template #label>{{ $ts.objectStoragePrefix }}</template>
+					<template #caption>{{ $ts.objectStoragePrefixDesc }}</template>
+				</FormInput>
 
-			<FormInput v-model="objectStorageEndpoint">
-				<span>{{ $ts.objectStorageEndpoint }}</span>
-				<template #desc>{{ $ts.objectStorageEndpointDesc }}</template>
-			</FormInput>
+				<FormInput v-model="objectStorageEndpoint" class="_formBlock">
+					<template #label>{{ $ts.objectStorageEndpoint }}</template>
+					<template #caption>{{ $ts.objectStorageEndpointDesc }}</template>
+				</FormInput>
 
-			<FormInput v-model="objectStorageRegion">
-				<span>{{ $ts.objectStorageRegion }}</span>
-				<template #desc>{{ $ts.objectStorageRegionDesc }}</template>
-			</FormInput>
+				<FormInput v-model="objectStorageRegion" class="_formBlock">
+					<template #label>{{ $ts.objectStorageRegion }}</template>
+					<template #caption>{{ $ts.objectStorageRegionDesc }}</template>
+				</FormInput>
 
-			<FormInput v-model="objectStorageAccessKey">
-				<template #prefix><i class="fas fa-key"></i></template>
-				<span>Access key</span>
-			</FormInput>
+				<FormSplit :min-width="280">
+					<FormInput v-model="objectStorageAccessKey" class="_formBlock">
+						<template #prefix><i class="fas fa-key"></i></template>
+						<template #label>Access key</template>
+					</FormInput>
 
-			<FormInput v-model="objectStorageSecretKey">
-				<template #prefix><i class="fas fa-key"></i></template>
-				<span>Secret key</span>
-			</FormInput>
+					<FormInput v-model="objectStorageSecretKey" class="_formBlock">
+						<template #prefix><i class="fas fa-key"></i></template>
+						<template #label>Secret key</template>
+					</FormInput>
+				</FormSplit>
 
-			<FormSwitch v-model="objectStorageUseSSL">
-				{{ $ts.objectStorageUseSSL }}
-				<template #desc>{{ $ts.objectStorageUseSSLDesc }}</template>
-			</FormSwitch>
+				<FormSwitch v-model="objectStorageUseSSL" class="_formBlock">
+					<template #label>{{ $ts.objectStorageUseSSL }}</template>
+					<template #caption>{{ $ts.objectStorageUseSSLDesc }}</template>
+				</FormSwitch>
 
-			<FormSwitch v-model="objectStorageUseProxy">
-				{{ $ts.objectStorageUseProxy }}
-				<template #desc>{{ $ts.objectStorageUseProxyDesc }}</template>
-			</FormSwitch>
+				<FormSwitch v-model="objectStorageUseProxy" class="_formBlock">
+					<template #label>{{ $ts.objectStorageUseProxy }}</template>
+					<template #caption>{{ $ts.objectStorageUseProxyDesc }}</template>
+				</FormSwitch>
 
-			<FormSwitch v-model="objectStorageSetPublicRead">
-				{{ $ts.objectStorageSetPublicRead }}
-			</FormSwitch>
+				<FormSwitch v-model="objectStorageSetPublicRead" class="_formBlock">
+					<template #label>{{ $ts.objectStorageSetPublicRead }}</template>
+				</FormSwitch>
 
-			<FormSwitch v-model="objectStorageS3ForcePathStyle">
-				s3ForcePathStyle
-			</FormSwitch>
-		</template>
-
-		<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+				<FormSwitch v-model="objectStorageS3ForcePathStyle" class="_formBlock">
+					<template #label>s3ForcePathStyle</template>
+				</FormSwitch>
+			</template>
+		</div>
 	</FormSuspense>
-</FormBase>
+</MkSpacer>
 </template>
 
 <script lang="ts">
 import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInput from '@/components/form/input.vue';
+import FormGroup from '@/components/form/group.vue';
+import FormSuspense from '@/components/form/suspense.vue';
+import FormSplit from '@/components/form/split.vue';
+import FormSection from '@/components/form/section.vue';
 import * as os from '@/os';
 import * as symbols from '@/symbols';
 import { fetchInstance } from '@/instance';
@@ -79,10 +81,10 @@ export default defineComponent({
 	components: {
 		FormSwitch,
 		FormInput,
-		FormBase,
 		FormGroup,
-		FormButton,
 		FormSuspense,
+		FormSplit,
+		FormSection,
 	},
 
 	emits: ['info'],
@@ -93,6 +95,12 @@ export default defineComponent({
 				title: this.$ts.objectStorage,
 				icon: 'fas fa-cloud',
 				bg: 'var(--bg)',
+				actions: [{
+					asFullButton: true,
+					icon: 'fas fa-check',
+					text: this.$ts.save,
+					handler: this.save,
+				}],
 			},
 			useObjectStorage: false,
 			objectStorageBaseUrl: null,
diff --git a/packages/client/src/pages/admin/security.vue b/packages/client/src/pages/admin/security.vue
index adfb2e786c..ae0eaf2572 100644
--- a/packages/client/src/pages/admin/security.vue
+++ b/packages/client/src/pages/admin/security.vue
@@ -1,31 +1,35 @@
 <template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
 	<FormSuspense :p="init">
-		<FormLink to="/admin/bot-protection">
-			<i class="fas fa-shield-alt"></i> {{ $ts.botProtection }}
-			<template v-if="enableHcaptcha" #suffix>hCaptcha</template>
-			<template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template>
-			<template v-else #suffix>{{ $ts.none }} ({{ $ts.notRecommended }})</template>
-		</FormLink>
+		<div class="_formRoot">
+			<FormSection>
+				<FormSwitch v-model="enableRegistration" class="_formBlock">
+					<template #label>{{ $ts.enableRegistration }}</template>
+				</FormSwitch>
 
-		<FormSwitch v-model="enableRegistration">{{ $ts.enableRegistration }}</FormSwitch>
+				<FormSwitch v-model="emailRequiredForSignup" class="_formBlock">
+					<template #label>{{ $ts.emailRequiredForSignup }}</template>
+				</FormSwitch>
+			</FormSection>
 
-		<FormSwitch v-model="emailRequiredForSignup">{{ $ts.emailRequiredForSignup }}</FormSwitch>
-
-		<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+			<FormLink to="/admin/bot-protection" class="_formBlock">
+				<i class="fas fa-shield-alt"></i> {{ $ts.botProtection }}
+				<template v-if="enableHcaptcha" #suffix>hCaptcha</template>
+				<template v-else-if="enableRecaptcha" #suffix>reCAPTCHA</template>
+				<template v-else #suffix>{{ $ts.none }} ({{ $ts.notRecommended }})</template>
+			</FormLink>
+		</div>
 	</FormSuspense>
-</FormBase>
+</MkSpacer>
 </template>
 
 <script lang="ts">
 import { defineAsyncComponent, defineComponent } from 'vue';
-import FormLink from '@/components/debobigego/link.vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormLink from '@/components/form/link.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInfo from '@/components/ui/info.vue';
+import FormSuspense from '@/components/form/suspense.vue';
+import FormSection from '@/components/form/section.vue';
 import * as os from '@/os';
 import * as symbols from '@/symbols';
 import { fetchInstance } from '@/instance';
@@ -34,10 +38,8 @@ export default defineComponent({
 	components: {
 		FormLink,
 		FormSwitch,
-		FormBase,
-		FormGroup,
-		FormButton,
 		FormInfo,
+		FormSection,
 		FormSuspense,
 	},
 
@@ -49,6 +51,12 @@ export default defineComponent({
 				title: this.$ts.security,
 				icon: 'fas fa-lock',
 				bg: 'var(--bg)',
+				actions: [{
+					asFullButton: true,
+					icon: 'fas fa-check',
+					text: this.$ts.save,
+					handler: this.save,
+				}],
 			},
 			enableHcaptcha: false,
 			enableRecaptcha: false,
diff --git a/packages/client/src/pages/admin/settings.vue b/packages/client/src/pages/admin/settings.vue
index d88445abdb..78c2616051 100644
--- a/packages/client/src/pages/admin/settings.vue
+++ b/packages/client/src/pages/admin/settings.vue
@@ -1,72 +1,75 @@
 <template>
-<FormBase>
+<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
 	<FormSuspense :p="init">
-		<FormInput v-model="name">
-			<span>{{ $ts.instanceName }}</span>
-		</FormInput>
+		<div class="_formRoot">
+			<FormInput v-model="name" class="_formBlock">
+				<template #label>{{ $ts.instanceName }}</template>
+			</FormInput>
 
-		<FormTextarea v-model="description">
-			<span>{{ $ts.instanceDescription }}</span>
-		</FormTextarea>
+			<FormTextarea v-model="description" class="_formBlock">
+				<template #label>{{ $ts.instanceDescription }}</template>
+			</FormTextarea>
 
-		<FormInput v-model="iconUrl">
-			<template #prefix><i class="fas fa-link"></i></template>
-			<span>{{ $ts.iconUrl }}</span>
-		</FormInput>
+			<FormInput v-model="iconUrl" class="_formBlock">
+				<template #prefix><i class="fas fa-link"></i></template>
+				<template #label>{{ $ts.iconUrl }}</template>
+			</FormInput>
 
-		<FormInput v-model="bannerUrl">
-			<template #prefix><i class="fas fa-link"></i></template>
-			<span>{{ $ts.bannerUrl }}</span>
-		</FormInput>
+			<FormInput v-model="bannerUrl" class="_formBlock">
+				<template #prefix><i class="fas fa-link"></i></template>
+				<template #label>{{ $ts.bannerUrl }}</template>
+			</FormInput>
 
-		<FormInput v-model="backgroundImageUrl">
-			<template #prefix><i class="fas fa-link"></i></template>
-			<span>{{ $ts.backgroundImageUrl }}</span>
-		</FormInput>
+			<FormInput v-model="backgroundImageUrl" class="_formBlock">
+				<template #prefix><i class="fas fa-link"></i></template>
+				<template #label>{{ $ts.backgroundImageUrl }}</template>
+			</FormInput>
 
-		<FormInput v-model="tosUrl">
-			<template #prefix><i class="fas fa-link"></i></template>
-			<span>{{ $ts.tosUrl }}</span>
-		</FormInput>
+			<FormInput v-model="tosUrl" class="_formBlock">
+				<template #prefix><i class="fas fa-link"></i></template>
+				<template #label>{{ $ts.tosUrl }}</template>
+			</FormInput>
 
-		<FormInput v-model="maintainerName">
-			<span>{{ $ts.maintainerName }}</span>
-		</FormInput>
+			<FormSplit :min-width="300">
+				<FormInput v-model="maintainerName" class="_formBlock">
+					<template #label>{{ $ts.maintainerName }}</template>
+				</FormInput>
 
-		<FormInput v-model="maintainerEmail" type="email">
-			<template #prefix><i class="fas fa-envelope"></i></template>
-			<span>{{ $ts.maintainerEmail }}</span>
-		</FormInput>
+				<FormInput v-model="maintainerEmail" type="email" class="_formBlock">
+					<template #prefix><i class="fas fa-envelope"></i></template>
+					<template #label>{{ $ts.maintainerEmail }}</template>
+				</FormInput>
+			</FormSplit>
 
-		<FormTextarea v-model="pinnedUsers">
-			<span>{{ $ts.pinnedUsers }}</span>
-			<template #desc>{{ $ts.pinnedUsersDescription }}</template>
-		</FormTextarea>
+			<FormTextarea v-model="pinnedUsers" class="_formBlock">
+				<template #label>{{ $ts.pinnedUsers }}</template>
+				<template #caption>{{ $ts.pinnedUsersDescription }}</template>
+			</FormTextarea>
 
-		<FormInput v-model="maxNoteTextLength" type="number">
-			<template #prefix><i class="fas fa-pencil-alt"></i></template>
-			<span>{{ $ts.maxNoteTextLength }}</span>
-		</FormInput>
+			<FormInput v-model="maxNoteTextLength" type="number" class="_formBlock">
+				<template #prefix><i class="fas fa-pencil-alt"></i></template>
+				<template #label>{{ $ts.maxNoteTextLength }}</template>
+			</FormInput>
 
-		<FormSwitch v-model="enableLocalTimeline">{{ $ts.enableLocalTimeline }}</FormSwitch>
-		<FormSwitch v-model="enableGlobalTimeline">{{ $ts.enableGlobalTimeline }}</FormSwitch>
-		<FormInfo>{{ $ts.disablingTimelinesInfo }}</FormInfo>
-
-		<FormButton primary @click="save"><i class="fas fa-save"></i> {{ $ts.save }}</FormButton>
+			<FormSection>
+				<FormSwitch v-model="enableLocalTimeline" class="_formBlock">{{ $ts.enableLocalTimeline }}</FormSwitch>
+				<FormSwitch v-model="enableGlobalTimeline" class="_formBlock">{{ $ts.enableGlobalTimeline }}</FormSwitch>
+				<FormInfo class="_formBlock">{{ $ts.disablingTimelinesInfo }}</FormInfo>
+			</FormSection>
+		</div>
 	</FormSuspense>
-</FormBase>
+</MkSpacer>
 </template>
 
 <script lang="ts">
 import { defineComponent } from 'vue';
-import FormSwitch from '@/components/debobigego/switch.vue';
-import FormInput from '@/components/debobigego/input.vue';
-import FormButton from '@/components/debobigego/button.vue';
-import FormBase from '@/components/debobigego/base.vue';
-import FormGroup from '@/components/debobigego/group.vue';
-import FormTextarea from '@/components/debobigego/textarea.vue';
-import FormInfo from '@/components/debobigego/info.vue';
-import FormSuspense from '@/components/debobigego/suspense.vue';
+import FormSwitch from '@/components/form/switch.vue';
+import FormInput from '@/components/form/input.vue';
+import FormTextarea from '@/components/form/textarea.vue';
+import FormInfo from '@/components/ui/info.vue';
+import FormSection from '@/components/form/section.vue';
+import FormSplit from '@/components/form/split.vue';
+import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os';
 import * as symbols from '@/symbols';
 import { fetchInstance } from '@/instance';
@@ -75,12 +78,11 @@ export default defineComponent({
 	components: {
 		FormSwitch,
 		FormInput,
-		FormBase,
-		FormGroup,
-		FormButton,
+		FormSuspense,
 		FormTextarea,
 		FormInfo,
-		FormSuspense,
+		FormSection,
+		FormSplit,
 	},
 
 	emits: ['info'],
@@ -91,6 +93,12 @@ export default defineComponent({
 				title: this.$ts.general,
 				icon: 'fas fa-cog',
 				bg: 'var(--bg)',
+				actions: [{
+					asFullButton: true,
+					icon: 'fas fa-check',
+					text: this.$ts.save,
+					handler: this.save,
+				}],
 			},
 			name: null,
 			description: null,
diff --git a/packages/client/src/pages/federation.vue b/packages/client/src/pages/federation.vue
index 4e5f428ff9..a467c5eeb8 100644
--- a/packages/client/src/pages/federation.vue
+++ b/packages/client/src/pages/federation.vue
@@ -6,7 +6,7 @@
 				<template #prefix><i class="fas fa-search"></i></template>
 				<template #label>{{ $ts.host }}</template>
 			</MkInput>
-			<div class="_inputSplit" style="margin-top: var(--margin);">
+			<FormSplit style="margin-top: var(--margin);">
 				<MkSelect v-model="state">
 					<template #label>{{ $ts.state }}</template>
 					<option value="all">{{ $ts.all }}</option>
@@ -38,7 +38,7 @@
 					<option value="+driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.descendingOrder }})</option>
 					<option value="-driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.ascendingOrder }})</option>
 				</MkSelect>
-			</div>
+			</FormSplit>
 		</div>
 
 		<MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination">
@@ -101,6 +101,7 @@ import MkButton from '@/components/ui/button.vue';
 import MkInput from '@/components/form/input.vue';
 import MkSelect from '@/components/form/select.vue';
 import MkPagination from '@/components/ui/pagination.vue';
+import FormSplit from '@/components/form/split.vue';
 import * as os from '@/os';
 import * as symbols from '@/symbols';
 
@@ -110,6 +111,7 @@ export default defineComponent({
 		MkInput,
 		MkSelect,
 		MkPagination,
+		FormSplit,
 	},
 
 	emits: ['info'],
diff --git a/packages/client/src/pages/settings/drive.vue b/packages/client/src/pages/settings/drive.vue
index 9ab99c6efe..c123159b61 100644
--- a/packages/client/src/pages/settings/drive.vue
+++ b/packages/client/src/pages/settings/drive.vue
@@ -5,7 +5,7 @@
 		<div class="_formBlock uawsfosz">
 			<div class="meter"><div :style="meterStyle"></div></div>
 		</div>
-		<div class="_inputSplit _formBlock">
+		<FormSplit>
 			<MkKeyValue class="_formBlock">
 				<template #key>{{ $ts.capacity }}</template>
 				<template #value>{{ bytes(capacity, 1) }}</template>
@@ -14,7 +14,7 @@
 				<template #key>{{ $ts.inUse }}</template>
 				<template #value>{{ bytes(usage, 1) }}</template>
 			</MkKeyValue>
-		</div>
+		</FormSplit>
 	</FormSection>
 
 	<FormSection>
@@ -38,6 +38,7 @@ import * as tinycolor from 'tinycolor2';
 import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
 import MkKeyValue from '@/components/key-value.vue';
+import FormSplit from '@/components/form/split.vue';
 import * as os from '@/os';
 import bytes from '@/filters/bytes';
 import * as symbols from '@/symbols';
@@ -49,6 +50,7 @@ export default defineComponent({
 		FormLink,
 		FormSection,
 		MkKeyValue,
+		FormSplit,
 	},
 
 	emits: ['info'],
diff --git a/packages/client/src/style.scss b/packages/client/src/style.scss
index 181521b4f5..b95a5c3950 100644
--- a/packages/client/src/style.scss
+++ b/packages/client/src/style.scss
@@ -386,16 +386,6 @@ hr {
 	backdrop-filter: var(--blur, blur(15px));
 }
 
-._inputSplit {
-	display: grid;
-	grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
-	grid-gap: 12px;
-
-	> * {
-		margin: 0 !important;
-	}
-}
-
 ._formBlock {
 	margin: 1.5em 0;
 }
diff --git a/packages/client/webpack.config.js b/packages/client/webpack.config.js
index 7bcfdcb15d..a50851e17f 100644
--- a/packages/client/webpack.config.js
+++ b/packages/client/webpack.config.js
@@ -47,6 +47,7 @@ module.exports = {
 				loader: 'vue-loader',
 				options: {
 					cssSourceMap: false,
+					reactivityTransform: true,
 					compilerOptions: {
 						preserveWhitespace: false
 					}
diff --git a/packages/client/yarn.lock b/packages/client/yarn.lock
index 4f666c252b..10607feb27 100644
--- a/packages/client/yarn.lock
+++ b/packages/client/yarn.lock
@@ -6162,10 +6162,10 @@ vue-eslint-parser@^8.0.1:
     lodash "^4.17.21"
     semver "^7.3.5"
 
-vue-loader@16.8.3:
-  version "16.8.3"
-  resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-16.8.3.tgz#d43e675def5ba9345d6c7f05914c13d861997087"
-  integrity sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==
+vue-loader@17.0.0:
+  version "17.0.0"
+  resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-17.0.0.tgz#2eaa80aab125b19f00faa794b5bd867b17f85acb"
+  integrity sha512-OWSXjrzIvbF2LtOUmxT3HYgwwubbfFelN8PAP9R9dwpIkj48TVioHhWWSx7W7fk+iF5cgg3CBJRxwTdtLU4Ecg==
   dependencies:
     chalk "^4.1.0"
     hash-sum "^2.0.0"