From b519ce29addb6dca5b864733fab67154ca38393f Mon Sep 17 00:00:00 2001 From: xxxigm <54813621+xxxigm@users.noreply.github.com> Date: Thu, 27 Aug 2026 01:38:19 +0700 Subject: [PATCH] fix(desktop): keep attachment close and code copy icons visible (#95611) * fix(desktop): keep attachment close and code copy icons visible Hover-only opacity-0 hid the composer remove control and code-block copy button, so they stayed clickable but invisible on Windows and other no-hover surfaces. * test(desktop): pin attachment close and code copy visibility at rest The remove chip and code-block copy control must stay in the tree without a hover class, so Windows and no-hover surfaces cannot hide them again. --- .../src/app/chat/composer/attachments.test.tsx | 12 ++++++++++++ apps/desktop/src/app/chat/composer/attachments.tsx | 6 +++--- .../assistant-ui/markdown-text.artifacts.test.tsx | 8 ++++++++ .../src/components/chat/shiki-highlighter.tsx | 5 +++-- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/apps/desktop/src/app/chat/composer/attachments.test.tsx b/apps/desktop/src/app/chat/composer/attachments.test.tsx index 7377691f0f..ab491a00a5 100644 --- a/apps/desktop/src/app/chat/composer/attachments.test.tsx +++ b/apps/desktop/src/app/chat/composer/attachments.test.tsx @@ -236,6 +236,18 @@ describe('AttachmentList', () => { ) }) + it('shows the remove control at rest and does not hide it behind hover', async () => { + const onRemove = vi.fn() + + await renderWithI18n() + + const remove = screen.getByRole('button', { name: 'Remove doc.pdf' }) + + expect(remove.className.split(/\s+/)).not.toContain('opacity-0') + fireEvent.click(remove) + expect(onRemove).toHaveBeenCalledWith('a') + }) + it('still routes a non-image attachment to the preview rail', async () => { $previewTabs.set([]) diff --git a/apps/desktop/src/app/chat/composer/attachments.tsx b/apps/desktop/src/app/chat/composer/attachments.tsx index b87e69068f..39216e0372 100644 --- a/apps/desktop/src/app/chat/composer/attachments.tsx +++ b/apps/desktop/src/app/chat/composer/attachments.tsx @@ -149,7 +149,7 @@ function AttachmentPill({ attachment, onRemove }: { attachment: ComposerAttachme return ( <> -
+
)}
diff --git a/apps/desktop/src/components/assistant-ui/markdown-text.artifacts.test.tsx b/apps/desktop/src/components/assistant-ui/markdown-text.artifacts.test.tsx index 89fc0b61c1..af573eadfd 100644 --- a/apps/desktop/src/components/assistant-ui/markdown-text.artifacts.test.tsx +++ b/apps/desktop/src/components/assistant-ui/markdown-text.artifacts.test.tsx @@ -65,6 +65,14 @@ describe('MarkdownTextContent artifacts', () => { expect(artifactsForSession('session-artifacts')).toHaveLength(0) }) + it('keeps the code-block copy control visible without hover', async () => { + render() + + const copy = await screen.findByRole('button', { name: 'Copy code' }) + + expect(copy.className.split(/\s+/)).not.toContain('opacity-0') + }) + it('does not register while the message is still streaming', async () => { render() diff --git a/apps/desktop/src/components/chat/shiki-highlighter.tsx b/apps/desktop/src/components/chat/shiki-highlighter.tsx index 6823bba7cf..eaa8a2bc67 100644 --- a/apps/desktop/src/components/chat/shiki-highlighter.tsx +++ b/apps/desktop/src/components/chat/shiki-highlighter.tsx @@ -15,7 +15,8 @@ import { isLikelyProseCodeBlock } from '@/lib/markdown-code' * own the wrapping `` here and neutralize the upstream * `data-streamdown="code-block"` chrome from styles.css. The card is * background-only — no header row, no language label — so a fence reads as a - * tinted slab of the reply; copy is a hover-reveal control in the corner. + * tinted slab of the reply. Copy stays visible in the corner (not hover-only) + * so Windows / touch / no-hover surfaces can still find it. * * `react-shiki` full bundle so all `bundledLanguages` work; theme switches * follow the document `color-scheme` via `defaultColor="light-dark()"`. @@ -148,7 +149,7 @@ export const SyntaxHighlighter: FC = ({