refactor(ui): ♻️ move runtime queue and task history to fixed bottom panel#198
Conversation
…panel Relocate RuntimeQueueTimeline and TaskHistoryTimeline from inline conversation flow to a fixed bottom overlay panel. This prevents layout jumps during streaming and keeps queue/history always visible at the viewport bottom. - Only show queue panel when messages are pending (not just when queue exists) - Add frosted-glass overlay with backdrop blur for the fixed panel - Adjust conversation bottom padding dynamically when panels are visible - Reposition scroll button above the fixed panel overlay
AI Code Review SummaryPR: #198 (refactor(ui): ♻️ move runtime queue and task history to fixed bottom panel) Overall AssessmentDetected 1 actionable findings, prioritize CRITICAL/HIGH before merge. Major Findings by Severity
Actionable Suggestions
Potential Risks
Test Suggestions
File-Level Coverage Notes
Inline Downgraded Items (processed but not inline)
Coverage Status
Uncovered list:
No-patch covered list:
Runtime/Budget
|
| </div> | ||
| </div> | ||
| ) : null} | ||
| <ConversationScrollButton className={cn("z-20", hasFixedBottomPanels ? "bottom-[calc(min(36vh,320px)_+_2rem)]" : "bottom-4")} /> |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| <div className="mx-auto w-full max-w-4xl px-6"> | ||
| <div className="pointer-events-auto max-h-[min(36vh,320px)] overflow-y-auto rounded-2xl border border-app-border/70 bg-app-menu/96 px-4 py-3 shadow-[0_24px_60px_-36px_rgba(15,23,42,0.48)] backdrop-blur-xl"> | ||
| <div className="flex flex-col gap-3"> | ||
| {hasPendingRuntimeQueue ? ( |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
… fixed bottom panels
| : lastPresentationRole; | ||
|
|
||
| const conversationBottomPadding = BASE_CONVERSATION_BOTTOM_PADDING; | ||
| const conversationBottomPadding = BASE_CONVERSATION_BOTTOM_PADDING |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| <ConversationScrollButton className="bottom-4" /> | ||
| {hasFixedBottomPanels ? ( | ||
| <div className="pointer-events-none absolute inset-x-0 bottom-0 z-10 px-0 pb-4"> | ||
| <ConversationScrollButton className="pointer-events-auto bottom-auto -top-10 left-1/2 -translate-x-1/2" /> |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| ) ?? null, | ||
| [tools], | ||
| ); | ||
| const hasPendingRuntimeQueue = Boolean( |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| } | ||
| const el = fixedBottomPanelsRef.current; | ||
| if (!el) return; | ||
| const observer = new ResizeObserver((entries) => { |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| const conversationBottomPadding = BASE_CONVERSATION_BOTTOM_PADDING; | ||
| const fixedBottomPanelsRef = useRef<HTMLDivElement>(null); | ||
| const [fixedBottomPanelsHeight, setFixedBottomPanelsHeight] = useState(0); | ||
| useEffect(() => { |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| } | ||
| const el = fixedBottomPanelsRef.current; | ||
| if (!el) return; | ||
| const observer = new ResizeObserver((entries) => { |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| if (!el) return; | ||
| const observer = new ResizeObserver((entries) => { | ||
| for (const entry of entries) { | ||
| setFixedBottomPanelsHeight(entry.target.getBoundingClientRect().height); |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| </div> | ||
| </div> | ||
| ) : ( | ||
| <ConversationScrollButton /> |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| </div> | ||
| </div> | ||
| ) : ( | ||
| <ConversationScrollButton /> |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| @@ -1691,9 +1691,16 @@ export function RuntimeThreadSurface({ | |||
| ) ?? null, | |||
There was a problem hiding this comment.
Automated review completed for this PR diff. No concrete inline issue was selected after aggregation.
| <span className={cn("rounded-md bg-app-surface-muted text-app-subtle", isCompact ? "px-1 py-0.5 text-[10px]" : "px-1.5 py-0.5 text-[11px]")}> | ||
| {t("queue.pendingCount", { count: pendingCount })} | ||
| </span> | ||
| {queue.isDeferringSteering ? ( |
There was a problem hiding this comment.
[MEDIUM] Potential Null Reference Exception on Nullable queue Prop
The RuntimeQueueTimeline component accepts a nullable queue prop. While optional chaining is safely used in other parts of the file (such as queue?.messages), the condition queue.isDeferringSteering directly dereferences the object. If the component is rendered or updated with a null queue, this will throw an uncaught TypeError and crash the React tree.
Suggestion: Buddy, we should use optional chaining here: queue?.isDeferringSteering. Additionally, we should add a robust unit test that renders <RuntimeQueueTimeline queue={null} /> to prevent future regressions.
Risk: A React render crash in the main thread/workbench surface whenever the queue state is cleared or reset to null during live updates.
Confidence: 1.00
Summary
FIXED_BOTTOM_PANELS_CONVERSATION_PADDINGand dynamically adjust conversation bottom padding when panels are visibleTest Plan
🤖 Generated with TiyCode