mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-11 12:04:24 +01:00
feat: open/close artifacts
This commit is contained in:
parent
6a8d4e43db
commit
5aee9db6de
5 changed files with 52 additions and 15 deletions
|
|
@ -7,6 +7,7 @@ import store from '~/store';
|
||||||
|
|
||||||
const ArtifactButton = ({ artifact }: { artifact: Artifact | null }) => {
|
const ArtifactButton = ({ artifact }: { artifact: Artifact | null }) => {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
|
const setVisible = useSetRecoilState(store.artifactsVisible);
|
||||||
const setArtifactId = useSetRecoilState(store.currentArtifactId);
|
const setArtifactId = useSetRecoilState(store.currentArtifactId);
|
||||||
if (artifact === null || artifact === undefined) {
|
if (artifact === null || artifact === undefined) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -17,7 +18,10 @@ const ArtifactButton = ({ artifact }: { artifact: Artifact | null }) => {
|
||||||
<div className="group relative my-4 rounded-xl text-sm text-text-primary">
|
<div className="group relative my-4 rounded-xl text-sm text-text-primary">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setArtifactId(artifact.id)}
|
onClick={() => {
|
||||||
|
setArtifactId(artifact.id);
|
||||||
|
setVisible(true);
|
||||||
|
}}
|
||||||
className="relative overflow-hidden rounded-xl border border-border-medium transition-all duration-300 hover:border-border-xheavy hover:shadow-lg"
|
className="relative overflow-hidden rounded-xl border border-border-medium transition-all duration-300 hover:border-border-xheavy hover:shadow-lg"
|
||||||
>
|
>
|
||||||
<div className="w-60 bg-surface-tertiary p-2 ">
|
<div className="w-60 bg-surface-tertiary p-2 ">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState, useEffect } from 'react';
|
||||||
import { RefreshCw } from 'lucide-react';
|
import { RefreshCw } from 'lucide-react';
|
||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
import * as Tabs from '@radix-ui/react-tabs';
|
import * as Tabs from '@radix-ui/react-tabs';
|
||||||
import { SandpackPreviewRef } from '@codesandbox/sandpack-react';
|
import { SandpackPreviewRef } from '@codesandbox/sandpack-react';
|
||||||
import useArtifacts from '~/hooks/Artifacts/useArtifacts';
|
import useArtifacts from '~/hooks/Artifacts/useArtifacts';
|
||||||
|
|
@ -7,13 +8,19 @@ import { CodeMarkdown, CopyCodeButton } from './Code';
|
||||||
import { getFileExtension } from '~/utils/artifacts';
|
import { getFileExtension } from '~/utils/artifacts';
|
||||||
import { ArtifactPreview } from './ArtifactPreview';
|
import { ArtifactPreview } from './ArtifactPreview';
|
||||||
import { cn } from '~/utils';
|
import { cn } from '~/utils';
|
||||||
|
import store from '~/store';
|
||||||
|
|
||||||
export default function Artifacts() {
|
export default function Artifacts() {
|
||||||
const previewRef = useRef<SandpackPreviewRef>();
|
const previewRef = useRef<SandpackPreviewRef>();
|
||||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||||
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
|
const setArtifactsVisible = useSetRecoilState(store.artifactsVisible);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsVisible(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isVisible,
|
|
||||||
activeTab,
|
activeTab,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
setActiveTab,
|
setActiveTab,
|
||||||
|
|
@ -51,7 +58,13 @@ export default function Artifacts() {
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between border-b border-border-medium bg-surface-primary-alt p-2">
|
<div className="flex items-center justify-between border-b border-border-medium bg-surface-primary-alt p-2">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<button className="mr-2 text-text-secondary" onClick={() => cycleArtifact('prev')}>
|
<button
|
||||||
|
className="mr-2 text-text-secondary"
|
||||||
|
onClick={() => {
|
||||||
|
setIsVisible(false);
|
||||||
|
setTimeout(() => setArtifactsVisible(false), 300);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="16"
|
width="16"
|
||||||
|
|
@ -95,7 +108,13 @@ export default function Artifacts() {
|
||||||
Code
|
Code
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
</Tabs.List>
|
</Tabs.List>
|
||||||
<button className="text-text-secondary">
|
<button
|
||||||
|
className="text-text-secondary"
|
||||||
|
onClick={() => {
|
||||||
|
setIsVisible(false);
|
||||||
|
setTimeout(() => setArtifactsVisible(false), 300);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="16"
|
width="16"
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ export default function Presentation({
|
||||||
const artifacts = useRecoilValue(store.artifactsState);
|
const artifacts = useRecoilValue(store.artifactsState);
|
||||||
const codeArtifacts = useRecoilValue(store.codeArtifacts);
|
const codeArtifacts = useRecoilValue(store.codeArtifacts);
|
||||||
const hideSidePanel = useRecoilValue(store.hideSidePanel);
|
const hideSidePanel = useRecoilValue(store.hideSidePanel);
|
||||||
|
const artifactsVisible = useRecoilValue(store.artifactsVisible);
|
||||||
|
|
||||||
const interfaceConfig = useMemo(
|
const interfaceConfig = useMemo(
|
||||||
() => startupConfig?.interface ?? defaultInterface,
|
() => startupConfig?.interface ?? defaultInterface,
|
||||||
[startupConfig],
|
[startupConfig],
|
||||||
|
|
@ -96,7 +98,11 @@ export default function Presentation({
|
||||||
defaultCollapsed={defaultCollapsed}
|
defaultCollapsed={defaultCollapsed}
|
||||||
fullPanelCollapse={fullCollapse}
|
fullPanelCollapse={fullCollapse}
|
||||||
artifacts={
|
artifacts={
|
||||||
codeArtifacts === true && Object.keys(artifacts ?? {}).length > 0 ? <Artifacts /> : null
|
artifactsVisible === true &&
|
||||||
|
codeArtifacts === true &&
|
||||||
|
Object.keys(artifacts ?? {}).length > 0 ? (
|
||||||
|
<Artifacts />
|
||||||
|
) : null
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<main className="flex h-full flex-col" role="main">
|
<main className="flex h-full flex-col" role="main">
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import store from '~/store';
|
||||||
export default function useArtifacts() {
|
export default function useArtifacts() {
|
||||||
const { isSubmitting, latestMessage, conversation } = useChatContext();
|
const { isSubmitting, latestMessage, conversation } = useChatContext();
|
||||||
|
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
|
||||||
const [activeTab, setActiveTab] = useState('preview');
|
const [activeTab, setActiveTab] = useState('preview');
|
||||||
const artifacts = useRecoilValue(store.artifactsState);
|
const artifacts = useRecoilValue(store.artifactsState);
|
||||||
const [currentArtifactId, setCurrentArtifactId] = useRecoilState(store.currentArtifactId);
|
const [currentArtifactId, setCurrentArtifactId] = useRecoilState(store.currentArtifactId);
|
||||||
|
|
@ -48,10 +47,6 @@ export default function useArtifacts() {
|
||||||
prevConversationIdRef.current = conversation?.conversationId ?? null;
|
prevConversationIdRef.current = conversation?.conversationId ?? null;
|
||||||
}, [conversation, resetArtifacts, resetCurrentArtifactId]);
|
}, [conversation, resetArtifacts, resetCurrentArtifactId]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setIsVisible(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (orderedArtifactIds.length > 0) {
|
if (orderedArtifactIds.length > 0) {
|
||||||
const latestArtifactId = orderedArtifactIds[orderedArtifactIds.length - 1];
|
const latestArtifactId = orderedArtifactIds[orderedArtifactIds.length - 1];
|
||||||
|
|
@ -110,14 +105,12 @@ export default function useArtifacts() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isVisible,
|
|
||||||
setIsVisible,
|
|
||||||
activeTab,
|
activeTab,
|
||||||
setActiveTab,
|
setActiveTab,
|
||||||
currentArtifact,
|
|
||||||
currentIndex,
|
currentIndex,
|
||||||
cycleArtifact,
|
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
|
cycleArtifact,
|
||||||
|
currentArtifact,
|
||||||
orderedArtifactIds,
|
orderedArtifactIds,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,3 +31,18 @@ export const currentArtifactId = atom<string | null>({
|
||||||
},
|
},
|
||||||
] as const,
|
] as const,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const artifactsVisible = atom<boolean>({
|
||||||
|
key: 'artifactsVisible',
|
||||||
|
default: true,
|
||||||
|
effects: [
|
||||||
|
({ onSet, node }) => {
|
||||||
|
onSet(async (newValue) => {
|
||||||
|
logger.log('artifacts', 'Recoil Effect: Setting artifactsVisible', {
|
||||||
|
key: node.key,
|
||||||
|
newValue,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
] as const,
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue