mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-04 11:01:16 +01:00
Surface attachment-backed artifact work products as a first-class Output section on the issue detail page so cloud users can watch and download agent-generated videos without host filesystem access. - ui/src/lib/issue-output.ts: formatBytes/formatDuration/getOutputFileGlyph helpers + getIssueOutputs selector that validates the Phase-2 attachment artifact metadata contract and tolerates malformed metadata (degraded). - issue-output components: IssueOutputSection, OutputPrimaryCard (native <video>/image/generic), OutputRow, OutputVideoPlayer, OutputFileTile. - IssueDetail: fetch work products and render the Output section between Documents and Attachments; reuse formatBytes in the attachments list. - DesignGuide: showcase multiple-output, degraded, and empty states. - Focused tests for video output, empty state, multiple outputs, and failed attachment metadata (15 tests). Co-Authored-By: Paperclip <noreply@paperclip.ing>
34 lines
1.1 KiB
TypeScript
34 lines
1.1 KiB
TypeScript
import { cn } from "@/lib/utils";
|
|
|
|
interface OutputVideoPlayerProps {
|
|
src: string;
|
|
poster?: string | null;
|
|
className?: string;
|
|
/** Accessible label, typically the filename. */
|
|
title?: string;
|
|
}
|
|
|
|
/**
|
|
* Thin wrapper around the native HTML5 `<video>` element with sensible
|
|
* defaults for issue outputs. We deliberately rely on the browser's native
|
|
* controls (play/pause/scrub/fullscreen/PiP) rather than building a custom
|
|
* scrubber — the backend serves byte ranges so seeking works.
|
|
*
|
|
* A fixed 16:9 box reserves height before metadata loads to avoid layout jump.
|
|
*/
|
|
export function OutputVideoPlayer({ src, poster, className, title }: OutputVideoPlayerProps) {
|
|
return (
|
|
<div className={cn("relative w-full overflow-hidden rounded-md bg-black aspect-video", className)}>
|
|
<video
|
|
src={src}
|
|
poster={poster ?? undefined}
|
|
controls
|
|
preload="metadata"
|
|
playsInline
|
|
aria-label={title ? `Video output: ${title}` : "Video output"}
|
|
className="absolute inset-0 h-full w-full"
|
|
/>
|
|
</div>
|
|
);
|
|
}
|