"use client"; import { useEffect, useState } from "react"; import { ExternalLink, Globe2, LoaderCircle, Search } from "lucide-react"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import { httpRequest } from "@/lib/request"; import { cn } from "@/lib/utils"; import type { SearchResult } from "./types"; const normalizeMarkdown = (text: string) => text .replace(/\ue200url\ue202([^\ue202\ue201]*)\ue202([^\ue201]*)\ue201/g, "[$1]($2)") .replace(/\ue200cite\ue202[^\ue201]*\ue201/g, "") .replace(/\ue200[^\ue201]*\ue201/g, "") .replace(/\ue200[^\ue201]*$/g, "") .replace(/\n{3,}/g, "\n\n") .trim(); const cleanUrl = (url: string) => url.replace(/[\ue200-\ue202].*$/g, "").trim(); const sourceKind = (url: string) => { const host = (() => { try { return new URL(url).hostname; } catch { return ""; } })(); return host.includes("github.com") ? "github" : "web"; }; function MarkdownResult({ content }: { content: string }) { return ( , h1: ({ className, ...props }) =>

, h2: ({ className, ...props }) =>

, h3: ({ className, ...props }) =>

, p: ({ className, ...props }) =>

, ul: ({ className, ...props }) =>

    , ol: ({ className, ...props }) =>
      , blockquote: ({ className, ...props }) =>
      , code: ({ className, ...props }) => , pre: ({ className, ...props }) =>
      ,
              table: ({ className, ...props }) => 
      , th: ({ className, ...props }) =>
      , td: ({ className, ...props }) => , }} > {content} ); } export function SearchPanel() { const [prompt, setPrompt] = useState("帮我搜索 chatgpt2api 相关项目"); const [result, setResult] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const [elapsedMs, setElapsedMs] = useState(0); const [startedAt, setStartedAt] = useState(0); const searched = loading || !!result || !!error; useEffect(() => { if (!loading || !startedAt) return; const timer = window.setInterval(() => setElapsedMs(Date.now() - startedAt), 100); return () => window.clearInterval(timer); }, [loading, startedAt]); const runSearch = async () => { const value = prompt.trim(); if (!value || loading) return; const start = Date.now(); setStartedAt(start); setElapsedMs(0); setLoading(true); setError(""); setResult(null); try { setResult(await httpRequest("/v1/search", { method: "POST", body: { prompt: value } })); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setElapsedMs(Date.now() - start); setLoading(false); } }; return (
      {!searched ? (

      利用ChatGPT先进的网页搜索功能进行搜索

      ) : null}
      { event.preventDefault(); void runSearch(); }} > setPrompt(event.target.value)} placeholder="搜索网页" className={cn("min-w-0 flex-1 bg-transparent text-[15px] text-stone-900 outline-none placeholder:text-stone-400 dark:text-stone-100 dark:placeholder:text-stone-500", searched ? "h-8" : "h-10")} />
      {searched ?
      {loading ? (
      搜索中... {(elapsedMs / 1000).toFixed(1)}s
      ) : null} {error ?
      {error}
      : null} {result ? (
      {result.status || "done"} {(elapsedMs / 1000).toFixed(2)}s {result.sources?.length || 0} sources
      {result.sources?.length ? ( ) : null}
      ) : null}
      : null}
      ); }