diff --git a/locales/en-US.yml b/locales/en-US.yml
index fa2a0b11ce..08fcc490ea 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -2239,4 +2239,5 @@ autocorrectNoteLanguage: "Show a warning if the post language does not match the
 incorrectLanguageWarning: "It looks like your post is in {detected}, but you selected
   {current}.\nWould you like to set the language to {detected} instead?"
 noteEditHistory: "Post edit history"
+slashQuote: "Chain quote"
 foldNotification: "Group similar notifications"
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index 7642fc80db..2b326c4066 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -2066,4 +2066,5 @@ autocorrectNoteLanguage: 当帖子语言不符合自动检测的结果的时候
 incorrectLanguageWarning: "看上去您帖子使用的语言是{detected},但您选择的语言是{current}。\n要改为以{detected}发帖吗?"
 noteEditHistory: "帖子编辑历史"
 media: 媒体
+slashQuote: "斜杠引用"
 foldNotification: "将通知按同类型分组"
diff --git a/packages/client/src/components/MkPostForm.vue b/packages/client/src/components/MkPostForm.vue
index 434d10afb9..78cb6350f7 100644
--- a/packages/client/src/components/MkPostForm.vue
+++ b/packages/client/src/components/MkPostForm.vue
@@ -373,6 +373,11 @@ const props = withDefaults(
 		autofocus?: boolean;
 		showMfmCheatSheet?: boolean;
 		editId?: entities.Note["id"];
+		selectRange?: [
+			start: number,
+			end: number,
+			direction?: "forward" | "backward" | "none",
+		];
 	}>(),
 	{
 		initialVisibleUsers: () => [],
@@ -692,10 +697,14 @@ function togglePoll() {
 function focus() {
 	if (textareaEl.value) {
 		textareaEl.value.focus();
-		textareaEl.value.setSelectionRange(
-			textareaEl.value.value.length,
-			textareaEl.value.value.length,
-		);
+		if (props.selectRange) {
+			textareaEl.value.setSelectionRange(...props.selectRange);
+		} else {
+			textareaEl.value.setSelectionRange(
+				textareaEl.value.value.length,
+				textareaEl.value.value.length,
+			);
+		}
 	}
 }
 
diff --git a/packages/client/src/components/MkPostFormDialog.vue b/packages/client/src/components/MkPostFormDialog.vue
index fafb4afdd4..a80daf0431 100644
--- a/packages/client/src/components/MkPostFormDialog.vue
+++ b/packages/client/src/components/MkPostFormDialog.vue
@@ -44,6 +44,11 @@ const props = defineProps<{
 	fixed?: boolean;
 	autofocus?: boolean;
 	editId?: entities.Note["id"];
+	selectRange?: [
+		start: number,
+		end: number,
+		direction?: "forward" | "backward" | "none",
+	];
 }>();
 
 const emit = defineEmits<{
diff --git a/packages/client/src/components/MkQuoteButton.vue b/packages/client/src/components/MkQuoteButton.vue
index f4004b0cdb..2617d3655e 100644
--- a/packages/client/src/components/MkQuoteButton.vue
+++ b/packages/client/src/components/MkQuoteButton.vue
@@ -1,5 +1,6 @@
 <template>
 	<button
+		ref="el"
 		v-if="canRenote && defaultStore.state.seperateRenoteQuote"
 		v-tooltip.noDelay.bottom="i18n.ts.quote"
 		class="eddddedb _button"
@@ -10,8 +11,8 @@
 </template>
 
 <script lang="ts" setup>
-import { computed } from "vue";
-import type { entities } from "firefish-js";
+import { computed, ref } from "vue";
+import { acct, type entities } from "firefish-js";
 import { pleaseLogin } from "@/scripts/please-login";
 import * as os from "@/os";
 import { me } from "@/me";
@@ -23,6 +24,8 @@ const props = defineProps<{
 	note: entities.Note;
 }>();
 
+const el = ref<HTMLButtonElement>();
+
 const canRenote = computed(
 	() =>
 		["public", "home"].includes(props.note.visibility) ||
@@ -31,10 +34,57 @@ const canRenote = computed(
 
 function quote(): void {
 	pleaseLogin();
+	if (
+		props.note.renote != null &&
+		(props.note.text != null ||
+			props.note.fileIds.length === 0 ||
+			props.note.poll != null)
+	) {
+		menu();
+	} else {
+		normalQuote();
+	}
+}
+
+function normalQuote(): void {
 	os.post({
 		renote: props.note,
 	});
 }
+
+function slashQuote(): void {
+	os.post({
+		initialText: ` // @${acct.toString(props.note.user)}: ${props.note.text}`,
+		selectRange: [0, 0],
+		renote: props.note.renote,
+		channel: props.note.channel,
+	});
+}
+
+function menu(viaKeyboard = false): void {
+	os.popupMenu(
+		[
+			{
+				text: i18n.ts.quote,
+				icon: `${icon("ph-quotes")}`,
+				action: normalQuote,
+			},
+			{
+				text: i18n.ts.slashQuote,
+				icon: `${icon("ph-notches")}`,
+				action: slashQuote,
+			},
+		],
+		el.value,
+		{
+			viaKeyboard,
+		},
+	).then(focus);
+}
+
+function focus(): void {
+	el.value!.focus();
+}
 </script>
 
 <style lang="scss" scoped>
diff --git a/packages/client/src/components/MkRenoteButton.vue b/packages/client/src/components/MkRenoteButton.vue
index 3925dc8c5a..53c1994e65 100644
--- a/packages/client/src/components/MkRenoteButton.vue
+++ b/packages/client/src/components/MkRenoteButton.vue
@@ -22,7 +22,7 @@
 
 <script lang="ts" setup>
 import { computed, ref } from "vue";
-import type { entities } from "firefish-js";
+import { acct, type entities } from "firefish-js";
 import Ripple from "@/components/MkRipple.vue";
 import XDetails from "@/components/MkUsersTooltip.vue";
 import { pleaseLogin } from "@/scripts/please-login";
@@ -223,6 +223,28 @@ const renote = (viaKeyboard = false, ev?: MouseEvent) => {
 				});
 			},
 		});
+		if (
+			props.note.renote != null &&
+			(props.note.text != null ||
+				props.note.fileIds.length === 0 ||
+				props.note.poll != null)
+		) {
+			buttonActions.push({
+				text: i18n.ts.slashQuote,
+				icon: `${icon("ph-notches")}`,
+				danger: false,
+				action: () => {
+					os.post({
+						initialText: ` // @${acct.toString(props.note.user)}: ${
+							props.note.text
+						}`,
+						selectRange: [0, 0],
+						renote: props.note.renote,
+						channel: props.note.channel,
+					});
+				},
+			});
+		}
 	}
 
 	if (hasRenotedBefore.value) {