mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-27 09:46:25 +02:00
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>
61 lines
1.9 KiB
TypeScript
61 lines
1.9 KiB
TypeScript
"use client";
|
|
|
|
import { makeAssistantToolUI } from "@assistant-ui/react";
|
|
import { z } from "zod";
|
|
import { ExternalLinkIcon, Loader2Icon } from "lucide-react";
|
|
|
|
const LinkPreviewArgsSchema = z.object({
|
|
url: z.string(),
|
|
}).passthrough();
|
|
|
|
const LinkPreviewResultSchema = z.object({
|
|
url: z.string().optional(),
|
|
title: z.string().optional(),
|
|
description: z.string().optional(),
|
|
image: z.string().optional(),
|
|
favicon: z.string().optional(),
|
|
error: z.string().optional(),
|
|
}).passthrough();
|
|
|
|
type LinkPreviewArgs = z.infer<typeof LinkPreviewArgsSchema>;
|
|
type LinkPreviewResult = z.infer<typeof LinkPreviewResultSchema>;
|
|
|
|
export const LinkPreviewToolUI = makeAssistantToolUI<LinkPreviewArgs, LinkPreviewResult>({
|
|
toolName: "link_preview",
|
|
render: ({ args, result, status }) => {
|
|
const isLoading = status.type === "running";
|
|
const url = result?.url ?? args?.url;
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="my-2 flex items-center gap-2 rounded-lg border bg-card/60 px-4 py-3">
|
|
<Loader2Icon className="size-4 animate-spin text-muted-foreground" />
|
|
<span className="text-sm text-muted-foreground">Loading preview...</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (result?.error || !url) return null;
|
|
|
|
return (
|
|
<a
|
|
href={url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="my-2 flex items-start gap-3 rounded-lg border bg-card/60 p-3 hover:bg-card transition-colors no-underline"
|
|
>
|
|
{result?.favicon && (
|
|
<img src={result.favicon} alt="" className="size-4 mt-0.5 shrink-0" />
|
|
)}
|
|
<div className="min-w-0 flex-1">
|
|
<p className="text-sm font-medium truncate">{result?.title ?? url}</p>
|
|
{result?.description && (
|
|
<p className="text-xs text-muted-foreground line-clamp-2 mt-0.5">{result.description}</p>
|
|
)}
|
|
<p className="text-xs text-muted-foreground truncate mt-1">{url}</p>
|
|
</div>
|
|
<ExternalLinkIcon className="size-3.5 shrink-0 text-muted-foreground mt-0.5" />
|
|
</a>
|
|
);
|
|
},
|
|
});
|