Skip to content

feat: traces and console improvement - #164

Merged
sergiofilhowz merged 1 commit into
mainfrom
feat/traces-improvement
May 19, 2026
Merged

feat: traces and console improvement#164
sergiofilhowz merged 1 commit into
mainfrom
feat/traces-improvement

Conversation

@sergiofilhowz

@sergiofilhowz sergiofilhowz commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Resizable, dockable chat panel with persistent width and open/close controls.
    • Consolidated chat experience (shared conversations provider, panel, view, and density modes).
    • Trace viewer: critical-path and engine-routing toggles, compact toolbar buttons, and a new icon toggle button.
  • Bug Fixes & Improvements

    • Improved responsive/resizable behavior across chat, sidebar, and trace panels with persisted preferences.
    • Redesigned trace filters into a top-bar with advanced popover and improved accessibility for resize controls.
    • Added tests for critical-path flattening logic.

Review Change Stack

@vercel

vercel Bot commented May 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workers Ready Ready Preview, Comment May 19, 2026 8:51pm

Request Review

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d09496af-9c4a-4931-acb2-be08bfd0ed94

📥 Commits

Reviewing files that changed from the base of the PR and between 193eb7b and e230f3c.

📒 Files selected for processing (21)
  • console/web/src/App.tsx
  • console/web/src/components/chat/ChatDock.tsx
  • console/web/src/components/chat/ChatPanel.tsx
  • console/web/src/components/chat/ChatView.tsx
  • console/web/src/components/chat/Composer.tsx
  • console/web/src/components/chat/MessageList.tsx
  • console/web/src/components/sidebar/ConversationSidebar.tsx
  • console/web/src/components/ui/Sheet.tsx
  • console/web/src/hooks/use-chat-dock.ts
  • console/web/src/lib/conversations-context.tsx
  • console/web/src/pages/Chat.tsx
  • console/web/src/pages/Traces/components/FlameGraph.tsx
  • console/web/src/pages/Traces/components/IconToggleButton.tsx
  • console/web/src/pages/Traces/components/TraceFilters.tsx
  • console/web/src/pages/Traces/components/WaterfallChart.tsx
  • console/web/src/pages/Traces/hooks/useResizablePanels.ts
  • console/web/src/pages/Traces/hooks/useShowEngineRouting.ts
  • console/web/src/pages/Traces/index.tsx
  • console/web/src/pages/Traces/lib/spanTree.test.ts
  • console/web/src/pages/Traces/lib/spanTree.ts
  • console/web/vite.config.ts
🚧 Files skipped from review as they are similar to previous changes (14)
  • console/web/src/components/chat/MessageList.tsx
  • console/web/vite.config.ts
  • console/web/src/pages/Traces/lib/spanTree.test.ts
  • console/web/src/App.tsx
  • console/web/src/pages/Traces/index.tsx
  • console/web/src/components/chat/Composer.tsx
  • console/web/src/lib/conversations-context.tsx
  • console/web/src/components/chat/ChatPanel.tsx
  • console/web/src/pages/Traces/components/IconToggleButton.tsx
  • console/web/src/components/sidebar/ConversationSidebar.tsx
  • console/web/src/components/chat/ChatDock.tsx
  • console/web/src/pages/Traces/components/FlameGraph.tsx
  • console/web/src/hooks/use-chat-dock.ts
  • console/web/src/pages/Traces/components/TraceFilters.tsx

📝 Walkthrough

Walkthrough

This PR adds a resizable chat dock with density-aware chat components and a ConversationsProvider, refactors trace filters into a top-row + popover, and introduces critical-path and engine-routing toggles with container-aware panel resizing and ARIA improvements.

Changes

Chat Dock and Density-Based Layout System

Layer / File(s) Summary
Chat dock constants, sizing, and state management
console/web/src/hooks/use-chat-dock.ts
Defines dock width constants and helper functions, computes viewport-aware maximum dock width, persists dock open/width state to localStorage with SSR safety, exposes hook interface for dock control.
ChatDock resizable panel component
console/web/src/components/chat/ChatDock.tsx
Renders left-sticky chat panel with draggable right-edge resize handle, tracks resizing state and viewport-aware max width, supports double-click reset to default width, includes ARIA separator semantics and keyboard-focusable handle.
Conversations context provider for shared state
console/web/src/lib/conversations-context.tsx
Centralizes conversation API, model picker options, and catalog loading state via React Context; merges backend, model options, and loading flags; exposes useConversationsCtx hook.
ChatPanel orchestrates chat UI with sidebar and density support
console/web/src/components/chat/ChatPanel.tsx
Composes ConversationSidebar with ChatView, persists sidebar-collapsed state to localStorage, forwards context-provided model/mode/catalog/message-handler props to ChatView, supports optional onClose callback.
Chat components adapted for route/dock density modes
console/web/src/components/chat/ChatView.tsx, console/web/src/components/chat/MessageList.tsx, console/web/src/components/sidebar/ConversationSidebar.tsx, console/web/src/components/chat/Composer.tsx
ChatView, MessageList, ConversationSidebar, and Composer updated to support density prop ("route" or "dock"), adjusting padding, copy buttons, collapsed sidebar state, and layout spacing accordingly.
App layout, Header, Sheet, and Chat route integration
console/web/src/App.tsx, console/web/src/components/ui/Sheet.tsx, console/web/src/pages/Chat.tsx
App computes dock eligibility by route, wraps content in ConversationsProvider and Sheet, conditionally renders ChatDock, extends Header with dock toggle button and ARIA labels; Chat page now delegates to ChatPanel.

Trace View Filtering and Critical-Path Toggles

Layer / File(s) Summary
Critical-path filtering in span tree
console/web/src/pages/Traces/lib/spanTree.ts, console/web/src/pages/Traces/lib/spanTree.test.ts
Extends FlattenOptions with optional onlyCriticalPath flag, updates flattenTree traversal to skip non-critical nodes when enabled, and adds tests validating behavior alone and composed with engine-routing filters.
useShowEngineRouting hook for persisted visibility toggle
console/web/src/pages/Traces/hooks/useShowEngineRouting.ts
New React hook that reads/writes engine-routing visibility preference to localStorage, defaults to false on server (SSR safe), returns [show, setShow] tuple with updates synced to storage.
IconToggleButton component for compact toolbar controls
console/web/src/pages/Traces/components/IconToggleButton.tsx
Reusable button component with tooltip, ARIA attributes (aria-label, aria-pressed), and conditional active/inactive styling; used by trace view toolbars for critical-path and engine-routing toggles.
Waterfall chart view control refactor with toolbar
console/web/src/pages/Traces/components/WaterfallChart.tsx
Refactors controls from checkbox UI to icon-based Toolbar with persisted useShowEngineRouting, enforces critical-path filtering via flattenTree option, removes isCritical prop from WaterfallRow, and simplifies row bar styling.
Flame graph view control refactor
console/web/src/pages/Traces/components/FlameGraph.tsx
Updates FlameGraph with showCriticalPath and showEngineRouting toggles, derives visible nodes from filtered flattenTree output with displayDepth remapping, renders IconToggleButton toolbar controls.
Dynamic panel-width resizing with container awareness
console/web/src/pages/Traces/hooks/useResizablePanels.ts, console/web/src/pages/Traces/index.tsx
Exports sizing constants, measures containerWidth and computes panelMaxes dynamically based on viewport space/coupling rules, re-clamps widths on resize events, and adds aria-valuemin/aria-valuemax attributes to resize handles.
Trace filters UI refactor with popover-based advanced controls
console/web/src/pages/Traces/components/TraceFilters.tsx
Redesigns to single always-visible top row (search, group-by, status, "more filters" button, stats) plus portal-based popover with advanced controls; maintains reducer-driven temp state and apply handlers, adds advanced-filter badge counting.

Sequence Diagram

sequenceDiagram
  participant User
  participant App as App.tsx
  participant Provider as ConversationsProvider
  participant Dock as ChatDock
  participant Panel as ChatPanel
  participant Sidebar as ConversationSidebar
  participant View as ChatView
  User->>App: navigate to chat or view dock
  App->>Provider: wrap layout with context
  Provider->>Provider: aggregate conversations + model state
  App->>Dock: conditionally render if eligible
  Dock->>Panel: render ChatPanel (density='dock')
  Panel->>Sidebar: render with conversation list
  Panel->>View: render active conversation with density prop
  User->>Dock: drag resize handle
  Dock->>Dock: compute clamped width, persist to localStorage
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • iii-hq/workers#160: Overlaps changes to console/web/src/components/chat/MessageList.tsx (density vs. function-call grouping).
  • iii-hq/workers#163: Modifies ChatView.tsx props/conditional UI similar to this PR's density/onClose additions.
  • iii-hq/workers#157: Related to ChatView header/session-id copy control changes that interact with density-based visibility.

Poem

🐰 A dock appears with nimble cheer,

Chats that shrink and chats that steer,
Context shared and panels wide,
Filters tucked and toggles tried,
Resize, persist, and trace the line—hooray, all works in time!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The pull request title 'feat: traces and console improvement' is overly broad and vague. While it references two areas (traces and console), it does not clearly summarize the main changes or specific improvements made in the changeset. Use a more specific title that describes the primary feature or improvement, such as 'feat: add collapsible chat dock and trace view enhancements' or break into focused commits with targeted titles.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/traces-improvement

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

skill-check — worker

0 verified, 10 skipped (no docs/).

Layer Result
structure
vale
ai

Three for three. Nicely done.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (2)
console/web/src/pages/Traces/components/TraceFilters.tsx (1)

198-205: ⚡ Quick win

Popover position can drift after open.

Position is only measured once. If the viewport/layout changes while open, the popover can detach from its trigger.

Suggested fix (recompute while open)
   useEffect(() => {
     if (!open || !triggerRef.current) return
-    const rect = triggerRef.current.getBoundingClientRect()
-    setPos({
-      top: rect.bottom + 4,
-      right: window.innerWidth - rect.right,
-    })
+    const updatePos = () => {
+      const rect = triggerRef.current?.getBoundingClientRect()
+      if (!rect) return
+      setPos({
+        top: rect.bottom + 4,
+        right: window.innerWidth - rect.right,
+      })
+    }
+    updatePos()
+    window.addEventListener('resize', updatePos)
+    window.addEventListener('scroll', updatePos, true)
+    return () => {
+      window.removeEventListener('resize', updatePos)
+      window.removeEventListener('scroll', updatePos, true)
+    }
   }, [open, triggerRef])

Also applies to: 207-224

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/web/src/pages/Traces/components/TraceFilters.tsx` around lines 198 -
205, The popover position is only measured once in the useEffect that reads
triggerRef.getBoundingClientRect() (when open), so it can drift; update
TraceFilters to recompute position while open by adding listeners (window resize
and scroll, and/or a MutationObserver) that call the same positioning logic used
in the existing useEffect and update setPos, and clean them up when open becomes
false; modify the effect that depends on [open, triggerRef] (and the similar
block at the other range) to register these listeners when open is true and
remove them on cleanup so the popover follows layout/viewport changes without
code duplication.
console/web/src/components/chat/ChatDock.tsx (1)

96-110: ⚡ Quick win

Add keyboard resize behavior for the separator handle.

The handle is focusable but not operable via keyboard; add onKeyDown (Arrow/Home/End) so keyboard-only users can resize.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/web/src/components/chat/ChatDock.tsx` around lines 96 - 110, Add
keyboard support to the separator by wiring an onKeyDown handler on the same div
that currently uses handleMouseDown and handleReset; intercept
ArrowLeft/ArrowRight (or ArrowUp/ArrowDown if your layout uses vertical keyboard
mapping) to decrement/increment the dock width in small steps, and handle Home
to set width to DOCK_MIN_WIDTH and End to set width to maxWidth. Use the
existing width state setter (the same state updated by handleMouseDown/drag) and
ensure you call event.preventDefault() for handled keys and update any
isResizing state consistently; reference the separator div, handleMouseDown,
handleReset, width, DOCK_MIN_WIDTH and maxWidth when implementing the onKeyDown
logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@console/web/src/App.tsx`:
- Line 9: The ConversationsProvider is being mounted unconditionally (causing
chat backend/model/catalog initialization) even on non-chat routes; wrap the
ConversationsProvider usage so it only renders when the chat UI is visible by
guarding it with the condition (view === 'chat' || dockVisible) where view is
the current active view and dockVisible is the dock visibility flag, e.g.
replace the unconditional <ConversationsProvider>...</ConversationsProvider>
block (the component named ConversationsProvider) with a conditional render that
returns the provider and its children only when that boolean expression is true,
leaving other routes to avoid loading chat state.

In `@console/web/src/components/chat/ChatDock.tsx`:
- Around line 56-77: The cleanup of the useEffect that tracks dragging only
removes event listeners and relies on handleMouseUp to reset global styles, so
if the component unmounts mid-drag document.body.style.cursor and
document.body.style.userSelect can stay set; update the effect cleanup to also
reset document.body.style.cursor and document.body.style.userSelect to their
defaults (empty string) so both the returned cleanup function and handleMouseUp
perform the same reset; locate the useEffect that depends on isResizing and
references handleMouseMove/handleMouseUp/reserveStartRef/onWidthChange and add
the style resets there.

In `@console/web/src/components/sidebar/ConversationSidebar.tsx`:
- Around line 30-41: The collapsed UI currently always renders an "expand"
button even when the optional onToggleCollapsed prop is undefined; update the
collapsed branch in ConversationSidebar (where collapsed is checked and the
button with PanelLeftOpen is rendered) to conditionally render that button only
if onToggleCollapsed is provided, so the interactive control is suppressed when
the callback is absent and avoids a dead/no-op control.

In `@console/web/src/pages/Traces/hooks/useResizablePanels.ts`:
- Around line 191-223: The effect in useResizablePanels currently only listens
to window.resize so container-driven layout changes can leave panel widths
stale; replace/augment the window listener by creating a ResizeObserver on
containerRef.current (use getContainerWidth inside the same sync callback) to
call sync whenever the container element's size changes, keep the existing
window.resize for viewport changes, and ensure both the observer and the window
listener are cleaned up in the returned cleanup; update any dependencies
accordingly while continuing to use setContainerWidth, setPanelWidths and
selectedSpanIdRef in the same sync logic.

In `@console/web/src/pages/Traces/hooks/useShowEngineRouting.ts`:
- Around line 11-18: The hook useShowEngineRouting currently accesses
window.localStorage directly in the useState initializer and in the useEffect
setter; wrap both localStorage.getItem and localStorage.setItem calls in
try/catch blocks (and still guard for typeof window === 'undefined') so any
SecurityError/QuotaExceededError is caught and handled, returning the safe
default (false) on read errors and silently failing on write errors; reference
STORAGE_KEY in the catches and ensure the useState initializer returns false if
an exception occurs and the useEffect swallow errors rather than letting them
propagate.

In `@console/web/vite.config.ts`:
- Line 14: Remove the insecure wildcard host setting by deleting the
allowedHosts: true entry in vite.config.ts and replace it with an explicit
allowlist (or omit the option to use Vite defaults); locate the dev server
config (export default defineConfig -> dev -> allowedHosts) and set allowedHosts
to a concrete array of trusted hostnames/IPs (e.g., 'localhost', '127.0.0.1',
and any required dev host) instead of true to prevent DNS rebinding.

---

Nitpick comments:
In `@console/web/src/components/chat/ChatDock.tsx`:
- Around line 96-110: Add keyboard support to the separator by wiring an
onKeyDown handler on the same div that currently uses handleMouseDown and
handleReset; intercept ArrowLeft/ArrowRight (or ArrowUp/ArrowDown if your layout
uses vertical keyboard mapping) to decrement/increment the dock width in small
steps, and handle Home to set width to DOCK_MIN_WIDTH and End to set width to
maxWidth. Use the existing width state setter (the same state updated by
handleMouseDown/drag) and ensure you call event.preventDefault() for handled
keys and update any isResizing state consistently; reference the separator div,
handleMouseDown, handleReset, width, DOCK_MIN_WIDTH and maxWidth when
implementing the onKeyDown logic.

In `@console/web/src/pages/Traces/components/TraceFilters.tsx`:
- Around line 198-205: The popover position is only measured once in the
useEffect that reads triggerRef.getBoundingClientRect() (when open), so it can
drift; update TraceFilters to recompute position while open by adding listeners
(window resize and scroll, and/or a MutationObserver) that call the same
positioning logic used in the existing useEffect and update setPos, and clean
them up when open becomes false; modify the effect that depends on [open,
triggerRef] (and the similar block at the other range) to register these
listeners when open is true and remove them on cleanup so the popover follows
layout/viewport changes without code duplication.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6342a17a-4adc-4777-baf5-92c6377e0324

📥 Commits

Reviewing files that changed from the base of the PR and between 6bc0045 and 193eb7b.

📒 Files selected for processing (21)
  • console/web/src/App.tsx
  • console/web/src/components/chat/ChatDock.tsx
  • console/web/src/components/chat/ChatPanel.tsx
  • console/web/src/components/chat/ChatView.tsx
  • console/web/src/components/chat/Composer.tsx
  • console/web/src/components/chat/MessageList.tsx
  • console/web/src/components/sidebar/ConversationSidebar.tsx
  • console/web/src/components/ui/Sheet.tsx
  • console/web/src/hooks/use-chat-dock.ts
  • console/web/src/lib/conversations-context.tsx
  • console/web/src/pages/Chat.tsx
  • console/web/src/pages/Traces/components/FlameGraph.tsx
  • console/web/src/pages/Traces/components/IconToggleButton.tsx
  • console/web/src/pages/Traces/components/TraceFilters.tsx
  • console/web/src/pages/Traces/components/WaterfallChart.tsx
  • console/web/src/pages/Traces/hooks/useResizablePanels.ts
  • console/web/src/pages/Traces/hooks/useShowEngineRouting.ts
  • console/web/src/pages/Traces/index.tsx
  • console/web/src/pages/Traces/lib/spanTree.test.ts
  • console/web/src/pages/Traces/lib/spanTree.ts
  • console/web/vite.config.ts

Comment thread console/web/src/App.tsx
import { useChatDock } from '@/hooks/use-chat-dock'
import { useHashRoute, type View } from '@/hooks/use-hash-route'
import { type Theme, useTheme } from '@/hooks/use-theme'
import { ConversationsProvider } from '@/lib/conversations-context'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid mounting chat state on every route.

Line 52 now mounts ConversationsProvider even when the active view is traces, examples, or playground and the dock is closed. Since this provider now owns the chat backend/model/catalog state, those routes will still pay that initialization cost and any related fetches with no chat UI on screen. Please scope the provider to a chat-visible condition such as view === 'chat' || dockVisible.

Also applies to: 52-86

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/web/src/App.tsx` at line 9, The ConversationsProvider is being
mounted unconditionally (causing chat backend/model/catalog initialization) even
on non-chat routes; wrap the ConversationsProvider usage so it only renders when
the chat UI is visible by guarding it with the condition (view === 'chat' ||
dockVisible) where view is the current active view and dockVisible is the dock
visibility flag, e.g. replace the unconditional
<ConversationsProvider>...</ConversationsProvider> block (the component named
ConversationsProvider) with a conditional render that returns the provider and
its children only when that boolean expression is true, leaving other routes to
avoid loading chat state.

Comment on lines +56 to +77
useEffect(() => {
if (!isResizing) return

const handleMouseMove = (e: MouseEvent) => {
const dx = e.clientX - resizeStartRef.current.x
onWidthChange(
clamp(resizeStartRef.current.width + dx, resizeStartRef.current.max),
)
}

const handleMouseUp = () => {
setIsResizing(false)
document.body.style.cursor = ''
document.body.style.userSelect = ''
}

document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
return () => {
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reset global body styles in effect cleanup.

When unmount happens mid-drag, document.body.style.cursor/userSelect can remain stuck because only handleMouseUp resets them.

💡 Suggested fix
   useEffect(() => {
     if (!isResizing) return
@@
     document.addEventListener('mousemove', handleMouseMove)
     document.addEventListener('mouseup', handleMouseUp)
     return () => {
       document.removeEventListener('mousemove', handleMouseMove)
       document.removeEventListener('mouseup', handleMouseUp)
+      document.body.style.cursor = ''
+      document.body.style.userSelect = ''
     }
   }, [isResizing, onWidthChange])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (!isResizing) return
const handleMouseMove = (e: MouseEvent) => {
const dx = e.clientX - resizeStartRef.current.x
onWidthChange(
clamp(resizeStartRef.current.width + dx, resizeStartRef.current.max),
)
}
const handleMouseUp = () => {
setIsResizing(false)
document.body.style.cursor = ''
document.body.style.userSelect = ''
}
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
return () => {
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
}
useEffect(() => {
if (!isResizing) return
const handleMouseMove = (e: MouseEvent) => {
const dx = e.clientX - resizeStartRef.current.x
onWidthChange(
clamp(resizeStartRef.current.width + dx, resizeStartRef.current.max),
)
}
const handleMouseUp = () => {
setIsResizing(false)
document.body.style.cursor = ''
document.body.style.userSelect = ''
}
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
return () => {
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
document.body.style.cursor = ''
document.body.style.userSelect = ''
}
}, [isResizing, onWidthChange])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/web/src/components/chat/ChatDock.tsx` around lines 56 - 77, The
cleanup of the useEffect that tracks dragging only removes event listeners and
relies on handleMouseUp to reset global styles, so if the component unmounts
mid-drag document.body.style.cursor and document.body.style.userSelect can stay
set; update the effect cleanup to also reset document.body.style.cursor and
document.body.style.userSelect to their defaults (empty string) so both the
returned cleanup function and handleMouseUp perform the same reset; locate the
useEffect that depends on isResizing and references
handleMouseMove/handleMouseUp/reserveStartRef/onWidthChange and add the style
resets there.

Comment on lines +30 to +41
if (collapsed) {
return (
<aside className="w-9 shrink-0 border-r border-rule flex flex-col items-center bg-bg gap-1 py-2">
<button
type="button"
onClick={onToggleCollapsed}
aria-label="expand conversations"
title="expand conversations"
className="flex items-center justify-center size-7 text-ink-faint hover:text-ink transition-colors"
>
<PanelLeftOpen className="size-4" />
</button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid rendering a dead “expand” button when no toggle handler is provided.

onToggleCollapsed is optional, but the collapsed branch always renders an interactive expand control. Guard this button behind the callback to prevent a no-op control.

💡 Suggested fix
-        <button
-          type="button"
-          onClick={onToggleCollapsed}
-          aria-label="expand conversations"
-          title="expand conversations"
-          className="flex items-center justify-center size-7 text-ink-faint hover:text-ink transition-colors"
-        >
-          <PanelLeftOpen className="size-4" />
-        </button>
+        {onToggleCollapsed ? (
+          <button
+            type="button"
+            onClick={onToggleCollapsed}
+            aria-label="expand conversations"
+            title="expand conversations"
+            className="flex items-center justify-center size-7 text-ink-faint hover:text-ink transition-colors"
+          >
+            <PanelLeftOpen className="size-4" />
+          </button>
+        ) : null}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (collapsed) {
return (
<aside className="w-9 shrink-0 border-r border-rule flex flex-col items-center bg-bg gap-1 py-2">
<button
type="button"
onClick={onToggleCollapsed}
aria-label="expand conversations"
title="expand conversations"
className="flex items-center justify-center size-7 text-ink-faint hover:text-ink transition-colors"
>
<PanelLeftOpen className="size-4" />
</button>
if (collapsed) {
return (
<aside className="w-9 shrink-0 border-r border-rule flex flex-col items-center bg-bg gap-1 py-2">
{onToggleCollapsed ? (
<button
type="button"
onClick={onToggleCollapsed}
aria-label="expand conversations"
title="expand conversations"
className="flex items-center justify-center size-7 text-ink-faint hover:text-ink transition-colors"
>
<PanelLeftOpen className="size-4" />
</button>
) : null}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/web/src/components/sidebar/ConversationSidebar.tsx` around lines 30 -
41, The collapsed UI currently always renders an "expand" button even when the
optional onToggleCollapsed prop is undefined; update the collapsed branch in
ConversationSidebar (where collapsed is checked and the button with
PanelLeftOpen is rendered) to conditionally render that button only if
onToggleCollapsed is provided, so the interactive control is suppressed when the
callback is absent and avoids a dead/no-op control.

Comment on lines +191 to +223
useEffect(() => {
if (typeof window === 'undefined') return
const sync = () => {
const cw = getContainerWidth(containerRef)
setContainerWidth(cw)
setPanelWidths((p) => {
const hasSpan = selectedSpanIdRef.current !== null
const handles = hasSpan ? HANDLE_WIDTH * 2 : HANDLE_WIDTH
const otherForTrace = hasSpan ? p.span : 0
const maxTrace = Math.max(
PANEL_MIN_WIDTH,
cw - PANEL_NEIGHBOR_MIN_WIDTH - handles - otherForTrace,
)
const nextTrace = Math.max(
PANEL_MIN_WIDTH,
Math.min(maxTrace, p.trace),
)
let nextSpan = p.span
if (hasSpan) {
const maxSpan = Math.max(
PANEL_MIN_WIDTH,
cw - PANEL_NEIGHBOR_MIN_WIDTH - handles - nextTrace,
)
nextSpan = Math.max(PANEL_MIN_WIDTH, Math.min(maxSpan, p.span))
}
if (nextTrace === p.trace && nextSpan === p.span) return p
return { trace: nextTrace, span: nextSpan }
})
}
sync()
window.addEventListener('resize', sync)
return () => window.removeEventListener('resize', sync)
}, [containerRef])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Re-clamping is not actually container-aware yet.

The effect only subscribes to window.resize, so panel widths/maxes can go stale when the container width changes due to layout changes (without viewport resize). This breaks the stated container-coupling behavior.

Suggested fix (observe the container directly)
   useEffect(() => {
-    if (typeof window === 'undefined') return
+    if (typeof window === 'undefined') return
     const sync = () => {
       const cw = getContainerWidth(containerRef)
       setContainerWidth(cw)
       setPanelWidths((p) => {
         const hasSpan = selectedSpanIdRef.current !== null
         const handles = hasSpan ? HANDLE_WIDTH * 2 : HANDLE_WIDTH
         const otherForTrace = hasSpan ? p.span : 0
         const maxTrace = Math.max(
           PANEL_MIN_WIDTH,
           cw - PANEL_NEIGHBOR_MIN_WIDTH - handles - otherForTrace,
         )
         const nextTrace = Math.max(
           PANEL_MIN_WIDTH,
           Math.min(maxTrace, p.trace),
         )
         let nextSpan = p.span
         if (hasSpan) {
           const maxSpan = Math.max(
             PANEL_MIN_WIDTH,
             cw - PANEL_NEIGHBOR_MIN_WIDTH - handles - nextTrace,
           )
           nextSpan = Math.max(PANEL_MIN_WIDTH, Math.min(maxSpan, p.span))
         }
         if (nextTrace === p.trace && nextSpan === p.span) return p
         return { trace: nextTrace, span: nextSpan }
       })
     }
     sync()
-    window.addEventListener('resize', sync)
-    return () => window.removeEventListener('resize', sync)
+    const node = containerRef.current
+    const ro =
+      node && typeof ResizeObserver !== 'undefined'
+        ? new ResizeObserver(sync)
+        : null
+    if (node && ro) ro.observe(node)
+    window.addEventListener('resize', sync)
+    return () => {
+      if (ro) ro.disconnect()
+      window.removeEventListener('resize', sync)
+    }
   }, [containerRef])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/web/src/pages/Traces/hooks/useResizablePanels.ts` around lines 191 -
223, The effect in useResizablePanels currently only listens to window.resize so
container-driven layout changes can leave panel widths stale; replace/augment
the window listener by creating a ResizeObserver on containerRef.current (use
getContainerWidth inside the same sync callback) to call sync whenever the
container element's size changes, keep the existing window.resize for viewport
changes, and ensure both the observer and the window listener are cleaned up in
the returned cleanup; update any dependencies accordingly while continuing to
use setContainerWidth, setPanelWidths and selectedSpanIdRef in the same sync
logic.

Comment thread console/web/src/pages/Traces/hooks/useShowEngineRouting.ts
},
},
server: {
allowedHosts: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Vite 8 official docs, what is the security guidance for server.allowedHosts: true?

💡 Result:

In the official Vite 8 documentation, setting server.allowedHosts to true is strongly discouraged due to significant security risks [1][2]. The documentation explicitly warns that setting this option to true allows any website to send requests to your development server, which makes you vulnerable to DNS rebinding attacks [1][2]. These attacks can allow malicious actors to download your source code and other sensitive content [1][2]. Vite recommends that you always use an explicit list of allowed hosts instead of setting the value to true [1][2]. For further details on the underlying security concern, the documentation references vulnerability GHSA-vg6x-rcgg-rjx6 [1][2].

Citations:


Remove allowedHosts: true to prevent DNS rebinding attacks.

Line 14 sets allowedHosts: true, which disables host filtering in the dev server. This is explicitly discouraged in Vite 8 documentation because it allows any website to send requests to your server, opening the door to DNS rebinding attacks that can expose source code and sensitive content. Use an explicit list of allowed hosts instead.

🔧 Proposed fix
   server: {
-    allowedHosts: true,
+    // Host checks enabled by default. Add specific hosts only when required
+    // (e.g. via __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS).
     proxy: {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
allowedHosts: true,
// Host checks enabled by default. Add specific hosts only when required
// (e.g. via __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/web/vite.config.ts` at line 14, Remove the insecure wildcard host
setting by deleting the allowedHosts: true entry in vite.config.ts and replace
it with an explicit allowlist (or omit the option to use Vite defaults); locate
the dev server config (export default defineConfig -> dev -> allowedHosts) and
set allowedHosts to a concrete array of trusted hostnames/IPs (e.g.,
'localhost', '127.0.0.1', and any required dev host) instead of true to prevent
DNS rebinding.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants