Skip to content

feat(session-ui): move user message actions into a menu - #60208

Merged
arvsrn merged 2 commits into
devfrom
message-footer-menu
Oct 29, 2026
Merged

arvsrn merged 2 commits into
devfrom
message-footer-menu

Conversation

@arvsrn

@arvsrn arvsrn commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

Summary

Tidies the footers under timeline messages.

Under user messages

  • Only two buttons: Rewind to here and a new More actions (…) menu.
  • The menu shows when the message was sent, then Fork from here and Copy message (plus Copy full message on voice messages).
  • Fork is disabled while a message is pending; Copy is disabled when the message has no text.
  • The relative time ("5m ago") is gone from the footer, since the menu now shows it. Agent and model labels stay.
  • The steer icon next to "Pending" is removed.

Under agent responses

  • The copy icon is 14px inside the same 20px button, with a 2px negative start margin so it lines up with the reply text.
  • The gap between the copy button and the labels is 4px (was 8px).

Both footers

  • Labels use 12px text with an 18px line height and a single faint colour; the separators and model variant are no longer dimmed further.
  • 8px above the footer (was 4px).

Tests

Updated the component and e2e tests that checked the old timestamp, the steer icon, the 0.66 opacity and the "Copy message" button. The timestamp test now covers the menu: the sent time, disabled items, and the footer staying visible while the menu is open.

  • bun run check passes.
  • session-message-projection.spec.ts and session-timeline-mobile.spec.ts pass, repeated 5× at 2 workers.
  • In built mode, timeline-geometry, session-queue, timeline-history, session-timeline-accessibility and btw-sidebar pass, and so do the touched cases in timeline-stream and session-header. Two other cases in those files fail locally on macOS and fail the same way on dev, which matches the known macOS shortcut issue in the e2e notes.

Visual

<svg xmlns="http://www.w3.org/2000/svg" width="240" height="72" viewBox="0 0 240 72" fill="none">
  <style>
    .menu { opacity: 0; transform-origin: 214px 40px; animation: open 4s ease-out infinite; }
    .dots { animation: press 4s ease-out infinite; }
    @keyframes open { 0%, 30% { opacity: 0; transform: scale(.94); } 38%, 80% { opacity: 1; transform: scale(1); } 88%, 100% { opacity: 0; transform: scale(.94); } }
    @keyframes press { 0%, 28% { opacity: .35; } 32%, 82% { opacity: .9; } 90%, 100% { opacity: .35; } }
    @media (prefers-reduced-motion: reduce) { .menu, .dots { animation: none; } .menu { opacity: 1; } }
  </style>
  <rect x="96" y="6" width="136" height="18" rx="9" fill="currentColor" opacity=".12"/>
  <rect x="106" y="13" width="96" height="4" rx="2" fill="currentColor" opacity=".45"/>
  <rect x="130" y="31" width="22" height="3" rx="1.5" fill="currentColor" opacity=".3"/>
  <circle cx="157" cy="32.5" r="1" fill="currentColor" opacity=".3"/>
  <rect x="162" y="31" width="30" height="3" rx="1.5" fill="currentColor" opacity=".3"/>
  <path d="M203 29.5a3.5 3.5 0 1 1-1 2.5m0-3v3h3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" opacity=".35"/>
  <g class="dots" fill="currentColor"><circle cx="219" cy="32.5" r="1.1"/><circle cx="223" cy="32.5" r="1.1"/><circle cx="227" cy="32.5" r="1.1"/></g>
  <g class="menu">
    <rect x="150" y="40" width="82" height="30" rx="5" fill="currentColor" opacity=".08"/>
    <rect x="150.5" y="40.5" width="81" height="29" rx="4.5" stroke="currentColor" opacity=".18"/>
    <rect x="157" y="45" width="40" height="3" rx="1.5" fill="currentColor" opacity=".3"/>
    <rect x="157" y="53" width="52" height="3" rx="1.5" fill="currentColor" opacity=".6"/>
    <rect x="157" y="61" width="44" height="3" rx="1.5" fill="currentColor" opacity=".6"/>
  </g>
</svg>

Added by the opencode bot (AI-generated).

Public API: openapi.json adds maximum 9007199254740991 to the integer fields times, remainingRuns and runCount (spec regeneration, backwards compatible)

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Works as described. I mounted the prompt message story on the base and on this branch and opened the new … menu: the menu shows the sent time, Fork and Copy, and Rewind stays in the footer. The two touched component test files pass (14/14), and tsc -b passes for session-ui. Public API unchanged: this is a UI-only change, plus one new English i18n key (ui.message.moreActions).

Worth addressing:

  • No copy feedback. Copy message and Copy full message from the menu now give no sign that they worked, or that the clipboard write failed. Before, the button changed to a check and "Copied", and the response copy button still does (see inline).
  • Dead code. packages/session-ui/src/message/relative-time.ts and its test are no longer used anywhere outside that test. The ui.message.time.justNow, minutesAgo, hoursAgo and daysAgo keys are also unused now, in every locale. They can be removed in this PR.

Before

After

<Menu.Item disabled={!props.actions?.fork || pending() || state.forking} onSelect={() => void fork()}>
{i18n.t("ui.message.forkMessage")}
</Menu.Item>
<Menu.Item disabled={!props.text} onSelect={() => void writeClipboard(props.text)}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copying from the menu now gives no feedback. The menu closes, and the user can't tell whether writeClipboard succeeded, which matters most when it returns false. The old button switched to a check and "Copied" for 2s, and the response copy button still does. Could the result be surfaced somehow, for example a toast, or a brief check on the … trigger?

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The new commit only regenerates packages/protocol/openapi.json. check:generated fails on dev (the committed spec is already stale there) and passes with this commit.

Public API: the OpenAPI spec adds maximum: 9007199254740991 to the integer fields times, remainingRuns and runCount. It's a documentation-only tightening to JavaScript's largest safe integer and backwards compatible; the server's behaviour doesn't change.

The earlier points are not addressed yet.

@arvsrn
arvsrn force-pushed the message-footer-menu branch from e43a034 to db8b40d Compare October 29, 2026 13:39

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This push only drops the OpenAPI regeneration commit. openapi.json matches dev again, so the PR no longer touches the public API, and the UI code is unchanged since my first review. Neither earlier point is addressed yet.

@arvsrn
arvsrn force-pushed the message-footer-menu branch from db8b40d to 8d62ee1 Compare October 29, 2026 14:05

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This push only rebases the branch onto newer dev (which includes #60228); the PR's own diff is unchanged, and the touched component tests still pass (14/14). Public API unchanged. The earlier points are not addressed yet.

@arvsrn
arvsrn enabled auto-merge (squash) October 29, 2026 14:35

@opencodejr opencodejr Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The new commit only resizes the response copy icon to 14px and shifts it 2px to line up with the reply text; I see no problems, and the touched component tests pass (14/14). Public API unchanged. The earlier copy-feedback point is not addressed yet.

@arvsrn
arvsrn merged commit 589c62d into dev Oct 29, 2026
22 checks passed
@arvsrn
arvsrn deleted the message-footer-menu branch October 29, 2026 14:39
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