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 = ({