Skip to content

feat: allow editing the last user message#703

Open
hankkyy wants to merge 1 commit into
fathah:mainfrom
hankkyy:feat/edit-sent-message
Open

feat: allow editing the last user message#703
hankkyy wants to merge 1 commit into
fathah:mainfrom
hankkyy:feat/edit-sent-message

Conversation

@hankkyy

@hankkyy hankkyy commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Feature

Allow editing the last user message and regenerating the agent response. An edit button (pencil icon) appears on hover over the last user message. Clicking it turns the message into an inline textarea. Enter saves and resends, Escape cancels.

Changes

  • MessageRow: onEditMessage prop, edit state with auto-resize textarea, edit button in hover-visible .chat-bubble-actions
  • MessageList: pass onEditMessage through
  • Chat: handleEditMessage updates messages state and resends via handleSendRef
  • main.css: .chat-bubble position:relative, .chat-bubble-actions hover reveal, .chat-bubble-edit-btn, .chat-bubble-edit textarea
  • i18n: editMessage added to all 10 locales

Add an edit button (pencil icon) to the last user message bubble that
appears on hover. Clicking it switches the message to an inline
textarea. Enter saves and resends the edited content to get a new
agent response. Escape cancels without changes.

Changes:
- MessageRow: add onEditMessage prop, edit state with auto-resize
  textarea, edit button in .chat-bubble-actions (hover-visible)
- MessageList: pass onEditMessage through to MessageRow
- Chat: handleEditMessage updates the message in state and resends
- main.css: .chat-bubble gets position:relative, .chat-bubble-actions
  (hover reveal), .chat-bubble-edit-btn, .chat-bubble-edit textarea
- i18n: add editMessage string to all 10 locales
@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds inline editing of the last user message: a hover-revealed pencil button turns the bubble into an auto-resizing textarea, with Enter to commit and Escape to cancel, wired through MessageList to a new handleEditMessage callback in Chat. i18n keys are added for all 10 locales and the CSS additions are clean.

  • Edit button never visible in normal use: canEdit inside MessageRow requires isLast (the message is the last visible message overall). In a completed conversation the last visible message is always the agent's response, so isLast is never true on a user row and the pencil button never renders. A separate "is this the last user bubble?" signal needs to be computed in MessageList and passed down.
  • Duplicate message on commit: handleEditMessage patches the existing message with setMessages and then calls handleSend, which unconditionally calls pushUser and appends a second, identical user message. The history passed to the gateway also reads messagesRef.current before the state update is flushed, so the agent receives the original unedited content as context.

Confidence Score: 2/5

The core editing feature does not work as described: the edit button will not appear during a normal completed conversation, and when triggered it would corrupt the message list with a duplicate entry.

Two independent defects affect the central user-facing path: the visibility condition is wrong so the button is never shown in normal use, and the send handler appends a duplicate user message instead of replacing the existing one. The CSS and i18n changes are solid, but the feature itself is broken end-to-end.

Chat.tsx (handleEditMessage logic) and MessageList.tsx (isLast vs last-user-message propagation) both need attention before this is functional.

Important Files Changed

Filename Overview
src/renderer/src/screens/Chat/Chat.tsx Adds handleEditMessage which patches the message in-place then calls handleSend — this appends a duplicate user message and sends stale history to the agent.
src/renderer/src/screens/Chat/MessageList.tsx Threads onEditMessage through to MessageRow but passes the generic isLast prop, so canEdit is never true for a user message when a subsequent agent response is present.
src/renderer/src/screens/Chat/MessageRow.tsx Adds inline edit state, textarea, auto-resize, and the pencil button — logic is mostly correct locally, but canEdit relies on isLast which prevents the button from appearing in normal use; also missing final newline.
src/renderer/src/assets/main.css Adds position:relative to .chat-bubble and new styles for .chat-bubble-actions, .chat-bubble-edit-btn, and .chat-bubble-edit textarea — clean and self-contained.
src/shared/i18n/locales/en/chat.ts Adds editMessage key to all 10 locales; translations look correct and consistent.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant U as User
    participant MR as MessageRow
    participant Chat as Chat.tsx
    participant HS as handleSend (useChatActions)
    participant API as hermesAPI

    Note over U,API: Current (buggy) flow
    U->>MR: clicks pencil, edits, presses Enter
    MR->>Chat: onEditMessage(msgId, newContent)
    Chat->>Chat: setMessages — patch msg.content in-place
    Chat->>HS: handleSendRef.current(newContent, [])
    HS->>HS: pushUser(newContent) — APPENDS duplicate user msg
    HS->>API: sendMessage with stale messagesRef.current

    Note over U,API: Expected flow
    U->>MR: clicks pencil, edits, presses Enter
    MR->>Chat: onEditMessage(msgId, newContent)
    Chat->>Chat: patch msg.content + truncate subsequent messages
    Chat->>API: sendToAgent(newContent, updatedHistory)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant U as User
    participant MR as MessageRow
    participant Chat as Chat.tsx
    participant HS as handleSend (useChatActions)
    participant API as hermesAPI

    Note over U,API: Current (buggy) flow
    U->>MR: clicks pencil, edits, presses Enter
    MR->>Chat: onEditMessage(msgId, newContent)
    Chat->>Chat: setMessages — patch msg.content in-place
    Chat->>HS: handleSendRef.current(newContent, [])
    HS->>HS: pushUser(newContent) — APPENDS duplicate user msg
    HS->>API: sendMessage with stale messagesRef.current

    Note over U,API: Expected flow
    U->>MR: clicks pencil, edits, presses Enter
    MR->>Chat: onEditMessage(msgId, newContent)
    Chat->>Chat: patch msg.content + truncate subsequent messages
    Chat->>API: sendToAgent(newContent, updatedHistory)
Loading

Comments Outside Diff (1)

  1. src/renderer/src/screens/Chat/MessageList.tsx, line 155-161 (link)

    P1 Edit button never shows in a completed conversation

    isLast is i === visibleMessages.length - 1, so canEdit inside MessageRow is only true when the user message happens to be the very last visible message. In a normal completed conversation the last visible message is always the agent's response, meaning isLast is false on every user row and the edit button is never rendered.

    The fix requires computing "is this the last user bubble?" independently of "is this the last message overall?" — e.g. derive a lastUserMsgId in MessageList and pass a separate isLastUserMessage prop, or pass that id down to MessageRow.

Reviews (1): Last reviewed commit: "feat: allow editing the last user messag..." | Re-trigger Greptile

Comment on lines +471 to +482
// Edit the last user message and resend
const handleEditMessage = useCallback(
(msgId: string, newContent: string) => {
setMessages((prev) =>
prev.map((m) =>
m.id === msgId ? { ...m, content: newContent } : m,
),
);
void handleSendRef.current(newContent, []);
},
[],
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Edit creates a duplicate user message and sends stale history

handleEditMessage does two things in sequence: it patches the existing message via setMessages, then immediately calls handleSendRef.current(newContent, []). handleSend unconditionally calls pushUser, which appends a brand-new user message. The result is that after committing an edit the conversation contains both the in-place updated message and a duplicate user message with identical content.

On top of that, sendToAgent reads messagesRef.current (a ref that is only synced after the next render via useEffect), so the history passed to the gateway is the snapshot from before the edit — the agent still sees the original, unedited user message in its context.

The intended behaviour (edit in-place then regenerate) requires truncating the conversation to just before the agent response that followed the edited message and using a dedicated send path that does not call pushUser.

Comment on lines 248 to +251
)}
</div>
);
});
}); No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing newline at end of file — the diff shows \ No newline at end of file, which will produce noisy future diffs and may trigger linter warnings.

Suggested change
)}
</div>
);
});
});
</div>
);
});

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant