chore: ran linting

This commit is contained in:
Anish Sarkar 2026-02-13 02:43:26 +05:30
parent 207b9e0ed3
commit a2dd5fb671
14 changed files with 124 additions and 162 deletions

View file

@ -6,7 +6,7 @@ import "katex/dist/katex.min.css";
import { cn } from "@/lib/utils";
const code = createCodePlugin({
themes: ["nord", "nord"]
themes: ["nord", "nord"],
});
const math = createMathPlugin({
@ -24,9 +24,7 @@ interface MarkdownViewerProps {
*/
function stripOuterMarkdownFence(content: string): string {
const trimmed = content.trim();
const match = trimmed.match(
/^```(?:markdown|md)?\s*\n([\s\S]+?)\n```\s*$/
);
const match = trimmed.match(/^```(?:markdown|md)?\s*\n([\s\S]+?)\n```\s*$/);
return match ? match[1] : content;
}
@ -121,8 +119,18 @@ export function MarkdownViewer({ content, className }: MarkdownViewerProps) {
<table className="w-full divide-y divide-border" {...props} />
</div>
),
th: ({ ...props }) => <th className="px-4 py-2.5 text-left text-sm font-semibold text-muted-foreground/80 bg-muted/30 border-r border-border/40 last:border-r-0" {...props} />,
td: ({ ...props }) => <td className="px-4 py-2.5 text-sm border-t border-r border-border/40 last:border-r-0" {...props} />,
th: ({ ...props }) => (
<th
className="px-4 py-2.5 text-left text-sm font-semibold text-muted-foreground/80 bg-muted/30 border-r border-border/40 last:border-r-0"
{...props}
/>
),
td: ({ ...props }) => (
<td
className="px-4 py-2.5 text-sm border-t border-r border-border/40 last:border-r-0"
{...props}
/>
),
};
return (

View file

@ -1,23 +1,13 @@
"use client";
import { useAtomValue, useSetAtom } from "jotai";
import {
ChevronDownIcon,
XIcon,
} from "lucide-react";
import { ChevronDownIcon, XIcon } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
import { z } from "zod";
import {
closeReportPanelAtom,
reportPanelAtom,
} from "@/atoms/chat/report-panel.atom";
import { closeReportPanelAtom, reportPanelAtom } from "@/atoms/chat/report-panel.atom";
import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import {
Drawer,
DrawerContent,
DrawerHandle,
} from "@/components/ui/drawer";
import { Drawer, DrawerContent, DrawerHandle } from "@/components/ui/drawer";
import {
DropdownMenu,
DropdownMenuContent,
@ -118,8 +108,7 @@ function ReportPanelContent({
/** When set, uses public endpoint for fetching report data (public shared chat) */
shareToken?: string | null;
}) {
const [reportContent, setReportContent] =
useState<ReportContentResponse | null>(null);
const [reportContent, setReportContent] = useState<ReportContentResponse | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [copied, setCopied] = useState(false);
@ -150,10 +139,7 @@ function ReportPanelContent({
if (parsed.success) {
// Check if the report was marked as failed in metadata
if (parsed.data.report_metadata?.status === "failed") {
setError(
parsed.data.report_metadata?.error_message ||
"Report generation failed"
);
setError(parsed.data.report_metadata?.error_message || "Report generation failed");
} else {
setReportContent(parsed.data);
// Update versions from the response
@ -162,18 +148,13 @@ function ReportPanelContent({
}
}
} else {
console.warn(
"Invalid report content response:",
parsed.error.issues
);
console.warn("Invalid report content response:", parsed.error.issues);
setError("Invalid response format");
}
} catch (err) {
if (cancelled) return;
console.error("Error fetching report content:", err);
setError(
err instanceof Error ? err.message : "Failed to load report"
);
setError(err instanceof Error ? err.message : "Failed to load report");
} finally {
if (!cancelled) setIsLoading(false);
}
@ -202,8 +183,10 @@ function ReportPanelContent({
async (format: "pdf" | "docx" | "md") => {
setExporting(format);
const safeTitle =
title.replace(/[^a-zA-Z0-9 _-]/g, "_").trim().slice(0, 80) ||
"report";
title
.replace(/[^a-zA-Z0-9 _-]/g, "_")
.trim()
.slice(0, 80) || "report";
try {
if (format === "md") {
// Download markdown content directly as a .md file
@ -248,7 +231,6 @@ function ReportPanelContent({
[activeReportId, title, reportContent?.content]
);
// Show full-page skeleton only on initial load (no data loaded yet).
// Once we have versions/content from a prior fetch, keep the action bar visible.
const hasLoadedBefore = versions.length > 0 || reportContent !== null;
@ -259,12 +241,7 @@ function ReportPanelContent({
{/* Minimal top bar with close button even during initial load */}
<div className="flex items-center justify-end px-4 py-2 shrink-0">
{onClose && (
<Button
variant="ghost"
size="icon"
onClick={onClose}
className="size-7 shrink-0"
>
<Button variant="ghost" size="icon" onClick={onClose} className="size-7 shrink-0">
<XIcon className="size-4" />
<span className="sr-only">Close report panel</span>
</Button>
@ -282,64 +259,63 @@ function ReportPanelContent({
{/* Action bar — always visible after initial load */}
<div className="flex items-center justify-between px-4 py-2 shrink-0">
<div className="flex items-center gap-2">
{/* Copy button */}
<Button
variant="outline"
size="sm"
onClick={handleCopy}
disabled={isLoading || !reportContent?.content}
className="h-8 min-w-[80px] px-3.5 py-4 text-[15px]"
>
{copied ? "Copied" : "Copy"}
</Button>
{/* Copy button */}
<Button
variant="outline"
size="sm"
onClick={handleCopy}
disabled={isLoading || !reportContent?.content}
className="h-8 min-w-[80px] px-3.5 py-4 text-[15px]"
>
{copied ? "Copied" : "Copy"}
</Button>
{/* Export dropdown */}
<DropdownMenu modal={insideDrawer ? false : undefined}>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
disabled={isLoading || !reportContent?.content}
className="h-8 px-3.5 py-4 text-[15px] gap-1.5"
{/* Export dropdown */}
<DropdownMenu modal={insideDrawer ? false : undefined}>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
disabled={isLoading || !reportContent?.content}
className="h-8 px-3.5 py-4 text-[15px] gap-1.5"
>
Export
<ChevronDownIcon className="size-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
className={`min-w-[180px]${insideDrawer ? " z-[100]" : ""}`}
>
Export
<ChevronDownIcon className="size-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className={`min-w-[180px]${insideDrawer ? " z-[100]" : ""}`}>
<DropdownMenuItem onClick={() => handleExport("md")}>
Download Markdown
</DropdownMenuItem>
{/* PDF/DOCX export requires server-side conversion via authenticated endpoint.
<DropdownMenuItem onClick={() => handleExport("md")}>
Download Markdown
</DropdownMenuItem>
{/* PDF/DOCX export requires server-side conversion via authenticated endpoint.
Hide for public viewers who have no auth token. */}
{!shareToken && (
<>
<DropdownMenuItem
onClick={() => handleExport("pdf")}
disabled={exporting !== null}
>
{exporting === "pdf" && (
<Spinner size="xs" />
)}
Download PDF
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => handleExport("docx")}
disabled={exporting !== null}
>
{exporting === "docx" && (
<Spinner size="xs" />
)}
Download DOCX
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
{!shareToken && (
<>
<DropdownMenuItem
onClick={() => handleExport("pdf")}
disabled={exporting !== null}
>
{exporting === "pdf" && <Spinner size="xs" />}
Download PDF
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => handleExport("docx")}
disabled={exporting !== null}
>
{exporting === "docx" && <Spinner size="xs" />}
Download DOCX
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
{/* Version switcher — only shown when multiple versions exist */}
{versions.length > 1 && (
insideDrawer ? (
{versions.length > 1 &&
(insideDrawer ? (
/* Mobile: compact dropdown */
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
@ -387,16 +363,10 @@ function ReportPanelContent({
{activeVersionIndex + 1} of {versions.length}
</span>
</div>
)
)}
))}
</div>
{onClose && (
<Button
variant="ghost"
size="icon"
onClick={onClose}
className="size-7 shrink-0"
>
<Button variant="ghost" size="icon" onClick={onClose} className="size-7 shrink-0">
<XIcon className="size-4" />
<span className="sr-only">Close report panel</span>
</Button>
@ -411,9 +381,7 @@ function ReportPanelContent({
<div className="flex flex-1 flex-col items-center justify-center gap-3 p-6 text-center">
<div>
<p className="font-medium text-foreground">Failed to load report</p>
<p className="text-sm text-red-500 mt-1">
{error || "An unknown error occurred"}
</p>
<p className="text-sm text-red-500 mt-1">{error || "An unknown error occurred"}</p>
</div>
</div>
) : (
@ -421,9 +389,7 @@ function ReportPanelContent({
{reportContent.content ? (
<MarkdownViewer content={reportContent.content} />
) : (
<p className="text-muted-foreground italic">
No content available.
</p>
<p className="text-muted-foreground italic">No content available.</p>
)}
</div>
)}
@ -485,10 +451,7 @@ function MobileReportDrawer() {
}}
shouldScaleBackground={false}
>
<DrawerContent
className="h-[90vh] max-h-[90vh] z-80"
overlayClassName="z-80"
>
<DrawerContent className="h-[90vh] max-h-[90vh] z-80" overlayClassName="z-80">
<DrawerHandle />
<div className="min-h-0 flex-1 flex flex-col overflow-hidden">
<ReportPanelContent

View file

@ -245,7 +245,9 @@ export function Article({
{/* Description */}
{description && (
<p className="text-muted-foreground text-[10px] sm:text-xs mt-1 line-clamp-2">{description}</p>
<p className="text-muted-foreground text-[10px] sm:text-xs mt-1 line-clamp-2">
{description}
</p>
)}
{/* Metadata row */}
@ -305,7 +307,6 @@ export function Article({
)}
</div>
</div>
</div>
{/* Response actions */}

View file

@ -7,10 +7,7 @@ import { useParams, usePathname } from "next/navigation";
import { useEffect, useState } from "react";
import { z } from "zod";
import { TextShimmerLoader } from "@/components/prompt-kit/loader";
import {
openReportPanelAtom,
reportPanelAtom,
} from "@/atoms/chat/report-panel.atom";
import { openReportPanelAtom, reportPanelAtom } from "@/atoms/chat/report-panel.atom";
import { baseApiService } from "@/lib/apis/base-api.service";
/**
@ -97,9 +94,7 @@ function ReportErrorState({ title, error }: { title: string; error: string }) {
<h3 className="font-semibold text-muted-foreground text-sm sm:text-base leading-tight line-clamp-2">
{title}
</h3>
<p className="text-muted-foreground/60 text-[11px] sm:text-xs mt-0.5 truncate">
{error}
</p>
<p className="text-muted-foreground/60 text-[11px] sm:text-xs mt-0.5 truncate">{error}</p>
</div>
</div>
</div>
@ -148,10 +143,7 @@ function ReportCard({
if (parsed.success) {
// Check if report was marked as failed in metadata
if (parsed.data.report_metadata?.status === "failed") {
setError(
parsed.data.report_metadata?.error_message ||
"Report generation failed"
);
setError(parsed.data.report_metadata?.error_message || "Report generation failed");
} else {
// Determine version label from versions array
let versionLabel: string | null = null;
@ -164,8 +156,7 @@ function ReportCard({
}
setMetadata({
title: parsed.data.title || title,
wordCount:
parsed.data.report_metadata?.word_count ?? wordCount ?? null,
wordCount: parsed.data.report_metadata?.word_count ?? wordCount ?? null,
versionLabel,
});
}
@ -219,9 +210,10 @@ function ReportCard({
<span className="inline-block h-3 w-24 rounded bg-muted/60 animate-pulse" />
) : (
<>
{metadata.wordCount != null &&
`${metadata.wordCount.toLocaleString()} words`}
{metadata.wordCount != null && metadata.versionLabel && <Dot className="inline size-4" />}
{metadata.wordCount != null && `${metadata.wordCount.toLocaleString()} words`}
{metadata.wordCount != null && metadata.versionLabel && (
<Dot className="inline size-4" />
)}
{metadata.versionLabel}
</>
)}
@ -241,10 +233,7 @@ function ReportCard({
* Unlike podcast (which uses polling), the report is generated inline
* and the result contains status: "ready" immediately.
*/
export const GenerateReportToolUI = makeAssistantToolUI<
GenerateReportArgs,
GenerateReportResult
>({
export const GenerateReportToolUI = makeAssistantToolUI<GenerateReportArgs, GenerateReportResult>({
toolName: "generate_report",
render: function GenerateReportUI({ args, result, status }) {
const params = useParams();
@ -288,7 +277,12 @@ export const GenerateReportToolUI = makeAssistantToolUI<
// Failed result
if (result.status === "failed") {
return <ReportErrorState title={result.title || topic} error={result.error || "Generation failed"} />;
return (
<ReportErrorState
title={result.title || topic}
error={result.error || "Generation failed"}
/>
);
}
// Ready with report_id

View file

@ -89,12 +89,7 @@ function ScrapeCancelledState({ url }: { url: string }) {
function ParsedArticle({ result }: { result: unknown }) {
const { description, ...article } = parseSerializableArticle(result);
return (
<Article
{...article}
maxWidth="480px"
/>
);
return <Article {...article} maxWidth="480px" />;
}
/**