fix(web): align uploaded attachments with user messages (#185)
Co-authored-by: Yaowei Zheng <hiyouga@buaa.edu.cn> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
* server appends aren't shown verbatim, they collapse into a single line reading
|
||||
* "Attached files: a.pdf, b.csv" (paperclip icon + static text, no navigation — the files live
|
||||
* in the session scratchpad and the model opens them by path); the body text around them is
|
||||
* rendered as usual by the caller. Same shape as SkillsBanner, so the two notices a message
|
||||
* can carry read as one family.
|
||||
* rendered as usual by the caller. It keeps the same visual language as message-level notices;
|
||||
* the caller owns its user-side alignment and timestamp footer.
|
||||
*/
|
||||
import { S } from "../../lib/strings";
|
||||
import { attachmentFileName } from "../../lib/attachments";
|
||||
@@ -22,7 +22,7 @@ export function AttachedFilesBanner({ files }: { files: string[] }) {
|
||||
// into a paragraph-tall block above the message. The full list stays reachable as a title.
|
||||
<p
|
||||
title={label}
|
||||
className="anim-msg my-2 flex w-fit max-w-full items-center gap-2 rounded-md border border-gray-200 bg-gray-50 px-3 py-2 text-xs text-gray-600 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-400"
|
||||
className="flex w-fit max-w-full items-center gap-2 rounded-md border border-gray-200 bg-gray-50 px-3 py-2 text-xs text-gray-600 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-400"
|
||||
>
|
||||
<GlyphIcon d={PAPERCLIP_ICON} className="shrink-0 text-gray-400 dark:text-gray-500" />
|
||||
<span className="min-w-0 truncate">{label}</span>
|
||||
|
||||
@@ -217,10 +217,6 @@ export function MessageItem({ item, ctx }: { item: ChatItem; ctx: StreamRenderCo
|
||||
<>
|
||||
{scheduled && <ScheduledBanner origin={scheduled.origin} />}
|
||||
{skills && <SkillsBanner names={skills.skills} />}
|
||||
{/* Files uploaded with this message: named above the bubble, like the other
|
||||
message-level notices — the bytes live in the session scratchpad, the model
|
||||
opens them by path (goal mode never gets here: it takes text and images only). */}
|
||||
{files.length > 0 && <AttachedFilesBanner files={files} />}
|
||||
{text && (
|
||||
<div className="anim-msg group my-4 flex flex-col items-end">
|
||||
<div className="max-w-[88%] rounded-lg bg-gray-100 px-4 py-2.5 md:max-w-[75%] dark:bg-gray-800">
|
||||
@@ -236,6 +232,19 @@ export function MessageItem({ item, ctx }: { item: ChatItem; ctx: StreamRenderCo
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{/* Files uploaded with this message: shown below the text in the same user-side
|
||||
container and with the same timestamp footer as uploaded images. The bytes live in
|
||||
the session scratchpad, where the model opens them by path (goal mode never gets
|
||||
here: it takes text and images only). */}
|
||||
{files.length > 0 && (
|
||||
<div className="anim-msg group my-4 flex flex-col items-end">
|
||||
<AttachedFilesBanner files={files} />
|
||||
<MessageMeta
|
||||
{...(item.atMs !== undefined ? { atMs: item.atMs } : {})}
|
||||
align="right"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{images.map((src, i) => (
|
||||
<div key={i} className="anim-msg group my-4 flex flex-col items-end">
|
||||
<div className="max-w-[88%] rounded-lg bg-gray-100 p-1.5 md:max-w-[75%] dark:bg-gray-800">
|
||||
@@ -262,8 +271,15 @@ export function MessageItem({ item, ctx }: { item: ChatItem; ctx: StreamRenderCo
|
||||
// Images sent with the message live inside the same chip: `item.images` on a vision
|
||||
// model (delivered as image messages right behind the text), or restored from the
|
||||
// [attached image: …] path lines without one — the same two shapes a user_text bubble
|
||||
// handles, so both render identically here.
|
||||
const { text: steerText, images: steerImages } = splitAttachments(item.text);
|
||||
// handles, so both render identically here. Files ride steering only as
|
||||
// [attached file: …] rows (there is no item.files channel) and collapse into the same
|
||||
// banner a full prompt uses, kept inside the chip — a files-only steering would
|
||||
// otherwise render as an empty chip with the filenames lost.
|
||||
const {
|
||||
text: steerText,
|
||||
images: steerImages,
|
||||
files: steerFiles,
|
||||
} = splitAttachments(item.text);
|
||||
const shown = [...steerImages, ...(item.images ?? [])];
|
||||
return (
|
||||
<div className="anim-msg group my-2 flex flex-col items-end">
|
||||
@@ -292,6 +308,11 @@ export function MessageItem({ item, ctx }: { item: ChatItem; ctx: StreamRenderCo
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{steerFiles.length > 0 && (
|
||||
<div className="flex justify-end">
|
||||
<AttachedFilesBanner files={steerFiles} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<MessageMeta
|
||||
{...(item.atMs !== undefined ? { atMs: item.atMs } : {})}
|
||||
|
||||
Reference in New Issue
Block a user