From 6fa71a28eaca4398247fad835f3989c56abdb395 Mon Sep 17 00:00:00 2001 From: Rafael Audibert <32079912+rafaeelaudibert@users.noreply.github.com> Date: Tue, 9 Jun 2026 12:30:59 -0700 Subject: [PATCH] feat(inbox): render expandable inbox report card under initial prompt When a task is associated with a PostHog inbox report (task.signal_report), the conversation now renders an expandable report card directly under the initial prompt, so the report can be read inline instead of navigating away to the inbox view. - New InboxReportCard component fetches the report via the existing useInboxReportById hook (shared query cache, stays in sync). Collapsed shows title + status badge; expanded shows the researched summary, priority/actionability badges, and an "Open in inbox" action. - UserMessage gains an optional signalReportId prop that renders the card below the message body, mirroring the existing slackThreadUrl pattern. - ConversationView passes task.signal_report to the first user message only. Generated-By: PostHog Code Task-Id: c0568add-708f-41eb-a334-563d52b70afa --- .../inbox/components/InboxReportCard.tsx | 120 ++++++++++++++++++ .../sessions/components/ConversationView.tsx | 14 +- .../components/session-update/UserMessage.tsx | 6 + 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 apps/code/src/renderer/features/inbox/components/InboxReportCard.tsx diff --git a/apps/code/src/renderer/features/inbox/components/InboxReportCard.tsx b/apps/code/src/renderer/features/inbox/components/InboxReportCard.tsx new file mode 100644 index 000000000..68720a367 --- /dev/null +++ b/apps/code/src/renderer/features/inbox/components/InboxReportCard.tsx @@ -0,0 +1,120 @@ +import { useInboxReportById } from "@features/inbox/hooks/useInboxReports"; +import { useInboxReportSelectionStore } from "@features/inbox/stores/inboxReportSelectionStore"; +import { useInboxSignalsFilterStore } from "@features/inbox/stores/inboxSignalsFilterStore"; +import { + ArrowSquareOut, + CaretDown, + CaretUp, + Tray, +} from "@phosphor-icons/react"; +import { Box, Flex, Spinner, Text } from "@radix-ui/themes"; +import { navigateToInbox } from "@renderer/navigationBridge"; +import { useCallback, useState } from "react"; +import { SignalReportActionabilityBadge } from "./utils/SignalReportActionabilityBadge"; +import { SignalReportPriorityBadge } from "./utils/SignalReportPriorityBadge"; +import { SignalReportStatusBadge } from "./utils/SignalReportStatusBadge"; +import { SignalReportSummaryMarkdown } from "./utils/SignalReportSummaryMarkdown"; + +interface InboxReportCardProps { + reportId: string; +} + +/** + * Compact, expandable card for the inbox report a task is associated with. + * Rendered under the initial prompt so the report can be read inline instead + * of navigating away to the inbox view. Reads the report from the same query + * cache the inbox uses (`useInboxReportById`), so it stays in sync and an + * "Open in inbox" action can reuse the warmed cache for the detail pane. + */ +export function InboxReportCard({ reportId }: InboxReportCardProps) { + const [expanded, setExpanded] = useState(false); + const { data: report, isLoading } = useInboxReportById(reportId, { + staleTime: 60_000, + }); + + const setSelectedReportIds = useInboxReportSelectionStore( + (s) => s.setSelectedReportIds, + ); + const resetFilters = useInboxSignalsFilterStore((s) => s.resetFilters); + + const handleOpenInInbox = useCallback(() => { + // Reset inbox-local filters first so the report isn't hidden by an active + // filter, then navigate and select it (mirrors the deep-link open path). + resetFilters(); + navigateToInbox(); + setSelectedReportIds([reportId]); + }, [reportId, resetFilters, setSelectedReportIds]); + + if (isLoading && !report) { + return ( + + + + Loading inbox report... + + + ); + } + + if (!report) return null; + + return ( + + + + {expanded && ( + + + + {(report.priority || report.actionability) && ( + + + + + )} + + + + )} + + ); +} diff --git a/apps/code/src/renderer/features/sessions/components/ConversationView.tsx b/apps/code/src/renderer/features/sessions/components/ConversationView.tsx index 2a1d9e994..92e16289b 100644 --- a/apps/code/src/renderer/features/sessions/components/ConversationView.tsx +++ b/apps/code/src/renderer/features/sessions/components/ConversationView.tsx @@ -202,6 +202,11 @@ export function ConversationView({ ? slackThreadUrl : undefined } + signalReportId={ + item.id === firstUserMessageId + ? (task?.signal_report ?? undefined) + : undefined + } /> ); case "git_action": @@ -245,7 +250,14 @@ export function ConversationView({ ); } }, - [repoPath, taskId, slackThreadUrl, firstUserMessageId, initialItemIds], + [ + repoPath, + taskId, + slackThreadUrl, + firstUserMessageId, + initialItemIds, + task?.signal_report, + ], ); const getItemKey = useCallback((item: ConversationItem) => item.id, []); diff --git a/apps/code/src/renderer/features/sessions/components/session-update/UserMessage.tsx b/apps/code/src/renderer/features/sessions/components/session-update/UserMessage.tsx index 698f9255a..3a5b0a688 100644 --- a/apps/code/src/renderer/features/sessions/components/session-update/UserMessage.tsx +++ b/apps/code/src/renderer/features/sessions/components/session-update/UserMessage.tsx @@ -1,5 +1,6 @@ import { Tooltip } from "@components/ui/Tooltip"; import { MarkdownRenderer } from "@features/editor/components/MarkdownRenderer"; +import { InboxReportCard } from "@features/inbox/components/InboxReportCard"; import { CaretDown, CaretUp, @@ -28,6 +29,9 @@ interface UserMessageProps { content: string; timestamp?: number; sourceUrl?: string; + /** Inbox report this message's task is associated with — rendered as an + * expandable card under the message (first user message only). */ + signalReportId?: string; attachments?: UserMessageAttachment[]; animate?: boolean; } @@ -52,6 +56,7 @@ export const UserMessage = memo(function UserMessage({ content, timestamp, sourceUrl, + signalReportId, attachments = [], animate = true, }: UserMessageProps) { @@ -160,6 +165,7 @@ export const UserMessage = memo(function UserMessage({ View Slack thread )} + {signalReportId && } {timestamp != null && (