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({...@@ -37,7 +37,7 @@ export const MessageCopyButton = memo(function MessageCopyButton({
37 <TooltipTrigger37 <TooltipTrigger
38 render={38 render={
39 <Button39 <Button
40 aria-label="Copy link"40 aria-label="Copy message"
41 disabled={isCopied}41 disabled={isCopied}
42 onClick={() => copyToClipboard(text)}42 onClick={() => copyToClipboard(text)}
43 ref={ref}43 ref={ref}
...@@ -51,7 +51,7 @@ export const MessageCopyButton = memo(function MessageCopyButton({...@@ -51,7 +51,7 @@ export const MessageCopyButton = memo(function MessageCopyButton({
51 {isCopied ? <CheckIcon className="size-3 text-primary" /> : <CopyIcon className="size-3" />}51 {isCopied ? <CheckIcon className="size-3 text-primary" /> : <CopyIcon className="size-3" />}
52 </TooltipTrigger>52 </TooltipTrigger>
53 <TooltipPopup>53 <TooltipPopup>
54 <p>Copy to clipboard</p>54 <p>Copy message</p>
55 </TooltipPopup>55 </TooltipPopup>
56 </Tooltip>56 </Tooltip>
57 );57 );
apps/web/src/components/chat/MessagesTimeline.test.tsx+1-1
...@@ -1320,7 +1320,7 @@ describe("MessagesTimeline", () => {...@@ -1320,7 +1320,7 @@ describe("MessagesTimeline", () => {
1320 />,1320 />,
1321 );1321 );
13221322
1323 expect(markup).toContain('aria-label="Copy link"');1323 expect(markup).toContain('aria-label="Copy message"');
1324 expect(markup).toContain('data-user-message-collapsed="true"');1324 expect(markup).toContain('data-user-message-collapsed="true"');
1325 expect(markup).toContain('data-user-message-footer="true"');1325 expect(markup).toContain('data-user-message-footer="true"');
1326 });1326 });
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.