Skip to content

Conversation

@weeco
Copy link
Contributor

@weeco weeco commented Jan 29, 2026

Summary

  • Add dedicated GetTraceHistogram RPC endpoint instead of bundling histogram with ListTraces
  • Extract reusable ToolEventCard component for tool call display
  • Add LinkedTraceBanner for viewing traces outside current time range
  • Enhance activity chart with highlighted trace time marker

Test plan

  • Verify histogram chart loads correctly on transcript list page
  • Verify linked trace banner appears when navigating to trace outside time range
  • Run frontend tests: bun test transcript

@github-actions
Copy link
Contributor

github-actions bot commented Jan 29, 2026

The latest Buf updates on your PR. Results from workflow Buf CI / validate (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed✅ passedJan 29, 2026, 9:20 PM

Mark fields 1 and 2 as reserved in ListTracesRequest since start_time
and end_time were moved to the nested Filter message.

import { Button } from 'components/redpanda-ui/components/button';
import { Text } from 'components/redpanda-ui/components/typography';
import { Link, X } from 'lucide-react';
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: use components/icons barrel file for this

Comment on lines 28 to 29
// Truncate trace ID for display (show first 8 and last 4 characters)
const truncatedId = traceId.length > 16 ? `${traceId.slice(0, 8)}...${traceId.slice(-4)}` : traceId;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's better if we can truncate using CSS, then show the ID on hover? or just let the ID show up based on how much space is provided, something like white-space:nowrap; text-overflow: ellipsis;

Comment on lines 40 to 42
<code className="rounded bg-muted px-1 py-0.5 font-mono text-xs" title={traceId}>
{truncatedId}
</code>
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you try using Code component from typography component, hopefully it looks ok

View Surrounding Traces
</Button>
<Button aria-label="Dismiss linked trace" className="h-7 w-7" onClick={onDismiss} size="icon" variant="ghost">
<X className="h-3.5 w-3.5" />
Copy link
Contributor

Choose a reason for hiding this comment

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

I think standard is multiples of 4 (most icons in the UI should have consistent size of 16px across the board)

* by the Apache License, Version 2.0
*/

import { render, screen } from '@testing-library/react';
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: import from test-utils


import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, expect, it } from 'vitest';
Copy link
Contributor

Choose a reason for hiding this comment

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

ultra nit: use test() because that's what we have elsewhere

- Use components/icons barrel file instead of direct lucide-react import
- Use CSS truncation with title hover instead of JavaScript truncation
it('renders tool name in header', () => {
render(<ToolEventCard content={smallPayload} testId="tool-card" toolName="readFile" type="call" />);

expect(screen.getByText('readFile')).toBeInTheDocument();
Copy link
Contributor

Choose a reason for hiding this comment

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

.toBeVisible() would be better than .toBeInTheDocument() since it will also verify it's not being obstructed by another z-index element in the DOM.

onBucketClick={jumpedTo || isLinkedTraceMode ? undefined : handleBucketClick}
queryEndMs={
isLinkedTraceMode && activeTraceTimeMs
? Math.min(activeTraceTimeMs + 60 * 60 * 1000, Date.now())
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe just say ONE_HOUR

// Exit linked mode and center time range on the linked trace's time
if (activeTraceTimeMs) {
const THIRTY_MINUTES_MS = 30 * 60 * 1000;
const startMs = activeTraceTimeMs - THIRTY_MINUTES_MS;
Copy link
Contributor

Choose a reason for hiding this comment

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

30 * ONE_MINUTE? we have time utils

{
pageSize: TRANSCRIPTS_PAGE_SIZE,
pageToken: currentPageToken,
filter: {
Copy link
Contributor

Choose a reason for hiding this comment

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

👏

setSelectedTraceId(null);
setSelectedSpanId(null);
}, [setSelectedTraceId, setSelectedSpanId]);
setSelectedTab(null);
Copy link
Contributor

@malinskibeniamin malinskibeniamin Jan 29, 2026

Choose a reason for hiding this comment

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

I see a lot of useState hooks, it can get a bit out of control eventually given there is a lot of state to manage. At that point we should either consider:

  • useReducer (staying within React bounds)
  • useContext (we will eventually use React 19 so this would need a refactor later)
  • recommended: zustand store for state management. (can be done in another PR) - simply add a new slice like useTranscriptStore, this will also let people save their settings globally.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Switched to useQueryStates() but did not use zustand because we want to keep using the url params for state management, so that we have

  1. Sharable URLs that open the exact same view
  2. Different tabs cannot interfere with each other due to shared state

const transport = createRouterTransport(({ rpc }) => {
rpc(listTraces, listTracesMock);
rpc(getTrace, getTraceMock);
rpc(getTraceHistogram, getTraceHistogramMock);
Copy link
Contributor

Choose a reason for hiding this comment

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

awesome, this is the way to do tests, super happy with this

LinkedTraceBanner:
- Use InlineCode component from typography instead of raw <code>
- Use 16px icon size (h-4 w-4) for consistency

tool-event-card.test.tsx:
- Import from test-utils instead of @testing-library/react
- Use test() instead of it() for consistency
- Use toBeVisible() instead of toBeInTheDocument() for visible elements

transcript-list-page.tsx:
- Rename ONE_HOUR_MS to ONE_HOUR

{/* Preview when collapsed */}
{!isExpanded && hasPreview ? (
<button
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we use a redpanda UI registry button

@weeco
Copy link
Contributor Author

weeco commented Jan 29, 2026

Re: Negative margins in transcripts-table.tsx:218

The negative margins (-top-1, -bottom-1) extend the tree connector lines slightly beyond the cell boundaries to create seamless visual connections between parent and child rows. Without them, there would be small gaps at the top/bottom of each row where the vertical lines don't quite meet. This is a common technique for tree visualizations to maintain visual continuity.


Re: Unordered list in tool-event-card.tsx:99

This is inline metadata display (Type • Call ID • Size) rather than a semantic list. Using <ul>/<li> would be semantically incorrect here since these aren't list items - they're metadata attributes with visual separators. The separator pattern is standard for inline metadata (similar to breadcrumbs or article bylines).

weeco added 6 commits January 29, 2026 15:58
tool-event-card.tsx:
- Change icon sizes from h-3 w-3 to h-4 w-4 (16px standard)
- Use Button component with variant="ghost" for preview area

transcript-list-page.tsx:
- Use ONE_MINUTE from time utils for time constants
- Replace hardcoded values with 30 * ONE_MINUTE and 60 * ONE_MINUTE
Replace 4 separate useQueryState hooks with single useQueryStates for
batched URL updates. This reduces hook count and ensures atomic updates
when clearing multiple params (e.g., resetQueryState, handleDismissLinkedTrace).
… tabs

Add consistent leading-relaxed line height and whitespace-pre-wrap text
wrapping across detail tabs. Add section labels (DESCRIPTION, ARGUMENTS,
RESPONSE) to tool call tab for better content organization.
Add updatePageTitle() to set uiState.pageTitle and pageBreadcrumbs,
matching the pattern used by AI Agents and Remote MCP pages.
Update the transcripts page description to better explain the feature
and add a "Learn more" link to the documentation, consistent with
other pages like connect-clusters.
Allow users to double-click on transcript and span rows to toggle
their expanded state, improving the table interaction UX.
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.

3 participants