Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/trace-nav-tooltips.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflow/web-shared': patch
---

Add keyboard-shortcut tooltips to the up/down span navigation buttons in the trace viewer detail panel.
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,20 @@ import { useSidebarDataOptional } from '../sidebar/sidebar-data-context';
import type { Trace } from '../trace-viewer/types';
import { formatDuration, getHighResInMs } from '../trace-viewer/util/timing';
import { IconButton } from '../ui/icon-button';
import { Kbd } from '../ui/kbd';
import EventList from './components/event-list';
import { SplitPane } from './components/split-pane';
import {
TIMELINE_PADDING_PX,
Timeline,
TimelineHeader,
} from './components/timeline';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from '../ui/tooltip';
import { ActiveSpanProvider, useActiveSpan } from './context';
import { DetailPanel } from './detail-panel';
import { searchSpans } from './search';
Expand Down Expand Up @@ -154,9 +161,11 @@ function useSelectedSpanInfo(): SelectedSpanInfo | null {

export function NewTraceViewer({ trace }: NewTraceViewerProps): ReactNode {
return (
<ActiveSpanProvider spans={trace.spans}>
<NewTraceViewerContent trace={trace} />
</ActiveSpanProvider>
<TooltipProvider delayDuration={300}>
<ActiveSpanProvider spans={trace.spans}>
<NewTraceViewerContent trace={trace} />
</ActiveSpanProvider>
</TooltipProvider>
);
}

Expand Down Expand Up @@ -634,22 +643,42 @@ function NewTraceViewerContent({ trace }: NewTraceViewerProps): ReactNode {
{selectedSpanName}
</span>
<div className="flex items-center gap-0.5 shrink-0">
<IconButton
aria-label="Navigate to previous span"
aria-keyshortcuts="K"
onClick={handleSelectPrevSpan}
disabled={!prevSpanId}
>
<ChevronUp className="w-4 h-4" />
</IconButton>
<IconButton
aria-label="Navigate to next span"
aria-keyshortcuts="J"
onClick={handleSelectNextSpan}
disabled={!nextSpanId}
>
<ChevronDown className="w-4 h-4" />
</IconButton>
<Tooltip>
<TooltipTrigger asChild>
<IconButton
aria-label="Navigate up"
aria-keyshortcuts="K"
onClick={handleSelectPrevSpan}
disabled={!prevSpanId}
>
<ChevronUp className="w-4 h-4" />
</IconButton>
</TooltipTrigger>
{prevSpanId ? (
<TooltipContent>
Navigate up
<Kbd>K</Kbd>
</TooltipContent>
) : null}
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<IconButton
aria-label="Navigate down"
aria-keyshortcuts="J"
onClick={handleSelectNextSpan}
disabled={!nextSpanId}
>
<ChevronDown className="w-4 h-4" />
</IconButton>
</TooltipTrigger>
{nextSpanId ? (
<TooltipContent>
Navigate down
<Kbd>J</Kbd>
</TooltipContent>
) : null}
</Tooltip>
<div aria-hidden className="w-px h-4 bg-gray-alpha-400 mx-1" />
<IconButton
aria-label="Close span details"
Expand Down
18 changes: 7 additions & 11 deletions packages/web-shared/src/components/ui/icon-button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { cva, type VariantProps } from 'class-variance-authority';
import type * as React from 'react';
import * as React from 'react';
import { cn } from '../../lib/utils';

const iconButtonVariants = cva(
Expand Down Expand Up @@ -38,20 +38,16 @@ export type IconButtonProps = Omit<
type?: 'button' | 'submit' | 'reset';
};

export function IconButton({
className,
variant,
size,
type = 'button',
...props
}: IconButtonProps) {
return (
export const IconButton = React.forwardRef<HTMLButtonElement, IconButtonProps>(
({ className, variant, size, type = 'button', ...props }, ref) => (
<button
ref={ref}
type={type}
className={cn(iconButtonVariants({ variant, size, className }))}
{...props}
/>
);
}
)
);
IconButton.displayName = 'IconButton';

export { iconButtonVariants };
21 changes: 21 additions & 0 deletions packages/web-shared/src/components/ui/kbd.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { ReactNode } from 'react';
import { cn } from '../../lib/utils';

export function Kbd({
children,
className,
}: {
children: ReactNode;
className?: string;
}): ReactNode {
return (
<kbd
className={cn(
'inline-flex h-5 min-w-5 items-center justify-center rounded bg-background-100/20 px-1 text-center font-sans text-xs leading-none',
className
)}
>
{children}
</kbd>
);
}
47 changes: 47 additions & 0 deletions packages/web-shared/src/components/ui/tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use client';

import * as TooltipPrimitive from '@radix-ui/react-tooltip';
import * as React from 'react';
import { cn } from '../../lib/utils';

const TooltipProvider = TooltipPrimitive.Provider;

const Tooltip = TooltipPrimitive.Root;

const TooltipTrigger = TooltipPrimitive.Trigger;

const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> & {
showArrow?: boolean;
}
>(
(
{ className, sideOffset = 8, showArrow = true, children, ...props },
ref
) => (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-[99999] flex items-center gap-1 rounded-[10px] bg-gray-1000 px-2 py-1.5 !text-label-13 text-background-100 shadow-md select-none',
className
)}
{...props}
>
{children}
{showArrow ? (
<TooltipPrimitive.Arrow
width={11}
height={5}
className="fill-gray-1000"
/>
) : null}
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
)
);
TooltipContent.displayName = TooltipPrimitive.Content.displayName;

export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
Loading