authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-09-07 20:52:21-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-07 20:52:21-03:00
log569a8cd2c3303f55fd869687eb31359b5658cc78
treeede16a5a9a0877f7591ebaa72889e1e5b2f2e054
parentd67157a09c28883d7856d03d2b1d3aecdbf1b890
signature Signed by PGP key B5690EEEBB952194

fix(web): play pull request videos inline (#10617)


1 files changed, 17 insertions(+), 14 deletions(-)

apps/web/src/components/pullRequest/PullRequestMarkdown.tsx+17-14
......@@ -1,4 +1,4 @@
1import { ExternalLinkIcon, PaperclipIcon, PlayIcon } from "lucide-react";
1import { ExternalLinkIcon, PaperclipIcon } from "lucide-react";
22import type { EnvironmentId, ScopedThreadRef } from "@t3tools/contracts";
33import { createContext, useContext, useMemo } from "react";
44import type { Options as ReactMarkdownOptions } from "react-markdown";
......@@ -6,17 +6,12 @@ import type { Options as ReactMarkdownOptions } from "react-markdown";
66import { cn } from "~/lib/utils";
77
88import ChatMarkdown from "../ChatMarkdown";
9import { MediaVideoPlayer } from "../media/MediaVideoPlayer";
910import { remarkPullRequestAutolinks, splitPullRequestBody } from "./pullRequestMarkdown.logic";
1011
1112export const PullRequestMarkdownContext = createContext<string | null>(null);
1213
13/**
14 * A pull request body, rendered with the app's markdown renderer plus a card for each upload
15 * embedded in it, which that renderer drops on the floor.
16 *
17 * These upload URLs do not identify the media format. The card links to GitHub, where the
18 * original upload can be opened or downloaded even when its codec cannot play in the client.
19 */
14/** Renders PR uploads inline, with retry and an original link when video playback fails. */
2015export function PullRequestMarkdown({
2116 text,
2217 cwd,
......@@ -52,8 +47,18 @@ export function PullRequestMarkdown({
5247 />
5348 );
5449 }
55 const isVideo = segment.media === "video";
56 const Icon = isVideo ? PlayIcon : PaperclipIcon;
50 if (segment.media === "video") {
51 return (
52 <MediaVideoPlayer
53 key={`${segment.id}:${segment.url}`}
54 src={segment.url}
55 originalUrl={segment.url}
56 label="Pull request video"
57 className="w-full"
58 videoClassName="rounded-lg border border-border/60"
59 />
60 );
61 }
5762 return (
5863 // A plain anchor rather than the page's openExternal button: the desktop window
5964 // turns a blocked _blank into openExternal itself, and in a browser tab — where
......@@ -65,10 +70,8 @@ export function PullRequestMarkdown({
6570 target="_blank"
6671 className="flex items-center gap-2 rounded-lg border border-border/60 bg-muted/30 px-3 py-2 text-sm hover:bg-muted/60"
6772 >
68 <Icon aria-hidden className="size-3.5 shrink-0 text-muted-foreground" />
69 <span className="min-w-0 flex-1 truncate">
70 {isVideo ? "Play video on GitHub" : "Open attachment on GitHub"}
71 </span>
73 <PaperclipIcon aria-hidden className="size-3.5 shrink-0 text-muted-foreground" />
74 <span className="min-w-0 flex-1 truncate">Open attachment on GitHub</span>
7275 <ExternalLinkIcon aria-hidden className="size-3 shrink-0 text-muted-foreground" />
7376 </a>
7477 );