Skip to content

Commit ee797ec

Browse files
committed
feat: refactor chat components and utilities
- Moved `getSuggestionsForAgent` from `agent-suggestions` to `chat.utils` for better organization. - Created new types in `chat.types.ts` to improve type safety and clarity in chat components. - Added utility functions in `chat.utils.ts` for extracting plans from text and tokenizing inline citations. - Updated `chat-input.tsx` to use the new utility functions and types. - Refactored `chat-messages.tsx` to improve readability and maintainability, including better handling of message statuses and stages. - Enhanced `MessageItem` component to display additional information about message stages and IDs. - Improved citation handling in `MessageItem` using the new tokenization utility. fix: update network components for better state management - Refactored `NetworkToolDisplay` to handle dynamic tools more effectively, ensuring correct input and output handling. - Updated `NetworkMessageParts` to correctly manage tool invocation states and improve error handling. - Simplified `NetworkProvider` to normalize progress event statuses and improve overall state management. chore: clean up unused variables and improve code quality - Removed unused variables and simplified conditions across various components. - Added missing type annotations and improved type safety in several places. - Cleaned up eslint warnings by disabling specific rules where necessary. style: general code formatting and organization - Improved code formatting for better readability and consistency across files. - Organized imports and grouped related code sections for clarity.
1 parent 3e82388 commit ee797ec

25 files changed

Lines changed: 534 additions & 448 deletions

app/chat/components/agent-artifact.tsx

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,12 @@ import { useState, useCallback } from "react"
3030
import type { BundledLanguage } from "shiki"
3131
import { AgentCodeSandbox } from "./agent-web-preview"
3232

33-
export interface ArtifactData {
34-
id: string
35-
title: string
36-
description?: string
37-
type: "code" | "markdown" | "json" | "text" | "html" | "react"
38-
language?: string
39-
content: string
40-
}
33+
import type { ArtifactData } from "./chat.types"
4134

42-
export interface AgentArtifactProps {
35+
interface AgentArtifactProps {
4336
artifact: ArtifactData
4437
onClose?: () => void
38+
// eslint-disable-next-line no-unused-vars
4539
onCodeUpdate?: (artifactId: string, newCode: string) => void
4640
}
4741

@@ -76,7 +70,7 @@ export function AgentArtifact({
7670
setCopied(true)
7771
setTimeout(() => setCopied(false), 2000)
7872
} catch (err) {
79-
console.error("Failed to copy:", err)
73+
void err
8074
}
8175
}, [editedCode])
8276

@@ -240,6 +234,7 @@ export function AgentArtifactCompact({
240234
// Floating action button for quick access to editor
241235
interface ArtifactEditorFABProps {
242236
artifact: ArtifactData
237+
// eslint-disable-next-line no-unused-vars
243238
onCodeChange?: (newCode: string) => void
244239
}
245240

app/chat/components/agent-chain-of-thought.tsx

Lines changed: 1 addition & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,7 @@ import {
1111
} from "@/src/components/ai-elements/chain-of-thought"
1212
import { Badge } from "@/ui/badge"
1313
import { SearchIcon, BrainIcon, CheckIcon, LoaderIcon, ClockIcon } from "lucide-react"
14-
15-
export interface ReasoningStep {
16-
id: string
17-
label: string
18-
description?: string
19-
status: "complete" | "active" | "pending"
20-
searchResults?: string[]
21-
duration?: number
22-
}
14+
import type { ReasoningStep } from "./chat.types"
2315

2416
interface AgentChainOfThoughtProps {
2517
steps: ReasoningStep[]
@@ -89,62 +81,3 @@ export function AgentChainOfThought({
8981
</ChainOfThought>
9082
)
9183
}
92-
93-
type StepType = "step" | "search" | "analysis" | "decision"
94-
95-
function categorizeStep(text: string): StepType {
96-
const lower = text.toLowerCase()
97-
if (lower.includes("search") || lower.includes("looking for") || lower.includes("finding")) {
98-
return "search"
99-
}
100-
if (lower.includes("analyzing") || lower.includes("examining") || lower.includes("reviewing")) {
101-
return "analysis"
102-
}
103-
if (lower.includes("decided") || lower.includes("conclusion") || lower.includes("therefore")) {
104-
return "decision"
105-
}
106-
return "step"
107-
}
108-
109-
export function parseReasoningToSteps(reasoning: string): ReasoningStep[] {
110-
if (!reasoning) {return []}
111-
112-
const lines = reasoning.split("\n").filter((line) => line.trim())
113-
const steps: ReasoningStep[] = []
114-
let currentSearchTerms: string[] = []
115-
116-
lines.forEach((line, index) => {
117-
const trimmed = line.trim()
118-
119-
// Skip very short lines
120-
if (trimmed.length < 5) {return}
121-
122-
// Check for bullet points or numbered lists
123-
const isBullet = trimmed.startsWith("-") || trimmed.startsWith("•") || /^\d+\./.test(trimmed)
124-
const content = isBullet
125-
? trimmed.replace(/^[-\d.]+\s*/, "")
126-
: trimmed
127-
128-
// Extract search terms if mentioned
129-
const searchMatch = /(?:search|looking for|finding)[:\s]+["']?([^"'\n]+)["']?/i.exec(content)
130-
if (searchMatch) {
131-
currentSearchTerms.push(searchMatch[1].trim())
132-
}
133-
134-
if (content.length > 10) {
135-
const stepType = categorizeStep(content)
136-
steps.push({
137-
id: `step-${index}`,
138-
label: content.length > 80 ? content.slice(0, 77) + "..." : content,
139-
description: content.length > 80 ? content : undefined,
140-
status: "complete",
141-
searchResults: stepType === "search" ? [...currentSearchTerms] : undefined,
142-
})
143-
144-
// Reset after each step to prevent accumulation
145-
currentSearchTerms = []
146-
}
147-
})
148-
149-
return steps.slice(0, 15)
150-
}

app/chat/components/agent-confirmation.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,19 @@ import {
1919
InfoIcon,
2020
} from "lucide-react"
2121
import { cn } from "@/lib/utils"
22-
23-
export type ConfirmationSeverity = "info" | "warning" | "danger"
22+
import type { ConfirmationSeverity } from "./chat.types"
2423

2524
interface AgentConfirmationProps {
2625
toolName: string
2726
description: string
2827
approval: ConfirmationProps["approval"]
2928
state: ToolUIPart["state"]
30-
onApprove: (approvalId: string) => void
31-
onReject: (approvalId: string) => void
3229
severity?: ConfirmationSeverity
3330
className?: string
31+
// eslint-disable-next-line no-unused-vars
32+
onApprove(approvalId: string): void
33+
// eslint-disable-next-line no-unused-vars
34+
onReject(approvalId: string): void
3435
}
3536

3637
const severityConfig: Record<ConfirmationSeverity, {

app/chat/components/agent-inline-citation.tsx

Lines changed: 1 addition & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,7 @@ import {
1616
InlineCitationSource,
1717
InlineCitationQuote,
1818
} from "@/src/components/ai-elements/inline-citation"
19-
import type { ReactNode } from "react"
20-
21-
export interface Citation {
22-
id: string
23-
number: string
24-
title: string
25-
url: string
26-
description?: string
27-
quote?: string
28-
}
19+
import type { Citation } from "./chat.types"
2920

3021
interface AgentInlineCitationProps {
3122
citations: Citation[]
@@ -68,52 +59,3 @@ export function AgentInlineCitation({ citations, text }: AgentInlineCitationProp
6859
</InlineCitation>
6960
)
7061
}
71-
72-
export function parseInlineCitations(
73-
content: string,
74-
sources: Array<{ url: string; title: string }>
75-
): ReactNode[] {
76-
if (!sources.length) {return [content]}
77-
78-
const parts: ReactNode[] = []
79-
const citationRegex = /\[(\d+)\]/g
80-
let lastIndex = 0
81-
let match
82-
83-
while ((match = citationRegex.exec(content)) !== null) {
84-
if (match.index > lastIndex) {
85-
parts.push(content.slice(lastIndex, match.index))
86-
}
87-
88-
const citationNumber = match[1]
89-
const sourceIndex = parseInt(citationNumber, 10) - 1
90-
const source = sources[sourceIndex]
91-
92-
if (source) {
93-
parts.push(
94-
<AgentInlineCitation
95-
key={`citation-${match.index}`}
96-
text={match[0]}
97-
citations={[
98-
{
99-
id: `cite-${sourceIndex}`,
100-
number: citationNumber,
101-
title: source.title,
102-
url: source.url,
103-
},
104-
]}
105-
/>
106-
)
107-
} else {
108-
parts.push(match[0])
109-
}
110-
111-
lastIndex = match.index + match[0].length
112-
}
113-
114-
if (lastIndex < content.length) {
115-
parts.push(content.slice(lastIndex))
116-
}
117-
118-
return parts.length ? parts : [content]
119-
}

app/chat/components/agent-plan.tsx

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,7 @@ import { Button } from "@/ui/button"
1515
import { PlayIcon, XIcon, CheckIcon, CircleIcon } from "lucide-react"
1616
import { useMemo } from "react"
1717

18-
export interface PlanStep {
19-
text: string
20-
completed?: boolean
21-
}
22-
23-
export interface AgentPlanData {
24-
title: string
25-
description: string
26-
steps: PlanStep[] | string[]
27-
isStreaming?: boolean
28-
currentStep?: number
29-
}
18+
import type { AgentPlanData, PlanStep } from "./chat.types"
3019

3120
interface AgentPlanProps {
3221
plan: AgentPlanData
@@ -126,33 +115,3 @@ export function AgentPlan({
126115
</Plan>
127116
)
128117
}
129-
130-
export function extractPlanFromText(text: string): AgentPlanData | null {
131-
const planPatterns = [
132-
/(?:plan|steps|approach|strategy|roadmap|outline)[:\s]*\n((?:[-\d].+\n?)+)/i,
133-
/(?:here's|here is|my|the) (?:plan|approach|strategy)[:\s]*\n((?:[-\d].+\n?)+)/i,
134-
/(?:i will|let me|first,)[:\s]*((?:[-\d].+\n?)+)/i,
135-
]
136-
137-
for (const pattern of planPatterns) {
138-
const match = pattern.exec(text)
139-
if (match) {
140-
const stepsText = match[1]
141-
const steps = stepsText
142-
.split("\n")
143-
.map((line) => line.replace(/^[-\d.]+\s*/, "").trim())
144-
.filter((line) => line.length > 0)
145-
146-
if (steps.length >= 2) {
147-
return {
148-
title: "Execution Plan",
149-
description: `${steps.length} steps to complete this task`,
150-
steps: steps.map((stepText) => ({ text: stepText, completed: false })),
151-
isStreaming: false,
152-
}
153-
}
154-
}
155-
}
156-
157-
return null
158-
}

app/chat/components/agent-queue.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,7 @@ import {
2727
} from "lucide-react"
2828
import { cn } from "@/lib/utils"
2929

30-
export interface QueuedTask {
31-
id: string
32-
title: string
33-
description?: string
34-
status: "pending" | "running" | "completed" | "failed"
35-
createdAt?: Date
36-
completedAt?: Date
37-
error?: string
38-
}
30+
import type { QueuedTask } from "./chat.types"
3931

4032
interface AgentQueueProps {
4133
tasks: QueuedTask[]

app/chat/components/agent-reasoning.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@ import {
66
ReasoningContent,
77
} from "@/src/components/ai-elements/reasoning"
88

9-
export interface AgentReasoningProps {
10-
reasoning: string
11-
isStreaming: boolean
12-
duration?: number
13-
className?: string
14-
}
9+
import type { AgentReasoningProps } from "./chat.types"
1510

1611
export function AgentReasoning({
1712
reasoning,

app/chat/components/agent-sources.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,7 @@ import {
99
import type { Source as SourceType } from "@/app/chat/providers/chat-context"
1010
import { ExternalLinkIcon } from "lucide-react"
1111
import { useMemo } from "react"
12-
13-
export interface AgentSourcesProps {
14-
sources: SourceType[]
15-
className?: string
16-
maxVisible?: number
17-
}
12+
import type { AgentSourcesProps } from "./chat.types"
1813

1914
function getDomain(url: string): string {
2015
try {

0 commit comments

Comments
 (0)