"use client";
import { useQuery } from "@tanstack/react-query";
import { useSetAtom } from "jotai";
import { ExternalLink, FileText } from "lucide-react";
import dynamic from "next/dynamic";
import type { FC } from "react";
import { useState } from "react";
import { openCitationPanelAtom } from "@/atoms/citation/citation-panel.atom";
import { useCitationMetadata } from "@/components/assistant-ui/citation-metadata-context";
import { CitationPanelContent } from "@/components/citation-panel/citation-panel";
import { Citation } from "@/components/tool-ui/citation";
import { CitationHoverPopover } from "@/components/tool-ui/citation/citation-hover-popover";
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerContent,
DrawerHandle,
DrawerHeader,
DrawerTitle,
} from "@/components/ui/drawer";
import { Spinner } from "@/components/ui/spinner";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useMediaQuery } from "@/hooks/use-media-query";
import { documentsApiService } from "@/lib/apis/documents-api.service";
import { cacheKeys } from "@/lib/query-client/cache-keys";
// Lazily load MarkdownViewer here to break the static import cycle:
// `markdown-viewer.tsx` → `citation-renderer.tsx` → `inline-citation.tsx`
// would otherwise pull `markdown-viewer.tsx` back in at module-init time.
// Only `SurfsenseDocCitation` (popover body) ever renders this viewer, so
// the lazy boundary is invisible to most call paths.
const MarkdownViewer = dynamic(
() => import("@/components/markdown-viewer").then((m) => m.MarkdownViewer),
{ ssr: false, loading: () => }
);
interface InlineCitationProps {
chunkId: number;
isDocsChunk?: boolean;
}
/**
* Inline citation badge for knowledge-base chunks (numeric chunk IDs) and
* Surfsense documentation chunks (`isDocsChunk`). Negative chunk IDs render as
* a static "doc" pill (anonymous/synthetic uploads).
*
* Numeric KB chunks: clicking opens the citation panel in the right
* sidebar (alongside the chat — does not replace it). The panel shows
* the cited chunk surrounded by adjacent chunks (via the API's
* `chunk_window`), with the cited one highlighted and an option to
* expand the window or jump into the full document via the editor panel.
*
* Surfsense docs chunks: rendered as a hover-controlled shadcn Popover that
* lazily fetches and previews the cited chunk inline, since those docs aren't
* indexed into the user's search space and have no tab to open.
*/
export const InlineCitation: FC = ({ chunkId, isDocsChunk = false }) => {
if (chunkId < 0) {
return (
doc
Uploaded document
);
}
if (isDocsChunk) {
return ;
}
return ;
};
const NumericChunkCitation: FC<{ chunkId: number }> = ({ chunkId }) => {
const isTouchLike = useMediaQuery("(hover: none), (pointer: coarse)");
const openCitationPanel = useSetAtom(openCitationPanelAtom);
const [mobilePreviewOpen, setMobilePreviewOpen] = useState(false);
const handleClick = () => {
if (isTouchLike) {
setMobilePreviewOpen(true);
return;
}
openCitationPanel({ chunkId });
};
return (
<>
Citation