From 914e503698f8cffaa4d3e97b8751364d498e5c94 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 09:52:53 +0000 Subject: [PATCH 1/2] feat(ui): add ability to edit user message when reverting Implement `onEditUserMessage` in `ChatView` to allow users to modify a message's text and revert the conversation to that specific point. This updates the composer draft and resets the cursor state to the end of the text. - Add `onEditUserMessage` handler to `ChatView` - Update `MessagesTimeline` to include an edit button (SquarePenIcon) next to the revert button - Update corresponding types and test mocks for `MessagesTimeline` --- apps/web/src/components/ChatView.tsx | 24 ++++++++++++ .../chat/MessagesTimeline.browser.tsx | 1 + .../components/chat/MessagesTimeline.test.tsx | 1 + .../src/components/chat/MessagesTimeline.tsx | 37 ++++++++++++++----- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 40cd1b4210..c7a09e33b3 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -133,6 +133,7 @@ import { } from "../composerDraftStore"; import { appendTerminalContextsToPrompt, + deriveDisplayedUserMessageState, formatTerminalContextLabel, type TerminalContextDraft, type TerminalContextSelection, @@ -3271,6 +3272,28 @@ export default function ChatView(props: ChatViewProps) { void onRevertToTurnCountRef.current(targetTurnCount); }, []); + const onEditUserMessage = useCallback( + async (messageId: MessageId, text: string) => { + const targetTurnCount = revertTurnCountRef.current.get(messageId); + if (typeof targetTurnCount !== "number") { + return; + } + + const displayedMessage = deriveDisplayedUserMessageState(text); + const draftText = displayedMessage.visibleText || text; + setComposerDraftPrompt(composerDraftTarget, draftText); + promptRef.current = draftText; + composerRef.current?.resetCursorState({ + cursor: draftText.length, + prompt: draftText, + detectTrigger: true, + }); + composerRef.current?.focusAtEnd(); + await onRevertToTurnCountRef.current(targetTurnCount); + }, + [composerDraftTarget, composerRef, setComposerDraftPrompt], + ); + // Empty state: no active thread if (!activeThread) { return ; @@ -3349,6 +3372,7 @@ export default function ChatView(props: ChatViewProps) { onOpenTurnDiff={onOpenTurnDiff} revertTurnCountByUserMessageId={revertTurnCountByUserMessageId} onRevertUserMessage={onRevertUserMessage} + onEditUserMessage={onEditUserMessage} isRevertingCheckpoint={isRevertingCheckpoint} onImageExpand={onExpandTimelineImage} markdownCwd={gitCwd ?? undefined} diff --git a/apps/web/src/components/chat/MessagesTimeline.browser.tsx b/apps/web/src/components/chat/MessagesTimeline.browser.tsx index 0eb5c8a1fc..efd52aa604 100644 --- a/apps/web/src/components/chat/MessagesTimeline.browser.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.browser.tsx @@ -62,6 +62,7 @@ function buildProps() { onOpenTurnDiff: vi.fn(), revertTurnCountByUserMessageId: new Map(), onRevertUserMessage: vi.fn(), + onEditUserMessage: vi.fn(), isRevertingCheckpoint: false, onImageExpand: vi.fn(), activeThreadEnvironmentId: EnvironmentId.make("environment-local"), diff --git a/apps/web/src/components/chat/MessagesTimeline.test.tsx b/apps/web/src/components/chat/MessagesTimeline.test.tsx index a8a53831a2..45c3f6cf12 100644 --- a/apps/web/src/components/chat/MessagesTimeline.test.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.test.tsx @@ -88,6 +88,7 @@ function buildProps() { onOpenTurnDiff: () => {}, revertTurnCountByUserMessageId: new Map(), onRevertUserMessage: () => {}, + onEditUserMessage: () => {}, isRevertingCheckpoint: false, onImageExpand: () => {}, activeThreadEnvironmentId: ACTIVE_THREAD_ENVIRONMENT_ID, diff --git a/apps/web/src/components/chat/MessagesTimeline.tsx b/apps/web/src/components/chat/MessagesTimeline.tsx index e4b683592e..314ebdd3b4 100644 --- a/apps/web/src/components/chat/MessagesTimeline.tsx +++ b/apps/web/src/components/chat/MessagesTimeline.tsx @@ -82,6 +82,7 @@ interface TimelineRowSharedState { workspaceRoot: string | undefined; activeThreadEnvironmentId: EnvironmentId; onRevertUserMessage: (messageId: MessageId) => void; + onEditUserMessage: (messageId: MessageId, text: string) => void; onImageExpand: (preview: ExpandedImagePreview) => void; onOpenTurnDiff: (turnId: TurnId, filePath?: string) => void; } @@ -106,6 +107,7 @@ interface MessagesTimelineProps { onOpenTurnDiff: (turnId: TurnId, filePath?: string) => void; revertTurnCountByUserMessageId: Map; onRevertUserMessage: (messageId: MessageId) => void; + onEditUserMessage: (messageId: MessageId, text: string) => void; isRevertingCheckpoint: boolean; onImageExpand: (preview: ExpandedImagePreview) => void; activeThreadEnvironmentId: EnvironmentId; @@ -134,6 +136,7 @@ export const MessagesTimeline = memo(function MessagesTimeline({ onOpenTurnDiff, revertTurnCountByUserMessageId, onRevertUserMessage, + onEditUserMessage, isRevertingCheckpoint, onImageExpand, activeThreadEnvironmentId, @@ -205,6 +208,7 @@ export const MessagesTimeline = memo(function MessagesTimeline({ workspaceRoot, activeThreadEnvironmentId, onRevertUserMessage, + onEditUserMessage, onImageExpand, onOpenTurnDiff, }), @@ -221,6 +225,7 @@ export const MessagesTimeline = memo(function MessagesTimeline({ workspaceRoot, activeThreadEnvironmentId, onRevertUserMessage, + onEditUserMessage, onImageExpand, onOpenTurnDiff, ], @@ -355,16 +360,28 @@ function TimelineRowContent({ row }: { row: TimelineRow }) { )} {canRevertAgentWork && ( - + <> + + + )}

From aadce42d8c2477aee05d3e142432b34547fbcc62 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 11:41:53 +0000 Subject: [PATCH 2/2] wip --- apps/web/src/components/ChatView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index c7a09e33b3..98ab7ce9ff 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -3281,6 +3281,7 @@ export default function ChatView(props: ChatViewProps) { const displayedMessage = deriveDisplayedUserMessageState(text); const draftText = displayedMessage.visibleText || text; + clearComposerDraftContent(composerDraftTarget); setComposerDraftPrompt(composerDraftTarget, draftText); promptRef.current = draftText; composerRef.current?.resetCursorState({ @@ -3291,7 +3292,7 @@ export default function ChatView(props: ChatViewProps) { composerRef.current?.focusAtEnd(); await onRevertToTurnCountRef.current(targetTurnCount); }, - [composerDraftTarget, composerRef, setComposerDraftPrompt], + [clearComposerDraftContent, composerDraftTarget, composerRef, setComposerDraftPrompt], ); // Empty state: no active thread