From fb8e0c6e14955ae5740cbc33ca5d6ca8440d79a1 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Fri, 10 Jul 2026 22:34:24 -0400 Subject: [PATCH] fix(threads): enable slash commands in the thread composer The thread composer already showed the /command autocomplete (RoomInput.tsx:954 was never gated), but the interpreter was disabled (:523), so /me, /shrug, /invite, etc. sent literally in threads - a confusing inconsistency and the other half of the threads "v1" limitation. Remove the thread gate: content-transform commands (/me, /notice, /shrug, /tableflip, /unflip) flow into the normal send path, which already routes to the thread via threadRootId; the rest are room-level actions. No command sends a mis-routed timeline message (verified against useCommands). Scheduling stays disabled in threads for now. Co-Authored-By: Claude Opus 4.8 --- LOTUS_FEATURES.md | 2 +- src/app/features/room/RoomInput.tsx | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/LOTUS_FEATURES.md b/LOTUS_FEATURES.md index 187f6a365..81e00d127 100644 --- a/LOTUS_FEATURES.md +++ b/LOTUS_FEATURES.md @@ -900,7 +900,7 @@ Root messages in the main timeline show a **"N replies · time"** chip (server-a ### Thread Composer -The panel embeds the full composer (uploads, emoji, stickers, GIFs, voice, location, polls) with drafts, reply state, and upload queues **isolated per thread** (`roomId::threadRootId` keys). Replies-to-replies produce spec-correct `m.thread` + `m.in_reply_to` (`is_falling_back: false`). Scheduling and slash commands are disabled inside threads (v1). +The panel embeds the full composer (uploads, emoji, stickers, GIFs, voice, location, polls) with drafts, reply state, and upload queues **isolated per thread** (`roomId::threadRootId` keys). Replies-to-replies produce spec-correct `m.thread` + `m.in_reply_to` (`is_falling_back: false`). **Slash commands work in threads** — content-transform commands (`/me`, `/notice`, `/shrug`, `/tableflip`, `/unflip`) route into the thread via the normal send path, and room-level commands (`/invite`, `/kick`, …) act on the room; this also matches the command autocomplete, which was already shown in the thread composer. Scheduling is still disabled inside threads (v1). **↑ to edit last reply**: pressing Up-arrow in the empty thread composer opens the editor on your most recent editable reply *in that thread* — parity with the main timeline. The thread composer carries a distinct `editableName="ThreadInput"` so the main timeline's global up-arrow handler and the thread's no longer cross-fire (previously the thread composer had the same name, so Up-arrow there wrongly targeted the main timeline's last message). diff --git a/src/app/features/room/RoomInput.tsx b/src/app/features/room/RoomInput.tsx index 050875d0b..1e929b066 100644 --- a/src/app/features/room/RoomInput.tsx +++ b/src/app/features/room/RoomInput.tsx @@ -518,9 +518,12 @@ export const RoomInput = forwardRef( const submit = useCallback(() => { uploadBoardHandlers.current?.handleSend(); - // Slash-command interpretation is disabled in thread mode (v1): "/foo" - // sends literally rather than being parsed as a command. - const commandName = threadRootId ? undefined : getBeginCommand(editor); + // Slash commands work in threads too: content-transform commands (/me, + // /notice, /shrug, /tableflip, /unflip) flow into the normal send below, + // which routes to the thread via `threadRootId`; the rest (/invite, /kick, + // …) are room-level actions. This also matches the command autocomplete, + // which is already shown in the thread composer. + const commandName = getBeginCommand(editor); let plainText = toPlainText(editor.children, isMarkdown).trim(); let customHtml = trimCustomHtml( toMatrixCustomHTML(editor.children, {