Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { HighlightedCode } from "@components/HighlightedCode";
import { List, ListItem } from "@components/List";
import { parseGithubIssueUrl } from "@features/message-editor/utils/githubIssueUrl";
import { Blockquote, Checkbox, Code, Kbd, Text } from "@radix-ui/themes";
import { trpcClient } from "@renderer/trpc/client";
import { isPostHogCodeDeeplink } from "@shared/deeplink";
import { memo, useMemo } from "react";
import type { Components } from "react-markdown";
import ReactMarkdown from "react-markdown";
import ReactMarkdown, { defaultUrlTransform } from "react-markdown";
import remarkGfm from "remark-gfm";
import type { PluggableList } from "unified";
import { GithubRefChip } from "./GithubRefChip";
Expand All @@ -24,6 +26,11 @@ function preprocessMarkdown(content: string): string {
return content.replace(/\n([^\n].*)\n(---+|___+|\*\*\*+)\n/g, "\n$1\n\n$2\n");
}

function markdownUrlTransform(value: string): string {
if (isPostHogCodeDeeplink(value)) return value;
return defaultUrlTransform(value);
}

const HeadingText = ({ children }: { children: React.ReactNode }) => (
<Text as="p" mb="3" className="text-(--accent-11) text-sm">
<strong>{children}</strong>
Expand Down Expand Up @@ -92,9 +99,15 @@ export const baseComponents: Components = {
</GithubRefChip>
);
}
const isDeeplink = isPostHogCodeDeeplink(href);
return (
<a
href={href}
onClick={(event) => {
if (!isDeeplink || !href) return;
event.preventDefault();
void trpcClient.os.openExternal.mutate({ url: href });
}}
Comment on lines +106 to +110
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any way we can avoid this inline import pattern?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in cfe9107. Inline import is gone — back to a top-level import { trpcClient } like every other call site.

The reason it was lazy in the first place was that UserMessage.test.tsx was failing at module init because trpc/client.ts calls ipcLink() which needs window.electronTRPC, and MarkdownRenderer was the first thing in UserMessage's import graph to reach it. Fixed properly by adding vi.mock("@renderer/trpc/client", ...) to the test — same pattern 14 other test files already use.

target="_blank"
rel="noopener noreferrer"
className="markdown-link inline-flex items-center gap-[2px]"
Expand Down Expand Up @@ -189,6 +202,7 @@ export const MarkdownRenderer = memo(function MarkdownRenderer({
remarkPlugins={plugins}
rehypePlugins={rehypePlugins}
components={components}
urlTransform={markdownUrlTransform}
>
{processedContent}
</ReactMarkdown>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ArrowSquareOutIcon,
CaretDownIcon,
CaretRightIcon,
ChatCircleIcon,
EyeIcon,
LinkSimpleIcon,
Plus,
Expand All @@ -26,9 +27,11 @@ import { Kbd } from "@posthog/quill";
import {
Box,
Flex,
Popover,
ScrollArea,
Spinner,
Text,
TextField,
Tooltip,
} from "@radix-ui/themes";
import { useTRPC } from "@renderer/trpc";
Expand All @@ -51,6 +54,7 @@ import { useNavigationStore } from "@stores/navigationStore";
import { useQuery } from "@tanstack/react-query";
import { isMac } from "@utils/platform";
import {
type FormEvent,
type ReactNode,
useCallback,
useEffect,
Expand All @@ -59,6 +63,7 @@ import {
useState,
} from "react";
import { toast } from "sonner";
import { useDiscussReport } from "../../hooks/useDiscussReport";
import { ReportImplementationPrLink } from "../utils/ReportImplementationPrLink";
import { SignalReportActionabilityBadge } from "../utils/SignalReportActionabilityBadge";
import { SignalReportPriorityBadge } from "../utils/SignalReportPriorityBadge";
Expand Down Expand Up @@ -186,6 +191,8 @@ export function ReportDetailPane({
onReportAction,
onScroll,
}: ReportDetailPaneProps) {
const [discussQuestion, setDiscussQuestion] = useState("");
const [discussQuestionOpen, setDiscussQuestionOpen] = useState(false);
const { data: me } = useMeQuery();

// ── Report data ─────────────────────────────────────────────────────────
Expand Down Expand Up @@ -366,6 +373,35 @@ export function ReportDetailPane({
fireDetailAction,
]);

const { discussReport, isDiscussing } = useDiscussReport({
reportId: report.id,
reportTitle: report.title,
cloudRepository: effectiveCloudRepository,
});

const handleDiscussReport = useCallback(
async (question?: string) => {
const trimmedQuestion = question?.trim();
fireDetailAction("discuss", {
has_question: !!trimmedQuestion,
question_text: trimmedQuestion
? trimmedQuestion.slice(0, 500)
: undefined,
});
setDiscussQuestionOpen(false);
await discussReport(trimmedQuestion);
},
[discussReport, fireDetailAction],
);

const handleDiscussSubmit = useCallback(
(event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
handleDiscussReport(discussQuestion);
},
[discussQuestion, handleDiscussReport],
);

// Bind native scroll listener to the Radix ScrollArea viewport (Radix doesn't forward onScroll).
// The viewport's data-report-id attribute is set from report.id so we both (a) track the
// current report in the DOM for debugging and (b) give biome's useExhaustiveDependencies
Expand Down Expand Up @@ -471,6 +507,73 @@ export function ReportDetailPane({
)}
Dismiss
</Button>
<Flex align="center" gap="0">
<Button
size="1"
variant="soft"
className="gap-1 rounded-r-none text-[12px]"
tooltipContent="Open a chat session about this report"
disabled={isDiscussing}
onClick={() => handleDiscussReport()}
>
{isDiscussing ? (
<Spinner size="1" />
) : (
<ChatCircleIcon size={12} />
)}
Discuss
</Button>
<Popover.Root
open={discussQuestionOpen}
onOpenChange={setDiscussQuestionOpen}
>
<Popover.Trigger>
<Button
size="1"
variant="soft"
className="rounded-l-none border-l border-l-(--gray-5) px-1"
aria-label="Ask an optional first question"
disabled={isDiscussing}
>
<CaretDownIcon size={12} />
</Button>
</Popover.Trigger>
<Popover.Content
align="end"
className="w-[320px] border border-(--gray-6) bg-(--color-panel-solid) p-3 shadow-6"
side="bottom"
sideOffset={6}
>
<form
className="flex flex-col gap-2"
onSubmit={handleDiscussSubmit}
>
<TextField.Root
aria-label="Optional first question for Discuss"
autoFocus
placeholder="Ask about this report..."
size="2"
value={discussQuestion}
onChange={(event) => setDiscussQuestion(event.target.value)}
/>
<Flex justify="end" gap="2">
<Button
color="gray"
size="1"
type="button"
variant="soft"
onClick={() => setDiscussQuestionOpen(false)}
>
Cancel
</Button>
<Button size="1" type="submit" variant="soft">
Discuss
</Button>
</Flex>
</form>
</Popover.Content>
</Popover.Root>
</Flex>
{headerImplementationPrUrl ? (
<ReportImplementationPrLink
prUrl={headerImplementationPrUrl}
Expand Down
Loading
Loading