Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ export function ArtifactFileDetail({
isSupportPreview,
toolResult,
});
const selectableArtifacts = useMemo(() => {
const artifactPaths = artifacts ?? [];
if (isWriteFile || artifactPaths.includes(filepath)) {
return artifactPaths;
}
return [filepath, ...artifactPaths];
}, [artifacts, filepath, isWriteFile]);
const { content, url } = useArtifactContent({
threadId,
filepath: filepathFromProps,
Expand Down Expand Up @@ -167,7 +174,7 @@ export function ArtifactFileDetail({
</SelectTrigger>
<SelectContent className="select-none">
<SelectGroup>
{(artifacts ?? []).map((filepath) => (
{selectableArtifacts.map((filepath) => (
<SelectItem key={filepath} value={filepath}>
{getFileName(filepath)}
</SelectItem>
Expand Down
62 changes: 62 additions & 0 deletions frontend/tests/e2e/artifact-preview.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const IN_PROGRESS_THREAD_ID = "00000000-0000-0000-0000-000000003119";
const COMPLETE_THREAD_ID = "00000000-0000-0000-0000-000000003120";
const MARKDOWN_THREAD_ID = "00000000-0000-0000-0000-000000003121";
const JSON_THREAD_ID = "00000000-0000-0000-0000-000000003122";
const PRESENT_FILES_THREAD_ID = "00000000-0000-0000-0000-000000003192";

function writeFileMessages({
path = ARTIFACT_PATH,
Expand Down Expand Up @@ -56,6 +57,30 @@ function writeFileMessages({
return messages;
}

function presentFileMessages(path = "/mnt/user-data/outputs/report.html") {
return [
{
type: "human",
id: "msg-human-present-file",
content: [{ type: "text", text: "Create a report artifact" }],
},
{
type: "ai",
id: "msg-ai-present-file",
content: "I created the report.",
tool_calls: [
{
id: "present-file-artifact",
name: "present_files",
args: {
filepaths: [path],
},
},
],
},
];
}

test.describe("Artifact preview stability", () => {
test("renders preview iframe for an in-progress write artifact", async ({
page,
Expand Down Expand Up @@ -171,4 +196,41 @@ test.describe("Artifact preview stability", () => {
artifactsPanel.getByText('"中文字段": "测试内容"'),
).toBeVisible();
});

test("shows the selected filename for presented files outside the thread artifact list", async ({
page,
}) => {
mockLangGraphAPI(page, {
threads: [
{
thread_id: PRESENT_FILES_THREAD_ID,
title: "Presented artifact without thread list entry",
messages: presentFileMessages(),
artifacts: [],
},
],
});
await page.route(
`**/api/threads/${PRESENT_FILES_THREAD_ID}/artifacts/mnt/user-data/outputs/report.html`,
(route) =>
route.fulfill({
status: 200,
contentType: "text/html",
body: "<!doctype html><html><body><h1>Presented report</h1></body></html>",
}),
);

await page.goto(`/workspace/chats/${PRESENT_FILES_THREAD_ID}`);

await expect(page.getByText("report.html")).toBeVisible({
timeout: 15_000,
});
await page.getByText("report.html").click();

const artifactsPanel = page.locator("#artifacts");
await expect(artifactsPanel.getByText("report.html")).toBeVisible();
await expect(
artifactsPanel.locator('iframe[title="Artifact preview"]'),
).toBeVisible();
});
});