SurfSense/surfsense_web/components/tool-ui/scrape-webpage.tsx
Vonic dc545f8028 fix: resolve runtime crashes in tool-ui components and backend import errors
Backend:
- Remove dead imports (display_image, link_preview, knowledge_base) from tools registry and __init__
- Delete orphaned elif chain (lines 784-1046) in knowledge_base.py left after asyncio.gather refactor
- Add missing NotionAPIError class to notion_history.py

Frontend:
- Add display-image.tsx, link-preview.tsx, scrape-webpage.tsx tool UI components
- Fix GeneratePodcastToolUI: add null guard for no-props render + optional chaining on status/args
- Fix SaveMemoryToolUI/RecallMemoryToolUI: add null guard when rendered without props in provider
- Update launch.json to use pnpm on port 3999

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 13:42:31 +07:00

56 lines
1.9 KiB
TypeScript

"use client";
import { makeAssistantToolUI } from "@assistant-ui/react";
import { z } from "zod";
import { GlobeIcon, Loader2Icon, CheckCircle2Icon, XCircleIcon } from "lucide-react";
const ScrapeWebpageArgsSchema = z.object({
url: z.string(),
}).passthrough();
const ScrapeWebpageResultSchema = z.object({
url: z.string().optional(),
title: z.string().optional(),
content: z.string().optional(),
error: z.string().optional(),
success: z.boolean().optional(),
}).passthrough();
type ScrapeWebpageArgs = z.infer<typeof ScrapeWebpageArgsSchema>;
type ScrapeWebpageResult = z.infer<typeof ScrapeWebpageResultSchema>;
export const ScrapeWebpageToolUI = makeAssistantToolUI<ScrapeWebpageArgs, ScrapeWebpageResult>({
toolName: "scrape_webpage",
render: ({ args, result, status }) => {
const isLoading = status.type === "running";
const url = result?.url ?? args?.url;
const hasError = result?.error || result?.success === false;
const isSuccess = result?.success !== false && !result?.error && status.type === "complete";
return (
<div className="my-3 flex items-center gap-3 rounded-lg border bg-card/60 px-4 py-3">
<div className="flex size-8 items-center justify-center rounded-full bg-primary/10 shrink-0">
{isLoading ? (
<Loader2Icon className="size-4 animate-spin text-primary" />
) : hasError ? (
<XCircleIcon className="size-4 text-destructive" />
) : (
<GlobeIcon className="size-4 text-primary" />
)}
</div>
<div className="min-w-0 flex-1">
<p className="text-sm font-medium">
{isLoading ? "Scraping webpage..." : hasError ? "Failed to scrape" : "Scraped webpage"}
</p>
{url && (
<p className="text-xs text-muted-foreground truncate">{url}</p>
)}
{hasError && result?.error && (
<p className="text-xs text-destructive mt-0.5">{result.error}</p>
)}
</div>
{isSuccess && <CheckCircle2Icon className="size-4 text-green-500 shrink-0" />}
</div>
);
},
});