mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-14 01:50:39 +09:00
241 lines
10 KiB
TypeScript
241 lines
10 KiB
TypeScript
import { useState, type ReactNode } from "react";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { IssueChatThread } from "../components/IssueChatThread";
|
|
import {
|
|
issueChatUxAgentMap,
|
|
issueChatUxFeedbackVotes,
|
|
issueChatUxLinkedRuns,
|
|
issueChatUxLiveComments,
|
|
issueChatUxLiveEvents,
|
|
issueChatUxLiveRuns,
|
|
issueChatUxMentions,
|
|
issueChatUxReassignOptions,
|
|
issueChatUxReviewComments,
|
|
issueChatUxReviewEvents,
|
|
issueChatUxTranscriptsByRunId,
|
|
} from "../fixtures/issueChatUxFixtures";
|
|
import { cn } from "../lib/utils";
|
|
import { Bot, FlaskConical, MessagesSquare, Route, Sparkles, WandSparkles } from "lucide-react";
|
|
|
|
const noop = async () => {};
|
|
|
|
const highlights = [
|
|
"Running assistant replies with streamed text, reasoning, tool cards, and background status notes",
|
|
"Historical issue events and linked runs rendered inline with the chat timeline",
|
|
"Queued user messages, settled assistant comments, and feedback controls",
|
|
"Empty and disabled-composer states without relying on live backend data",
|
|
];
|
|
|
|
function LabSection({
|
|
id,
|
|
eyebrow,
|
|
title,
|
|
description,
|
|
accentClassName,
|
|
children,
|
|
}: {
|
|
id?: string;
|
|
eyebrow: string;
|
|
title: string;
|
|
description: string;
|
|
accentClassName?: string;
|
|
children: ReactNode;
|
|
}) {
|
|
return (
|
|
<section
|
|
id={id}
|
|
className={cn(
|
|
"rounded-[28px] border border-border/70 bg-background/80 p-4 shadow-[0_24px_60px_rgba(15,23,42,0.08)] sm:p-5",
|
|
accentClassName,
|
|
)}
|
|
>
|
|
<div className="mb-4 flex flex-wrap items-start justify-between gap-3">
|
|
<div className="min-w-0">
|
|
<div className="text-[11px] font-semibold uppercase tracking-[0.22em] text-muted-foreground">
|
|
{eyebrow}
|
|
</div>
|
|
<h2 className="mt-1 text-xl font-semibold tracking-tight">{title}</h2>
|
|
<p className="mt-2 max-w-3xl text-sm text-muted-foreground">{description}</p>
|
|
</div>
|
|
</div>
|
|
{children}
|
|
</section>
|
|
);
|
|
}
|
|
|
|
export function IssueChatUxLab() {
|
|
const [showComposer, setShowComposer] = useState(true);
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="overflow-hidden rounded-[32px] border border-border/70 bg-[linear-gradient(135deg,rgba(8,145,178,0.10),transparent_28%),linear-gradient(180deg,rgba(245,158,11,0.10),transparent_44%),var(--background)] shadow-[0_30px_80px_rgba(15,23,42,0.10)]">
|
|
<div className="grid gap-6 lg:grid-cols-[minmax(0,1.2fr)_320px]">
|
|
<div className="p-6 sm:p-7">
|
|
<div className="inline-flex items-center gap-2 rounded-full border border-cyan-500/25 bg-cyan-500/[0.08] px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.24em] text-cyan-700 dark:text-cyan-300">
|
|
<FlaskConical className="h-3.5 w-3.5" />
|
|
Chat UX Lab
|
|
</div>
|
|
<h1 className="mt-4 text-3xl font-semibold tracking-tight">Issue chat review surface</h1>
|
|
<p className="mt-3 max-w-3xl text-sm leading-6 text-muted-foreground">
|
|
This page exercises the real assistant-ui issue chat with fixture-backed messages. Use it to review
|
|
spacing, chronology, running states, tool rendering, activity rows, queueing, and composer behavior
|
|
without needing a live issue in progress.
|
|
</p>
|
|
|
|
<div className="mt-5 flex flex-wrap items-center gap-2">
|
|
<Badge variant="outline" className="rounded-full px-3 py-1 text-[10px] uppercase tracking-[0.18em]">
|
|
/tests/ux/chat
|
|
</Badge>
|
|
<Badge variant="outline" className="rounded-full px-3 py-1 text-[10px] uppercase tracking-[0.18em]">
|
|
assistant-ui thread
|
|
</Badge>
|
|
<Badge variant="outline" className="rounded-full px-3 py-1 text-[10px] uppercase tracking-[0.18em]">
|
|
fixture-backed live run
|
|
</Badge>
|
|
</div>
|
|
|
|
<div className="mt-6 flex flex-wrap items-center gap-3">
|
|
<Button variant="outline" size="sm" className="rounded-full" onClick={() => setShowComposer((value) => !value)}>
|
|
{showComposer ? "Hide composer in primary preview" : "Show composer in primary preview"}
|
|
</Button>
|
|
<a
|
|
href="#live-execution"
|
|
className="inline-flex items-center gap-2 rounded-full border border-border/70 bg-background/80 px-3 py-1.5 text-xs text-muted-foreground transition-colors hover:text-foreground"
|
|
>
|
|
<Route className="h-3.5 w-3.5" />
|
|
Jump to live execution preview
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<aside className="border-t border-border/60 bg-background/70 p-6 lg:border-l lg:border-t-0">
|
|
<div className="mb-4 flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.2em] text-muted-foreground">
|
|
<WandSparkles className="h-4 w-4 text-cyan-700 dark:text-cyan-300" />
|
|
Covered states
|
|
</div>
|
|
<div className="space-y-3">
|
|
{highlights.map((highlight) => (
|
|
<div
|
|
key={highlight}
|
|
className="rounded-2xl border border-border/70 bg-background/85 px-4 py-3 text-sm text-muted-foreground"
|
|
>
|
|
{highlight}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
</div>
|
|
|
|
<LabSection
|
|
id="live-execution"
|
|
eyebrow="Primary preview"
|
|
title="Live execution thread"
|
|
description="Shows the fully active state: timeline events, historical run marker, a running assistant reply with reasoning and tools, and a queued follow-up from the user."
|
|
accentClassName="bg-[linear-gradient(180deg,rgba(6,182,212,0.05),transparent_28%),var(--background)]"
|
|
>
|
|
<IssueChatThread
|
|
comments={issueChatUxLiveComments}
|
|
linkedRuns={issueChatUxLinkedRuns.slice(0, 1)}
|
|
timelineEvents={issueChatUxLiveEvents}
|
|
liveRuns={issueChatUxLiveRuns}
|
|
issueStatus="todo"
|
|
agentMap={issueChatUxAgentMap}
|
|
currentUserId="user-1"
|
|
onAdd={noop}
|
|
onVote={noop}
|
|
onCancelRun={noop}
|
|
onInterruptQueued={noop}
|
|
draftKey="issue-chat-ux-lab-primary"
|
|
enableReassign
|
|
reassignOptions={issueChatUxReassignOptions}
|
|
currentAssigneeValue="agent:agent-1"
|
|
suggestedAssigneeValue="agent:agent-2"
|
|
mentions={issueChatUxMentions}
|
|
showComposer={showComposer}
|
|
enableLiveTranscriptPolling={false}
|
|
transcriptsByRunId={issueChatUxTranscriptsByRunId}
|
|
hasOutputForRun={(runId) => issueChatUxTranscriptsByRunId.has(runId)}
|
|
/>
|
|
</LabSection>
|
|
|
|
<div className="grid gap-6 xl:grid-cols-2">
|
|
<LabSection
|
|
eyebrow="Settled review"
|
|
title="Durable comments and feedback"
|
|
description="Shows the post-run state: assistant comment feedback controls, historical run context, and timeline reassignment without any active stream."
|
|
accentClassName="bg-[linear-gradient(180deg,rgba(168,85,247,0.05),transparent_26%),var(--background)]"
|
|
>
|
|
<IssueChatThread
|
|
comments={issueChatUxReviewComments}
|
|
linkedRuns={issueChatUxLinkedRuns.slice(1)}
|
|
timelineEvents={issueChatUxReviewEvents}
|
|
feedbackVotes={issueChatUxFeedbackVotes}
|
|
feedbackTermsUrl="/feedback-terms"
|
|
issueStatus="in_review"
|
|
agentMap={issueChatUxAgentMap}
|
|
currentUserId="user-1"
|
|
onAdd={noop}
|
|
onVote={noop}
|
|
draftKey="issue-chat-ux-lab-review"
|
|
showComposer={false}
|
|
enableLiveTranscriptPolling={false}
|
|
/>
|
|
</LabSection>
|
|
|
|
<div className="space-y-6">
|
|
<LabSection
|
|
eyebrow="Empty thread"
|
|
title="Empty state and disabled composer"
|
|
description="Keeps the message area visible even when there is no thread yet, and replaces the composer with an explicit warning when replies are blocked."
|
|
accentClassName="bg-[linear-gradient(180deg,rgba(245,158,11,0.08),transparent_26%),var(--background)]"
|
|
>
|
|
<IssueChatThread
|
|
comments={[]}
|
|
linkedRuns={[]}
|
|
timelineEvents={[]}
|
|
issueStatus="done"
|
|
agentMap={issueChatUxAgentMap}
|
|
currentUserId="user-1"
|
|
onAdd={noop}
|
|
composerDisabledReason="This workspace is closed, so new chat replies are disabled until the issue is reopened."
|
|
draftKey="issue-chat-ux-lab-empty"
|
|
enableLiveTranscriptPolling={false}
|
|
/>
|
|
</LabSection>
|
|
|
|
<Card className="gap-4 border-border/70 bg-background/85 py-0">
|
|
<CardHeader className="px-5 pt-5 pb-0">
|
|
<div className="flex items-center gap-2 text-[11px] font-semibold uppercase tracking-[0.18em] text-muted-foreground">
|
|
<MessagesSquare className="h-4 w-4 text-cyan-700 dark:text-cyan-300" />
|
|
Review checklist
|
|
</div>
|
|
<CardTitle className="text-lg">What to evaluate on this page</CardTitle>
|
|
<CardDescription>
|
|
This route should be the fastest way to inspect the chat system before or after tweaks.
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3 px-5 pb-5 pt-0 text-sm text-muted-foreground">
|
|
<div className="rounded-2xl border border-border/70 bg-background/80 px-4 py-3">
|
|
<div className="mb-1 flex items-center gap-2 font-medium text-foreground">
|
|
<Bot className="h-4 w-4 text-cyan-700 dark:text-cyan-300" />
|
|
Message hierarchy
|
|
</div>
|
|
Check that user, assistant, and system rows scan differently without feeling like separate products.
|
|
</div>
|
|
<div className="rounded-2xl border border-border/70 bg-background/80 px-4 py-3">
|
|
<div className="mb-1 flex items-center gap-2 font-medium text-foreground">
|
|
<Sparkles className="h-4 w-4 text-cyan-700 dark:text-cyan-300" />
|
|
Stream polish
|
|
</div>
|
|
Watch the live preview for reasoning density, tool expansion behavior, and queued follow-up readability.
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|