Show image previews in optimistic user messages

This commit is contained in:
Mohamed Boudra
2026-02-16 12:19:38 +07:00
parent 75dc802a3a
commit f8f4c55976
10 changed files with 294 additions and 20 deletions
@@ -72,6 +72,7 @@ import {
derivePendingPermissionKey,
normalizeAgentSnapshot,
} from "@/utils/agent-snapshots";
import { mergePendingCreateImages } from "@/utils/pending-create-images";
import type { FetchAgentsEntry } from "@server/client/daemon-client";
import {
DropdownMenu,
@@ -492,6 +493,7 @@ function AgentScreenContent({
(state) => state.sessions[serverId]?.pendingPermissions
);
const setAgents = useSessionStore((state) => state.setAgents);
const setAgentStreamTail = useSessionStore((state) => state.setAgentStreamTail);
const setPendingPermissions = useSessionStore(
(state) => state.setPendingPermissions
);
@@ -637,6 +639,9 @@ function AgentScreenContent({
id: pendingCreate.messageId,
text: pendingCreate.text,
timestamp: new Date(pendingCreate.timestamp),
...(pendingCreate.images && pendingCreate.images.length > 0
? { images: pendingCreate.images }
: {}),
},
];
}, [isPendingCreateForRoute, pendingCreate]);
@@ -718,6 +723,32 @@ function AgentScreenContent({
(item.id === pendingCreate.messageId || item.text === pendingCreate.text)
);
if (agent && hasUserMessage) {
if (
resolvedAgentId &&
pendingCreate.images &&
pendingCreate.images.length > 0
) {
setAgentStreamTail(serverId, (prev) => {
const current = prev.get(resolvedAgentId);
if (!current) {
return prev;
}
const merged = mergePendingCreateImages({
streamItems: current,
messageId: pendingCreate.messageId,
text: pendingCreate.text,
images: pendingCreate.images,
});
if (merged === current) {
return prev;
}
const next = new Map(prev);
next.set(resolvedAgentId, merged);
return next;
});
}
clearPendingCreate();
}
}, [
@@ -725,6 +756,9 @@ function AgentScreenContent({
clearPendingCreate,
isPendingCreateForRoute,
pendingCreate,
resolvedAgentId,
serverId,
setAgentStreamTail,
streamItems,
]);
@@ -37,8 +37,11 @@ import { useCreateFlowStore } from "@/stores/create-flow-store";
import { MAX_CONTENT_WIDTH } from "@/constants/layout";
import { WelcomeScreen } from "@/components/welcome-screen";
import type { Agent } from "@/contexts/session-context";
import type { StreamItem } from "@/types/stream";
import { generateMessageId } from "@/types/stream";
import {
generateMessageId,
type StreamItem,
type UserMessageImageAttachment,
} from "@/types/stream";
import { encodeImages } from "@/utils/encode-images";
import type {
AgentProvider,
@@ -259,6 +262,7 @@ export function DraftAgentScreen({
messageId: string;
text: string;
timestamp: Date;
images?: UserMessageImageAttachment[];
};
type DraftAgentMachineState =
@@ -727,6 +731,9 @@ export function DraftAgentScreen({
id: machine.attempt.messageId,
text: machine.attempt.text,
timestamp: machine.attempt.timestamp,
...(machine.attempt.images && machine.attempt.images.length > 0
? { images: machine.attempt.images }
: {}),
},
];
}, [machine]);
@@ -789,7 +796,7 @@ export function DraftAgentScreen({
images,
}: {
text: string;
images?: Array<{ uri: string; mimeType: string }>;
images?: UserMessageImageAttachment[];
}) => {
if (isSubmitting) {
throw new Error("Already loading");
@@ -848,6 +855,7 @@ export function DraftAgentScreen({
messageId: generateMessageId(),
text: trimmedPrompt,
timestamp: new Date(),
...(images && images.length > 0 ? { images } : {}),
};
setPendingCreateAttempt({
serverId: selectedServerId,
@@ -855,6 +863,9 @@ export function DraftAgentScreen({
messageId: attempt.messageId,
text: attempt.text,
timestamp: attempt.timestamp.getTime(),
...(attempt.images && attempt.images.length > 0
? { images: attempt.images }
: {}),
});
const modeId =
modeOptions.length > 0 && selectedMode !== "" ? selectedMode : undefined;