Fix workflow Preview run blockers and multi-artifact chips

Make the doc-driven Preview flow usable and surface every produced artifact:
- A Preview submit with attachments but no text now runs the workflow (opt-in on
  MessageInput; no filler text is injected), so a doc-driven workflow can be run
  by attaching its documents.
- The builder's global Escape no longer tears down the whole Preview/drawer, and
  no longer fires from inside text fields.
- The Preview drawer has a visible close button, and the upload toast moves off
  the attach/send controls (to the left) while the Preview is open.
- A turn that produces multiple artifacts now renders one chip per artifact
  (labelled by tool, indexed when several), instead of exposing only the last.
This commit is contained in:
Alex committed 2026-06-25 14:18:38 +01:00
1 parent 60a06081ad
commit 7c91517d62
7 files changed
+169 -99

No files matched your search

+21 -16
View File
@@ -293,6 +293,10 @@ type MessageInputProps = {
showSourceButton?: boolean;
showToolButton?: boolean;
autoFocus?: boolean;
// Opt-in: enable send with empty text when there are completed
// attachments (used by doc-driven workflow runs). Normal chat leaves this
// unset, preserving the text-required behavior.
allowSendWithoutText?: boolean;
};
export default function MessageInput({
@@ -301,6 +305,7 @@ export default function MessageInput({
showSourceButton = true,
showToolButton = true,
autoFocus = true,
allowSendWithoutText = false,
}: MessageInputProps) {
const { t } = useTranslation();
const [value, setValue] = useState('');
@@ -1467,13 +1472,21 @@ export default function MessageInput({
setIsSourcesPopupOpen(false);
};
// When ``allowSendWithoutText`` is set, an attachment-only submit is
// permitted as long as at least one attachment has finished processing.
const hasCompletedAttachment = attachments.some(
(attachment) => attachment.status === 'completed',
);
const hasSubmittableContent =
Boolean(value.trim()) || (allowSendWithoutText && hasCompletedAttachment);
const canSubmit =
hasSubmittableContent &&
!loading &&
recordingState !== 'recording' &&
recordingState !== 'transcribing';
const handleSubmit = () => {
if (
value.trim() &&
!loading &&
recordingState !== 'recording' &&
recordingState !== 'transcribing'
) {
if (canSubmit) {
onSubmit(value);
setValue('');
if (isTouch) {
@@ -1635,19 +1648,11 @@ export default function MessageInput({
onClick={handleSubmit}
aria-label={t('send')}
className={`ml-auto h-7 w-7 shrink-0 rounded-full transition-colors duration-300 ease-in-out sm:h-9 sm:w-9 ${
value.trim() &&
!loading &&
recordingState !== 'recording' &&
recordingState !== 'transcribing'
canSubmit
? 'bg-primary text-white'
: 'bg-muted text-muted-foreground dark:bg-accent dark:text-muted-foreground'
}`}
disabled={
!value.trim() ||
loading ||
recordingState === 'recording' ||
recordingState === 'transcribing'
}
disabled={!canSubmit}
>
<SendArrow
className="mx-auto my-auto block h-3.5 w-3.5 sm:h-4 sm:w-4"