mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 15:03:16 +00:00
feat: dark mode & general web app improvements (#615)
* feat: viewability improvements * chore: code formatting * fix: code fixes * fix: merge changes lost from main * fix: attached doc styling * chore: attachments logo * fix: remove attachments button * fix: text visibility & tooltip mardown * fix: auth state change * feat conflict with main resolved * fix: feat conflicts with main * fix: conversation summary consistency with theme * fix: conversation summary * chore: removed text * cr --------- Co-authored-by: bracesproul <braceasproul@gmail.com>
This commit is contained in:
parent
7a23f5fdcf
commit
9906dd4e2a
30 changed files with 528 additions and 374 deletions
|
|
@ -16,6 +16,7 @@ import { MANAGER_GRAPH_ID } from "@open-swe/shared/constants";
|
|||
import { useThreadsStatus } from "@/hooks/useThreadsStatus";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { threadsToMetadata } from "@/lib/thread-utils";
|
||||
import { OpenSWELogoSVG } from "@/components/icons/openswe";
|
||||
|
||||
type FilterStatus =
|
||||
| "all"
|
||||
|
|
@ -109,10 +110,10 @@ function AllThreadsPageContent() {
|
|||
<ArrowLeft className="h-3 w-3" />
|
||||
</Button>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-2 w-2 rounded-full bg-green-500"></div>
|
||||
<span className="text-muted-foreground font-mono text-sm">
|
||||
All Threads
|
||||
</span>
|
||||
<OpenSWELogoSVG
|
||||
width={120}
|
||||
height={18}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-auto flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -129,7 +130,7 @@ function AllThreadsPageContent() {
|
|||
</div>
|
||||
|
||||
{/* Search and Filters */}
|
||||
<div className="border-border bg-muted/50 border-b px-4 py-3 dark:bg-gray-950">
|
||||
<div className="border-border bg-muted/50 border-b px-4 py-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="relative max-w-md flex-1">
|
||||
<Search className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 transform" />
|
||||
|
|
@ -137,7 +138,7 @@ function AllThreadsPageContent() {
|
|||
placeholder="Search threads..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="border-border bg-background text-foreground placeholder:text-muted-foreground pl-10 dark:bg-gray-900"
|
||||
className="border-border bg-background text-foreground placeholder:text-muted-foreground pl-10"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
|
|
@ -162,7 +163,7 @@ function AllThreadsPageContent() {
|
|||
className={cn(
|
||||
"h-7 text-xs",
|
||||
statusFilter === status
|
||||
? "bg-muted text-foreground dark:bg-gray-700"
|
||||
? "bg-muted text-foreground"
|
||||
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||
)}
|
||||
onClick={() => setStatusFilter(status)}
|
||||
|
|
@ -172,7 +173,7 @@ function AllThreadsPageContent() {
|
|||
: status.charAt(0).toUpperCase() + status.slice(1)}
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="bg-muted/70 text-muted-foreground ml-1 text-xs dark:bg-gray-800"
|
||||
className="bg-muted/70 text-muted-foreground ml-1 text-xs"
|
||||
>
|
||||
{statusCounts[status]}
|
||||
</Badge>
|
||||
|
|
@ -197,7 +198,7 @@ function AllThreadsPageContent() {
|
|||
</h2>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="bg-muted/70 text-muted-foreground text-xs dark:bg-gray-800"
|
||||
className="bg-muted/70 text-muted-foreground text-xs"
|
||||
>
|
||||
{threads.length}
|
||||
</Badge>
|
||||
|
|
|
|||
|
|
@ -41,38 +41,38 @@
|
|||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.145 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.145 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.985 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.396 0.141 25.723);
|
||||
--destructive-foreground: oklch(0.637 0.237 25.331);
|
||||
--border: oklch(0.269 0 0);
|
||||
--input: oklch(0.269 0 0);
|
||||
--ring: oklch(0.439 0 0);
|
||||
--background: oklch(0.29 0.005 280);
|
||||
--foreground: oklch(0.92 0.01 280);
|
||||
--card: oklch(0.32 0.005 280);
|
||||
--card-foreground: oklch(0.92 0.01 280);
|
||||
--popover: oklch(0.32 0.005 280);
|
||||
--popover-foreground: oklch(0.92 0.01 280);
|
||||
--primary: oklch(0.92 0.01 280);
|
||||
--primary-foreground: oklch(0.24 0.005 280);
|
||||
--secondary: oklch(0.38 0.005 280);
|
||||
--secondary-foreground: oklch(0.92 0.01 280);
|
||||
--muted: oklch(0.35 0.005 280);
|
||||
--muted-foreground: oklch(0.65 0.01 280);
|
||||
--accent: oklch(0.38 0.005 280);
|
||||
--accent-foreground: oklch(0.92 0.01 280);
|
||||
--destructive: oklch(0.55 0.15 20);
|
||||
--destructive-foreground: oklch(0.95 0.01 280);
|
||||
--border: oklch(0.42 0.005 280);
|
||||
--input: oklch(0.42 0.005 280);
|
||||
--ring: oklch(0.5 0.01 280);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar: oklch(0.3 0.005 280);
|
||||
--sidebar-foreground: oklch(0.92 0.01 280);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(0.269 0 0);
|
||||
--sidebar-ring: oklch(0.439 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.92 0.01 280);
|
||||
--sidebar-accent: oklch(0.38 0.005 280);
|
||||
--sidebar-accent-foreground: oklch(0.92 0.01 280);
|
||||
--sidebar-border: oklch(0.42 0.005 280);
|
||||
--sidebar-ring: oklch(0.5 0.01 280);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
|
|
@ -167,6 +167,6 @@
|
|||
}
|
||||
|
||||
.dark .scrollbar-pretty-auto::-webkit-scrollbar-thumb {
|
||||
background-color: rgb(75 85 99);
|
||||
background-color: rgb(95 105 119);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { JSX, useState } from "react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import {
|
||||
Terminal,
|
||||
FileText,
|
||||
|
|
@ -165,7 +166,7 @@ function MatchCaseIcon({ matchCase }: { matchCase: boolean }) {
|
|||
"rounded-sm border border-gray-300 px-1 py-[2px] text-xs dark:border-gray-600",
|
||||
matchCase
|
||||
? "border-blue-500 bg-blue-500/80 text-white"
|
||||
: "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200",
|
||||
: "dark:bg-muted dark:text-muted-foreground bg-muted text-gray-800",
|
||||
)}
|
||||
>
|
||||
<p className="font-mono">Aa</p>
|
||||
|
|
@ -554,7 +555,7 @@ function ActionItem(props: ActionItemProps) {
|
|||
props.output
|
||||
) {
|
||||
return (
|
||||
<div className="bg-muted text-foreground/90 overflow-x-auto p-2 dark:bg-gray-900">
|
||||
<div className="bg-muted text-foreground/90 overflow-x-auto p-2">
|
||||
<pre className="text-xs font-normal whitespace-pre-wrap">
|
||||
{props.output}
|
||||
</pre>
|
||||
|
|
@ -577,7 +578,7 @@ function ActionItem(props: ActionItemProps) {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="bg-muted overflow-x-auto p-2 dark:bg-gray-900">
|
||||
<div className="bg-muted overflow-x-auto p-2">
|
||||
{hasArgs && (
|
||||
<div className="mb-3">
|
||||
<div className="text-muted-foreground mb-2 text-xs font-medium tracking-wide uppercase">
|
||||
|
|
@ -623,7 +624,7 @@ function ActionItem(props: ActionItemProps) {
|
|||
);
|
||||
} else if (props.actionType === "get_url_content" && props.output) {
|
||||
return (
|
||||
<div className="bg-muted text-foreground/90 overflow-x-auto p-2 dark:bg-gray-900">
|
||||
<div className="bg-muted text-foreground/90 overflow-x-auto p-2">
|
||||
<pre className="text-xs font-normal whitespace-pre-wrap">
|
||||
{props.output}
|
||||
</pre>
|
||||
|
|
@ -631,7 +632,7 @@ function ActionItem(props: ActionItemProps) {
|
|||
);
|
||||
} else if (props.actionType === "apply-patch" && props.diff) {
|
||||
return (
|
||||
<div className="bg-muted overflow-x-auto p-2 dark:bg-gray-900">
|
||||
<div className="bg-muted overflow-x-auto p-2">
|
||||
<pre
|
||||
className="text-foreground/90 text-xs font-normal whitespace-pre-wrap"
|
||||
dangerouslySetInnerHTML={{ __html: formatDiff(props.diff) }}
|
||||
|
|
@ -664,7 +665,7 @@ function ActionItem(props: ActionItemProps) {
|
|||
props.scratchpad.length > 0
|
||||
) {
|
||||
return (
|
||||
<div className="bg-muted overflow-x-auto p-2 dark:bg-gray-900">
|
||||
<div className="bg-muted overflow-x-auto p-2">
|
||||
<ul className="list-disc pl-5 text-xs font-normal">
|
||||
{props.scratchpad.map((note, i) => (
|
||||
<li
|
||||
|
|
@ -684,7 +685,7 @@ function ActionItem(props: ActionItemProps) {
|
|||
|
||||
return (
|
||||
<div className="border-border mb-2 overflow-hidden rounded-md border last:mb-0">
|
||||
<div className="border-border flex w-full items-center border-b bg-gray-50 p-2 dark:bg-gray-800">
|
||||
<div className="border-border bg-muted/50 flex w-full items-center border-b p-2">
|
||||
{renderHeaderIcon()}
|
||||
{renderHeaderContent()}
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
|
|
@ -707,15 +708,24 @@ function ActionItem(props: ActionItemProps) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{renderContent()}
|
||||
<AnimatePresence>
|
||||
{expanded && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeInOut" }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
{renderContent()}
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ActionStep(props: ActionStepProps) {
|
||||
const [showReasoning, setShowReasoning] = useState(true);
|
||||
const [showSummary, setShowSummary] = useState(true);
|
||||
|
||||
const reasoningText =
|
||||
"reasoningText" in props ? props.reasoningText : undefined;
|
||||
const summaryText = "summaryText" in props ? props.summaryText : undefined;
|
||||
|
|
@ -724,21 +734,41 @@ export function ActionStep(props: ActionStepProps) {
|
|||
(action: ActionItemProps) => action.status === "done",
|
||||
);
|
||||
|
||||
const [showReasoning, setShowReasoning] = useState(!!reasoningText);
|
||||
const [showSummary, setShowSummary] = useState(!!summaryText);
|
||||
|
||||
return (
|
||||
<div className="border-border overflow-hidden rounded-md border">
|
||||
<div className="border-b border-blue-300 bg-blue-100/50 p-2 dark:border-blue-800 dark:bg-blue-900/50">
|
||||
<div className="border-border bg-muted overflow-hidden rounded-md border">
|
||||
<div className="bg-muted dark:bg-muted border-b border-l-4 border-gray-200 border-l-gray-500 p-2 dark:border-gray-600 dark:border-l-gray-400">
|
||||
<button
|
||||
onClick={() => setShowReasoning(!showReasoning)}
|
||||
className="flex cursor-pointer items-center gap-1 text-xs font-normal text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
className="flex cursor-pointer items-center gap-1.5 text-xs font-medium text-gray-700 transition-colors duration-200 hover:text-gray-800 dark:text-gray-300 dark:hover:text-gray-200"
|
||||
>
|
||||
<MessageSquare className="h-3 w-3" />
|
||||
{showReasoning ? "Hide reasoning" : "Show reasoning"}
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
"h-3 w-3 transition-transform duration-200",
|
||||
showReasoning && "rotate-180",
|
||||
)}
|
||||
/>
|
||||
<span>{showReasoning ? "Hide reasoning" : "Show reasoning"}</span>
|
||||
</button>
|
||||
{showReasoning && (
|
||||
<BasicMarkdownText className="mt-1 text-xs font-normal text-blue-700 dark:text-blue-300">
|
||||
{reasoningText || "No reasoning provided."}
|
||||
</BasicMarkdownText>
|
||||
)}
|
||||
<AnimatePresence>
|
||||
{showReasoning && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeInOut" }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="mt-3 rounded-md bg-gray-50/50 px-3 py-2 dark:bg-gray-800/30">
|
||||
<BasicMarkdownText className="text-xs leading-relaxed text-gray-700 dark:text-gray-300">
|
||||
{reasoningText || "No reasoning provided."}
|
||||
</BasicMarkdownText>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
<div className="p-2">
|
||||
|
|
@ -751,19 +781,36 @@ export function ActionStep(props: ActionStepProps) {
|
|||
</div>
|
||||
|
||||
{summaryText && anyActionDone && (
|
||||
<div className="border-t border-green-300 bg-green-100/50 p-2 dark:border-green-800 dark:bg-green-900/50">
|
||||
<div className="bg-muted dark:bg-muted border-t border-gray-300 p-2 dark:border-gray-600">
|
||||
<button
|
||||
onClick={() => setShowSummary(!showSummary)}
|
||||
className="flex cursor-pointer items-center gap-1 text-xs font-normal text-green-600 hover:text-green-700 dark:text-green-400 dark:hover:text-green-300"
|
||||
className="flex cursor-pointer items-center gap-1.5 text-xs font-medium text-gray-600 transition-colors duration-200 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
|
||||
>
|
||||
<FileText className="h-3 w-3" />
|
||||
{showSummary ? "Hide summary" : "Show summary"}
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
"h-3 w-3 transition-transform duration-200",
|
||||
showSummary && "rotate-180",
|
||||
)}
|
||||
/>
|
||||
<span>{showSummary ? "Hide summary" : "Show summary"}</span>
|
||||
</button>
|
||||
{showSummary && (
|
||||
<span className="text-green-700 dark:text-green-300">
|
||||
<BasicMarkdownText>{summaryText}</BasicMarkdownText>
|
||||
</span>
|
||||
)}
|
||||
<AnimatePresence>
|
||||
{showSummary && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeInOut" }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="mt-3 rounded-md bg-gray-50/50 px-3 py-2 dark:bg-gray-800/30">
|
||||
<BasicMarkdownText className="text-xs leading-relaxed text-gray-700 dark:text-gray-300">
|
||||
{summaryText}
|
||||
</BasicMarkdownText>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
"use client";
|
||||
|
||||
import { Sparkles } from "lucide-react";
|
||||
import { FileSearch } from "lucide-react";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { Check } from "lucide-react";
|
||||
import { Clock } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type CodeReviewStartedProps = {
|
||||
|
|
@ -13,36 +13,34 @@ export function CodeReviewStarted({ status = "done" }: CodeReviewStartedProps) {
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group via-background to-background dark:via-background dark:to-background rounded-xl border bg-gradient-to-br from-blue-50/50 transition-shadow dark:from-blue-950/20",
|
||||
"dark:border-muted-foreground/20 dark:bg-muted/30 rounded-lg border border-blue-200/60 bg-blue-50/30 shadow-sm transition-shadow",
|
||||
"shadow-sm hover:shadow-md",
|
||||
)}
|
||||
>
|
||||
{/* Header */}
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex items-center bg-gradient-to-r from-blue-50 to-blue-50/50 p-4 backdrop-blur-sm dark:from-blue-950/30 dark:to-blue-950/10",
|
||||
"rounded-xl",
|
||||
"dark:bg-muted/40 relative flex items-center bg-blue-50/50 p-3",
|
||||
"rounded-lg",
|
||||
)}
|
||||
>
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-blue-500 shadow-md dark:bg-blue-600">
|
||||
<Sparkles className="h-4 w-4 text-white" />
|
||||
<div className="flex h-7 w-7 items-center justify-center rounded-full bg-blue-500/90 dark:bg-blue-600">
|
||||
<FileSearch className="h-3.5 w-3.5 text-white" />
|
||||
</div>
|
||||
|
||||
<div className="ml-3 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="text-foreground text-sm font-semibold">
|
||||
Code review started
|
||||
</h3>
|
||||
<h3 className="text-foreground text-sm font-medium">Code review</h3>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="border-blue-200 bg-blue-100 text-blue-700 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-300"
|
||||
className="border-blue-200/60 bg-blue-100/80 text-blue-700 dark:border-blue-700/40 dark:bg-blue-900/50 dark:text-blue-300"
|
||||
>
|
||||
<Check className="h-3 w-3" />
|
||||
In Progress
|
||||
<Clock className="h-3 w-3" />
|
||||
In progress
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-muted-foreground mt-1 text-xs">
|
||||
Analyzing code for best practices and potential improvements
|
||||
<p className="text-muted-foreground/80 mt-1 text-xs">
|
||||
Analyzing code quality
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { FileText, ChevronDown, Check } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { MarkdownText } from "../thread/markdown-text";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Button } from "../ui/button";
|
||||
|
|
@ -15,18 +16,18 @@ export function ConversationHistorySummary({ summary }: { summary: string }) {
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group via-background to-background dark:via-background dark:to-background rounded-xl border bg-gradient-to-br from-blue-50/50 transition-shadow dark:from-blue-950/20",
|
||||
"group via-background to-background dark:via-background dark:to-background rounded-xl border bg-gradient-to-br from-gray-50/50 transition-shadow dark:from-gray-900/20",
|
||||
"shadow-sm hover:shadow-md",
|
||||
)}
|
||||
>
|
||||
{/* Header */}
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex items-center bg-gradient-to-r from-blue-50 to-blue-50/50 p-4 backdrop-blur-sm dark:from-blue-950/30 dark:to-blue-950/10",
|
||||
"relative flex items-center bg-gradient-to-r from-gray-50 to-gray-50/50 p-4 backdrop-blur-sm dark:from-gray-900/20 dark:to-gray-900/10",
|
||||
"rounded-xl",
|
||||
)}
|
||||
>
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-blue-500 shadow-md dark:bg-blue-600">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-500 shadow-md dark:bg-gray-600">
|
||||
<FileText className="h-4 w-4 text-white" />
|
||||
</div>
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ export function ConversationHistorySummary({ summary }: { summary: string }) {
|
|||
</h3>
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="border-blue-200 bg-blue-100 text-blue-700 dark:border-blue-800 dark:bg-blue-950 dark:text-blue-300"
|
||||
className="border-gray-200 bg-gray-100 text-gray-700 dark:border-gray-700 dark:bg-gray-800/50 dark:text-gray-300"
|
||||
>
|
||||
<Check className="h-3 w-3" />
|
||||
Complete
|
||||
|
|
@ -64,13 +65,23 @@ export function ConversationHistorySummary({ summary }: { summary: string }) {
|
|||
</div>
|
||||
|
||||
{/* Content */}
|
||||
{expanded && (
|
||||
<div className="p-4">
|
||||
<div className="text-sm">
|
||||
<MarkdownText>{summary}</MarkdownText>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<AnimatePresence>
|
||||
{expanded && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeInOut" }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="p-4">
|
||||
<div className="text-sm">
|
||||
<MarkdownText>{summary}</MarkdownText>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import {
|
||||
GitPullRequest,
|
||||
Loader2,
|
||||
|
|
@ -43,7 +44,7 @@ export function PullRequestOpened({
|
|||
return (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="border-purple-200 bg-purple-100 text-purple-700 dark:border-purple-800 dark:bg-purple-950 dark:text-purple-300"
|
||||
className="border-gray-200 bg-gray-100 text-gray-700 dark:border-gray-700 dark:bg-gray-800/50 dark:text-gray-300"
|
||||
>
|
||||
<Clock className="h-3 w-3" />
|
||||
Preparing
|
||||
|
|
@ -53,7 +54,7 @@ export function PullRequestOpened({
|
|||
return (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="border-purple-200 bg-purple-100 text-purple-700 dark:border-purple-800 dark:bg-purple-950 dark:text-purple-300"
|
||||
className="border-gray-200 bg-gray-100 text-gray-700 dark:border-gray-700 dark:bg-gray-800/50 dark:text-gray-300"
|
||||
>
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
Opening
|
||||
|
|
@ -64,7 +65,7 @@ export function PullRequestOpened({
|
|||
return (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="border-slate-200 bg-slate-100 text-slate-700 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-300"
|
||||
className="border-slate-200 bg-slate-100 text-slate-700 dark:border-slate-700 dark:bg-slate-800/50 dark:text-slate-300"
|
||||
>
|
||||
<GitPullRequestDraft className="h-3 w-3" />
|
||||
Draft
|
||||
|
|
@ -74,7 +75,7 @@ export function PullRequestOpened({
|
|||
return (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="border-emerald-200 bg-emerald-100 text-emerald-700 dark:border-emerald-800 dark:bg-emerald-950 dark:text-emerald-300"
|
||||
className="border-emerald-200 bg-emerald-100 text-emerald-700 dark:border-emerald-700 dark:bg-emerald-800/50 dark:text-emerald-300"
|
||||
>
|
||||
<Check className="h-3 w-3" />
|
||||
Opened
|
||||
|
|
@ -110,18 +111,18 @@ export function PullRequestOpened({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"group via-background to-background dark:via-background dark:to-background rounded-xl border bg-gradient-to-br from-purple-50/50 transition-shadow dark:from-purple-950/20",
|
||||
"group via-background to-background dark:via-background dark:to-background rounded-xl border bg-gradient-to-br from-gray-50/50 transition-shadow dark:from-gray-900/20",
|
||||
"shadow-sm hover:shadow-md",
|
||||
)}
|
||||
>
|
||||
{/* Header */}
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex items-center bg-gradient-to-r from-purple-50 to-purple-50/50 p-4 backdrop-blur-sm dark:from-purple-950/30 dark:to-purple-950/10",
|
||||
"relative flex items-center bg-gradient-to-r from-gray-50 to-gray-50/50 p-4 backdrop-blur-sm dark:from-gray-900/20 dark:to-gray-900/10",
|
||||
"rounded-xl",
|
||||
)}
|
||||
>
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-purple-500 shadow-md dark:bg-purple-600">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-500 shadow-md dark:bg-gray-600">
|
||||
{isDraft ? (
|
||||
<GitPullRequestDraft className="h-4 w-4 text-white" />
|
||||
) : (
|
||||
|
|
@ -178,18 +179,28 @@ export function PullRequestOpened({
|
|||
</div>
|
||||
|
||||
{/* Content */}
|
||||
{expanded && description && status === "done" && (
|
||||
<div className="border-t p-4">
|
||||
<div className="bg-muted/30 rounded-lg p-3">
|
||||
<h4 className="text-muted-foreground mb-2 text-xs font-medium">
|
||||
Description
|
||||
</h4>
|
||||
<pre className="text-foreground text-sm whitespace-pre-wrap">
|
||||
{description}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<AnimatePresence>
|
||||
{expanded && description && status === "done" && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeInOut" }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="border-t p-4">
|
||||
<div className="rounded-lg bg-gray-50/50 p-3 dark:bg-gray-800/30">
|
||||
<h4 className="text-muted-foreground mb-2 text-xs font-medium">
|
||||
Description
|
||||
</h4>
|
||||
<pre className="text-foreground text-sm whitespace-pre-wrap">
|
||||
{description}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,16 +72,16 @@ export function PushChanges({
|
|||
return (
|
||||
<div className="overflow-hidden rounded-md border border-gray-200">
|
||||
{reasoningText && (
|
||||
<div className="border-b border-blue-100 bg-blue-50 p-2">
|
||||
<div className="border-b border-blue-200 bg-blue-50 p-2 dark:border-blue-800/50 dark:bg-blue-950/20">
|
||||
<button
|
||||
onClick={() => setShowReasoning(!showReasoning)}
|
||||
className="flex items-center gap-1 text-xs font-normal text-blue-700 hover:text-blue-800"
|
||||
className="flex items-center gap-1 text-xs font-normal text-blue-700 hover:text-blue-800 dark:text-blue-300 dark:hover:text-blue-200"
|
||||
>
|
||||
<MessageSquare className="h-3 w-3" />
|
||||
{showReasoning ? "Hide reasoning" : "Show reasoning"}
|
||||
</button>
|
||||
{showReasoning && (
|
||||
<BasicMarkdownText className="mt-1 text-xs font-normal text-blue-800">
|
||||
<BasicMarkdownText className="mt-1 text-xs font-normal text-blue-800 dark:text-blue-200">
|
||||
{reasoningText}
|
||||
</BasicMarkdownText>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,9 @@ export function ReplanningStep({
|
|||
case "generating":
|
||||
return <Loader2 className="h-3.5 w-3.5 animate-spin text-gray-500" />;
|
||||
case "done":
|
||||
return <CheckCircle className="h-3.5 w-3.5 text-green-500" />;
|
||||
return (
|
||||
<CheckCircle className="h-3.5 w-3.5 text-green-600 dark:text-green-400" />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -52,16 +54,16 @@ export function ReplanningStep({
|
|||
return (
|
||||
<div className="overflow-hidden rounded-md border border-gray-200">
|
||||
{reasoningText && (
|
||||
<div className="border-b border-blue-100 bg-blue-50 p-2">
|
||||
<div className="border-b border-blue-200 bg-blue-50 p-2 dark:border-blue-800/50 dark:bg-blue-950/20">
|
||||
<button
|
||||
onClick={() => setShowReasoning(!showReasoning)}
|
||||
className="flex items-center gap-1 text-xs font-normal text-blue-700 hover:text-blue-800"
|
||||
className="flex items-center gap-1 text-xs font-normal text-blue-700 hover:text-blue-800 dark:text-blue-300 dark:hover:text-blue-200"
|
||||
>
|
||||
<MessageSquare className="h-3 w-3" />
|
||||
{showReasoning ? "Hide reasoning" : "Show reasoning"}
|
||||
</button>
|
||||
{showReasoning && (
|
||||
<BasicMarkdownText className="mt-1 text-xs font-normal text-blue-800">
|
||||
<BasicMarkdownText className="mt-1 text-xs font-normal text-blue-800 dark:text-blue-200">
|
||||
{reasoningText}
|
||||
</BasicMarkdownText>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export function MarkTaskCompleted({
|
|||
);
|
||||
case "done":
|
||||
return (
|
||||
<CheckCircle className="h-3.5 w-3.5 text-green-500 dark:text-green-400" />
|
||||
<CheckCircle className="h-3.5 w-3.5 text-green-600 dark:text-green-400" />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
@ -61,16 +61,16 @@ export function MarkTaskCompleted({
|
|||
return (
|
||||
<div className="overflow-hidden rounded-md border border-gray-200 dark:border-gray-800">
|
||||
{reasoningText && (
|
||||
<div className="border-b border-blue-100 bg-blue-50 p-2 dark:border-blue-800 dark:bg-blue-900/50">
|
||||
<div className="border-b border-blue-200 bg-blue-50 p-2 dark:border-blue-800/50 dark:bg-blue-950/20">
|
||||
<button
|
||||
onClick={() => setShowReasoning(!showReasoning)}
|
||||
className="flex items-center gap-1 text-xs font-normal text-blue-700 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
className="flex items-center gap-1 text-xs font-normal text-blue-700 hover:text-blue-800 dark:text-blue-300 dark:hover:text-blue-200"
|
||||
>
|
||||
<MessageSquare className="h-3 w-3" />
|
||||
{showReasoning ? "Hide reasoning" : "Show reasoning"}
|
||||
</button>
|
||||
{showReasoning && (
|
||||
<BasicMarkdownText className="mt-1 text-xs font-normal text-blue-800 dark:text-blue-400">
|
||||
<BasicMarkdownText className="mt-1 text-xs font-normal text-blue-800 dark:text-blue-200">
|
||||
{reasoningText}
|
||||
</BasicMarkdownText>
|
||||
)}
|
||||
|
|
@ -185,10 +185,10 @@ export function MarkTaskIncomplete({
|
|||
return (
|
||||
<div className="overflow-hidden rounded-md border border-gray-200 dark:border-gray-800">
|
||||
{reasoningText && (
|
||||
<div className="border-b border-blue-100 bg-blue-50 p-2 dark:border-blue-800 dark:bg-blue-900/50">
|
||||
<div className="border-b border-blue-200 bg-blue-50 p-2 dark:border-blue-800/50 dark:bg-blue-950/20">
|
||||
<button
|
||||
onClick={() => setShowReasoning(!showReasoning)}
|
||||
className="flex items-center gap-1 text-xs font-normal text-blue-700 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
className="flex items-center gap-1 text-xs font-normal text-blue-700 hover:text-blue-800 dark:text-blue-300 dark:hover:text-blue-200"
|
||||
>
|
||||
<MessageSquare className="h-3 w-3" />
|
||||
{showReasoning ? "Hide reasoning" : "Show reasoning"}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { CheckCircle, Loader2, FileText, MinusCircle } from "lucide-react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { CheckCircle, Loader2, ChevronDown, MinusCircle } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { BasicMarkdownText } from "../thread/markdown-text";
|
||||
|
||||
|
|
@ -29,9 +30,13 @@ export function TaskSummary({
|
|||
);
|
||||
case "done":
|
||||
if (completed === false) {
|
||||
return <MinusCircle className="size-3.5 text-amber-500" />;
|
||||
return (
|
||||
<MinusCircle className="size-3.5 text-amber-500 dark:text-amber-400" />
|
||||
);
|
||||
}
|
||||
return <CheckCircle className="size-3.5 text-green-500" />;
|
||||
return (
|
||||
<CheckCircle className="size-3.5 text-green-600 dark:text-green-400" />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -52,7 +57,7 @@ export function TaskSummary({
|
|||
return (
|
||||
<div className="border-border overflow-hidden rounded-md border">
|
||||
<div
|
||||
className={"flex items-center border-b bg-gray-50 p-2 dark:bg-gray-800"}
|
||||
className={"bg-muted/50 flex items-center border-b p-2"}
|
||||
onClick={
|
||||
status === "done" && summaryText
|
||||
? () => setExpanded(!expanded)
|
||||
|
|
@ -68,36 +73,60 @@ export function TaskSummary({
|
|||
{summaryText && (
|
||||
<div
|
||||
className={cn(
|
||||
"border-t p-2",
|
||||
"border-t border-l-4 p-2",
|
||||
completed === false
|
||||
? "border-amber-300 bg-amber-100/50 dark:border-amber-800 dark:bg-amber-900/50"
|
||||
: "border-green-300 bg-green-100/50 dark:border-green-800 dark:bg-green-900/50",
|
||||
? "border-amber-300 border-l-amber-500 bg-amber-100/50 dark:border-amber-700/50 dark:border-l-amber-400/70 dark:bg-amber-900/30"
|
||||
: "border-green-200/80 border-l-green-500/80 bg-green-50/60 dark:border-green-800/30 dark:border-l-green-400/70 dark:bg-green-950/10",
|
||||
)}
|
||||
>
|
||||
<button
|
||||
onClick={() => setShowSummary(!showSummary)}
|
||||
className={cn(
|
||||
"flex cursor-pointer items-center gap-1 text-xs font-normal",
|
||||
"flex cursor-pointer items-center gap-1.5 text-xs font-medium transition-colors duration-200",
|
||||
completed === false
|
||||
? "text-amber-600 hover:text-amber-700 dark:text-amber-400 dark:hover:text-amber-300"
|
||||
? "text-amber-600 hover:text-amber-700 dark:text-amber-400/90 dark:hover:text-amber-300"
|
||||
: "text-green-600 hover:text-green-700 dark:text-green-400 dark:hover:text-green-300",
|
||||
)}
|
||||
>
|
||||
<FileText className="h-3 w-3" />
|
||||
{showSummary ? "Hide reflection" : "Show reflection"}
|
||||
</button>
|
||||
{showSummary && (
|
||||
<BasicMarkdownText
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
"mt-1 text-xs",
|
||||
completed === false
|
||||
? "text-amber-700 dark:text-amber-300"
|
||||
: "text-green-700 dark:text-green-300",
|
||||
"h-3 w-3 transition-transform duration-200",
|
||||
showSummary && "rotate-180",
|
||||
)}
|
||||
>
|
||||
{summaryText}
|
||||
</BasicMarkdownText>
|
||||
)}
|
||||
/>
|
||||
<span>{showSummary ? "Hide reflection" : "Show reflection"}</span>
|
||||
</button>
|
||||
<AnimatePresence>
|
||||
{showSummary && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeInOut" }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"mt-3 rounded-md px-3 py-2",
|
||||
completed === false
|
||||
? "bg-amber-50/50 dark:bg-amber-900/20"
|
||||
: "bg-green-50/50 dark:bg-green-900/20",
|
||||
)}
|
||||
>
|
||||
<BasicMarkdownText
|
||||
className={cn(
|
||||
"text-xs leading-relaxed",
|
||||
completed === false
|
||||
? "text-amber-700 dark:text-amber-300/90"
|
||||
: "text-green-700 dark:text-green-300",
|
||||
)}
|
||||
>
|
||||
{summaryText}
|
||||
</BasicMarkdownText>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ function AuthStatusContent() {
|
|||
if (!showGetStarted && !showInstallApp && !showLoading) {
|
||||
router.push("/chat");
|
||||
}
|
||||
}, [showGetStarted, showInstallApp, showLoading]);
|
||||
}, [showGetStarted, showInstallApp, showLoading, router]);
|
||||
|
||||
if (showGetStarted) {
|
||||
return (
|
||||
|
|
@ -172,6 +172,7 @@ function AuthStatusContent() {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export default function AuthStatus() {
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ export function RepositoryBranchSelectors() {
|
|||
const [threadId] = useQueryState("threadId");
|
||||
const chatStarted = !!threadId;
|
||||
const defaultButtonStyles =
|
||||
"bg-inherit border-none text-muted-foreground hover:text-muted-foreground/70 text-xs p-0 px-0 py-0 !p-0 !px-0 !py-0 h-fit hover:bg-inherit shadow-none";
|
||||
"bg-inherit border-none text-foreground hover:text-foreground/80 text-xs p-0 px-0 py-0 !p-0 !px-0 !py-0 h-fit hover:bg-inherit shadow-none";
|
||||
const defaultStylesChatStarted =
|
||||
"hover:bg-inherit cursor-default hover:cursor-default text-muted-foreground hover:border-gray-300 hover:ring-inherit shadow-none p-0 px-0 py-0 !p-0 !px-0 !py-0";
|
||||
"hover:bg-inherit cursor-default hover:cursor-default text-foreground hover:border-gray-300 hover:ring-inherit shadow-none p-0 px-0 py-0 !p-0 !px-0 !py-0";
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-1 rounded-md border border-gray-200 p-1 dark:border-gray-700">
|
||||
|
|
|
|||
28
apps/web/src/components/icons/openswe.tsx
Normal file
28
apps/web/src/components/icons/openswe.tsx
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -5,6 +5,7 @@ import { TooltipIconButton } from "../ui/tooltip-icon-button";
|
|||
import { Dispatch, SetStateAction, useState } from "react";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { Button } from "../ui/button";
|
||||
import { MarkdownText } from "../thread/markdown-text";
|
||||
|
||||
interface PlanViewerProps {
|
||||
planItems: PlanItem[];
|
||||
|
|
@ -166,7 +167,7 @@ export function PlanViewer({
|
|||
className="text-foreground"
|
||||
/>
|
||||
) : (
|
||||
<p
|
||||
<MarkdownText
|
||||
className={cn("text-sm leading-relaxed", {
|
||||
"text-foreground": status === "current",
|
||||
"text-foreground/80":
|
||||
|
|
@ -175,7 +176,7 @@ export function PlanViewer({
|
|||
})}
|
||||
>
|
||||
{planItems[item.index].plan}
|
||||
</p>
|
||||
</MarkdownText>
|
||||
)}
|
||||
|
||||
{/* Summary for completed tasks */}
|
||||
|
|
@ -184,9 +185,9 @@ export function PlanViewer({
|
|||
<p className="mb-1 text-xs font-medium text-green-700 dark:text-green-300">
|
||||
Summary:
|
||||
</p>
|
||||
<p className="text-xs text-green-600 dark:text-green-400">
|
||||
<MarkdownText className="text-xs text-green-600 dark:text-green-400">
|
||||
{item.summary}
|
||||
</p>
|
||||
</MarkdownText>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { PlanViewer } from "./plan-viewer";
|
||||
import { Button } from "../ui/button";
|
||||
import { useProposedPlan } from "../thread/agent-inbox/hooks/useProposedPlan";
|
||||
import { PlanItem } from "@open-swe/shared/open-swe/types";
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
import { X, ArrowDown } from "lucide-react";
|
||||
|
||||
export function ProposedPlan({
|
||||
originalPlanItems,
|
||||
|
|
@ -28,21 +28,32 @@ export function ProposedPlan({
|
|||
setPlanItems={setPlanItems}
|
||||
isProposedPlan={true}
|
||||
/>
|
||||
<div className="flex w-full flex-row items-center justify-center gap-2">
|
||||
<Button
|
||||
onClick={handleResumePlan}
|
||||
className="w-full"
|
||||
variant="brand"
|
||||
>
|
||||
{changesMade ? "Submit" : "Approve"}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleRejectPlan}
|
||||
className="w-full"
|
||||
variant="destructive"
|
||||
>
|
||||
Reject
|
||||
</Button>
|
||||
<div className="py-8">
|
||||
<div className="flex items-center justify-center gap-8">
|
||||
<button
|
||||
onClick={handleRejectPlan}
|
||||
className="group flex cursor-pointer flex-col items-center gap-2 transition-all"
|
||||
>
|
||||
<div className="rounded-full border-2 border-dashed border-red-500 p-3 transition-all group-hover:border-solid group-hover:bg-red-50 dark:group-hover:bg-red-950/50">
|
||||
<X className="h-5 w-5 text-red-500" />
|
||||
</div>
|
||||
<span className="text-muted-foreground text-xs group-hover:text-red-600 dark:group-hover:text-red-400">
|
||||
Reject
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={handleResumePlan}
|
||||
className="group flex cursor-pointer flex-col items-center gap-2 transition-all"
|
||||
>
|
||||
<div className="rounded-full border-2 border-dashed border-green-500 p-3 transition-all group-hover:border-solid group-hover:bg-green-50 dark:group-hover:bg-green-950/50">
|
||||
<ArrowDown className="h-5 w-5 text-green-500" />
|
||||
</div>
|
||||
<span className="text-muted-foreground text-xs group-hover:text-green-600 dark:group-hover:text-green-400">
|
||||
{changesMade ? "Submit changes" : "Continue"}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -102,10 +102,12 @@ export function TasksSidebar({
|
|||
switch (state) {
|
||||
case "completed":
|
||||
return (
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600 dark:text-green-400" />
|
||||
<CheckCircle2 className="h-4 w-4 text-green-500 dark:text-green-400/80" />
|
||||
);
|
||||
case "current":
|
||||
return <Play className="h-4 w-4 text-blue-500 dark:text-blue-400" />;
|
||||
return (
|
||||
<Play className="h-4 w-4 text-orange-600 dark:text-orange-400" />
|
||||
);
|
||||
default:
|
||||
return <Circle className="h-4 w-4 text-gray-400 dark:text-gray-500" />;
|
||||
}
|
||||
|
|
@ -140,7 +142,7 @@ export function TasksSidebar({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"fixed top-0 right-0 z-10 h-screen border-l border-gray-200 bg-white shadow-lg transition-all duration-300 dark:border-gray-700 dark:bg-gray-900",
|
||||
"dark:bg-background fixed top-0 right-0 z-10 h-screen bg-white shadow-lg transition-all duration-300",
|
||||
isOpen ? "w-80 md:w-xl" : "w-0 overflow-hidden border-l-0",
|
||||
)}
|
||||
>
|
||||
|
|
@ -161,7 +163,7 @@ export function TasksSidebar({
|
|||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={onClose}
|
||||
className="h-8 w-8 p-0 hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
className="h-8 w-8 p-0 hover:bg-gray-100 dark:hover:bg-gray-700/50"
|
||||
>
|
||||
<PanelLeftClose className="h-4 w-4" />
|
||||
</Button>
|
||||
|
|
@ -174,7 +176,7 @@ export function TasksSidebar({
|
|||
Task
|
||||
</label>
|
||||
<select
|
||||
className="w-full rounded border border-gray-200 px-2 py-1 text-sm dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
|
||||
className="bg-muted dark:bg-muted w-full rounded border border-gray-200 px-2 py-1 text-sm dark:border-gray-600 dark:text-gray-200"
|
||||
value={currentTask.id}
|
||||
onChange={(e) => {
|
||||
const newTaskIndex = taskPlan.tasks.findIndex(
|
||||
|
|
@ -205,12 +207,12 @@ export function TasksSidebar({
|
|||
Plan Revision
|
||||
</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex flex-1 items-center gap-1 rounded border border-gray-200 bg-gray-50 px-2 py-1 dark:border-gray-600 dark:bg-gray-800">
|
||||
<div className="bg-muted dark:bg-muted flex flex-1 items-center gap-1 rounded border border-gray-200 px-2 py-1 dark:border-gray-600">
|
||||
<div className="flex items-center gap-1">
|
||||
{currentRevision?.createdBy === "agent" ? (
|
||||
<Bot className="h-3 w-3 text-blue-500" />
|
||||
<Bot className="h-3 w-3 text-gray-600 dark:text-gray-400" />
|
||||
) : (
|
||||
<User className="h-3 w-3 text-green-500" />
|
||||
<User className="h-3 w-3 text-green-500 dark:text-green-400/80" />
|
||||
)}
|
||||
<Clock className="h-3 w-3 text-gray-500 dark:text-gray-400" />
|
||||
</div>
|
||||
|
|
@ -278,7 +280,7 @@ export function TasksSidebar({
|
|||
|
||||
{/* Filter controls */}
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center gap-1 rounded border border-gray-200 bg-gray-50 px-2 py-1 dark:border-gray-600 dark:bg-gray-800">
|
||||
<div className="bg-muted dark:bg-muted flex items-center gap-1 rounded border border-gray-200 px-2 py-1 dark:border-gray-600">
|
||||
<Filter className="h-3 w-3 text-gray-500 dark:text-gray-400" />
|
||||
<select
|
||||
className="border-none bg-transparent text-xs outline-none dark:text-gray-300"
|
||||
|
|
@ -312,11 +314,11 @@ export function TasksSidebar({
|
|||
className={cn(
|
||||
"rounded-lg border p-3",
|
||||
state === "current" &&
|
||||
"border-blue-200 bg-blue-50 dark:border-blue-700 dark:bg-blue-900/20",
|
||||
"border-orange-200 bg-orange-50 dark:border-orange-800/50 dark:bg-orange-950/20",
|
||||
state === "completed" &&
|
||||
"border-green-200 bg-green-50 dark:border-green-700 dark:bg-green-900/20",
|
||||
"border-green-200/80 bg-green-50/60 dark:border-green-800/30 dark:bg-green-950/15",
|
||||
state === "remaining" &&
|
||||
"border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800",
|
||||
"bg-muted dark:bg-muted border-gray-200 dark:border-gray-700",
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
|
|
@ -340,11 +342,11 @@ export function TasksSidebar({
|
|||
className={cn(
|
||||
"rounded-full px-2 py-1 text-xs",
|
||||
state === "completed" &&
|
||||
"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
|
||||
"bg-green-100/70 text-green-600 dark:bg-green-900/20 dark:text-green-300/90",
|
||||
state === "current" &&
|
||||
"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
|
||||
"bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300",
|
||||
state === "remaining" &&
|
||||
"bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300",
|
||||
"bg-muted dark:bg-muted text-gray-700 dark:text-gray-300",
|
||||
)}
|
||||
>
|
||||
{state === "completed"
|
||||
|
|
@ -373,7 +375,7 @@ export function TasksSidebar({
|
|||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 p-0 text-xs text-green-700 dark:text-green-400"
|
||||
className="h-6 p-0 text-xs text-green-600 dark:text-green-300/90"
|
||||
>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="mr-1 h-3 w-3" />
|
||||
|
|
@ -384,7 +386,7 @@ export function TasksSidebar({
|
|||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className="mt-2">
|
||||
<div className="rounded border border-green-200 bg-green-50 p-2 text-xs text-green-800 dark:border-green-700 dark:bg-green-900/30 dark:text-green-400">
|
||||
<div className="rounded border border-green-200/80 bg-green-50/60 p-2 text-xs text-green-700 dark:border-green-800/30 dark:bg-green-950/15 dark:text-green-300/90">
|
||||
<BasicMarkdownText>
|
||||
{item.summary}
|
||||
</BasicMarkdownText>
|
||||
|
|
@ -412,7 +414,7 @@ export function TaskPlanView({ taskPlan, onTaskChange }: TaskPlanViewProps) {
|
|||
|
||||
if (taskPlan.tasks.length === 0) {
|
||||
return (
|
||||
<div className="w-full rounded border border-gray-200 bg-gray-50 p-2 dark:border-gray-700 dark:bg-gray-800">
|
||||
<div className="bg-muted dark:bg-muted w-full rounded border border-gray-200 p-2 dark:border-gray-700">
|
||||
<div className="text-center text-xs text-gray-500 dark:text-gray-400">
|
||||
No tasks available
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import {
|
|||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from "@/components/ui/hover-card";
|
||||
import { InlineMarkdownText } from "../thread/markdown-text";
|
||||
import { InlineMarkdownText } from "@/components/thread/markdown-text";
|
||||
|
||||
interface ProgressBarProps {
|
||||
taskPlan?: TaskPlan;
|
||||
|
|
@ -39,7 +39,7 @@ export function ProgressBar({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 sm:mt-4 dark:border-gray-700 dark:bg-gray-800",
|
||||
"w-full rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 sm:mt-4 dark:border-gray-600/50 dark:bg-gray-700/30",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
@ -73,7 +73,7 @@ export function ProgressBar({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 sm:mt-4 dark:border-gray-700 dark:bg-gray-800",
|
||||
"w-full rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 sm:mt-4 dark:border-gray-600/50 dark:bg-gray-700/30",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
@ -105,7 +105,7 @@ export function ProgressBar({
|
|||
const getSegmentColor = (state: string) => {
|
||||
switch (state) {
|
||||
case "completed":
|
||||
return "bg-green-400 dark:bg-green-500";
|
||||
return "bg-green-500/90 dark:bg-green-400/80";
|
||||
case "current":
|
||||
return "bg-blue-400 dark:bg-blue-500";
|
||||
default:
|
||||
|
|
@ -116,7 +116,7 @@ export function ProgressBar({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"bg-muted/70 w-full overflow-hidden rounded-lg px-1 py-1.5 sm:px-3 dark:bg-gray-800",
|
||||
"dark:bg-muted w-full overflow-hidden rounded-lg bg-gray-50 px-1 py-1.5 sm:px-3",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
@ -140,11 +140,12 @@ export function ProgressBar({
|
|||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
side="top"
|
||||
className="max-w-xs p-2 text-xs"
|
||||
className="bg-popover text-popover-foreground max-w-xs border p-2 text-xs before:hidden after:hidden"
|
||||
sideOffset={5}
|
||||
>
|
||||
<div className="space-y-1">
|
||||
<p className="font-medium">Plan Progress</p>
|
||||
<p>
|
||||
<p className="text-foreground font-medium">Plan Progress</p>
|
||||
<p className="text-muted-foreground">
|
||||
Shows the current progress of the AI agent's plan execution.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -165,7 +166,7 @@ export function ProgressBar({
|
|||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onOpenSidebar}
|
||||
className="h-6 border-blue-200 text-xs hover:bg-blue-50 dark:border-blue-700 dark:hover:bg-blue-900/20"
|
||||
className="hover:bg-muted/80 hover:border-muted-foreground/50 h-6 text-xs"
|
||||
>
|
||||
<List className="size-3" />
|
||||
<span className="hidden sm:inline">Tasks</span>
|
||||
|
|
@ -176,9 +177,9 @@ export function ProgressBar({
|
|||
</div>
|
||||
|
||||
{showLegend && (
|
||||
<div className="mb-2 flex flex-wrap items-center gap-2 rounded border border-gray-200 bg-gray-50 px-2 py-1 text-xs sm:gap-3 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200">
|
||||
<div className="mb-2 flex flex-wrap items-center gap-2 rounded border border-gray-200 bg-gray-50 px-2 py-1 text-xs sm:gap-3 dark:border-gray-600/50 dark:bg-gray-700/30 dark:text-gray-200">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="h-2 w-2 rounded-full bg-green-400 dark:bg-green-500"></div>
|
||||
<div className="h-2 w-2 rounded-full bg-green-500/90 dark:bg-green-400/80"></div>
|
||||
<span>Completed</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export const ContentBlocksPreview: React.FC<ContentBlocksPreviewProps> = ({
|
|||
}) => {
|
||||
if (!blocks.length) return null;
|
||||
return (
|
||||
<div className={cn("flex flex-wrap gap-2 pb-1", className)}>
|
||||
<div className={cn("flex flex-wrap gap-2 pb-3", className)}>
|
||||
{blocks.map((block, idx) => (
|
||||
<MultimodalPreview
|
||||
key={idx}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,14 @@ export const MultimodalPreview: React.FC<MultimodalPreviewProps> = ({
|
|||
block.mime_type.startsWith("image/")
|
||||
) {
|
||||
const url = `data:${block.mime_type};base64,${block.data}`;
|
||||
let imgClass: string = "rounded-md object-cover h-16 w-16 text-lg";
|
||||
if (size === "sm") imgClass = "rounded-md object-cover h-10 w-10 text-base";
|
||||
if (size === "lg") imgClass = "rounded-md object-cover h-24 w-24 text-xl";
|
||||
let imgClass: string =
|
||||
"rounded-md object-cover h-16 w-16 text-lg shadow-sm ring-1 ring-border";
|
||||
if (size === "sm")
|
||||
imgClass =
|
||||
"rounded-md object-cover h-10 w-10 text-base shadow-sm ring-1 ring-border";
|
||||
if (size === "lg")
|
||||
imgClass =
|
||||
"rounded-md object-cover h-24 w-24 text-xl shadow-sm ring-1 ring-border";
|
||||
return (
|
||||
<div className={cn("relative inline-block", className)}>
|
||||
<Image
|
||||
|
|
@ -41,11 +46,11 @@ export const MultimodalPreview: React.FC<MultimodalPreviewProps> = ({
|
|||
{removable && (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute top-1 right-1 z-10 cursor-pointer rounded-full p-1 text-white hover:bg-gray-800"
|
||||
className="bg-destructive/90 text-destructive-foreground hover:bg-destructive absolute -top-2 -right-2 z-10 cursor-pointer rounded-full p-1 shadow-sm"
|
||||
onClick={onRemove}
|
||||
aria-label="Remove image"
|
||||
>
|
||||
<XIcon className="h-4 w-4" />
|
||||
<XIcon className="h-3 w-3" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -63,20 +68,20 @@ export const MultimodalPreview: React.FC<MultimodalPreviewProps> = ({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex items-center justify-start gap-2 rounded-md border-[1px] border-gray-800 bg-inherit px-3 py-2",
|
||||
"border-border bg-card/50 relative flex items-center justify-start gap-2 rounded-md border px-3 py-2",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-shrink-0 flex-col items-start justify-start">
|
||||
<File
|
||||
className={cn(
|
||||
"text-teal-700",
|
||||
"text-primary",
|
||||
size === "sm" ? "h-5 w-5" : "h-7 w-7",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
className={cn("min-w-0 flex-1 text-sm break-all text-gray-300")}
|
||||
className={cn("text-foreground min-w-0 flex-1 text-sm break-all")}
|
||||
style={{ wordBreak: "break-all", whiteSpace: "pre-wrap" }}
|
||||
>
|
||||
{String(filename)}
|
||||
|
|
@ -84,7 +89,7 @@ export const MultimodalPreview: React.FC<MultimodalPreviewProps> = ({
|
|||
{removable && (
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer self-start rounded-full p-1 text-teal-700 hover:bg-gray-800"
|
||||
className="text-muted-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer self-start rounded-full p-1"
|
||||
onClick={onRemove}
|
||||
aria-label="Remove PDF"
|
||||
>
|
||||
|
|
@ -99,7 +104,7 @@ export const MultimodalPreview: React.FC<MultimodalPreviewProps> = ({
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-2 rounded-md border-[1px] border-gray-800 bg-inherit px-3 py-2 text-gray-500",
|
||||
"border-border bg-card/50 text-muted-foreground flex items-center gap-2 rounded-md border px-3 py-2",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
@ -108,7 +113,7 @@ export const MultimodalPreview: React.FC<MultimodalPreviewProps> = ({
|
|||
{removable && (
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer self-start rounded-full p-1 text-gray-500 hover:bg-gray-800"
|
||||
className="text-muted-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer self-start rounded-full p-1"
|
||||
onClick={onRemove}
|
||||
aria-label="Remove file"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ const CodeHeader: FC<CodeHeaderProps> = ({ language, code }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-4 rounded-t-lg bg-zinc-900 px-4 py-2 text-sm font-semibold text-white">
|
||||
<div className="bg-muted text-foreground flex items-center justify-between gap-4 rounded-t-lg border-b px-4 py-2 text-sm font-semibold">
|
||||
<span className="lowercase [&>span]:text-xs">{language}</span>
|
||||
<TooltipIconButton
|
||||
tooltip="Copy"
|
||||
|
|
@ -196,7 +196,7 @@ const defaultComponents: any = {
|
|||
pre: ({ className, ...props }: { className?: string }) => (
|
||||
<pre
|
||||
className={cn(
|
||||
"w-full overflow-x-auto rounded-lg bg-black text-white",
|
||||
"bg-muted text-foreground w-full overflow-x-auto rounded-lg",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
|
@ -321,12 +321,17 @@ const InlineMarkdownTextImpl: FC<{ children: string; className?: string }> = ({
|
|||
if (match) {
|
||||
// If it's a code block, render as plain text to keep it inline
|
||||
return (
|
||||
<span className={cn("font-mono", className)}>{String(children)}</span>
|
||||
<span className={cn("font-mono text-sm", className)}>
|
||||
{String(children)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<code
|
||||
className={cn("bg-muted rounded px-1 py-0.5 font-mono", className)}
|
||||
className={cn(
|
||||
"bg-muted rounded px-1 py-0.5 font-mono text-sm",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@ export function ToolCalls({
|
|||
key={idx}
|
||||
className="border-border overflow-hidden rounded-lg border"
|
||||
>
|
||||
<div className="border-border bg-card border-b px-4 py-2 dark:bg-gray-800">
|
||||
<div className="border-border bg-card border-b px-4 py-2">
|
||||
<h3 className="text-foreground font-medium">
|
||||
{tc.name}
|
||||
{tc.id && (
|
||||
<code className="bg-muted ml-2 rounded px-2 py-1 text-sm dark:bg-gray-700">
|
||||
<code className="bg-muted ml-2 rounded px-2 py-1 text-sm">
|
||||
{tc.id}
|
||||
</code>
|
||||
)}
|
||||
|
|
@ -44,7 +44,7 @@ export function ToolCalls({
|
|||
</td>
|
||||
<td className="text-foreground/80 px-4 py-2 text-sm">
|
||||
{isComplexValue(value) ? (
|
||||
<code className="bg-muted/70 rounded px-2 py-1 font-mono text-sm break-all dark:bg-gray-800">
|
||||
<code className="bg-muted/70 rounded px-2 py-1 font-mono text-sm break-all">
|
||||
{JSON.stringify(value, null, 2)}
|
||||
</code>
|
||||
) : (
|
||||
|
|
@ -98,12 +98,12 @@ export function ToolResult({ message }: { message: ToolMessage }) {
|
|||
return (
|
||||
<div className="mx-auto grid grid-rows-[1fr_auto] gap-2">
|
||||
<div className="border-border overflow-hidden rounded-lg border">
|
||||
<div className="border-border bg-card border-b px-4 py-2 dark:bg-gray-800">
|
||||
<div className="border-border bg-card border-b px-4 py-2">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
{message.name ? (
|
||||
<h3 className="text-foreground font-medium">
|
||||
Tool Result:{" "}
|
||||
<code className="bg-muted rounded px-2 py-1 dark:bg-gray-700">
|
||||
<code className="bg-muted rounded px-2 py-1">
|
||||
{message.name}
|
||||
</code>
|
||||
</h3>
|
||||
|
|
@ -111,14 +111,14 @@ export function ToolResult({ message }: { message: ToolMessage }) {
|
|||
<h3 className="text-foreground font-medium">Tool Result</h3>
|
||||
)}
|
||||
{message.tool_call_id && (
|
||||
<code className="bg-muted text-foreground ml-2 rounded px-2 py-1 text-sm dark:bg-gray-700">
|
||||
<code className="bg-muted text-foreground ml-2 rounded px-2 py-1 text-sm">
|
||||
{message.tool_call_id}
|
||||
</code>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<motion.div
|
||||
className="bg-muted min-w-full dark:bg-gray-700"
|
||||
className="bg-muted min-w-full"
|
||||
initial={false}
|
||||
animate={{ height: "auto" }}
|
||||
transition={{ duration: 0.3 }}
|
||||
|
|
@ -152,9 +152,9 @@ export function ToolResult({ message }: { message: ToolMessage }) {
|
|||
<td className="text-foreground px-4 py-2 text-sm font-medium whitespace-nowrap">
|
||||
{key}
|
||||
</td>
|
||||
<td className="text-foreground/80 px-4 py-2 text-sm">
|
||||
<td className="text-foreground px-4 py-2 text-sm">
|
||||
{isComplexValue(value) ? (
|
||||
<code className="bg-muted/70 rounded px-2 py-1 font-mono text-sm break-all dark:bg-gray-800">
|
||||
<code className="bg-muted/70 rounded px-2 py-1 font-mono text-sm break-all">
|
||||
{JSON.stringify(value, null, 2)}
|
||||
</code>
|
||||
) : (
|
||||
|
|
@ -167,7 +167,7 @@ export function ToolResult({ message }: { message: ToolMessage }) {
|
|||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<code className="text-foreground/80 block text-sm">
|
||||
<code className="text-foreground block text-sm">
|
||||
{displayedContent}
|
||||
</code>
|
||||
)}
|
||||
|
|
@ -180,7 +180,7 @@ export function ToolResult({ message }: { message: ToolMessage }) {
|
|||
parsedContent.length > 5)) && (
|
||||
<motion.button
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
className="border-border text-muted-foreground hover:bg-muted hover:text-foreground flex w-full cursor-pointer items-center justify-center border-t-[1px] py-2 transition-all duration-200 ease-in-out dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
||||
className="border-border text-muted-foreground hover:bg-muted hover:text-foreground flex w-full cursor-pointer items-center justify-center border-t-[1px] py-2 transition-all duration-200 ease-in-out"
|
||||
initial={{ scale: 1 }}
|
||||
whileHover={{ scale: 1.02 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { FilePlus2, Archive, ListChecks } from "lucide-react";
|
||||
import { Archive, ListChecks } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { TerminalInput } from "./terminal-input";
|
||||
import { useFileUpload } from "@/hooks/useFileUpload";
|
||||
|
|
@ -12,7 +12,6 @@ import {
|
|||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "../ui/tooltip";
|
||||
import { Label } from "../ui/label";
|
||||
import { ContentBlocksPreview } from "../thread/ContentBlocksPreview";
|
||||
import { ThemeToggle } from "../theme-toggle";
|
||||
import { ThreadCard, ThreadCardLoading } from "./thread-card";
|
||||
|
|
@ -32,6 +31,7 @@ import { useState, useMemo } from "react";
|
|||
import { threadsToMetadata } from "@/lib/thread-utils";
|
||||
import { Settings, BookOpen } from "lucide-react";
|
||||
import NextLink from "next/link";
|
||||
import { OpenSWELogoSVG } from "../icons/openswe";
|
||||
|
||||
function OpenSettingsButton() {
|
||||
return (
|
||||
|
|
@ -89,7 +89,6 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
|
|||
const {
|
||||
contentBlocks,
|
||||
setContentBlocks,
|
||||
handleFileUpload,
|
||||
dropRef,
|
||||
removeBlock,
|
||||
dragOver,
|
||||
|
|
@ -121,10 +120,10 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
|
|||
<div className="border-border bg-card border-b px-4 py-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-2 w-2 rounded-full bg-green-500"></div>
|
||||
<span className="text-muted-foreground font-mono text-sm">
|
||||
Open SWE
|
||||
</span>
|
||||
<OpenSWELogoSVG
|
||||
width={120}
|
||||
height={18}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<InstallationSelector />
|
||||
|
|
@ -148,7 +147,7 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
|
|||
{/* Terminal Chat Input */}
|
||||
<Card
|
||||
className={cn(
|
||||
"border-border bg-card py-0 dark:bg-gray-950",
|
||||
"border-border bg-card py-0",
|
||||
dragOver
|
||||
? "border-primary border-2 border-dotted"
|
||||
: "border border-solid",
|
||||
|
|
@ -160,14 +159,6 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
|
|||
blocks={contentBlocks}
|
||||
onRemove={removeBlock}
|
||||
/>
|
||||
<input
|
||||
id="file-input"
|
||||
type="file"
|
||||
onChange={handleFileUpload}
|
||||
multiple
|
||||
accept="image/jpeg,image/png,image/gif,image/webp,application/pdf"
|
||||
className="hidden"
|
||||
/>
|
||||
<div className="space-y-3">
|
||||
<TerminalInput
|
||||
placeholder="Describe your coding task or ask a question..."
|
||||
|
|
@ -183,27 +174,13 @@ export function DefaultView({ threads, threadsLoading }: DefaultViewProps) {
|
|||
setAutoAcceptPlan={setAutoAccept}
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Label
|
||||
htmlFor="file-input"
|
||||
className="text-muted-foreground hover:text-foreground flex cursor-pointer items-center justify-center rounded-full bg-inherit"
|
||||
>
|
||||
<FilePlus2 className="size-4" />
|
||||
</Label>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
Attach files
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
<TooltipIconButton
|
||||
variant={autoAccept ? "brand" : "ghost"}
|
||||
variant={autoAccept ? "default" : "ghost"}
|
||||
tooltip="Automatically accept the plan"
|
||||
className={cn(
|
||||
"transition-colors duration-200",
|
||||
autoAccept
|
||||
? "text-secondary"
|
||||
? "bg-primary hover:bg-primary/90"
|
||||
: "text-muted-foreground hover:text-foreground",
|
||||
)}
|
||||
onClick={() => setAutoAccept((prev) => !prev)}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import {
|
||||
AlertTriangle,
|
||||
Loader2,
|
||||
CheckCircle,
|
||||
MessageSquare,
|
||||
ChevronDown,
|
||||
} from "lucide-react";
|
||||
import { BasicMarkdownText } from "../thread/markdown-text";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type DiagnoseErrorActionProps = {
|
||||
status: "loading" | "generating" | "done";
|
||||
|
|
@ -31,7 +34,9 @@ export function DiagnoseErrorAction({
|
|||
<Loader2 className="text-muted-foreground size-3.5 animate-spin" />
|
||||
);
|
||||
case "done":
|
||||
return <CheckCircle className="size-3.5 text-green-500" />;
|
||||
return (
|
||||
<CheckCircle className="size-3.5 text-green-600 dark:text-green-400" />
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -49,19 +54,36 @@ export function DiagnoseErrorAction({
|
|||
return (
|
||||
<div className="border-border overflow-hidden rounded-md border">
|
||||
{reasoningText && (
|
||||
<div className="border-b border-blue-100 bg-blue-50 p-2 dark:border-blue-800 dark:bg-blue-950">
|
||||
<div className="border-b border-l-4 border-blue-200 border-l-blue-500 bg-blue-50 p-2 dark:border-blue-800/50 dark:border-l-blue-400 dark:bg-blue-950/20">
|
||||
<button
|
||||
onClick={() => setShowReasoning(!showReasoning)}
|
||||
className="flex items-center gap-1 text-xs font-normal text-blue-700 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300"
|
||||
className="flex cursor-pointer items-center gap-1.5 text-xs font-medium text-blue-700 transition-colors duration-200 hover:text-blue-800 dark:text-blue-300 dark:hover:text-blue-200"
|
||||
>
|
||||
<MessageSquare className="h-3 w-3" />
|
||||
{showReasoning ? "Hide reasoning" : "Show reasoning"}
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
"h-3 w-3 transition-transform duration-200",
|
||||
showReasoning && "rotate-180",
|
||||
)}
|
||||
/>
|
||||
<span>{showReasoning ? "Hide reasoning" : "Show reasoning"}</span>
|
||||
</button>
|
||||
{showReasoning && (
|
||||
<BasicMarkdownText className="mt-1 text-xs font-normal text-blue-800 dark:text-blue-300">
|
||||
{reasoningText}
|
||||
</BasicMarkdownText>
|
||||
)}
|
||||
<AnimatePresence>
|
||||
{showReasoning && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeInOut" }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="mt-3 rounded-md bg-blue-50/50 px-3 py-2 dark:bg-blue-900/20">
|
||||
<BasicMarkdownText className="text-xs leading-relaxed text-blue-800 dark:text-blue-200">
|
||||
{reasoningText}
|
||||
</BasicMarkdownText>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,15 +5,7 @@ import { useState } from "react";
|
|||
import { StickToBottom } from "use-stick-to-bottom";
|
||||
import { TooltipIconButton } from "../ui/tooltip-icon-button";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import {
|
||||
Bot,
|
||||
Copy,
|
||||
CopyCheck,
|
||||
Send,
|
||||
User,
|
||||
Loader2,
|
||||
AlertCircle,
|
||||
} from "lucide-react";
|
||||
import { Bot, Copy, CopyCheck, ArrowUp, User, AlertCircle } from "lucide-react";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { Button } from "../ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
|
@ -21,6 +13,7 @@ import { isAIMessageSDK } from "@/lib/langchain-messages";
|
|||
import { BasicMarkdownText } from "../thread/markdown-text";
|
||||
import { ErrorState } from "./types";
|
||||
import { CollapsibleAlert } from "./collapsible-alert";
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
function MessageCopyButton({ content }: { content: string }) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
|
@ -102,7 +95,7 @@ export function ManagerChat({
|
|||
errorState,
|
||||
}: ManagerChatProps) {
|
||||
return (
|
||||
<div className="border-border bg-muted/30 flex h-full w-1/3 flex-col border-r dark:bg-gray-950">
|
||||
<div className="border-border bg-muted/30 flex h-full w-1/3 flex-col border-r">
|
||||
<div className="relative flex-1">
|
||||
<StickToBottom
|
||||
className="absolute inset-0"
|
||||
|
|
@ -119,16 +112,16 @@ export function ManagerChat({
|
|||
return (
|
||||
<div
|
||||
key={message.id}
|
||||
className="group flex gap-3"
|
||||
className="group bg-muted flex gap-3 rounded-lg p-3"
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
{message.type === "human" ? (
|
||||
<div className="bg-muted flex h-6 w-6 items-center justify-center rounded-full dark:bg-gray-700">
|
||||
<div className="bg-muted flex h-6 w-6 items-center justify-center rounded-full">
|
||||
<User className="text-muted-foreground h-3 w-3" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-6 w-6 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900">
|
||||
<Bot className="h-3 w-3 text-blue-700 dark:text-blue-400" />
|
||||
<div className="flex h-6 w-6 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-950/50">
|
||||
<Bot className="h-3 w-3 text-blue-700 dark:text-blue-300" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -166,13 +159,13 @@ export function ManagerChat({
|
|||
</StickToBottom>
|
||||
</div>
|
||||
|
||||
<div className="border-border bg-muted/30 border-t p-4 dark:bg-gray-950">
|
||||
<div className="border-border bg-muted/30 border-t p-4">
|
||||
<div className="flex gap-2">
|
||||
<Textarea
|
||||
value={chatInput}
|
||||
onChange={(e) => setChatInput(e.target.value)}
|
||||
placeholder="Type your message..."
|
||||
className="border-border bg-background text-foreground placeholder:text-muted-foreground min-h-[60px] flex-1 resize-none text-sm dark:bg-gray-900"
|
||||
className="border-border bg-background text-foreground placeholder:text-muted-foreground min-h-[60px] flex-1 resize-none text-sm"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && !isLoading) {
|
||||
e.preventDefault();
|
||||
|
|
@ -180,22 +173,26 @@ export function ManagerChat({
|
|||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
onClick={isLoading ? cancelRun : handleSendMessage}
|
||||
disabled={isLoading ? false : !chatInput.trim()}
|
||||
size={isLoading ? "sm" : "icon"}
|
||||
variant={isLoading ? "destructive" : "brand"}
|
||||
className={cn(isLoading ? "h-12 px-4 py-2" : "")}
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
Cancel
|
||||
</>
|
||||
) : (
|
||||
<Send className="size-4" />
|
||||
)}
|
||||
</Button>
|
||||
{isLoading ? (
|
||||
<TooltipIconButton
|
||||
className="size-8 rounded-full border border-white/20 transition-all duration-200 hover:border-white/30 disabled:border-transparent"
|
||||
variant="destructive"
|
||||
onClick={cancelRun}
|
||||
tooltip="Cancel run"
|
||||
>
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
</TooltipIconButton>
|
||||
) : (
|
||||
<Button
|
||||
onClick={handleSendMessage}
|
||||
disabled={!chatInput.trim()}
|
||||
size="icon"
|
||||
variant="brand"
|
||||
className="size-8 rounded-full border border-white/20 transition-all duration-200 hover:border-white/30 disabled:border-transparent"
|
||||
>
|
||||
<ArrowUp className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-muted-foreground mt-2 text-xs">
|
||||
Press Cmd+Enter to send
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ function DevReadmePromptQuickAction({
|
|||
return (
|
||||
<Card
|
||||
onClick={() => setQuickActionPrompt(DEV_README_PROMPT)}
|
||||
className="border-border bg-card hover:bg-muted cursor-pointer py-3 transition-shadow hover:shadow-lg dark:bg-gray-950"
|
||||
className="border-border bg-card hover:bg-muted/30 dark:hover:bg-muted/20 hover:shadow-primary/2 cursor-pointer py-3 transition-all duration-200 hover:shadow-sm"
|
||||
>
|
||||
<CardHeader className="px-3">
|
||||
<CardTitle className="text-foreground text-sm">
|
||||
|
|
@ -71,7 +71,7 @@ export function QuickActions({ setQuickActionPrompt }: QuickActionsProps) {
|
|||
/>
|
||||
<Card
|
||||
onClick={() => setQuickActionPrompt(GENERATE_RULES_PROMPT)}
|
||||
className="border-border bg-card hover:bg-muted cursor-pointer py-3 transition-shadow hover:shadow-lg dark:bg-gray-950"
|
||||
className="border-border bg-card hover:bg-muted/30 dark:hover:bg-muted/20 hover:shadow-primary/2 cursor-pointer py-3 transition-all duration-200 hover:shadow-sm"
|
||||
>
|
||||
<CardHeader className="px-3">
|
||||
<CardTitle className="text-foreground text-sm">
|
||||
|
|
|
|||
|
|
@ -4,11 +4,10 @@ import type React from "react";
|
|||
import { v4 as uuidv4 } from "uuid";
|
||||
import { Dispatch, SetStateAction, useEffect, useState } from "react";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Loader2, Send } from "lucide-react";
|
||||
import { ArrowUp, Loader2 } from "lucide-react";
|
||||
import { RepositoryBranchSelectors } from "../github/repo-branch-selectors";
|
||||
import { Button } from "../ui/button";
|
||||
import { useStream } from "@langchain/langgraph-sdk/react";
|
||||
import { StreamMode } from "@langchain/langgraph-sdk";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useGitHubAppProvider } from "@/providers/GitHubApp";
|
||||
import { GraphState } from "@open-swe/shared/open-swe/types";
|
||||
|
|
@ -18,7 +17,6 @@ import { DEFAULT_CONFIG_KEY, useConfigStore } from "@/hooks/useConfigStore";
|
|||
import {
|
||||
API_KEY_REQUIRED_MESSAGE,
|
||||
MANAGER_GRAPH_ID,
|
||||
OPEN_SWE_STREAM_MODE,
|
||||
} from "@open-swe/shared/constants";
|
||||
import { ManagerGraphUpdate } from "@open-swe/shared/open-swe/manager/types";
|
||||
import { useDraftStorage } from "@/hooks/useDraftStorage";
|
||||
|
|
@ -106,7 +104,7 @@ export function TerminalInput({
|
|||
},
|
||||
ifNotExists: "create",
|
||||
streamResumable: true,
|
||||
streamMode: OPEN_SWE_STREAM_MODE as StreamMode[],
|
||||
streamMode: ["values", "messages-tuple", "custom"],
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -172,9 +170,9 @@ export function TerminalInput({
|
|||
}, [draftToLoad, setMessage]);
|
||||
|
||||
return (
|
||||
<div className="border-border bg-muted rounded-md border p-2 font-mono text-xs dark:bg-black">
|
||||
<div className="border-border bg-muted hover:border-muted-foreground/50 hover:bg-muted/80 focus-within:border-muted-foreground/70 focus-within:bg-muted/80 focus-within:shadow-muted-foreground/20 rounded-md border p-2 font-mono text-xs transition-all duration-200 focus-within:shadow-md">
|
||||
<div className="text-foreground flex items-center gap-1">
|
||||
<div className="flex items-center gap-1 rounded-md border border-gray-200 p-1 dark:border-gray-700">
|
||||
<div className="border-border bg-background/50 flex items-center gap-1 rounded-md border p-1 transition-colors duration-200">
|
||||
<span className="text-muted-foreground">open-swe</span>
|
||||
<span className="text-muted-foreground/70">@</span>
|
||||
<span className="text-muted-foreground">github</span>
|
||||
|
|
@ -191,12 +189,12 @@ export function TerminalInput({
|
|||
disabled={disabled || !message.trim() || !selectedRepository}
|
||||
size="icon"
|
||||
variant="brand"
|
||||
className="ml-auto size-8"
|
||||
className="ml-auto size-8 rounded-full border border-white/20 transition-all duration-200 hover:border-white/30 disabled:border-transparent"
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
<Send className="size-4" />
|
||||
<ArrowUp className="size-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -209,7 +207,7 @@ export function TerminalInput({
|
|||
onKeyDown={handleKeyPress}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
className="text-foreground placeholder:text-muted-foreground max-h-[50vh] min-h-[80px] flex-1 resize-none border-none bg-transparent p-0 font-mono text-xs shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
|
||||
className="text-foreground placeholder:text-muted-foreground focus:placeholder:text-muted-foreground/60 max-h-[50vh] min-h-[80px] flex-1 resize-none border-none bg-transparent p-0 font-mono text-xs shadow-none transition-all duration-200 focus-visible:ring-0 focus-visible:ring-offset-0"
|
||||
rows={6}
|
||||
onPaste={onPaste}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import {
|
|||
CheckCircle,
|
||||
GitBranch,
|
||||
GitPullRequest,
|
||||
Loader2,
|
||||
AlertCircle,
|
||||
Pause,
|
||||
XCircle,
|
||||
Clock,
|
||||
Loader2,
|
||||
} from "lucide-react";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
|
@ -76,19 +76,19 @@ export function ThreadCard({
|
|||
const getStatusColor = (status: ThreadUIStatus) => {
|
||||
switch (status) {
|
||||
case "running":
|
||||
return "dark:bg-blue-950 bg-blue-100 dark:text-blue-400 text-blue-700";
|
||||
return "bg-blue-100 dark:bg-blue-950/50 text-blue-700 dark:text-blue-300";
|
||||
case "completed":
|
||||
return "dark:bg-green-950 bg-green-100 dark:text-green-400 text-green-700";
|
||||
return "bg-green-100 dark:bg-green-950/50 text-green-700 dark:text-green-300";
|
||||
case "error":
|
||||
return "dark:bg-red-950 bg-red-100 dark:text-red-400 text-red-700";
|
||||
return "bg-red-100 dark:bg-red-950/50 text-red-700 dark:text-red-300";
|
||||
case "paused":
|
||||
return "dark:bg-yellow-950 bg-yellow-100 dark:text-yellow-400 text-yellow-700";
|
||||
return "bg-yellow-100 dark:bg-yellow-950/50 text-yellow-700 dark:text-yellow-300";
|
||||
case "failed":
|
||||
return "dark:bg-red-950 bg-red-100 dark:text-red-400 text-red-700";
|
||||
return "bg-red-100 dark:bg-red-950/50 text-red-700 dark:text-red-300";
|
||||
case "pending":
|
||||
return "dark:bg-yellow-950 bg-yellow-100 dark:text-yellow-400 text-yellow-700";
|
||||
return "bg-yellow-100 dark:bg-yellow-950/50 text-yellow-700 dark:text-yellow-300";
|
||||
default:
|
||||
return "dark:bg-gray-800 bg-gray-200 dark:text-gray-400 text-gray-700";
|
||||
return "bg-gray-200 dark:bg-muted text-gray-700 dark:text-muted-foreground";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -129,7 +129,7 @@ export function ThreadCard({
|
|||
return (
|
||||
<Card
|
||||
key={thread.id}
|
||||
className="border-border bg-card hover:bg-muted cursor-pointer px-0 py-3 transition-shadow hover:shadow-lg dark:bg-gray-950"
|
||||
className="border-border bg-card hover:bg-muted/50 hover:shadow-primary/3 hover:border-primary/10 group cursor-pointer px-0 py-3 transition-all duration-200 hover:shadow-md"
|
||||
onClick={() => {
|
||||
router.push(`/chat/${thread.id}`);
|
||||
}}
|
||||
|
|
@ -137,7 +137,7 @@ export function ThreadCard({
|
|||
<CardHeader>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<CardTitle className="text-foreground truncate text-sm">
|
||||
<CardTitle className="text-foreground line-clamp-2 text-sm leading-tight">
|
||||
<InlineMarkdownText>{threadTitle}</InlineMarkdownText>
|
||||
</CardTitle>
|
||||
<div className="mt-1 flex items-center gap-1">
|
||||
|
|
@ -150,9 +150,9 @@ export function ThreadCard({
|
|||
<Badge
|
||||
variant="secondary"
|
||||
className={cn(
|
||||
"text-xs",
|
||||
"text-xs transition-all duration-300 group-hover:scale-105",
|
||||
isStatusLoading
|
||||
? "bg-gray-200 text-gray-600 dark:bg-gray-800 dark:text-gray-400"
|
||||
? "dark:bg-muted dark:text-muted-foreground bg-gray-200 text-gray-600"
|
||||
: getStatusColor(displayStatus),
|
||||
)}
|
||||
>
|
||||
|
|
@ -160,7 +160,9 @@ export function ThreadCard({
|
|||
{isStatusLoading ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
getStatusIcon(displayStatus)
|
||||
<div className="transition-transform duration-300 group-hover:rotate-12">
|
||||
{getStatusIcon(displayStatus)}
|
||||
</div>
|
||||
)}
|
||||
<span className="capitalize">
|
||||
{isStatusLoading ? "Loading..." : displayStatus}
|
||||
|
|
@ -187,13 +189,13 @@ export function ThreadCard({
|
|||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-muted-foreground hover:text-foreground h-5 w-5 p-0"
|
||||
className="text-muted-foreground hover:text-foreground h-5 w-5 p-0 transition-all duration-200 hover:scale-110 hover:bg-orange-100 dark:hover:bg-orange-950"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
window.open(thread.githubIssue!.url, "_blank");
|
||||
}}
|
||||
>
|
||||
<Bug className="h-3 w-3" />
|
||||
<Bug className="h-3 w-3 transition-colors duration-200" />
|
||||
</Button>
|
||||
)}
|
||||
{thread.pullRequest && (
|
||||
|
|
@ -201,15 +203,21 @@ export function ThreadCard({
|
|||
variant="ghost"
|
||||
size="sm"
|
||||
className={cn(
|
||||
"h-5 w-5 p-0 hover:text-gray-300",
|
||||
"h-5 w-5 p-0 transition-all duration-200 hover:scale-110 hover:text-gray-300",
|
||||
getPRStatusColor(thread.pullRequest.status),
|
||||
thread.pullRequest.status === "merged" &&
|
||||
"hover:bg-purple-100 dark:hover:bg-purple-950",
|
||||
thread.pullRequest.status === "open" &&
|
||||
"hover:bg-green-100 dark:hover:bg-green-950",
|
||||
thread.pullRequest.status === "closed" &&
|
||||
"hover:bg-red-100 dark:hover:bg-red-950",
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
window.open(thread.pullRequest!.url, "_blank");
|
||||
}}
|
||||
>
|
||||
<GitPullRequest className="h-3 w-3" />
|
||||
<GitPullRequest className="h-3 w-3 transition-colors duration-200" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -221,7 +229,7 @@ export function ThreadCard({
|
|||
|
||||
export function ThreadCardLoading() {
|
||||
return (
|
||||
<Card className="border-border bg-card px-0 py-3 dark:bg-gray-950">
|
||||
<Card className="border-border bg-card px-0 py-3">
|
||||
<CardHeader>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="min-w-0 flex-1">
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export function ThreadSwitcher({
|
|||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="border-border bg-card text-muted-foreground hover:bg-muted hover:text-foreground h-7 gap-1 text-xs dark:border-gray-700 dark:bg-gray-900 dark:hover:bg-gray-800 dark:hover:text-gray-300"
|
||||
className="border-border bg-card text-muted-foreground hover:bg-muted hover:text-foreground h-7 gap-1 text-xs"
|
||||
>
|
||||
<Layers3 className="h-3 w-3" />
|
||||
<span className="hidden sm:inline">Switch Thread</span>
|
||||
|
|
@ -55,7 +55,7 @@ export function ThreadSwitcher({
|
|||
</SheetTrigger>
|
||||
<SheetContent
|
||||
side="right"
|
||||
className="border-border bg-background w-80 sm:w-96 dark:border-gray-800 dark:bg-gray-950"
|
||||
className="border-border bg-background w-80 sm:w-96"
|
||||
>
|
||||
<SheetHeader className="pb-4">
|
||||
<SheetTitle className="text-foreground text-base">
|
||||
|
|
@ -70,7 +70,7 @@ export function ThreadSwitcher({
|
|||
router.push("/chat");
|
||||
setOpen(false);
|
||||
}}
|
||||
className="border-border bg-card text-foreground hover:bg-muted h-8 w-full justify-start gap-2 text-xs dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:hover:bg-gray-800"
|
||||
className="border-border bg-card text-foreground hover:bg-muted h-8 w-full justify-start gap-2 text-xs"
|
||||
variant="outline"
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
|
|
@ -79,7 +79,7 @@ export function ThreadSwitcher({
|
|||
|
||||
{/* Current Thread */}
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-muted-foreground text-xs font-medium tracking-wide uppercase dark:text-gray-500">
|
||||
<h3 className="text-muted-foreground text-xs font-medium tracking-wide uppercase">
|
||||
Current Thread
|
||||
</h3>
|
||||
<ThreadCard thread={currentThread} />
|
||||
|
|
@ -88,7 +88,7 @@ export function ThreadSwitcher({
|
|||
{/* Other Threads */}
|
||||
{otherThreads.length > 0 && (
|
||||
<div className="h-full space-y-2">
|
||||
<h3 className="text-muted-foreground text-xs font-medium tracking-wide uppercase dark:text-gray-500">
|
||||
<h3 className="text-muted-foreground text-xs font-medium tracking-wide uppercase">
|
||||
Other Threads
|
||||
</h3>
|
||||
<ScrollArea className="h-full">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { Card, CardHeader } from "@/components/ui/card";
|
||||
import { ArrowLeft, Loader2 } from "lucide-react";
|
||||
|
||||
interface ThreadViewLoadingProps {
|
||||
|
|
@ -18,35 +18,35 @@ export function LoadingActionsCardContent() {
|
|||
{Array.from({ length: 8 }, (_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="border-border bg-muted/30 space-y-2 rounded-lg border p-3 dark:bg-gray-900"
|
||||
className="border-border bg-muted/30 space-y-2 rounded-lg border p-3"
|
||||
>
|
||||
{isEven(i) ? (
|
||||
<div className="bg-background space-y-1 rounded p-2 dark:bg-black">
|
||||
<div className="bg-muted h-2 w-32 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-2 w-48 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-background space-y-1 rounded p-2">
|
||||
<div className="bg-muted h-2 w-32 animate-pulse rounded"></div>
|
||||
<div className="bg-muted h-2 w-48 animate-pulse rounded"></div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="bg-muted h-4 w-4 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-3 w-40 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-4 w-4 animate-pulse rounded"></div>
|
||||
<div className="bg-muted h-3 w-40 animate-pulse rounded"></div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="bg-muted h-3 w-16 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-4 w-4 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-3 w-16 animate-pulse rounded"></div>
|
||||
<div className="bg-muted h-4 w-4 animate-pulse rounded"></div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="border-border space-y-2 rounded-lg border-2 border-dashed p-3">
|
||||
<div className="bg-muted h-12 animate-pulse rounded dark:bg-gray-800"></div>
|
||||
<div className="bg-muted h-7 w-16 animate-pulse rounded dark:bg-gray-800"></div>
|
||||
<div className="bg-muted h-12 animate-pulse rounded"></div>
|
||||
<div className="bg-muted h-7 w-16 animate-pulse rounded"></div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 pt-3">
|
||||
<div className="bg-muted h-8 flex-1 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-8 flex-1 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-8 flex-1 animate-pulse rounded"></div>
|
||||
<div className="bg-muted h-8 flex-1 animate-pulse rounded"></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -54,12 +54,12 @@ export function LoadingActionsCardContent() {
|
|||
|
||||
export function LoadingActionsCard() {
|
||||
return (
|
||||
<Card className="border-border bg-card dark:bg-gray-950">
|
||||
<Card className="border-border bg-card">
|
||||
<CardHeader className="p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="bg-muted h-4 w-32 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="ml-auto flex items-center gap-1">
|
||||
<Loader2 className="text-muted-foreground h-3 w-3 animate-spin" />
|
||||
<div className="bg-muted h-4 w-32 animate-pulse rounded"></div>
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
<span className="text-muted-foreground text-xs">
|
||||
Loading actions...
|
||||
</span>
|
||||
|
|
@ -77,7 +77,7 @@ export function LoadingChatSection() {
|
|||
return (
|
||||
<div className="flex-1 space-y-4 overflow-y-auto p-4">
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<div className="text-muted-foreground flex items-center gap-2">
|
||||
<div className="text-muted-foreground flex items-center gap-3">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
<span className="text-sm">Loading conversation...</span>
|
||||
</div>
|
||||
|
|
@ -89,18 +89,18 @@ export function LoadingChatSection() {
|
|||
className="flex gap-3"
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
<div className="bg-muted h-6 w-6 animate-pulse rounded-full dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-6 w-6 animate-pulse rounded-full"></div>
|
||||
</div>
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="bg-muted h-3 w-16 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-3 w-12 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-3 w-16 animate-pulse rounded"></div>
|
||||
<div className="bg-muted h-3 w-12 animate-pulse rounded"></div>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="bg-muted h-3 w-full animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-3 w-3/4 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-3 w-full animate-pulse rounded"></div>
|
||||
<div className="bg-muted h-3 w-3/4 animate-pulse rounded"></div>
|
||||
{i === 1 && (
|
||||
<div className="bg-muted h-3 w-1/2 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-3 w-1/2 animate-pulse rounded"></div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -124,30 +124,30 @@ export function ThreadViewLoading({ onBackToHome }: ThreadViewLoadingProps) {
|
|||
<ArrowLeft className="h-3 w-3" />
|
||||
</Button>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2">
|
||||
<div className="bg-muted h-2 w-2 animate-pulse rounded-full dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-3 w-48 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-2 w-2 animate-pulse rounded-full"></div>
|
||||
<div className="bg-muted h-3 w-48 animate-pulse rounded"></div>
|
||||
<span className="text-muted-foreground text-xs">•</span>
|
||||
<div className="bg-muted h-3 w-24 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-3 w-24 animate-pulse rounded"></div>
|
||||
</div>
|
||||
<div className="bg-muted/70 h-7 w-28 animate-pulse rounded dark:bg-gray-800"></div>
|
||||
<div className="bg-muted/70 h-7 w-28 animate-pulse rounded"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex h-full w-full pt-12">
|
||||
<div className="border-border bg-muted/30 flex h-full w-1/3 flex-col border-r dark:bg-gray-950">
|
||||
<div className="border-border bg-muted/30 flex h-full w-1/3 flex-col border-r">
|
||||
<LoadingChatSection />
|
||||
|
||||
<div className="border-border bg-muted/30 border-t p-4 dark:bg-gray-950">
|
||||
<div className="border-border bg-muted/30 border-t p-4">
|
||||
<div className="flex gap-2">
|
||||
<div className="border-border bg-background flex-1 rounded-md border p-3 dark:bg-gray-900">
|
||||
<div className="border-border bg-background flex-1 rounded-md border p-3">
|
||||
<div className="space-y-2">
|
||||
<div className="bg-muted h-3 w-1/3 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-3 w-1/2 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-3 w-1/3 animate-pulse rounded"></div>
|
||||
<div className="bg-muted h-3 w-1/2 animate-pulse rounded"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-muted h-10 w-10 animate-pulse self-end rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted h-10 w-10 animate-pulse self-end rounded"></div>
|
||||
</div>
|
||||
<div className="bg-muted mt-2 h-3 w-32 animate-pulse rounded dark:bg-gray-700"></div>
|
||||
<div className="bg-muted mt-2 h-3 w-32 animate-pulse rounded"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -346,7 +346,7 @@ export function ThreadView({
|
|||
}
|
||||
>
|
||||
<div className="flex flex-shrink-0 items-center gap-3">
|
||||
<TabsList className="bg-muted/70 dark:bg-gray-800">
|
||||
<TabsList className="bg-muted/70">
|
||||
<TabsTrigger value="planner">Planner</TabsTrigger>
|
||||
<TabsTrigger value="programmer">Programmer</TabsTrigger>
|
||||
</TabsList>
|
||||
|
|
@ -390,7 +390,7 @@ export function ThreadView({
|
|||
value="planner"
|
||||
className="mb-2"
|
||||
>
|
||||
<Card className="border-border bg-card relative h-full p-0 dark:bg-gray-950">
|
||||
<Card className="border-border bg-card relative h-full p-0">
|
||||
<CardContent className="h-full p-0">
|
||||
<StickToBottom
|
||||
className="absolute inset-0 h-full"
|
||||
|
|
@ -403,7 +403,6 @@ export function ThreadView({
|
|||
{plannerSession ? (
|
||||
<div className="scrollbar-pretty-auto overflow-y-auto px-2">
|
||||
<ActionsRenderer<PlannerGraphState>
|
||||
taskPlan={programmerTaskPlan}
|
||||
runId={plannerSession.runId}
|
||||
customNodeEvents={customPlannerNodeEvents}
|
||||
setCustomNodeEvents={
|
||||
|
|
@ -437,7 +436,7 @@ export function ThreadView({
|
|||
value="programmer"
|
||||
className="mb-2"
|
||||
>
|
||||
<Card className="border-border bg-card relative h-full p-0 dark:bg-gray-950">
|
||||
<Card className="border-border bg-card relative h-full p-0">
|
||||
<CardContent className="h-full p-0">
|
||||
<StickToBottom
|
||||
className="absolute inset-0 h-full"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue