diff --git a/apps/desktop/src/app/chat/composer/attachments.tsx b/apps/desktop/src/app/chat/composer/attachments.tsx index 5d5028466a9c2..8ae78e5140570 100644 --- a/apps/desktop/src/app/chat/composer/attachments.tsx +++ b/apps/desktop/src/app/chat/composer/attachments.tsx @@ -33,6 +33,7 @@ export function AttachmentList({ function AttachmentPill({ attachment, onRemove }: { attachment: ComposerAttachment; onRemove?: (id: string) => void }) { const { t } = useI18n() const c = t.composer + const Icon = { file: FileText, folder: FolderOpen, @@ -41,19 +42,23 @@ function AttachmentPill({ attachment, onRemove }: { attachment: ComposerAttachme terminal: Terminal, url: Link }[attachment.kind] + // The tile's cwd when this pill lives in a tile composer, not the primary's: // a relative attachment path has to resolve against its own session's root. const cwd = useStore(useSessionView().$cwd) const isUploading = attachment.uploadState === 'uploading' const hasUploadError = attachment.uploadState === 'error' + // A review card's detail is its resolved-comment JSON, not a previewable // path — clicking it should do nothing rather than toast a bogus failure. const canPreview = attachment.kind !== 'folder' && attachment.kind !== 'terminal' && attachment.kind !== 'review' && !isUploading + const detail = attachment.kind !== 'review' && attachment.detail && attachment.detail !== attachment.label ? attachment.detail : undefined + // An attached image already holds its full bytes as a data URL, so it belongs // in the same lightbox the thread uses. The rail is for files you read or // edit — not a picture you just want to look at. Images that never resolved a diff --git a/apps/desktop/src/app/chat/composer/hooks/use-composer-queue.test.tsx b/apps/desktop/src/app/chat/composer/hooks/use-composer-queue.test.tsx index c39af052eeddd..bb2c921226069 100644 --- a/apps/desktop/src/app/chat/composer/hooks/use-composer-queue.test.tsx +++ b/apps/desktop/src/app/chat/composer/hooks/use-composer-queue.test.tsx @@ -23,9 +23,7 @@ import { useComposerQueue } from './use-composer-queue' const SESSION_KEY = 'stored-session-queue-hook' -function renderQueueHook( - overrides: { busy?: boolean; onCancel?: () => void; onSteer?: ChatBarProps['onSteer'] } = {} -) { +function renderQueueHook(overrides: { busy?: boolean; onCancel?: () => void; onSteer?: ChatBarProps['onSteer'] } = {}) { const onSubmit = vi.fn(async () => true) const onCancel = overrides.onCancel ?? vi.fn() const onSteer = overrides.onSteer diff --git a/apps/desktop/src/app/chat/hooks/use-composer-actions.ts b/apps/desktop/src/app/chat/hooks/use-composer-actions.ts index 0c560b3450cc0..db3c16e8fb6cb 100644 --- a/apps/desktop/src/app/chat/hooks/use-composer-actions.ts +++ b/apps/desktop/src/app/chat/hooks/use-composer-actions.ts @@ -355,7 +355,9 @@ export function useComposerActions({ void (async () => { const comment = currentCwd - ? await (desktopGit()?.review.fetchPrComment(currentCwd, url).catch(() => null) ?? null) + ? await (desktopGit() + ?.review.fetchPrComment(currentCwd, url) + .catch(() => null) ?? null) : null if (comment) { diff --git a/apps/desktop/src/lib/chat-runtime.ts b/apps/desktop/src/lib/chat-runtime.ts index 0e703857258a2..191ed5dcc3942 100644 --- a/apps/desktop/src/lib/chat-runtime.ts +++ b/apps/desktop/src/lib/chat-runtime.ts @@ -184,9 +184,11 @@ export const PR_COMMENT_URL_RE = export function reviewCommentBlock(detail: string): null | string { try { const c = JSON.parse(detail) + const anchor = c.path ? `${c.path}${c.line ? `:${c.startLine && c.startLine !== c.line ? `${c.startLine}-` : ''}${c.line}` : ''}` : `PR #${c.prNumber}` + const hunk = c.diffHunk ? `\n--- diff hunk ---\n${String(c.diffHunk).trim()}` : '' return `\`\`\`review-comment ${anchor}\n@${c.author} on ${c.url}\n\n${String(c.body).trim()}${hunk}\n\`\`\``