mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-15 06:58:51 +01:00
✨ feat: Artifact Management Enhancements, Version Control, and UI Refinements (#10318)
* ✨ feat: Enhance Artifact Management with Version Control and UI Improvements ✨ feat: Improve mobile layout and responsiveness in Artifacts component ✨ feat: Refactor imports and remove unnecessary props in Artifact components ✨ feat: Enhance Artifacts and SidePanel components with improved mobile responsiveness and layout transitions feat: Enhance artifact panel animations and improve UI responsiveness - Updated Thinking component button styles for smoother transitions. - Implemented dynamic rendering for artifacts panel with animation effects. - Refactored localization keys for consistency across multiple languages. - Added new CSS animations for iOS-inspired smooth transitions. - Improved Tailwind CSS configuration to support enhanced animation effects. ✨ feat: Add fullWidth and icon support to Radio component for enhanced flexibility refactor: Remove unused PreviewProps import in ArtifactPreview component refactor: Improve button class handling and blur effect constants in Artifact components ✨ feat: Refactor Artifacts component structure and add mobile/desktop variants for improved UI chore: Bump @librechat/client version to 0.3.2 refactor: Update button styles and transition durations for improved UI responsiveness refactor: revert back localization key refactor: remove unused scaling and animation properties for cleaner CSS refactor: remove unused animation properties for cleaner configuration * ✨ refactor: Simplify className usage in ArtifactTabs, ArtifactsHeader, and SidePanelGroup components * refactor: Remove cycleArtifact function from useArtifacts hook * ✨ feat: Implement Chromium resize lag fix with performance optimizations and new ArtifactsPanel component * ✨ feat: Update Badge component for responsive design and improve tap scaling behavior * chore: Update react-resizable-panels dependency to version 3.0.6 * ✨ feat: Refactor Artifacts components for improved structure and performance; remove unused files and optimize styles * ✨ style: Update text color for improved visibility in Artifacts component * ✨ style: Remove text color class for improved Spinner styling in Artifacts component * refactor: Split EditorContext into MutationContext and CodeContext to optimize re-renders; update related components to use new hooks * refactor: Optimize debounced mutation handling in CodeEditor component using refs to maintain current values and reduce re-renders * fix: Correct endpoint for message artifacts by changing URL segment from 'artifacts' to 'artifact' * feat: Enhance useEditArtifact mutation with optimistic updates and rollback on error; improve type safety with context management * fix: proper switch to preview as soon as artifact becomes enclosed * refactor: Remove optimistic updates from useEditArtifact mutation to prevent errors; simplify onMutate logic * test: Add comprehensive unit tests for useArtifacts hook to validate artifact handling, tab switching, and state management * test: Enhance unit tests for useArtifacts hook to cover new conversation transitions and null message handling --------- Co-authored-by: Marco Beretta <81851188+berry-13@users.noreply.github.com>
This commit is contained in:
parent
4186db3ce2
commit
b8b1217c34
25 changed files with 1565 additions and 345 deletions
82
client/src/components/SidePanel/ArtifactsPanel.tsx
Normal file
82
client/src/components/SidePanel/ArtifactsPanel.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { useRef, useEffect, memo } from 'react';
|
||||
import { ResizableHandleAlt, ResizablePanel } from '@librechat/client';
|
||||
import type { ImperativePanelHandle } from 'react-resizable-panels';
|
||||
|
||||
const ANIMATION_DURATION = 500;
|
||||
|
||||
interface ArtifactsPanelProps {
|
||||
artifacts: React.ReactNode | null;
|
||||
currentLayout: number[];
|
||||
minSizeMain: number;
|
||||
shouldRender: boolean;
|
||||
onRenderChange: (shouldRender: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* ArtifactsPanel component - memoized to prevent unnecessary re-renders
|
||||
* Only re-renders when artifacts visibility or layout changes
|
||||
*/
|
||||
const ArtifactsPanel = memo(function ArtifactsPanel({
|
||||
artifacts,
|
||||
currentLayout,
|
||||
minSizeMain,
|
||||
shouldRender,
|
||||
onRenderChange,
|
||||
}: ArtifactsPanelProps) {
|
||||
const artifactsPanelRef = useRef<ImperativePanelHandle>(null);
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (artifacts != null) {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
timeoutRef.current = null;
|
||||
}
|
||||
onRenderChange(true);
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
artifactsPanelRef.current?.expand();
|
||||
});
|
||||
});
|
||||
} else if (shouldRender) {
|
||||
artifactsPanelRef.current?.collapse();
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
onRenderChange(false);
|
||||
}, ANIMATION_DURATION);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
};
|
||||
}, [artifacts, shouldRender, onRenderChange]);
|
||||
|
||||
if (!shouldRender) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{artifacts != null && (
|
||||
<ResizableHandleAlt withHandle className="bg-border-medium text-text-primary" />
|
||||
)}
|
||||
<ResizablePanel
|
||||
ref={artifactsPanelRef}
|
||||
defaultSize={artifacts != null ? currentLayout[1] : 0}
|
||||
minSize={minSizeMain}
|
||||
maxSize={70}
|
||||
collapsible={true}
|
||||
collapsedSize={0}
|
||||
order={2}
|
||||
id="artifacts-panel"
|
||||
>
|
||||
<div className="h-full min-w-[400px] overflow-hidden">{artifacts}</div>
|
||||
</ResizablePanel>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
ArtifactsPanel.displayName = 'ArtifactsPanel';
|
||||
|
||||
export default ArtifactsPanel;
|
||||
|
|
@ -2,14 +2,10 @@ 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 {
|
||||
ResizableHandleAlt,
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
useMediaQuery,
|
||||
} from '@librechat/client';
|
||||
import { ResizablePanel, ResizablePanelGroup, useMediaQuery } from '@librechat/client';
|
||||
import type { ImperativePanelHandle } from 'react-resizable-panels';
|
||||
import { useGetStartupConfig } from '~/data-provider';
|
||||
import ArtifactsPanel from './ArtifactsPanel';
|
||||
import { normalizeLayout } from '~/utils';
|
||||
import SidePanel from './SidePanel';
|
||||
import store from '~/store';
|
||||
|
|
@ -46,6 +42,7 @@ const SidePanelGroup = memo(
|
|||
const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
|
||||
const [fullCollapse, setFullCollapse] = useState(fullPanelCollapse);
|
||||
const [collapsedSize, setCollapsedSize] = useState(navCollapsedSize);
|
||||
const [shouldRenderArtifacts, setShouldRenderArtifacts] = useState(artifacts != null);
|
||||
|
||||
const isSmallScreen = useMediaQuery('(max-width: 767px)');
|
||||
const hideSidePanel = useRecoilValue(store.hideSidePanel);
|
||||
|
|
@ -109,7 +106,7 @@ const SidePanelGroup = memo(
|
|||
<ResizablePanelGroup
|
||||
direction="horizontal"
|
||||
onLayout={(sizes) => throttledSaveLayout(sizes)}
|
||||
className="transition-width relative h-full w-full flex-1 overflow-auto bg-presentation"
|
||||
className="relative h-full w-full flex-1 overflow-auto bg-presentation"
|
||||
>
|
||||
<ResizablePanel
|
||||
defaultSize={currentLayout[0]}
|
||||
|
|
@ -119,19 +116,17 @@ const SidePanelGroup = memo(
|
|||
>
|
||||
{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>
|
||||
</>
|
||||
|
||||
{!isSmallScreen && (
|
||||
<ArtifactsPanel
|
||||
artifacts={artifacts}
|
||||
currentLayout={currentLayout}
|
||||
minSizeMain={minSizeMain}
|
||||
shouldRender={shouldRenderArtifacts}
|
||||
onRenderChange={setShouldRenderArtifacts}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!hideSidePanel && interfaceConfig.sidePanel === true && (
|
||||
<SidePanel
|
||||
panelRef={panelRef}
|
||||
|
|
@ -149,6 +144,9 @@ const SidePanelGroup = memo(
|
|||
/>
|
||||
)}
|
||||
</ResizablePanelGroup>
|
||||
{artifacts != null && isSmallScreen && (
|
||||
<div className="fixed inset-0 z-[100]">{artifacts}</div>
|
||||
)}
|
||||
<button
|
||||
aria-label="Close right side panel"
|
||||
className={`nav-mask ${!isCollapsed ? 'active' : ''}`}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue