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
12 changes: 10 additions & 2 deletions apps/web/src/components/ChatMarkdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ interface ChatMarkdownProps {
text: string;
cwd: string | undefined;
threadRef?: ScopedThreadRef | undefined;
/** Panel that receives pull request links, including the standalone PR view. */
pullRequestPanelRef?: ScopedThreadRef | undefined;
/** Environment that owns non-thread markdown, such as a pull request panel. */
environmentId?: EnvironmentId | undefined;
onTaskListChange?: ((input: { markerOffset: number; checked: boolean }) => void) | undefined;
Expand Down Expand Up @@ -2160,6 +2162,7 @@ function useChatMarkdownState({
text,
cwd,
threadRef,
pullRequestPanelRef,
environmentId: explicitEnvironmentId,
onTaskListChange,
isStreaming = false,
Expand Down Expand Up @@ -2320,7 +2323,7 @@ function useChatMarkdownState({
event.clipboardData.setData("text/plain", payload.text);
event.clipboardData.setData("text/html", payload.html);
}, []);
const openChangeRequestLink = useOpenChangeRequestLink(threadRef);
const openChangeRequestLink = useOpenChangeRequestLink(threadRef, pullRequestPanelRef);
const openDeferredMarkdownLink = useOpenLink(threadRef);
// Subscribed rather than read at click time: the anchor has to decide
// synchronously whether to intercept its `_blank`, and a subscription is what
Expand Down Expand Up @@ -2815,7 +2818,12 @@ const CHAT_MARKDOWN_COMPONENTS = {
// A link to a change request in a workspace project opens beside the
// conversation instead of in a browser: it is the thing being talked about, and
// the panel it opens offers the browser as one of its actions.
if (!href || openChangeRequestLink(event, href)) return;
if (
!href ||
openChangeRequestLink(event, href, undefined, environmentId ?? undefined)
) {
return;
}
// Anything else follows the "Open links in" setting. The system browser
// keeps the `_blank` the shell already handles; the in-app browser needs
// the click intercepted here. A modifier click is the way out of the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,10 @@ export function PullRequestDetailPanel({
if (detail?.autoMergeMethod !== undefined) setMergeMethod(detail.autoMergeMethod);
}, [detail?.autoMergeMethod, pullRequestKey]);
const repositoryUrl = detail === null ? null : changeRequestRepositoryUrl(detail.url);
const markdownContext = useMemo(
() => ({ repositoryUrl: detail?.provider === "github" ? repositoryUrl : null, threadRef }),
[detail?.provider, repositoryUrl, threadRef],
);
const authorProfileUrl =
detail?.provider === "github" &&
detail.author !== null &&
Expand Down Expand Up @@ -2328,7 +2332,7 @@ export function PullRequestDetailPanel({
{...(unavailableGitHubUrl ? { gitHubUrl: unavailableGitHubUrl } : {})}
/>
) : detail ? (
<PullRequestMarkdownContext value={detail.provider === "github" ? repositoryUrl : null}>
<PullRequestMarkdownContext value={markdownContext}>
{mountedTabs.has("summary") ? (
<div className={cn("absolute inset-0", tab !== "summary" && "invisible")}>
<PullRequestSummaryTab
Expand Down
13 changes: 10 additions & 3 deletions apps/web/src/components/pullRequest/PullRequestMarkdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ import { createContext, useContext, useMemo } from "react";
import type { Options as ReactMarkdownOptions } from "react-markdown";

import { cn } from "~/lib/utils";
import { PULL_REQUESTS_PANEL_REF } from "~/rightPanelStore";

import ChatMarkdown from "../ChatMarkdown";
import { remarkPullRequestAutolinks, splitPullRequestBody } from "./pullRequestMarkdown.logic";

export const PullRequestMarkdownContext = createContext<string | null>(null);
export const PullRequestMarkdownContext = createContext<{
repositoryUrl: string | null;
threadRef: ScopedThreadRef | null;
} | null>(null);

/**
* A pull request body, rendered with the app's markdown renderer plus a card for each upload
Expand All @@ -32,7 +36,9 @@ export function PullRequestMarkdown({
className?: string;
}) {
const segments = splitPullRequestBody(text);
const repositoryUrl = useContext(PullRequestMarkdownContext);
const context = useContext(PullRequestMarkdownContext);
const repositoryUrl = context?.repositoryUrl;
const resolvedThreadRef = threadRef ?? context?.threadRef ?? undefined;
const extraRemarkPlugins = useMemo<NonNullable<ReactMarkdownOptions["remarkPlugins"]>>(
() => (repositoryUrl ? [[remarkPullRequestAutolinks, { repositoryUrl }]] : []),
[repositoryUrl],
Expand All @@ -46,7 +52,8 @@ export function PullRequestMarkdown({
key={segment.id}
text={segment.text}
cwd={cwd}
threadRef={threadRef ?? undefined}
threadRef={resolvedThreadRef}
pullRequestPanelRef={resolvedThreadRef ?? PULL_REQUESTS_PANEL_REF}
environmentId={environmentId}
extraRemarkPlugins={extraRemarkPlugins}
/>
Expand Down
27 changes: 24 additions & 3 deletions apps/web/src/lib/openPullRequestLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export function shouldOpenPullRequestExternally(

export function useOpenChangeRequestLink(
threadRef?: ScopedThreadRef,
panelRef?: ScopedThreadRef,
): (
event: Pick<
MouseEvent<HTMLElement>,
Expand All @@ -260,6 +261,7 @@ export function useOpenChangeRequestLink(
(event, targetUrl, targetThreadRef, targetEnvironmentId) => {
if (shouldOpenPullRequestExternally(event)) return false;
const resolvedThreadRef = targetThreadRef ?? threadRef;
const resolvedPanelRef = panelRef ?? resolvedThreadRef;
const parsed = parseChangeRequestUrl(targetUrl);
if (parsed === null) return false;
const reads = (environmentId: string) =>
Expand All @@ -286,14 +288,33 @@ export function useOpenChangeRequestLink(
if (project === undefined || !reads(project.environmentId)) return false;
event.preventDefault();
event.stopPropagation();
if (resolvedThreadRef) {
useRightPanelStore.getState().openPullRequest(resolvedThreadRef, {
if (resolvedPanelRef) {
useRightPanelStore.getState().openPullRequest(resolvedPanelRef, {
// The standalone PR panel has a synthetic ref; each tab keeps its real environment.
...(resolvedPanelRef.environmentId === project.environmentId
? {}
: { environmentId: project.environmentId }),
projectId: project.id,
// The identity's own spelling, not the one read out of the URL: the panel asks the
// provider for this repository, while matching a link only ever compares lower case.
repository: project.repositoryIdentity?.displayName ?? parsed.repository,
number: parsed.number,
});
if (!resolvedThreadRef) {
void navigate({
to: "/pull-requests",
search: (previous) => ({
...previous,
involvement: previous.involvement ?? "all",
state: previous.state ?? "all",
repository: project.repositoryIdentity?.displayName ?? parsed.repository,
number: parsed.number,
selectedProjectId: project.id,
selectedEnvironmentId: project.environmentId,
}),
replace: true,
});
}
return true;
}
void navigate({
Expand All @@ -312,7 +333,7 @@ export function useOpenChangeRequestLink(
});
return true;
},
[allProjects, navigate, primaryEnvironmentId, serverConfigs, threadRef],
[allProjects, navigate, panelRef, primaryEnvironmentId, serverConfigs, threadRef],
);
}

Expand Down
Loading