fix(editor): stabilize context menu selection sync

This commit is contained in:
clawdbot 2026-01-28 17:34:03 +08:00
parent ebf89a7b79
commit e111184acc
2 changed files with 13 additions and 2 deletions

View File

@ -40,6 +40,12 @@ function isPrimaryButton(e: React.PointerEvent | PointerEvent): boolean {
return (e as PointerEvent).buttons === 1 || (e as PointerEvent).button === 0; return (e as PointerEvent).buttons === 1 || (e as PointerEvent).button === 0;
} }
function selectionKeyOf(ids: string[]): string {
// Keep stable regardless of selection order.
// This avoids false mismatches between context-menu state and reducer state.
return [...ids].sort().join('|');
}
export function Canvas(props: CanvasProps) { export function Canvas(props: CanvasProps) {
const ref = useRef<HTMLDivElement | null>(null); const ref = useRef<HTMLDivElement | null>(null);
const [box, setBox] = useState<{ x1: number; y1: number; x2: number; y2: number } | null>(null); const [box, setBox] = useState<{ x1: number; y1: number; x2: number; y2: number } | null>(null);
@ -148,7 +154,7 @@ export function Canvas(props: CanvasProps) {
? [...props.selectionIds, targetId] ? [...props.selectionIds, targetId]
: [targetId] : [targetId]
: props.selectionIds; : props.selectionIds;
const selectionKey = nextSelectionIds.join('|'); const selectionKey = selectionKeyOf(nextSelectionIds);
if (targetId) { if (targetId) {
if (!props.selectionIds.includes(targetId)) { if (!props.selectionIds.includes(targetId)) {

View File

@ -86,6 +86,11 @@ export function EditorApp() {
const closeContextMenu = useCallback(() => setCtxMenu(null), []); const closeContextMenu = useCallback(() => setCtxMenu(null), []);
const selectionKeyOf = useCallback((ids: string[]) => {
// Keep stable regardless of ordering.
return [...ids].sort().join('|');
}, []);
const ctxMenuSyncedRef = useRef(false); const ctxMenuSyncedRef = useRef(false);
useEffect(() => { useEffect(() => {
@ -99,7 +104,7 @@ export function EditorApp() {
useEffect(() => { useEffect(() => {
if (!ctxMenu) return; if (!ctxMenu) return;
const currentKey = state.selection.ids.join('|'); const currentKey = selectionKeyOf(state.selection.ids);
// Wait until reducer state has caught up with the context menu's intended selection. // Wait until reducer state has caught up with the context menu's intended selection.
if (!ctxMenuSyncedRef.current) { if (!ctxMenuSyncedRef.current) {