diff --git a/locales/en-US.yml b/locales/en-US.yml
index 6538c43ff..3f04adb67 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -1284,6 +1284,8 @@ noteOfThisUser: "Notes by this user"
clipNoteLimitExceeded: "No more notes can be added to this clip."
timeTravel: "Time Travel"
timeTravelDescription: "Show posts before this date."
+undoPostForm: "Undo"
+clearPostForm: "Clear"
pariPlusInfo: "Pari Plus! customized Misskey~!"
pariPlusSystemSettings: "Pari Plus! system settings"
pariPlusNoteSettings: "Pari Plus! note settings"
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index aca759dcb..435372f6a 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -90,6 +90,7 @@ SPDX-License-Identifier: AGPL-3.0-only
+
@@ -260,6 +261,44 @@ const canPost = computed((): boolean => {
const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags'));
const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags'));
+const textHistory = ref([]);
+const currentHistoryIndex = ref(-1);
+const showTextManageButton = computed(() => text.value !== '' || currentHistoryIndex.value >= 0);
+const textManageButtonIcon = computed(() => {
+ if (currentHistoryIndex.value >= 0) return 'ti ti-arrow-back-up';
+ return text.value !== '' ? 'ti ti-trash' : '';
+});
+
+function clearText() {
+ if (text.value !== '') {
+ saveToHistory();
+ text.value = '';
+ nextTick(() => textareaEl.value && autosize.update(textareaEl.value));
+ }
+}
+
+function saveToHistory() {
+ textHistory.value = textHistory.value.slice(0, currentHistoryIndex.value + 1);
+ textHistory.value.push(text.value);
+ currentHistoryIndex.value = textHistory.value.length - 1;
+}
+
+function undoTextChange() {
+ if (currentHistoryIndex.value >= 0) {
+ text.value = textHistory.value[currentHistoryIndex.value];
+ currentHistoryIndex.value--;
+ nextTick(() => textareaEl.value && autosize.update(textareaEl.value));
+ }
+}
+
+function handleTextManageClick() {
+ if (currentHistoryIndex.value >= 0) {
+ undoTextChange();
+ } else {
+ clearText();
+ }
+}
+
watch(text, () => {
checkMissingMention();
}, { immediate: true });
@@ -576,6 +615,10 @@ function clear() {
function onKeydown(ev: KeyboardEvent) {
if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post();
+ if (!ev.ctrlKey && !ev.metaKey && !ev.altKey && !justEndedComposition.value && !ev.isComposing) {
+ saveToHistory();
+ }
+
// justEndedComposition.value is for Safari, which keyDown occurs after compositionend.
// ev.isComposing is for another browsers.
if (ev.key === 'Escape' && !justEndedComposition.value && !ev.isComposing) emit('esc');