2026-03-11 01:22:24 +05:30
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
|
|
|
import { PanelRight, PanelRightClose } from "lucide-react";
|
2026-03-11 02:02:05 +05:30
|
|
|
import { startTransition, useEffect } from "react";
|
2026-03-17 23:33:22 +05:30
|
|
|
import { closeHitlEditPanelAtom, hitlEditPanelAtom } from "@/atoms/chat/hitl-edit-panel.atom";
|
2026-03-11 01:22:24 +05:30
|
|
|
import { closeReportPanelAtom, reportPanelAtom } from "@/atoms/chat/report-panel.atom";
|
|
|
|
|
import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
|
2026-03-17 16:55:31 +05:30
|
|
|
import { closeEditorPanelAtom, editorPanelAtom } from "@/atoms/editor/editor-panel.atom";
|
2026-03-11 01:22:24 +05:30
|
|
|
import { rightPanelCollapsedAtom, rightPanelTabAtom } from "@/atoms/layout/right-panel.atom";
|
2026-03-17 16:55:31 +05:30
|
|
|
import { EditorPanelContent } from "@/components/editor-panel/editor-panel";
|
2026-03-17 23:33:22 +05:30
|
|
|
import { HitlEditPanelContent } from "@/components/hitl-edit-panel/hitl-edit-panel";
|
2026-03-11 01:22:24 +05:30
|
|
|
import { ReportPanelContent } from "@/components/report-panel/report-panel";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
|
|
|
import { DocumentsSidebar } from "../sidebar";
|
|
|
|
|
|
|
|
|
|
interface RightPanelProps {
|
|
|
|
|
documentsPanel?: {
|
|
|
|
|
open: boolean;
|
|
|
|
|
onOpenChange: (open: boolean) => void;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function CollapseButton({ onClick }: { onClick: () => void }) {
|
|
|
|
|
return (
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
<Button variant="ghost" size="icon" onClick={onClick} className="h-8 w-8 shrink-0">
|
|
|
|
|
<PanelRightClose className="h-4 w-4" />
|
|
|
|
|
<span className="sr-only">Collapse panel</span>
|
|
|
|
|
</Button>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent side="left">Collapse panel</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Absolutely positioned expand button — renders at top-right of the main
|
|
|
|
|
* container so it occupies the same screen position as the collapse button
|
|
|
|
|
* inside the Documents header.
|
|
|
|
|
*/
|
|
|
|
|
export function RightPanelExpandButton() {
|
|
|
|
|
const [collapsed, setCollapsed] = useAtom(rightPanelCollapsedAtom);
|
|
|
|
|
const documentsOpen = useAtomValue(documentsSidebarOpenAtom);
|
|
|
|
|
const reportState = useAtomValue(reportPanelAtom);
|
2026-03-17 16:55:31 +05:30
|
|
|
const editorState = useAtomValue(editorPanelAtom);
|
2026-03-17 23:33:22 +05:30
|
|
|
const hitlEditState = useAtomValue(hitlEditPanelAtom);
|
2026-03-11 01:22:24 +05:30
|
|
|
const reportOpen = reportState.isOpen && !!reportState.reportId;
|
2026-03-17 16:55:31 +05:30
|
|
|
const editorOpen = editorState.isOpen && !!editorState.documentId;
|
2026-03-17 23:33:22 +05:30
|
|
|
const hitlEditOpen = hitlEditState.isOpen && !!hitlEditState.onSave;
|
|
|
|
|
const hasContent = documentsOpen || reportOpen || editorOpen || hitlEditOpen;
|
2026-03-11 01:22:24 +05:30
|
|
|
|
|
|
|
|
if (!collapsed || !hasContent) return null;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="absolute top-4 right-4 z-20">
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="icon"
|
2026-03-11 02:18:59 +05:30
|
|
|
onClick={() => startTransition(() => setCollapsed(false))}
|
|
|
|
|
className="h-8 w-8 shrink-0"
|
2026-03-11 01:22:24 +05:30
|
|
|
>
|
|
|
|
|
<PanelRight className="h-4 w-4" />
|
|
|
|
|
<span className="sr-only">Expand panel</span>
|
|
|
|
|
</Button>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
<TooltipContent side="left">Expand panel</TooltipContent>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 23:33:22 +05:30
|
|
|
const PANEL_WIDTHS = { sources: 420, report: 640, editor: 640, "hitl-edit": 640 } as const;
|
2026-03-11 01:44:55 +05:30
|
|
|
|
2026-03-11 01:22:24 +05:30
|
|
|
export function RightPanel({ documentsPanel }: RightPanelProps) {
|
|
|
|
|
const [activeTab] = useAtom(rightPanelTabAtom);
|
|
|
|
|
const reportState = useAtomValue(reportPanelAtom);
|
|
|
|
|
const closeReport = useSetAtom(closeReportPanelAtom);
|
2026-03-17 16:55:31 +05:30
|
|
|
const editorState = useAtomValue(editorPanelAtom);
|
|
|
|
|
const closeEditor = useSetAtom(closeEditorPanelAtom);
|
2026-03-17 23:33:22 +05:30
|
|
|
const hitlEditState = useAtomValue(hitlEditPanelAtom);
|
|
|
|
|
const closeHitlEdit = useSetAtom(closeHitlEditPanelAtom);
|
2026-03-11 01:22:24 +05:30
|
|
|
const [collapsed, setCollapsed] = useAtom(rightPanelCollapsedAtom);
|
|
|
|
|
|
|
|
|
|
const documentsOpen = documentsPanel?.open ?? false;
|
|
|
|
|
const reportOpen = reportState.isOpen && !!reportState.reportId;
|
2026-03-17 16:55:31 +05:30
|
|
|
const editorOpen = editorState.isOpen && !!editorState.documentId;
|
2026-03-17 23:33:22 +05:30
|
|
|
const hitlEditOpen = hitlEditState.isOpen && !!hitlEditState.onSave;
|
2026-03-11 01:22:24 +05:30
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-03-17 23:33:22 +05:30
|
|
|
if (!reportOpen && !editorOpen && !hitlEditOpen) return;
|
2026-03-11 01:22:24 +05:30
|
|
|
const handleKeyDown = (e: KeyboardEvent) => {
|
2026-03-17 16:55:31 +05:30
|
|
|
if (e.key === "Escape") {
|
2026-03-17 23:33:22 +05:30
|
|
|
if (hitlEditOpen) closeHitlEdit();
|
|
|
|
|
else if (editorOpen) closeEditor();
|
2026-03-17 16:55:31 +05:30
|
|
|
else if (reportOpen) closeReport();
|
|
|
|
|
}
|
2026-03-11 01:22:24 +05:30
|
|
|
};
|
|
|
|
|
document.addEventListener("keydown", handleKeyDown);
|
|
|
|
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
2026-03-17 23:33:22 +05:30
|
|
|
}, [reportOpen, editorOpen, hitlEditOpen, closeReport, closeEditor, closeHitlEdit]);
|
2026-03-11 01:22:24 +05:30
|
|
|
|
2026-03-17 23:33:22 +05:30
|
|
|
const isVisible = (documentsOpen || reportOpen || editorOpen || hitlEditOpen) && !collapsed;
|
2026-03-11 01:22:24 +05:30
|
|
|
|
2026-03-17 16:55:31 +05:30
|
|
|
let effectiveTab = activeTab;
|
2026-03-17 23:33:22 +05:30
|
|
|
if (effectiveTab === "hitl-edit" && !hitlEditOpen) {
|
|
|
|
|
effectiveTab = editorOpen ? "editor" : reportOpen ? "report" : "sources";
|
|
|
|
|
} else if (effectiveTab === "editor" && !editorOpen) {
|
2026-03-17 16:55:31 +05:30
|
|
|
effectiveTab = reportOpen ? "report" : "sources";
|
|
|
|
|
} else if (effectiveTab === "report" && !reportOpen) {
|
|
|
|
|
effectiveTab = editorOpen ? "editor" : "sources";
|
|
|
|
|
} else if (effectiveTab === "sources" && !documentsOpen) {
|
2026-03-21 13:20:13 +05:30
|
|
|
effectiveTab = hitlEditOpen
|
|
|
|
|
? "hitl-edit"
|
|
|
|
|
: editorOpen
|
|
|
|
|
? "editor"
|
|
|
|
|
: reportOpen
|
|
|
|
|
? "report"
|
|
|
|
|
: "sources";
|
2026-03-17 16:55:31 +05:30
|
|
|
}
|
2026-03-11 01:22:24 +05:30
|
|
|
|
2026-03-11 01:44:55 +05:30
|
|
|
const targetWidth = PANEL_WIDTHS[effectiveTab];
|
2026-03-11 01:22:24 +05:30
|
|
|
const collapseButton = <CollapseButton onClick={() => setCollapsed(true)} />;
|
|
|
|
|
|
2026-03-11 16:37:56 -07:00
|
|
|
if (!isVisible) return null;
|
2026-03-11 01:22:24 +05:30
|
|
|
|
|
|
|
|
return (
|
2026-03-11 16:37:56 -07:00
|
|
|
<aside
|
|
|
|
|
style={{ width: targetWidth }}
|
2026-03-17 14:16:22 +05:30
|
|
|
className="flex h-full shrink-0 flex-col rounded-xl border bg-sidebar text-sidebar-foreground overflow-hidden transition-[width] duration-200 ease-out"
|
2026-03-11 16:37:56 -07:00
|
|
|
>
|
|
|
|
|
<div className="relative flex-1 min-h-0 overflow-hidden">
|
|
|
|
|
{effectiveTab === "sources" && documentsOpen && documentsPanel && (
|
|
|
|
|
<div className="h-full">
|
|
|
|
|
<DocumentsSidebar
|
|
|
|
|
open={documentsPanel.open}
|
|
|
|
|
onOpenChange={documentsPanel.onOpenChange}
|
|
|
|
|
embedded
|
|
|
|
|
headerAction={collapseButton}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{effectiveTab === "report" && reportOpen && (
|
2026-03-17 14:16:22 +05:30
|
|
|
<div className="h-full flex flex-col">
|
2026-03-11 16:37:56 -07:00
|
|
|
<ReportPanelContent
|
2026-03-17 14:16:22 +05:30
|
|
|
reportId={reportState.reportId as number}
|
2026-03-11 16:37:56 -07:00
|
|
|
title={reportState.title || "Report"}
|
|
|
|
|
onClose={closeReport}
|
|
|
|
|
shareToken={reportState.shareToken}
|
|
|
|
|
/>
|
2026-03-11 01:22:24 +05:30
|
|
|
</div>
|
2026-03-11 16:37:56 -07:00
|
|
|
)}
|
2026-03-17 16:55:31 +05:30
|
|
|
{effectiveTab === "editor" && editorOpen && (
|
|
|
|
|
<div className="h-full flex flex-col">
|
|
|
|
|
<EditorPanelContent
|
|
|
|
|
documentId={editorState.documentId as number}
|
|
|
|
|
searchSpaceId={editorState.searchSpaceId as number}
|
|
|
|
|
title={editorState.title}
|
|
|
|
|
onClose={closeEditor}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-03-17 23:33:22 +05:30
|
|
|
{effectiveTab === "hitl-edit" && hitlEditOpen && hitlEditState.onSave && (
|
|
|
|
|
<div className="h-full flex flex-col">
|
2026-03-21 13:20:13 +05:30
|
|
|
<HitlEditPanelContent
|
|
|
|
|
title={hitlEditState.title}
|
|
|
|
|
content={hitlEditState.content}
|
|
|
|
|
toolName={hitlEditState.toolName}
|
|
|
|
|
extraFields={hitlEditState.extraFields}
|
|
|
|
|
onSave={hitlEditState.onSave}
|
|
|
|
|
onClose={closeHitlEdit}
|
|
|
|
|
/>
|
2026-03-17 23:33:22 +05:30
|
|
|
</div>
|
|
|
|
|
)}
|
2026-03-11 16:37:56 -07:00
|
|
|
</div>
|
|
|
|
|
</aside>
|
2026-03-11 01:22:24 +05:30
|
|
|
);
|
|
|
|
|
}
|