2025-05-01 14:40:39 -04:00
|
|
|
import { useEffect, useRef } from 'react';
|
|
|
|
|
import debounce from 'lodash/debounce';
|
|
|
|
|
import { useLocation } from 'react-router-dom';
|
|
|
|
|
import { useRecoilState, useSetRecoilState, useResetRecoilState } from 'recoil';
|
2024-08-27 17:03:16 -04:00
|
|
|
import type { Artifact } from '~/common';
|
|
|
|
|
import FilePreview from '~/components/Chat/Input/Files/FilePreview';
|
2025-11-13 22:59:46 +01:00
|
|
|
import { cn, getFileType, logger, isArtifactRoute } from '~/utils';
|
2024-08-27 17:03:16 -04:00
|
|
|
import { useLocalize } from '~/hooks';
|
|
|
|
|
import store from '~/store';
|
|
|
|
|
|
|
|
|
|
const ArtifactButton = ({ artifact }: { artifact: Artifact | null }) => {
|
|
|
|
|
const localize = useLocalize();
|
2025-05-01 14:40:39 -04:00
|
|
|
const location = useLocation();
|
|
|
|
|
const setVisible = useSetRecoilState(store.artifactsVisibility);
|
|
|
|
|
const [artifacts, setArtifacts] = useRecoilState(store.artifactsState);
|
2025-11-12 13:32:47 -05:00
|
|
|
const [currentArtifactId, setCurrentArtifactId] = useRecoilState(store.currentArtifactId);
|
2025-04-26 04:30:58 -04:00
|
|
|
const resetCurrentArtifactId = useResetRecoilState(store.currentArtifactId);
|
2025-11-12 13:32:47 -05:00
|
|
|
const isSelected = artifact?.id === currentArtifactId;
|
2025-05-01 14:40:39 -04:00
|
|
|
const [visibleArtifacts, setVisibleArtifacts] = useRecoilState(store.visibleArtifacts);
|
|
|
|
|
|
|
|
|
|
const debouncedSetVisibleRef = useRef(
|
|
|
|
|
debounce((artifactToSet: Artifact) => {
|
|
|
|
|
logger.log(
|
|
|
|
|
'artifacts_visibility',
|
|
|
|
|
'Setting artifact to visible state from Artifact button',
|
|
|
|
|
artifactToSet,
|
|
|
|
|
);
|
|
|
|
|
setVisibleArtifacts((prev) => ({
|
|
|
|
|
...prev,
|
|
|
|
|
[artifactToSet.id]: artifactToSet,
|
|
|
|
|
}));
|
|
|
|
|
}, 750),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (artifact == null || artifact?.id == null || artifact.id === '') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-13 22:59:46 +01:00
|
|
|
if (!isArtifactRoute(location.pathname)) {
|
2025-05-01 14:40:39 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const debouncedSetVisible = debouncedSetVisibleRef.current;
|
|
|
|
|
debouncedSetVisible(artifact);
|
|
|
|
|
return () => {
|
|
|
|
|
debouncedSetVisible.cancel();
|
|
|
|
|
};
|
|
|
|
|
}, [artifact, location.pathname]);
|
|
|
|
|
|
2024-08-27 17:03:16 -04:00
|
|
|
if (artifact === null || artifact === undefined) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
const fileType = getFileType('artifact');
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="group relative my-4 rounded-xl text-sm text-text-primary">
|
2025-11-12 13:32:47 -05:00
|
|
|
{(() => {
|
|
|
|
|
const handleClick = () => {
|
|
|
|
|
if (isSelected) {
|
|
|
|
|
resetCurrentArtifactId();
|
|
|
|
|
setVisible(false);
|
2025-05-01 14:40:39 -04:00
|
|
|
return;
|
|
|
|
|
}
|
2025-11-12 13:32:47 -05:00
|
|
|
|
2025-04-26 04:30:58 -04:00
|
|
|
resetCurrentArtifactId();
|
2024-08-27 17:03:16 -04:00
|
|
|
setVisible(true);
|
2025-11-12 13:32:47 -05:00
|
|
|
|
2025-05-01 14:40:39 -04:00
|
|
|
if (artifacts?.[artifact.id] == null) {
|
|
|
|
|
setArtifacts(visibleArtifacts);
|
|
|
|
|
}
|
2025-11-12 13:32:47 -05:00
|
|
|
|
2025-04-26 04:30:58 -04:00
|
|
|
setTimeout(() => {
|
|
|
|
|
setCurrentArtifactId(artifact.id);
|
|
|
|
|
}, 15);
|
2025-11-12 13:32:47 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const buttonClass = cn(
|
|
|
|
|
'relative overflow-hidden rounded-xl transition-all duration-300 hover:border-border-medium hover:bg-surface-hover hover:shadow-lg active:scale-[0.98]',
|
|
|
|
|
{
|
|
|
|
|
'border-border-medium bg-surface-hover shadow-lg': isSelected,
|
|
|
|
|
'border-border-light bg-surface-tertiary shadow-sm': !isSelected,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const actionLabel = isSelected
|
|
|
|
|
? localize('com_ui_click_to_close')
|
|
|
|
|
: localize('com_ui_artifact_click');
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<button type="button" onClick={handleClick} className={buttonClass}>
|
|
|
|
|
<div className="w-fit p-2">
|
|
|
|
|
<div className="flex flex-row items-center gap-2">
|
|
|
|
|
<FilePreview fileType={fileType} className="relative" />
|
|
|
|
|
<div className="overflow-hidden text-left">
|
|
|
|
|
<div className="truncate font-medium">{artifact.title}</div>
|
|
|
|
|
<div className="truncate text-text-secondary">{actionLabel}</div>
|
|
|
|
|
</div>
|
2024-08-27 17:03:16 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-11-12 13:32:47 -05:00
|
|
|
</button>
|
|
|
|
|
);
|
|
|
|
|
})()}
|
2024-08-27 17:03:16 -04:00
|
|
|
<br />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default ArtifactButton;
|