🔄 refactor: Artifact Visibility Management (#7181)

* fix: Reset artifacts on unmount and remove useIdChangeEffect hook

* feat: Replace SVG icons with Lucide icons for improved consistency

* fix: Refactor artifact reset logic on unmount and conversation change

* refactor: Rename artifactsVisible to artifactsVisibility for consistency

* feat: Replace custom SVG icons with Lucide icons for improved consistency

* feat: Add visibleArtifacts atom for managing visibility state

* feat: Implement debounced visibility state management for artifacts

* refactor: Add useIdChangeEffect hook to reset visible artifacts on conversation ID change

* refactor: Remove unnecessary dependency from useMemo in TextPart component

* refactor: Enhance artifact visibility management by incorporating location checks for search path

* refactor: Improve transition effects for artifact visibility in Artifacts component

* chore: Remove preprocessCodeArtifacts function and related tests

* fix: Update regex for detecting enclosed artifacts in latest message

* refactor: Update artifact visibility checks to be more generic (not just search)

* chore: Enhance artifact visibility logging

* refactor: Extract closeArtifacts function to improve button click handling

* refactor: remove nested logic from use artifacts effect

* refactor: Update regex for detecting enclosed artifacts to handle new line variations
This commit is contained in:
Danny Avila 2025-05-01 14:40:39 -04:00 committed by GitHub
parent e6e7935fd8
commit 9a7f763714
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 132 additions and 209 deletions

View file

@ -1,9 +1,9 @@
import { useMemo, useState, useEffect, useRef } from 'react';
import { Constants } from 'librechat-data-provider';
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil';
import { getLatestText, logger } from '~/utils';
import { useChatContext } from '~/Providers';
import { getKey } from '~/utils/artifacts';
import { getLatestText } from '~/utils';
import store from '~/store';
export default function useArtifacts() {
@ -37,16 +37,20 @@ export default function useArtifacts() {
hasEnclosedArtifactRef.current = false;
};
if (
conversation &&
conversation.conversationId !== prevConversationIdRef.current &&
conversation?.conversationId !== prevConversationIdRef.current &&
prevConversationIdRef.current != null
) {
resetState();
} else if (conversation && conversation.conversationId === Constants.NEW_CONVO) {
} else if (conversation?.conversationId === Constants.NEW_CONVO) {
resetState();
}
prevConversationIdRef.current = conversation?.conversationId ?? null;
}, [conversation, resetArtifacts, resetCurrentArtifactId]);
/** Resets artifacts when unmounting */
return () => {
logger.log('artifacts_visibility', 'Unmounting artifacts');
resetState();
};
}, [conversation?.conversationId, resetArtifacts, resetCurrentArtifactId]);
useEffect(() => {
if (orderedArtifactIds.length > 0) {
@ -56,30 +60,39 @@ export default function useArtifacts() {
}, [setCurrentArtifactId, orderedArtifactIds]);
useEffect(() => {
if (isSubmitting && orderedArtifactIds.length > 0 && latestMessage) {
const latestArtifactId = orderedArtifactIds[orderedArtifactIds.length - 1];
const latestArtifact = artifacts?.[latestArtifactId];
if (!isSubmitting) {
return;
}
if (orderedArtifactIds.length === 0) {
return;
}
if (latestMessage == null) {
return;
}
const latestArtifactId = orderedArtifactIds[orderedArtifactIds.length - 1];
const latestArtifact = artifacts?.[latestArtifactId];
if (latestArtifact?.content === lastContentRef.current) {
return;
}
if (latestArtifact?.content !== lastContentRef.current) {
setCurrentArtifactId(latestArtifactId);
lastContentRef.current = latestArtifact?.content ?? null;
setCurrentArtifactId(latestArtifactId);
lastContentRef.current = latestArtifact?.content ?? null;
const latestMessageText = getLatestText(latestMessage);
const hasEnclosedArtifact = /:::artifact[\s\S]*?(```|:::)\s*$/.test(
latestMessageText.trim(),
);
const latestMessageText = getLatestText(latestMessage);
const hasEnclosedArtifact =
/:::artifact(?:\{[^}]*\})?(?:\s|\n)*(?:```[\s\S]*?```(?:\s|\n)*)?:::/m.test(
latestMessageText.trim(),
);
if (hasEnclosedArtifact && !hasEnclosedArtifactRef.current) {
setActiveTab('preview');
hasEnclosedArtifactRef.current = true;
hasAutoSwitchedToCodeRef.current = false;
} else if (!hasEnclosedArtifactRef.current && !hasAutoSwitchedToCodeRef.current) {
const artifactStartContent = latestArtifact?.content?.slice(0, 50) ?? '';
if (artifactStartContent.length > 0 && latestMessageText.includes(artifactStartContent)) {
setActiveTab('code');
hasAutoSwitchedToCodeRef.current = true;
}
}
if (hasEnclosedArtifact && !hasEnclosedArtifactRef.current) {
setActiveTab('preview');
hasEnclosedArtifactRef.current = true;
hasAutoSwitchedToCodeRef.current = false;
} else if (!hasEnclosedArtifactRef.current && !hasAutoSwitchedToCodeRef.current) {
const artifactStartContent = latestArtifact?.content?.slice(0, 50) ?? '';
if (artifactStartContent.length > 0 && latestMessageText.includes(artifactStartContent)) {
setActiveTab('code');
hasAutoSwitchedToCodeRef.current = true;
}
}
}, [setCurrentArtifactId, isSubmitting, orderedArtifactIds, artifacts, latestMessage]);

View file

@ -4,18 +4,18 @@ import { logger } from '~/utils';
import store from '~/store';
/**
* Hook to reset artifacts when the conversation ID changes
* Hook to reset visible artifacts when the conversation ID changes
* @param conversationId - The current conversation ID
*/
export default function useIdChangeEffect(conversationId: string) {
const lastConvoId = useRef<string | null>(null);
const resetArtifacts = useResetRecoilState(store.artifactsState);
const resetVisibleArtifacts = useResetRecoilState(store.visibleArtifacts);
useEffect(() => {
if (conversationId !== lastConvoId.current) {
logger.log('conversation', 'Conversation ID change');
resetArtifacts();
resetVisibleArtifacts();
}
lastConvoId.current = conversationId;
}, [conversationId, resetArtifacts]);
}, [conversationId, resetVisibleArtifacts]);
}