From 849bc7694cd40db89e5ea56cc86e2305844572f2 Mon Sep 17 00:00:00 2001 From: 7w1 Date: Fri, 20 Mar 2026 21:22:15 -0500 Subject: [PATCH 1/5] reimplement up arrow to edit --- .changeset/fix-up-to-edit.md | 5 +++++ src/app/features/room/RoomInput.tsx | 12 +++++++++++- src/app/features/room/RoomTimeline.tsx | 19 +++++++++++++++++++ src/app/features/room/RoomView.tsx | 3 +++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-up-to-edit.md diff --git a/.changeset/fix-up-to-edit.md b/.changeset/fix-up-to-edit.md new file mode 100644 index 000000000..171a83d7c --- /dev/null +++ b/.changeset/fix-up-to-edit.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix up arrow to edit messages not editing messages. diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index f6a0f61c3..f43375c6a 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -231,9 +231,10 @@ interface RoomInputProps { roomId: string; room: Room; threadRootId?: string; + onEditLastMessage?: () => void; } export const RoomInput = forwardRef( - ({ editor, fileDropContainerRef, roomId, room, threadRootId }, ref) => { + ({ editor, fileDropContainerRef, roomId, room, threadRootId, onEditLastMessage }, ref) => { // When in thread mode, isolate drafts by thread root ID so thread replies // don't clobber the main room draft (and vice versa). const draftKey = threadRootId ?? roomId; @@ -891,6 +892,15 @@ export const RoomInput = forwardRef( } } + if (isKeyHotkey('arrowup', evt) && isEmptyEditor(editor)) { + const { selection } = editor; + if (selection && Editor.isStart(editor, selection.anchor, [])) { + evt.preventDefault(); + onEditLastMessage?.(); + return; + } + } + if ( (isKeyHotkey('mod+enter', evt) || (!enterForNewline && isKeyHotkey('enter', evt))) && !isComposing(evt) diff --git a/src/app/features/room/RoomTimeline.tsx b/src/app/features/room/RoomTimeline.tsx index 7b2badc4b..79f43775f 100644 --- a/src/app/features/room/RoomTimeline.tsx +++ b/src/app/features/room/RoomTimeline.tsx @@ -109,6 +109,7 @@ export type RoomTimelineProps = { eventId?: string; editor: Editor; onEditorReset?: () => void; + onEditLastMessageRef?: React.MutableRefObject<(() => void) | undefined>; }; export function RoomTimeline({ @@ -116,6 +117,7 @@ export function RoomTimeline({ eventId, editor, onEditorReset, + onEditLastMessageRef, }: Readonly) { const mx = useMatrixClient(); const alive = useAlive(); @@ -660,6 +662,23 @@ export function RoomTimeline({ processedEventsRef.current = processedEvents; + useEffect(() => { + if (!onEditLastMessageRef) return; + const ref = onEditLastMessageRef; + ref.current = () => { + const myUserId = mx.getUserId(); + const found = [...processedEventsRef.current] + .reverse() + .find( + (e) => + e.mEvent.getSender() === myUserId && + e.mEvent.getType() === 'm.room.message' && + !e.mEvent.isRedacted() + ); + if (found?.mEvent.getId()) actions.handleEdit(found.mEvent.getId()); + }; + }, [onEditLastMessageRef, mx, actions]); + useEffect(() => { const v = vListRef.current; if (!v) return; diff --git a/src/app/features/room/RoomView.tsx b/src/app/features/room/RoomView.tsx index 933bf9e36..421561616 100644 --- a/src/app/features/room/RoomView.tsx +++ b/src/app/features/room/RoomView.tsx @@ -73,6 +73,7 @@ const shouldFocusMessageField = (evt: KeyboardEvent): boolean => { export function RoomView({ eventId }: { eventId?: string }) { const roomInputRef = useRef(null); const roomViewRef = useRef(null); + const editLastMessageRef = useRef<(() => void) | undefined>(); const [hideReads] = useSetting(settingsAtom, 'hideReads'); const screenSize = useScreenSizeContext(); @@ -161,6 +162,7 @@ export function RoomView({ eventId }: { eventId?: string }) { eventId={eventId} editor={editor} onEditorReset={handleResetEditor} + onEditLastMessageRef={editLastMessageRef} /> @@ -186,6 +188,7 @@ export function RoomView({ eventId }: { eventId?: string }) { roomId={roomId} fileDropContainerRef={roomViewRef} ref={roomInputRef} + onEditLastMessage={() => editLastMessageRef.current?.()} /> )} {!canMessage && ( From 0b795e1c4fddb8cce1a6357b16f593dff777294b Mon Sep 17 00:00:00 2001 From: 7w1 Date: Fri, 20 Mar 2026 21:26:29 -0500 Subject: [PATCH 2/5] fix extra padding in message editor for text messages --- .changeset/fix-extra-space-editor.md | 5 +++++ src/app/features/room/message/MessageEditor.tsx | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-extra-space-editor.md diff --git a/.changeset/fix-extra-space-editor.md b/.changeset/fix-extra-space-editor.md new file mode 100644 index 000000000..9733baef3 --- /dev/null +++ b/.changeset/fix-extra-space-editor.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix extra spacing in message editor. diff --git a/src/app/features/room/message/MessageEditor.tsx b/src/app/features/room/message/MessageEditor.tsx index cb516c1db..b9c8f5bc5 100644 --- a/src/app/features/room/message/MessageEditor.tsx +++ b/src/app/features/room/message/MessageEditor.tsx @@ -351,7 +351,16 @@ export const MessageEditor = as<'div', MessageEditorProps>( Date: Fri, 20 Mar 2026 21:30:54 -0500 Subject: [PATCH 3/5] Fix message composer clearing when edited messages are saved. --- .changeset/fix-editor-clearing.md | 5 +++++ src/app/hooks/timeline/useTimelineActions.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-editor-clearing.md diff --git a/.changeset/fix-editor-clearing.md b/.changeset/fix-editor-clearing.md new file mode 100644 index 000000000..1f63ed9fc --- /dev/null +++ b/.changeset/fix-editor-clearing.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix message composer clearing when edited messages are saved. diff --git a/src/app/hooks/timeline/useTimelineActions.ts b/src/app/hooks/timeline/useTimelineActions.ts index 3a90fa438..e7bb45f68 100644 --- a/src/app/hooks/timeline/useTimelineActions.ts +++ b/src/app/hooks/timeline/useTimelineActions.ts @@ -5,7 +5,7 @@ import { ReactEditor } from 'slate-react'; import { getMxIdLocalPart, toggleReaction } from '$utils/matrix'; import { getMemberDisplayName, getEditedEvent } from '$utils/room'; -import { createMentionElement, moveCursor } from '$components/editor'; +import { createMentionElement, isEmptyEditor, moveCursor } from '$components/editor'; export interface UseTimelineActionsOptions { room: Room; @@ -216,10 +216,10 @@ export function useTimelineActions({ return; } setEditId(undefined); - onEditorReset?.(); requestAnimationFrame(() => { if (!alive()) return; + if (isEmptyEditor(editor)) onEditorReset?.(); ReactEditor.focus(editor); moveCursor(editor); }); From b1d6d833e59c491fcba57401df45f7e3bc66d6f9 Mon Sep 17 00:00:00 2001 From: 7w1 Date: Fri, 20 Mar 2026 21:31:30 -0500 Subject: [PATCH 4/5] linter fix --- src/app/features/room/RoomInput.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index f43375c6a..ea1808f70 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -932,6 +932,8 @@ export const RoomInput = forwardRef( autocompleteQuery, isComposing, showAudioRecorder, + editor, + onEditLastMessage, ] ); From 37a66c42a608944b02d176d6b7a9b06584d3b80c Mon Sep 17 00:00:00 2001 From: 7w1 Date: Fri, 20 Mar 2026 21:56:22 -0500 Subject: [PATCH 5/5] fix editor flowing off screen --- .changeset/fix-editor-fullwidth.md | 5 +++++ src/app/features/room/message/Message.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-editor-fullwidth.md diff --git a/.changeset/fix-editor-fullwidth.md b/.changeset/fix-editor-fullwidth.md new file mode 100644 index 000000000..e7299c34c --- /dev/null +++ b/.changeset/fix-editor-fullwidth.md @@ -0,0 +1,5 @@ +--- +default: patch +--- + +Fix editor flowing off screen when editing large messages in compact and bubble layouts. diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index 909b0ab0b..f21b77bfe 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -683,7 +683,7 @@ function MessageInternal(