authorgravatar for saphid@gmail.comAlex Southwell <saphid@gmail.com> 2026-09-22 08:33:28+10:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-21 19:33:28-03:00
logd2a90b921a5843654a58448d0bc59828e1e3ccc4
tree4167859185068ff27c54a54e3fe84ca0ba7ac633
parent2d377bfb1c8c85a61679fa6cdb6e8181cfb13626
signature Signed by PGP key B5690EEEBB952194

fix(web): name message copy actions accurately (#12865)


3 files changed, 42 insertions(+), 3 deletions(-)

apps/web/src/components/chat/MessageCopyButton.tsx+2-2
......@@ -37,7 +37,7 @@ export const MessageCopyButton = memo(function MessageCopyButton({
3737 <TooltipTrigger
3838 render={
3939 <Button
40 aria-label="Copy link"
40 aria-label="Copy message"
4141 disabled={isCopied}
4242 onClick={() => copyToClipboard(text)}
4343 ref={ref}
......@@ -51,7 +51,7 @@ export const MessageCopyButton = memo(function MessageCopyButton({
5151 {isCopied ? <CheckIcon className="size-3 text-primary" /> : <CopyIcon className="size-3" />}
5252 </TooltipTrigger>
5353 <TooltipPopup>
54 <p>Copy to clipboard</p>
54 <p>Copy message</p>
5555 </TooltipPopup>
5656 </Tooltip>
5757 );
apps/web/src/components/chat/MessagesTimeline.test.tsx+1-1
......@@ -1320,7 +1320,7 @@ describe("MessagesTimeline", () => {
13201320 />,
13211321 );
13221322
1323 expect(markup).toContain('aria-label="Copy link"');
1323 expect(markup).toContain('aria-label="Copy message"');
13241324 expect(markup).toContain('data-user-message-collapsed="true"');
13251325 expect(markup).toContain('data-user-message-footer="true"');
13261326 });
docs/internals/consistency-action-names.md created+39
......@@ -0,0 +1,39 @@
1# Action names agree across presentations
2
3Proposed invariant: a command's visible label, tooltip, menu wording and accessible
4name describe the same action on the same object. An icon-only presentation must
5retain that meaning. This applies to shared web/Electron controls and corresponding
6React Native iOS/Android actions, wherever those actions are available.
7
8Separate strings can drift while each presentation still looks plausible. A message
9copy action called “Copy link” promises a different clipboard payload to someone
10using a screen reader. Use wording that identifies the actual target, including
11when a message contains links or structured context. Additional clipboard formats
12do not turn copying a message into copying a link.
13
14This proposal draws on Apple's [Writing](https://developer.apple.com/design/human-interface-guidelines/writing)
15and [Accessibility](https://developer.apple.com/design/human-interface-guidelines/accessibility)
16guidance. It is a product constraint, not a claim of Apple certification or complete
17application compliance.
18
19## Boundaries
20
21Native menus and assistive technologies may use different wording or omit a tooltip.
22Labels need semantic agreement, not identical strings across platforms. A contextual
23“Copy” is sufficient when its target is unambiguous; do not globally expand it.
24Temporary “Copied” feedback may replace an action label without implying a different
25target. Clipboard transport, success timing and error reporting are separate concerns.
26
27## Observable cases
28
29- User and assistant message copy controls identify the message, in both tooltip
30 and accessible name. Collapsing a long message does not change the action's meaning.
31- Copying a message with structured context still identifies the message; it does
32 not promise to copy only a context link.
33- Code and plan copy actions retain their own targets. A contextual plan menu can
34 say “Copy to clipboard”; a code control must not be announced as copying a message.
35- An icon, labeled button or native menu for an equivalent command conveys the same
36 operation. Completed feedback never names a different object.
37
38Verify visible wording alongside the runtime accessible name and resulting action.
39Source inspection and screenshots alone do not establish accessibility-tree behavior.