Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 28 additions & 8 deletions webview-ui/src/components/chat/ErrorRow.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useCallback, memo } from "react"
import React, { useState, useCallback, memo, useMemo } from "react"
import { useTranslation } from "react-i18next"
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
import { BookOpenText, MessageCircleWarning, Info, Copy, Check } from "lucide-react"
Expand All @@ -7,6 +7,8 @@ import { vscode } from "@src/utils/vscode"
import CodeBlock from "../common/CodeBlock"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@src/components/ui/dialog"
import { Button, Tooltip, TooltipContent, TooltipTrigger } from "../ui"
import { useExtensionState } from "@src/context/ExtensionStateContext"
import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel"

/**
* Unified error display component for all error types in the chat.
Expand Down Expand Up @@ -91,6 +93,24 @@ export const ErrorRow = memo(
const [isDetailsDialogOpen, setIsDetailsDialogOpen] = useState(false)
const [showDetailsCopySuccess, setShowDetailsCopySuccess] = useState(false)
const { copyWithFeedback } = useCopyToClipboard()
const { version, apiConfiguration } = useExtensionState()
const { provider, id: modelId } = useSelectedModel(apiConfiguration)

// Format error details with metadata prepended
const formattedErrorDetails = useMemo(() => {
if (!errorDetails) return undefined

const metadata = [
`Date/time: ${new Date().toISOString()}`,
`Extension version: ${version}`,
`Provider: ${provider}`,
`Model: ${modelId}`,
"",
"",
].join("\n")

return metadata + errorDetails
}, [errorDetails, version, provider, modelId])

// Default titles for different error types
const getDefaultTitle = () => {
Expand Down Expand Up @@ -139,8 +159,8 @@ export const ErrorRow = memo(
const handleCopyDetails = useCallback(
async (e: React.MouseEvent) => {
e.stopPropagation()
if (errorDetails) {
const success = await copyWithFeedback(errorDetails)
if (formattedErrorDetails) {
const success = await copyWithFeedback(formattedErrorDetails)
if (success) {
setShowDetailsCopySuccess(true)
setTimeout(() => {
Expand All @@ -149,7 +169,7 @@ export const ErrorRow = memo(
}
}
},
[errorDetails, copyWithFeedback],
[formattedErrorDetails, copyWithFeedback],
)

const errorTitle = getDefaultTitle()
Expand Down Expand Up @@ -209,12 +229,12 @@ export const ErrorRow = memo(
{t("chat:apiRequest.errorMessage.docs")}
</a>
)}
{errorDetails && (
{formattedErrorDetails && (
<Tooltip>
<TooltipTrigger asChild>
<button
onClick={() => setIsDetailsDialogOpen(true)}
className="transition-opacity opacity-0 group-hover:opacity-100 cursor-pointer"
className="transition-opacity opacity-30 group-hover:opacity-100 cursor-pointer"
aria-label={t("chat:errorDetails.title")}>
<Info className="size-4" />
</button>
Expand All @@ -238,15 +258,15 @@ export const ErrorRow = memo(
</div>

{/* Error Details Dialog */}
{errorDetails && (
{formattedErrorDetails && (
<Dialog open={isDetailsDialogOpen} onOpenChange={setIsDetailsDialogOpen}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>{t("chat:errorDetails.title")}</DialogTitle>
</DialogHeader>
<div className="max-h-96 overflow-auto px-3 bg-vscode-editor-background rounded-xl border border-vscode-editorGroup-border">
<pre className="font-mono text-sm whitespace-pre-wrap break-words bg-transparent">
{errorDetails}
{formattedErrorDetails}
</pre>
</div>
<DialogFooter>
Expand Down
Loading