Skip to content

Commit 00b527c

Browse files
Misc UI improvements and polish (#669)
1 parent 211404d commit 00b527c

File tree

8 files changed

+176
-291
lines changed

8 files changed

+176
-291
lines changed

src/ui/src/components/inline-progress.tsx

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,35 +27,23 @@ export interface InlineProgressProps {
2727
total: number;
2828
/** Compact mode: hide progress bar, show only text */
2929
compact?: boolean;
30-
/** Width of the progress bar */
31-
barWidth?: string;
3230
/** Additional content to render after the label (e.g., icons) */
3331
children?: React.ReactNode;
3432
/** Additional className for the container */
3533
className?: string;
3634
}
3735

38-
// =============================================================================
39-
// Component
40-
// =============================================================================
41-
4236
/**
43-
* InlineProgress - Horizontal progress display for table cells.
44-
*
45-
* Renders a progress bar with a "{used}/{total}" fraction label.
46-
* Suitable for table cells showing utilization.
37+
* Horizontal progress display for table cells.
4738
*
4839
* @example
49-
* ```tsx
5040
* <InlineProgress used={6} total={8} />
5141
* <InlineProgress used={6} total={8} compact />
52-
* ```
5342
*/
5443
export const InlineProgress = memo(function InlineProgress({
5544
used,
5645
total,
5746
compact = false,
58-
barWidth = "w-16",
5947
children,
6048
className,
6149
}: InlineProgressProps) {
@@ -70,19 +58,19 @@ export const InlineProgress = memo(function InlineProgress({
7058
);
7159
}
7260

73-
const maxBarWidth = barWidth.replace(/^w-/, "max-w-");
74-
7561
return (
7662
<div className={cn("flex min-w-0 flex-1 items-center gap-2", className)}>
77-
<div className={cn(maxBarWidth, "min-w-6 shrink grow basis-0")}>
63+
<div className="min-w-4 flex-1">
7864
<ProgressBar
7965
value={used}
8066
max={total}
8167
size="md"
8268
thresholdColors
8369
/>
8470
</div>
85-
<span className="text-xs whitespace-nowrap text-zinc-600 tabular-nums dark:text-zinc-400">{label}</span>
71+
<span className="min-w-[4.5rem] shrink-0 text-xs whitespace-nowrap text-zinc-600 tabular-nums dark:text-zinc-400">
72+
{label}
73+
</span>
8674
{children}
8775
</div>
8876
);

src/ui/src/components/log-viewer/components/footer.tsx

Lines changed: 0 additions & 161 deletions
This file was deleted.

src/ui/src/components/log-viewer/components/log-viewer.tsx

Lines changed: 110 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
import { memo, useMemo, useRef, useCallback, useEffect, useState, startTransition, useDeferredValue } from "react";
2020
import { useShallow } from "zustand/react/shallow";
21-
import { User, Cpu, ZoomIn, ZoomOut } from "lucide-react";
21+
import { User, Cpu, ZoomIn, ZoomOut, Download, ExternalLink, Tag, WrapText } from "lucide-react";
2222
import { cn } from "@/lib/utils";
2323
import { useFormattedHotkey, useModKey } from "@/hooks/use-hotkey-label";
2424
import type { LogEntry, HistogramBucket } from "@/lib/api/log-adapter/types";
@@ -36,7 +36,7 @@ import {
3636
type TimelineContainerHandle,
3737
} from "@/components/log-viewer/components/timeline/components/timeline-container";
3838
import { LogList, type LogListHandle } from "@/components/log-viewer/components/log-list";
39-
import { Footer } from "@/components/log-viewer/components/footer";
39+
import { ScrollPinControl } from "@/components/log-viewer/components/scroll-pin-control";
4040
import { LogViewerSkeleton } from "@/components/log-viewer/components/log-viewer-skeleton";
4141
import { useLogViewerStore } from "@/components/log-viewer/store/log-viewer-store";
4242
import { HISTOGRAM_BUCKET_JUMP_WINDOW_MS } from "@/components/log-viewer/lib/constants";
@@ -476,20 +476,117 @@ function LogViewerInner({ data, filter, timeline, className, showTimeline = true
476476
/>
477477
)}
478478

479-
{/* Section 1: Filter bar — excluded from focus redirect so dropdown items work */}
479+
{/* Section 1: Filter bar + Actions — excluded from focus redirect so dropdown items work */}
480480
<div
481-
className="shrink-0 border-b p-2"
481+
className="shrink-0 border-b px-3 py-2"
482482
data-no-focus-redirect
483483
>
484-
<FilterBar
485-
ref={filterBarRef}
486-
data={rawEntries}
487-
fields={filterFields}
488-
chips={filterChips}
489-
onChipsChange={handleFilterChipsChange}
490-
presets={LOG_FILTER_PRESETS}
491-
placeholder={`Search logs (${searchShortcut})...`}
492-
/>
484+
<div className="flex items-center gap-2">
485+
<div className="flex-1">
486+
<FilterBar
487+
ref={filterBarRef}
488+
data={rawEntries}
489+
fields={filterFields}
490+
chips={filterChips}
491+
onChipsChange={handleFilterChipsChange}
492+
presets={LOG_FILTER_PRESETS}
493+
placeholder={`Search logs (${searchShortcut})...`}
494+
/>
495+
</div>
496+
497+
{/* Action buttons */}
498+
<div className="flex shrink-0 items-center gap-1">
499+
{/* Scroll/Pin controls */}
500+
<ScrollPinControl
501+
isStreaming={isStreaming ?? false}
502+
isPinned={isPinnedToBottom}
503+
onScrollToBottom={handleJumpToBottom}
504+
onTogglePin={handleTogglePin}
505+
/>
506+
507+
{/* Show task toggle (hidden when scoped to a single task) */}
508+
{scope !== "task" && (
509+
<Tooltip>
510+
<TooltipTrigger asChild>
511+
<Button
512+
variant="ghost"
513+
size="icon-sm"
514+
onClick={toggleShowTask}
515+
className={
516+
showTask
517+
? "bg-foreground text-background hover:bg-foreground hover:text-background dark:hover:bg-foreground dark:hover:text-background"
518+
: ""
519+
}
520+
aria-label={`${showTask ? "Hide" : "Show"} task`}
521+
aria-pressed={showTask}
522+
>
523+
<Tag className="size-4" />
524+
</Button>
525+
</TooltipTrigger>
526+
<TooltipContent side="bottom">{showTask ? "Hide" : "Show"} task</TooltipContent>
527+
</Tooltip>
528+
)}
529+
530+
{/* Wrap lines toggle */}
531+
<Tooltip>
532+
<TooltipTrigger asChild>
533+
<Button
534+
variant="ghost"
535+
size="icon-sm"
536+
onClick={toggleWrapLines}
537+
className={
538+
wrapLines
539+
? "bg-foreground text-background hover:bg-foreground hover:text-background dark:hover:bg-foreground dark:hover:text-background"
540+
: ""
541+
}
542+
aria-label={`${wrapLines ? "Disable" : "Enable"} line wrap`}
543+
aria-pressed={wrapLines}
544+
>
545+
<WrapText className="size-4" />
546+
</Button>
547+
</TooltipTrigger>
548+
<TooltipContent side="bottom">{wrapLines ? "Disable" : "Enable"} line wrap</TooltipContent>
549+
</Tooltip>
550+
551+
{/* Download button */}
552+
<Tooltip>
553+
<TooltipTrigger asChild>
554+
<Button
555+
variant="ghost"
556+
size="icon-sm"
557+
onClick={handleDownload}
558+
aria-label="Download logs"
559+
>
560+
<Download className="size-4" />
561+
</Button>
562+
</TooltipTrigger>
563+
<TooltipContent side="bottom">Download logs</TooltipContent>
564+
</Tooltip>
565+
566+
{/* External link - opens raw logs in new tab */}
567+
{externalLogUrl && (
568+
<Tooltip>
569+
<TooltipTrigger asChild>
570+
<Button
571+
variant="ghost"
572+
size="icon-sm"
573+
asChild
574+
aria-label="Open raw logs in new tab"
575+
>
576+
<a
577+
href={externalLogUrl}
578+
target="_blank"
579+
rel="noopener noreferrer"
580+
>
581+
<ExternalLink className="size-4" />
582+
</a>
583+
</Button>
584+
</TooltipTrigger>
585+
<TooltipContent side="bottom">Open raw logs in new tab</TooltipContent>
586+
</Tooltip>
587+
)}
588+
</div>
589+
</div>
493590
</div>
494591

495592
{/* Section 2: Timeline Histogram — excluded from focus redirect so draggers work */}
@@ -584,22 +681,6 @@ function LogViewerInner({ data, filter, timeline, className, showTimeline = true
584681
hideTask={scope === "task"}
585682
/>
586683
</div>
587-
588-
{/* Section 4: Footer */}
589-
<div className="shrink-0">
590-
<Footer
591-
wrapLines={wrapLines}
592-
onToggleWrapLines={toggleWrapLines}
593-
showTask={scope === "task" ? false : showTask}
594-
onToggleShowTask={scope === "task" ? undefined : toggleShowTask}
595-
externalLogUrl={externalLogUrl}
596-
onDownload={handleDownload}
597-
isStreaming={isStreaming}
598-
isPinnedToBottom={isPinnedToBottom}
599-
onScrollToBottom={handleJumpToBottom}
600-
onTogglePinnedToBottom={handleTogglePin}
601-
/>
602-
</div>
603684
</div>
604685
);
605686
}

0 commit comments

Comments
 (0)