feat(core,server,web): images reach every input — steering, goal objectives, one fold (#71)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
de6380f278
commit
97852f43b8
40 files changed
+1474
-254
No files matched your search
@@ -203,12 +203,13 @@ export function MessageItem({ item, ctx }: { item: ChatItem; ctx: StreamRenderCo
|
||||
// files come out of the same pass and collapse into one banner naming them.
|
||||
const { text, images, files } = splitAttachments(skills ? skills.rest : afterScheduled);
|
||||
// Every goal round reads like a normal user message: the body in a user bubble with
|
||||
// the round notice beneath (the system IS re-sending the user's request each round).
|
||||
// the round notice beneath (the system IS re-sending the user's request each round) —
|
||||
// the objective's images included, since they ride it as path lines.
|
||||
if (goalRound) {
|
||||
return (
|
||||
<>
|
||||
{skills && <SkillsBanner names={skills.skills} />}
|
||||
<GoalRoundBanner round={goalRound.round} objective={text} />
|
||||
<GoalRoundBanner round={goalRound.round} objective={text} images={images} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -218,7 +219,7 @@ export function MessageItem({ item, ctx }: { item: ChatItem; ctx: StreamRenderCo
|
||||
{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 rejects non-text input). */}
|
||||
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">
|
||||
@@ -253,32 +254,53 @@ export function MessageItem({ item, ctx }: { item: ChatItem; ctx: StreamRenderCo
|
||||
</>
|
||||
);
|
||||
}
|
||||
case "user_steering":
|
||||
case "user_steering": {
|
||||
// Mid-run steering ([user_steering]-wrapped user text delivered between turns): a
|
||||
// compact right-aligned user-styled chip inside the running Task's flow — visually
|
||||
// lighter than a full prompt bubble, since it doesn't start a new Task (the Trace page
|
||||
// still shows the raw marker text as-is).
|
||||
// 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);
|
||||
const shown = [...steerImages, ...(item.images ?? [])];
|
||||
return (
|
||||
<div className="anim-msg group my-2 flex flex-col items-end">
|
||||
<div className="flex max-w-[88%] items-start gap-1.5 rounded-md border border-gray-200 bg-gray-100 px-3 py-1.5 md:max-w-[75%] dark:border-gray-700 dark:bg-gray-800">
|
||||
<GlyphIcon
|
||||
d={USER_STEERING_ICON}
|
||||
className="mt-1 shrink-0 text-gray-400 dark:text-gray-500"
|
||||
/>
|
||||
<p className="wrap-anywhere whitespace-pre-wrap text-sm leading-relaxed text-gray-800 dark:text-gray-100">
|
||||
<span className="mr-1.5 text-xs font-semibold text-gray-500 dark:text-gray-400">
|
||||
{S.chat.userSteering}
|
||||
</span>
|
||||
{item.text}
|
||||
</p>
|
||||
<div className="flex max-w-[88%] flex-col gap-1.5 rounded-md border border-gray-200 bg-gray-100 px-3 py-1.5 md:max-w-[75%] dark:border-gray-700 dark:bg-gray-800">
|
||||
<div className="flex items-start gap-1.5">
|
||||
<GlyphIcon
|
||||
d={USER_STEERING_ICON}
|
||||
className="mt-1 shrink-0 text-gray-400 dark:text-gray-500"
|
||||
/>
|
||||
<p className="wrap-anywhere whitespace-pre-wrap text-sm leading-relaxed text-gray-800 dark:text-gray-100">
|
||||
<span className="mr-1.5 text-xs font-semibold text-gray-500 dark:text-gray-400">
|
||||
{S.chat.userSteering}
|
||||
</span>
|
||||
{steerText}
|
||||
</p>
|
||||
</div>
|
||||
{shown.length > 0 && (
|
||||
<div className="flex flex-wrap justify-end gap-1.5">
|
||||
{shown.map((src, i) => (
|
||||
<ZoomableImage
|
||||
key={i}
|
||||
src={src}
|
||||
alt={S.chat.imageAlt}
|
||||
className="max-h-28 max-w-full rounded-md"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<MessageMeta
|
||||
{...(item.atMs !== undefined ? { atMs: item.atMs } : {})}
|
||||
text={item.text}
|
||||
text={steerText}
|
||||
align="right"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
case "user_image":
|
||||
return (
|
||||
<div className="anim-msg group my-4 flex flex-col items-end">
|
||||
|
||||
Reference in new issue
Block a user