fix: Undo/redo events duplicated (#12525)

* fix: Undo/redo events duplicated

* fix: Guard history use
Prevent cross polination of editors

* Remove unused check
This commit is contained in:
Tom Moor
2026-05-29 20:04:11 -04:00
committed by GitHub
parent 370934bb0e
commit 03fe74710c
8 changed files with 74 additions and 3 deletions
+5
View File
@@ -2,6 +2,7 @@ import { useCallback, useMemo, useState } from "react";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import * as Toolbar from "@radix-ui/react-toolbar";
import { closeHistory } from "@shared/editor/lib/closeHistory";
import type { MenuItem } from "@shared/editor/types";
import { hideScrollbars, s } from "@shared/styles";
import { TooltipProvider } from "~/components/TooltipContext";
@@ -54,11 +55,13 @@ function ToolbarDropdown(props: ToolbarDropdownProps) {
}
if (commands[menuItem.name]) {
closeHistory(view);
commands[menuItem.name](
typeof menuItem.attrs === "function"
? menuItem.attrs(state)
: menuItem.attrs
);
closeHistory(view);
} else if (menuItem.onClick) {
menuItem.onClick();
}
@@ -158,9 +161,11 @@ function ToolbarMenu(props: Props) {
}
// otherwise, run the associated editor command
closeHistory(view);
commands[item.name](
typeof item.attrs === "function" ? item.attrs(state) : item.attrs
);
closeHistory(view);
};
return (
+10
View File
@@ -7,6 +7,8 @@ import {
yUndoPlugin,
undo,
redo,
undoCommand,
redoCommand,
} from "y-prosemirror";
import * as Y from "yjs";
import Extension from "@shared/editor/lib/Extension";
@@ -136,4 +138,12 @@ export default class Multiplayer extends Extension<MultiplayerOptions> {
redo: () => redo,
};
}
keys() {
return {
"Mod-z": undoCommand,
"Mod-y": redoCommand,
"Shift-Mod-z": redoCommand,
};
}
}
@@ -27,6 +27,7 @@ import type { Editor as TEditor } from "~/editor";
import type { Properties } from "~/types";
import { useLocationSidebarContext } from "~/hooks/useLocationSidebarContext";
import useStores from "~/hooks/useStores";
import isTextInput from "~/utils/isTextInput";
import { client } from "~/utils/ApiClient";
import { emojiToUrl } from "~/utils/emoji";
import { documentHistoryPath, documentEditPath } from "~/utils/routeHelpers";
@@ -151,6 +152,17 @@ function DocumentScene({
const onUndoRedo = useCallback(
(event: KeyboardEvent) => {
if (isModKey(event)) {
const target =
event.target instanceof Element ? event.target : undefined;
// The editor handles undo/redo through its own keymap when focused
if (
editorRef.current?.view?.hasFocus() ||
(target && (isTextInput(target) || !!target.closest(".ProseMirror")))
) {
return;
}
event.preventDefault();
if (event.shiftKey) {
@@ -15,6 +15,7 @@ import { toast } from "sonner";
import { IndexeddbPersistence } from "y-indexeddb";
import * as Y from "yjs";
import { EditorUpdateError } from "@shared/collaboration/CloseEvents";
import History from "@shared/editor/extensions/History";
import EDITOR_VERSION from "@shared/editor/version";
import { supportsPassiveListener } from "@shared/utils/browser";
import type { Props as EditorProps } from "~/components/Editor";
@@ -256,8 +257,12 @@ function MultiplayerEditor(
return props.extensions;
}
// The Yjs undo manager (added by the Multiplayer extension below) is the
// sole source of undo/redo history when collaborating.
return [
...(props.extensions || []),
...(props.extensions || []).filter(
(extension) => extension !== History && !(extension instanceof History)
),
new MultiplayerExtension({
user,
provider: remoteProvider,