feat(web): toast anchoring and model-selection rejection banner

The toast sits 120px from the viewport top and centers over its anchor —
the composer card, so it centers on the chat column rather than the window;
a rejected model selection (e.g. picking a text-only model while the
session holds images) announces through the same banner while the in-menu
strip with Retry stays the catalog-load surface. The attachment rail
consumes every wheel tick with a vertical component: a diagonal pan keeps
its horizontal intent and nothing scrolls the conversation behind the
composer.
This commit is contained in:
creatixchu
2026-08-11 18:00:56 +08:00
parent 87e3c95027
commit dc42e6b822
17 changed files with 158 additions and 42 deletions
@@ -89,6 +89,7 @@ export function InputBar({
if (promptError !== null) showToast(`${promptError.error.message} (${promptError.error.code})`)
}, [promptError, showToast])
const inputRef = useRef<HTMLTextAreaElement | null>(null)
const cardRef = useRef<HTMLDivElement | null>(null)
const dragDepthRef = useRef(0)
const scrollRef = useRef<HTMLDivElement | null>(null)
const mirrorRef = useRef<HTMLDivElement | null>(null)
@@ -577,6 +578,7 @@ export function InputBar({
key={toast.seq}
text={toast.text}
icon={<IconWarningOutline16 />}
anchor={cardRef.current}
onDone={dismissToast}
/>
)}
@@ -591,6 +593,7 @@ export function InputBar({
pointerdown stops here so the Menu's outside-close cannot race the
click's reopen (close-then-open flickers the chip's open echo). */}
<div
ref={cardRef}
className={clsx(css.card, workspaceTrigger && css.cardWorkspaceTrigger, dragActive && css.dragActive)}
data-composer-card
onClick={workspaceTrigger ? onRequestWorkspace : undefined}