mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-26 21:39:43 +02:00
feat: render artifacts in right panel
This commit is contained in:
parent
695ad19620
commit
050d6bf998
1 changed files with 64 additions and 31 deletions
|
|
@ -8,9 +8,14 @@ import { closeReportPanelAtom, reportPanelAtom } from "@/atoms/chat/report-panel
|
||||||
import { citationPanelAtom, closeCitationPanelAtom } from "@/atoms/citation/citation-panel.atom";
|
import { citationPanelAtom, closeCitationPanelAtom } from "@/atoms/citation/citation-panel.atom";
|
||||||
import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
|
import { documentsSidebarOpenAtom } from "@/atoms/documents/ui.atoms";
|
||||||
import { closeEditorPanelAtom, editorPanelAtom } from "@/atoms/editor/editor-panel.atom";
|
import { closeEditorPanelAtom, editorPanelAtom } from "@/atoms/editor/editor-panel.atom";
|
||||||
import { rightPanelCollapsedAtom, rightPanelTabAtom } from "@/atoms/layout/right-panel.atom";
|
import {
|
||||||
|
type RightPanelTab,
|
||||||
|
rightPanelCollapsedAtom,
|
||||||
|
rightPanelTabAtom,
|
||||||
|
} from "@/atoms/layout/right-panel.atom";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
|
import { artifactsPanelOpenAtom, closeArtifactsPanelAtom } from "@/features/chat-artifacts";
|
||||||
import { closeHitlEditPanelAtom, hitlEditPanelAtom } from "@/features/chat-messages/hitl";
|
import { closeHitlEditPanelAtom, hitlEditPanelAtom } from "@/features/chat-messages/hitl";
|
||||||
import { useMediaQuery } from "@/hooks/use-media-query";
|
import { useMediaQuery } from "@/hooks/use-media-query";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
@ -48,6 +53,14 @@ const ReportPanelContent = dynamic(
|
||||||
{ ssr: false, loading: () => null }
|
{ ssr: false, loading: () => null }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const ArtifactsPanelContent = dynamic(
|
||||||
|
() =>
|
||||||
|
import("@/features/chat-artifacts").then((m) => ({
|
||||||
|
default: m.ArtifactsPanelContent,
|
||||||
|
})),
|
||||||
|
{ ssr: false, loading: () => null }
|
||||||
|
);
|
||||||
|
|
||||||
interface RightPanelProps {
|
interface RightPanelProps {
|
||||||
documentsPanel?: {
|
documentsPanel?: {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|
@ -101,6 +114,7 @@ export function RightPanelToggleButton({
|
||||||
const editorState = useAtomValue(editorPanelAtom);
|
const editorState = useAtomValue(editorPanelAtom);
|
||||||
const hitlEditState = useAtomValue(hitlEditPanelAtom);
|
const hitlEditState = useAtomValue(hitlEditPanelAtom);
|
||||||
const citationState = useAtomValue(citationPanelAtom);
|
const citationState = useAtomValue(citationPanelAtom);
|
||||||
|
const artifactsOpen = useAtomValue(artifactsPanelOpenAtom);
|
||||||
const reportOpen = reportState.isOpen && !!reportState.reportId;
|
const reportOpen = reportState.isOpen && !!reportState.reportId;
|
||||||
const editorOpen =
|
const editorOpen =
|
||||||
editorState.isOpen &&
|
editorState.isOpen &&
|
||||||
|
|
@ -111,7 +125,8 @@ export function RightPanelToggleButton({
|
||||||
: !!editorState.localFilePath);
|
: !!editorState.localFilePath);
|
||||||
const hitlEditOpen = hitlEditState.isOpen && !!hitlEditState.onSave;
|
const hitlEditOpen = hitlEditState.isOpen && !!hitlEditState.onSave;
|
||||||
const citationOpen = citationState.isOpen && citationState.chunkId != null;
|
const citationOpen = citationState.isOpen && citationState.chunkId != null;
|
||||||
const hasContent = documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen;
|
const hasContent =
|
||||||
|
documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen || artifactsOpen;
|
||||||
const label = collapsed ? "Expand panel" : "Collapse panel";
|
const label = collapsed ? "Expand panel" : "Collapse panel";
|
||||||
|
|
||||||
if (!hasContent) return null;
|
if (!hasContent) return null;
|
||||||
|
|
@ -153,6 +168,7 @@ export function RightPanelExpandButton() {
|
||||||
const editorState = useAtomValue(editorPanelAtom);
|
const editorState = useAtomValue(editorPanelAtom);
|
||||||
const hitlEditState = useAtomValue(hitlEditPanelAtom);
|
const hitlEditState = useAtomValue(hitlEditPanelAtom);
|
||||||
const citationState = useAtomValue(citationPanelAtom);
|
const citationState = useAtomValue(citationPanelAtom);
|
||||||
|
const artifactsOpen = useAtomValue(artifactsPanelOpenAtom);
|
||||||
const reportOpen = reportState.isOpen && !!reportState.reportId;
|
const reportOpen = reportState.isOpen && !!reportState.reportId;
|
||||||
const editorOpen =
|
const editorOpen =
|
||||||
editorState.isOpen &&
|
editorState.isOpen &&
|
||||||
|
|
@ -163,7 +179,8 @@ export function RightPanelExpandButton() {
|
||||||
: !!editorState.localFilePath);
|
: !!editorState.localFilePath);
|
||||||
const hitlEditOpen = hitlEditState.isOpen && !!hitlEditState.onSave;
|
const hitlEditOpen = hitlEditState.isOpen && !!hitlEditState.onSave;
|
||||||
const citationOpen = citationState.isOpen && citationState.chunkId != null;
|
const citationOpen = citationState.isOpen && citationState.chunkId != null;
|
||||||
const hasContent = documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen;
|
const hasContent =
|
||||||
|
documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen || artifactsOpen;
|
||||||
|
|
||||||
if (!collapsed || !hasContent) return null;
|
if (!collapsed || !hasContent) return null;
|
||||||
|
|
||||||
|
|
@ -180,8 +197,31 @@ const PANEL_WIDTHS = {
|
||||||
editor: 640,
|
editor: 640,
|
||||||
"hitl-edit": 640,
|
"hitl-edit": 640,
|
||||||
citation: 560,
|
citation: 560,
|
||||||
|
artifacts: 420,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Priority order used to fall back to another open surface when the active
|
||||||
|
* tab's content closes. Artifacts sit just above the always-available sources
|
||||||
|
* tab.
|
||||||
|
*/
|
||||||
|
const TAB_FALLBACK_ORDER: RightPanelTab[] = [
|
||||||
|
"hitl-edit",
|
||||||
|
"citation",
|
||||||
|
"editor",
|
||||||
|
"report",
|
||||||
|
"artifacts",
|
||||||
|
"sources",
|
||||||
|
];
|
||||||
|
|
||||||
|
function resolveEffectiveTab(
|
||||||
|
activeTab: RightPanelTab,
|
||||||
|
openByTab: Record<RightPanelTab, boolean>
|
||||||
|
): RightPanelTab {
|
||||||
|
if (openByTab[activeTab]) return activeTab;
|
||||||
|
return TAB_FALLBACK_ORDER.find((tab) => openByTab[tab]) ?? "sources";
|
||||||
|
}
|
||||||
|
|
||||||
export function RightPanel({
|
export function RightPanel({
|
||||||
documentsPanel,
|
documentsPanel,
|
||||||
showCollapseButton = true,
|
showCollapseButton = true,
|
||||||
|
|
@ -196,6 +236,8 @@ export function RightPanel({
|
||||||
const closeHitlEdit = useSetAtom(closeHitlEditPanelAtom);
|
const closeHitlEdit = useSetAtom(closeHitlEditPanelAtom);
|
||||||
const citationState = useAtomValue(citationPanelAtom);
|
const citationState = useAtomValue(citationPanelAtom);
|
||||||
const closeCitation = useSetAtom(closeCitationPanelAtom);
|
const closeCitation = useSetAtom(closeCitationPanelAtom);
|
||||||
|
const artifactsOpen = useAtomValue(artifactsPanelOpenAtom);
|
||||||
|
const closeArtifacts = useSetAtom(closeArtifactsPanelAtom);
|
||||||
const [collapsed, setCollapsed] = useAtom(rightPanelCollapsedAtom);
|
const [collapsed, setCollapsed] = useAtom(rightPanelCollapsedAtom);
|
||||||
// Desktop-only surface; mobile uses the dedicated Mobile* drawers. Without
|
// Desktop-only surface; mobile uses the dedicated Mobile* drawers. Without
|
||||||
// this guard both render together and two editors fight over one model.
|
// this guard both render together and two editors fight over one model.
|
||||||
|
|
@ -214,13 +256,14 @@ export function RightPanel({
|
||||||
const citationOpen = citationState.isOpen && citationState.chunkId != null;
|
const citationOpen = citationState.isOpen && citationState.chunkId != null;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!reportOpen && !editorOpen && !hitlEditOpen && !citationOpen) return;
|
if (!reportOpen && !editorOpen && !hitlEditOpen && !citationOpen && !artifactsOpen) return;
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
if (e.key === "Escape") {
|
if (e.key === "Escape") {
|
||||||
if (hitlEditOpen) closeHitlEdit();
|
if (hitlEditOpen) closeHitlEdit();
|
||||||
else if (citationOpen) closeCitation();
|
else if (citationOpen) closeCitation();
|
||||||
else if (editorOpen) closeEditor();
|
else if (editorOpen) closeEditor();
|
||||||
else if (reportOpen) closeReport();
|
else if (reportOpen) closeReport();
|
||||||
|
else if (artifactsOpen) closeArtifacts();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
document.addEventListener("keydown", handleKeyDown);
|
document.addEventListener("keydown", handleKeyDown);
|
||||||
|
|
@ -230,41 +273,26 @@ export function RightPanel({
|
||||||
editorOpen,
|
editorOpen,
|
||||||
hitlEditOpen,
|
hitlEditOpen,
|
||||||
citationOpen,
|
citationOpen,
|
||||||
|
artifactsOpen,
|
||||||
closeReport,
|
closeReport,
|
||||||
closeEditor,
|
closeEditor,
|
||||||
closeHitlEdit,
|
closeHitlEdit,
|
||||||
closeCitation,
|
closeCitation,
|
||||||
|
closeArtifacts,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isVisible =
|
const isVisible =
|
||||||
(documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen) && !collapsed;
|
(documentsOpen || reportOpen || editorOpen || hitlEditOpen || citationOpen || artifactsOpen) &&
|
||||||
|
!collapsed;
|
||||||
|
|
||||||
let effectiveTab = activeTab;
|
const effectiveTab = resolveEffectiveTab(activeTab, {
|
||||||
if (effectiveTab === "hitl-edit" && !hitlEditOpen) {
|
sources: documentsOpen,
|
||||||
effectiveTab = citationOpen
|
report: reportOpen,
|
||||||
? "citation"
|
editor: editorOpen,
|
||||||
: editorOpen
|
"hitl-edit": hitlEditOpen,
|
||||||
? "editor"
|
citation: citationOpen,
|
||||||
: reportOpen
|
artifacts: artifactsOpen,
|
||||||
? "report"
|
});
|
||||||
: "sources";
|
|
||||||
} else if (effectiveTab === "citation" && !citationOpen) {
|
|
||||||
effectiveTab = editorOpen ? "editor" : reportOpen ? "report" : "sources";
|
|
||||||
} else if (effectiveTab === "editor" && !editorOpen) {
|
|
||||||
effectiveTab = citationOpen ? "citation" : reportOpen ? "report" : "sources";
|
|
||||||
} else if (effectiveTab === "report" && !reportOpen) {
|
|
||||||
effectiveTab = citationOpen ? "citation" : editorOpen ? "editor" : "sources";
|
|
||||||
} else if (effectiveTab === "sources" && !documentsOpen) {
|
|
||||||
effectiveTab = hitlEditOpen
|
|
||||||
? "hitl-edit"
|
|
||||||
: citationOpen
|
|
||||||
? "citation"
|
|
||||||
: editorOpen
|
|
||||||
? "editor"
|
|
||||||
: reportOpen
|
|
||||||
? "report"
|
|
||||||
: "sources";
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetWidth = PANEL_WIDTHS[effectiveTab];
|
const targetWidth = PANEL_WIDTHS[effectiveTab];
|
||||||
const collapseButton = showCollapseButton ? (
|
const collapseButton = showCollapseButton ? (
|
||||||
|
|
@ -335,6 +363,11 @@ export function RightPanel({
|
||||||
<CitationPanelContent chunkId={citationState.chunkId} onClose={closeCitation} />
|
<CitationPanelContent chunkId={citationState.chunkId} onClose={closeCitation} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{effectiveTab === "artifacts" && artifactsOpen && (
|
||||||
|
<div className="h-full flex flex-col">
|
||||||
|
<ArtifactsPanelContent onClose={closeArtifacts} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue