Skip to content

Commit e5beb17

Browse files
committed
Stabilize assistant outputs rail layout
1 parent 47c801b commit e5beb17

2 files changed

Lines changed: 83 additions & 24 deletions

File tree

apps/frontend/src/components/assistant-ui/thread.tsx

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ export const Thread: FC<{
267267
</ThreadPrimitive.ViewportFooter>
268268
</ThreadPrimitive.Viewport>
269269

270-
{showArtifacts && artifacts.length > 0 ? <ThreadOutputs artifacts={artifacts} /> : null}
270+
{showArtifacts ? <ThreadOutputs artifacts={artifacts} /> : null}
271271
</div>
272272
</ThreadPrimitive.Root>
273273
);
@@ -278,10 +278,7 @@ const ThreadOutputs: FC<{
278278
}> = ({ artifacts }) => {
279279
const visibleArtifacts = useMemo(() => selectVisibleArtifacts(artifacts), [artifacts]);
280280
const [isExpanded, setIsExpanded] = useState(true);
281-
282-
if (visibleArtifacts.length === 0) {
283-
return null;
284-
}
281+
const hasArtifacts = visibleArtifacts.length > 0;
285282

286283
const handleOpenArtifact = useCallback((artifact: RunArtifactRecord) => {
287284
const content = typeof artifact.content === "string" ? artifact.content : "";
@@ -309,23 +306,35 @@ const ThreadOutputs: FC<{
309306
<ChevronDownIcon className="assistant-outputs-toggle-icon" />
310307
</button>
311308
<div className="assistant-outputs-list" role="list">
312-
{visibleArtifacts.map((artifact) => {
313-
const disabled = typeof artifact.content !== "string" || artifact.content.trim().length === 0;
314-
return (
315-
<button
316-
key={`${artifact.r2Key ?? artifact.filename}-${artifact.createdAt ?? ""}`}
317-
type="button"
318-
className="assistant-output-link"
319-
role="listitem"
320-
onClick={() => handleOpenArtifact(artifact)}
321-
disabled={disabled}
322-
title={disabled ? "This output is not available inline yet." : artifact.filename}
323-
>
324-
<FileTextIcon className="assistant-output-link-icon" />
325-
<span className="assistant-output-link-name">{formatArtifactLabel(artifact, visibleArtifacts)}</span>
326-
</button>
327-
);
328-
})}
309+
{hasArtifacts
310+
? visibleArtifacts.map((artifact) => {
311+
const disabled = typeof artifact.content !== "string" || artifact.content.trim().length === 0;
312+
return (
313+
<button
314+
key={`${artifact.r2Key ?? artifact.filename}-${artifact.createdAt ?? ""}`}
315+
type="button"
316+
className="assistant-output-link"
317+
role="listitem"
318+
onClick={() => handleOpenArtifact(artifact)}
319+
disabled={disabled}
320+
title={disabled ? "This output is not available inline yet." : artifact.filename}
321+
>
322+
<FileTextIcon className="assistant-output-link-icon" />
323+
<span className="assistant-output-link-name">{formatArtifactLabel(artifact, visibleArtifacts)}</span>
324+
</button>
325+
);
326+
})
327+
: Array.from({ length: 4 }, (_, index) => (
328+
<div
329+
key={`output-placeholder-${index}`}
330+
className="assistant-output-placeholder"
331+
role="listitem"
332+
aria-hidden="true"
333+
>
334+
<span className="assistant-output-placeholder-icon" />
335+
<span className="assistant-output-placeholder-line" />
336+
</div>
337+
))}
329338
</div>
330339
</aside>
331340
);

apps/frontend/src/styles.css

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ button {
740740
}
741741

742742
.assistant-outputs-rail {
743-
width: min(280px, 30vw);
743+
width: min(220px, 22vw);
744744
flex: 0 0 auto;
745745
min-height: 0;
746746
padding: 18px 8px 10px 0;
@@ -753,7 +753,7 @@ button {
753753
}
754754

755755
.assistant-outputs-rail.is-expanded {
756-
width: min(340px, 34vw);
756+
width: min(220px, 22vw);
757757
}
758758

759759
.assistant-outputs-toggle {
@@ -839,6 +839,56 @@ button {
839839
cursor: pointer;
840840
}
841841

842+
.assistant-output-placeholder {
843+
width: 100%;
844+
display: grid;
845+
grid-template-columns: 14px minmax(0, 1fr);
846+
align-items: center;
847+
gap: 9px;
848+
padding: 6px 0;
849+
}
850+
851+
.assistant-output-placeholder-icon,
852+
.assistant-output-placeholder-line {
853+
display: block;
854+
background: rgba(38, 38, 38, 0.08);
855+
animation: assistant-output-placeholder-pulse 1.6s ease-in-out infinite;
856+
}
857+
858+
.assistant-output-placeholder-icon {
859+
width: 12px;
860+
height: 12px;
861+
border-radius: 3px;
862+
}
863+
864+
.assistant-output-placeholder-line {
865+
width: 100%;
866+
height: 0.9rem;
867+
border-radius: 999px;
868+
}
869+
870+
.assistant-output-placeholder:nth-child(2) .assistant-output-placeholder-line {
871+
width: 82%;
872+
}
873+
874+
.assistant-output-placeholder:nth-child(3) .assistant-output-placeholder-line {
875+
width: 74%;
876+
}
877+
878+
.assistant-output-placeholder:nth-child(4) .assistant-output-placeholder-line {
879+
width: 88%;
880+
}
881+
882+
@keyframes assistant-output-placeholder-pulse {
883+
0%,
884+
100% {
885+
opacity: 0.45;
886+
}
887+
50% {
888+
opacity: 0.9;
889+
}
890+
}
891+
842892
.assistant-output-link:hover:not(:disabled) .assistant-output-link-name {
843893
color: rgba(38, 38, 38, 0.72);
844894
}

0 commit comments

Comments
 (0)