Repository: humanlayer/humanlayer
File: humanlayer-wui/src/components/internal/SessionDetail.tsx (TypeScript)

Code before:
    if (focusedEventId) {
      const focusedEvent = events.find(e => e.id === focusedEventId)
      if (focusedEvent?.approval_status === 'pending' && focusedEvent.approval_id) {
        handleApprove(focusedEvent.approval_id)
      }
    }
  })

  // D key to deny focused event that has pending approval
  useHotkeys('d', () => {
    if (focusedEventId) {
      const focusedEvent = events.find(e => e.id === focusedEventId)
      if (focusedEvent?.approval_status === 'pending' && focusedEvent.approval_id) {
        // For now, deny with a default reason - could be enhanced to show input
        handleDeny(focusedEvent.approval_id, 'Denied via hotkey')
      }
    }
  })


  const isLongQuery = session.query.length > 50

  return (
    <section className="flex flex-col gap-4">
      <hgroup className="flex flex-col gap-1">
        {isLongQuery ? (
          <Collapsible open={isQueryExpanded} onOpenChange={setIsQueryExpanded}>
            <CollapsibleTrigger className="flex items-center gap-2 text-left w-full">
              <h2 className="text-lg font-medium text-foreground font-mono">
                {truncate(session.query, 50)}{' '}
                {session.parent_session_id && (
                  <span className="text-muted-foreground">[continued]</span>
                )}
              </h2>
              {isQueryExpanded ? (
                <ChevronDown className="w-4 h-4 text-muted-foreground" />
              ) : (
                <ChevronRight className="w-4 h-4 text-muted-foreground" />
              )}
            </CollapsibleTrigger>
            <CollapsibleContent>
              <div className="mt-2 p-3 bg-muted/20 rounded-md">
                <p className="text-sm font-mono text-foreground whitespace-pre-wrap">{session.query}</p>
              </div>
            </CollapsibleContent>
          </Collapsible>
        ) : (
          <h2 className="text-lg font-medium text-foreground font-mono">
            {session.query}{' '}
            {session.parent_session_id && <span className="text-muted-foreground">[continued]</span>}
          </h2>

Review comment (suggestion):
<img width="713" alt="Screenshot 2025-06-24 at 5 57 14 AM" src="https://github.com/user-attachments/assets/3b2784cf-332a-4eef-9254-9d315a2b8ef7" />
<img width="911" alt="Screenshot 2025-06-24 at 5 57 30 AM" src="https://github.com/user-attachments/assets/916623c1-4210-4113-adb9-580fad564675" />

In TUI it shows the whole Query as the first user message in expanded view and then just the truncated portion on session list. I think for me it makes more sense to see the whole of the messages in the conversation view. Would add an extra step to need to go and expand any individual message to remember/see what was typed/written vs just scrolling past it.

Code after:
  const toolResults = events.filter(event => event.event_type === ConversationEventType.ToolResult)
  const toolResultsByKey = keyBy(toolResults, 'tool_result_for_id')

  // Create synthetic first user message event from session.query
  const firstUserMessageEvent: ConversationEvent = {
    id: -1, // Use negative ID to avoid conflicts
    session_id: session.id,
    claude_session_id: session.claude_session_id || '',
    sequence: 0,
    event_type: ConversationEventType.Message,
    created_at: session.start_time,
    role: ConversationRole.User,
    content: session.query,
    is_completed: true,
  }

  // Inject the first user message at the beginning of the events list
  const eventsWithFirstMessage = [firstUserMessageEvent, ...events]

  const displayObjects = eventsWithFirstMessage
    .filter(event => event.event_type !== ConversationEventType.ToolResult)
    .map(event =>
      eventToDisplayObject(
        event,
        onApprove,
        onDeny,
        denyingApprovalId,
        setDenyingApprovalId,
        () => setDenyingApprovalId(null),
        approvingApprovalId,
        isSplitView,
        onToggleSplitView,
        event.tool_id ? toolResultsByKey[event.tool_id] : undefined,
      ),
    )
  const nonEmptyDisplayObjects = displayObjects.filter(displayObject => displayObject !== null)

  // Navigation handlers
  const focusNextEvent = () => {
    if (nonEmptyDisplayObjects.length === 0) return

    const currentIndex = focusedEventId
      ? nonEmptyDisplayObjects.findIndex(obj => obj.id === focusedEventId)
      : -1

    if (currentIndex === -1 || currentIndex === nonEmptyDisplayObjects.length - 1) {
      setFocusedEventId(nonEmptyDisplayObjects[0].id)
    } else {
      setFocusedEventId(nonEmptyDisplayObjects[currentIndex + 1].id)
    }
    setFocusSource('keyboard')