mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-23 03:40:14 +01:00
🪄 feat: Agent Artifacts (#5804)
* refactor: remove artifacts toggle
* refactor: allow hiding side panel while allowing artifacts view
* chore: rename SidePanelGroup to SidePanel for clarity
* Revert "refactor: remove artifacts toggle"
This reverts commit f884c2cfcd.
* feat: add artifacts capability to agent configuration
* refactor: conditionally set artifacts mode based on endpoint type
* feat: Artifacts Capability for Agents
* refactor: enhance getStreamText method to handle intermediate replies and add `stream_options` for openai/azure
* feat: localize progress text and improve UX in CodeAnalyze and ExecuteCode components for expanding analysis
This commit is contained in:
parent
46f034250d
commit
bfbaaebd2b
26 changed files with 534 additions and 310 deletions
152
client/src/components/SidePanel/SidePanelGroup.tsx
Normal file
152
client/src/components/SidePanel/SidePanelGroup.tsx
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
import { useState, useRef, useCallback, useEffect, useMemo, memo } from 'react';
|
||||
import throttle from 'lodash/throttle';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { getConfigDefaults } from 'librechat-data-provider';
|
||||
import type { ImperativePanelHandle } from 'react-resizable-panels';
|
||||
import { ResizableHandleAlt, ResizablePanel, ResizablePanelGroup } from '~/components/ui/Resizable';
|
||||
import { useGetStartupConfig } from '~/data-provider';
|
||||
import { normalizeLayout } from '~/utils';
|
||||
import { useMediaQuery } from '~/hooks';
|
||||
import SidePanel from './SidePanel';
|
||||
import store from '~/store';
|
||||
|
||||
interface SidePanelProps {
|
||||
defaultLayout?: number[] | undefined;
|
||||
defaultCollapsed?: boolean;
|
||||
navCollapsedSize?: number;
|
||||
fullPanelCollapse?: boolean;
|
||||
artifacts?: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const defaultMinSize = 20;
|
||||
const defaultInterface = getConfigDefaults().interface;
|
||||
|
||||
const SidePanelGroup = ({
|
||||
defaultLayout = [97, 3],
|
||||
defaultCollapsed = false,
|
||||
fullPanelCollapse = false,
|
||||
navCollapsedSize = 3,
|
||||
artifacts,
|
||||
children,
|
||||
}: SidePanelProps) => {
|
||||
const { data: startupConfig } = useGetStartupConfig();
|
||||
const interfaceConfig = useMemo(
|
||||
() => startupConfig?.interface ?? defaultInterface,
|
||||
[startupConfig],
|
||||
);
|
||||
|
||||
const panelRef = useRef<ImperativePanelHandle>(null);
|
||||
const [minSize, setMinSize] = useState(defaultMinSize);
|
||||
const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
|
||||
const [fullCollapse, setFullCollapse] = useState(fullPanelCollapse);
|
||||
const [collapsedSize, setCollapsedSize] = useState(navCollapsedSize);
|
||||
|
||||
const isSmallScreen = useMediaQuery('(max-width: 767px)');
|
||||
const hideSidePanel = useRecoilValue(store.hideSidePanel);
|
||||
|
||||
const calculateLayout = useCallback(() => {
|
||||
if (artifacts == null) {
|
||||
const navSize = defaultLayout.length === 2 ? defaultLayout[1] : defaultLayout[2];
|
||||
return [100 - navSize, navSize];
|
||||
} else {
|
||||
const navSize = 0;
|
||||
const remainingSpace = 100 - navSize;
|
||||
const newMainSize = Math.floor(remainingSpace / 2);
|
||||
const artifactsSize = remainingSpace - newMainSize;
|
||||
return [newMainSize, artifactsSize, navSize];
|
||||
}
|
||||
}, [artifacts, defaultLayout]);
|
||||
|
||||
const currentLayout = useMemo(() => normalizeLayout(calculateLayout()), [calculateLayout]);
|
||||
|
||||
const throttledSaveLayout = useCallback(
|
||||
throttle((sizes: number[]) => {
|
||||
const normalizedSizes = normalizeLayout(sizes);
|
||||
localStorage.setItem('react-resizable-panels:layout', JSON.stringify(normalizedSizes));
|
||||
}, 350),
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSmallScreen) {
|
||||
setIsCollapsed(true);
|
||||
setCollapsedSize(0);
|
||||
setMinSize(defaultMinSize);
|
||||
setFullCollapse(true);
|
||||
localStorage.setItem('fullPanelCollapse', 'true');
|
||||
panelRef.current?.collapse();
|
||||
return;
|
||||
} else {
|
||||
setIsCollapsed(defaultCollapsed);
|
||||
setCollapsedSize(navCollapsedSize);
|
||||
setMinSize(defaultMinSize);
|
||||
}
|
||||
}, [isSmallScreen, defaultCollapsed, navCollapsedSize, fullPanelCollapse]);
|
||||
|
||||
const minSizeMain = useMemo(() => (artifacts != null ? 15 : 30), [artifacts]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResizablePanelGroup
|
||||
direction="horizontal"
|
||||
onLayout={(sizes) => throttledSaveLayout(sizes)}
|
||||
className="transition-width relative h-full w-full flex-1 overflow-auto bg-presentation"
|
||||
>
|
||||
<ResizablePanel
|
||||
defaultSize={currentLayout[0]}
|
||||
minSize={minSizeMain}
|
||||
order={1}
|
||||
id="messages-view"
|
||||
>
|
||||
{children}
|
||||
</ResizablePanel>
|
||||
{artifacts != null && (
|
||||
<>
|
||||
<ResizableHandleAlt withHandle className="ml-3 bg-border-medium text-text-primary" />
|
||||
<ResizablePanel
|
||||
defaultSize={currentLayout[1]}
|
||||
minSize={minSizeMain}
|
||||
order={2}
|
||||
id="artifacts-panel"
|
||||
>
|
||||
{artifacts}
|
||||
</ResizablePanel>
|
||||
</>
|
||||
)}
|
||||
{!hideSidePanel && interfaceConfig.sidePanel === true && (
|
||||
<SidePanel
|
||||
panelRef={panelRef}
|
||||
minSize={minSize}
|
||||
setMinSize={setMinSize}
|
||||
isCollapsed={isCollapsed}
|
||||
setIsCollapsed={setIsCollapsed}
|
||||
collapsedSize={collapsedSize}
|
||||
setCollapsedSize={setCollapsedSize}
|
||||
fullCollapse={fullCollapse}
|
||||
setFullCollapse={setFullCollapse}
|
||||
defaultSize={currentLayout[currentLayout.length - 1]}
|
||||
hasArtifacts={artifacts != null}
|
||||
interfaceConfig={interfaceConfig}
|
||||
/>
|
||||
)}
|
||||
</ResizablePanelGroup>
|
||||
<button
|
||||
aria-label="Close right side panel"
|
||||
className={`nav-mask ${!isCollapsed ? 'active' : ''}`}
|
||||
onClick={() => {
|
||||
setIsCollapsed(() => {
|
||||
localStorage.setItem('fullPanelCollapse', 'true');
|
||||
setFullCollapse(true);
|
||||
setCollapsedSize(0);
|
||||
setMinSize(0);
|
||||
return false;
|
||||
});
|
||||
panelRef.current?.collapse();
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(SidePanelGroup);
|
||||
Loading…
Add table
Add a link
Reference in a new issue