feat: integrated with artifacts in states

This commit is contained in:
Henry Li
2026-01-17 17:21:37 +08:00
parent 384353d613
commit f1c6991194
8 changed files with 118 additions and 70 deletions

View File

@@ -12,17 +12,19 @@ import {
ArtifactAction,
ArtifactActions,
ArtifactContent,
ArtifactDescription,
ArtifactHeader,
ArtifactTitle,
} from "@/components/ai-elements/artifact";
import { Select, SelectItem } from "@/components/ui/select";
import {
SelectContent,
SelectGroup,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { useArtifactContent } from "@/core/artifacts/hooks";
import { urlOfArtifact } from "@/core/artifacts/utils";
import {
checkCodeFile,
getFileExtensionDisplayName,
getFileName,
} from "@/core/utils/files";
import { checkCodeFile, getFileName } from "@/core/utils/files";
import { cn } from "@/lib/utils";
import { useArtifacts } from "./context";
@@ -37,7 +39,7 @@ export function ArtifactFileDetail({
filepath: string;
threadId: string;
}) {
const { setOpen } = useArtifacts();
const { artifacts, setOpen, select } = useArtifacts();
const { isCodeFile } = useMemo(() => checkCodeFile(filepath), [filepath]);
const { content } = useArtifactContent({
threadId,
@@ -46,12 +48,24 @@ export function ArtifactFileDetail({
});
return (
<Artifact className={cn("rounded-none", className)}>
<ArtifactHeader>
<ArtifactHeader className="px-2">
<div>
<ArtifactTitle>{getFileName(filepath)}</ArtifactTitle>
<ArtifactDescription className="mt-1 text-xs">
{getFileExtensionDisplayName(filepath)} file
</ArtifactDescription>
<ArtifactTitle>
<Select value={filepath} onValueChange={select}>
<SelectTrigger className="border-none bg-transparent! select-none focus:outline-0 active:outline-0">
<SelectValue placeholder="Select a file" />
</SelectTrigger>
<SelectContent className="select-none">
<SelectGroup>
{(artifacts ?? []).map((filepath) => (
<SelectItem key={filepath} value={filepath}>
{getFileName(filepath)}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</ArtifactTitle>
</div>
<div className="flex items-center gap-2">
<ArtifactActions>

View File

@@ -24,12 +24,13 @@ export function ArtifactFileList({
files: string[];
threadId: string;
}) {
const { openArtifact } = useArtifacts();
const { select: selectArtifact, setOpen } = useArtifacts();
const handleClick = useCallback(
(filepath: string) => {
openArtifact(filepath);
selectArtifact(filepath);
setOpen(true);
},
[openArtifact],
[selectArtifact, setOpen],
);
return (
<ul className={cn("flex w-full flex-col gap-4", className)}>

View File

@@ -4,13 +4,15 @@ import { useSidebar } from "@/components/ui/sidebar";
export interface ArtifactsContextType {
artifacts: string[];
setArtifacts: (artifacts: string[]) => void;
selectedArtifact: string | null;
open: boolean;
setOpen: (open: boolean) => void;
deselect: () => void;
addArtifacts: (artifacts: string[]) => void;
openArtifact: (artifact: string) => void;
select: (artifact: string) => void;
}
const ArtifactsContext = createContext<ArtifactsContextType | undefined>(
@@ -27,23 +29,25 @@ export function ArtifactsProvider({ children }: ArtifactsProviderProps) {
const [open, setOpen] = useState(false);
const { setOpen: setSidebarOpen } = useSidebar();
const addArtifacts = (newArtifacts: string[]) => {
setArtifacts((prev) => [...prev, ...newArtifacts]);
const select = (artifact: string) => {
setSelectedArtifact(artifact);
setSidebarOpen(false);
};
const openArtifact = (artifact: string) => {
setSelectedArtifact(artifact);
setOpen(true);
setSidebarOpen(false);
const deselect = () => {
setSelectedArtifact(null);
};
const value: ArtifactsContextType = {
artifacts,
selectedArtifact,
setArtifacts,
open,
setOpen,
addArtifacts,
openArtifact,
selectedArtifact,
select,
deselect,
};
return (