File size: 4,676 Bytes
a414867 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | "use client";
import {
CheckCircle2,
Circle,
Crosshair,
Download,
FileInput,
GitBranchPlus,
Radio,
Save,
Square,
Upload
} from "lucide-react";
import { Button } from "@/components/ui/Button";
import { PlaybackControls } from "@/components/tree-visualizer/PlaybackControls";
import type { TreeNodeViewMode } from "@/components/tree-visualizer/viewMode";
import { cn } from "@/lib/utils";
interface TreeCommandBarProps {
hasTree: boolean;
isDirty: boolean;
isPlaying: boolean;
isSaving: boolean;
isValidating: boolean;
nodeViewMode: TreeNodeViewMode;
onCreate: () => void;
onImport: () => void;
onModelStream: () => void;
onSave: () => void;
onExportRaw: () => void;
onValidate: () => void;
onReplay: () => void;
onPause: () => void;
onStep: () => void;
onFitView: () => void;
onNodeViewModeChange: (mode: TreeNodeViewMode) => void;
}
export function TreeCommandBar({
hasTree,
isDirty,
isPlaying,
isSaving,
isValidating,
nodeViewMode,
onCreate,
onImport,
onModelStream,
onSave,
onExportRaw,
onValidate,
onReplay,
onPause,
onStep,
onFitView,
onNodeViewModeChange
}: TreeCommandBarProps) {
const modeOptions = [
{ mode: "full" as const, label: "Full", icon: Square },
{ mode: "atom" as const, label: "Atom", icon: Circle }
];
return (
<header className="absolute left-4 right-4 top-4 z-20 flex min-h-14 items-center justify-between gap-3 rounded-xl border border-paper-300 bg-white/82 px-3 py-2 shadow-panel backdrop-blur-xl">
<div className="flex min-w-0 items-center gap-2 overflow-x-auto scrollbar-thin">
<Button variant="primary" onClick={onCreate} title="Create a new tree from pasted trajectory text">
<GitBranchPlus className="h-4 w-4" />
Create Tree
</Button>
<Button onClick={onImport} title="Import raw trajectory text">
<Upload className="h-4 w-4" />
Import
</Button>
<Button onClick={onModelStream} title="Create a new tree by streaming model trajectory nodes">
<Radio className="h-4 w-4" />
Create tree by model
</Button>
<Button onClick={onSave} disabled={!hasTree || isSaving || !isDirty} title="Save current tree edits">
<Save className="h-4 w-4" />
{isSaving ? "Saving" : "Save"}
</Button>
<Button
className="min-w-[138px]"
onClick={onExportRaw}
disabled={!hasTree}
title="Export the full original pasted trajectory message"
>
<Download className="h-4 w-4" />
Export Raw
</Button>
<Button onClick={onValidate} disabled={!hasTree || isValidating} title="Validate current tree">
<CheckCircle2 className="h-4 w-4" />
{isValidating ? "Validating" : "Validate"}
</Button>
</div>
<div className="flex shrink-0 items-center gap-2">
<div
role="radiogroup"
aria-label="Node view mode"
className="inline-flex h-9 items-center rounded-lg border border-paper-300 bg-paper-50 p-1 shadow-sm"
>
{modeOptions.map(({ mode, label, icon: Icon }) => {
const active = nodeViewMode === mode;
return (
<button
key={mode}
type="button"
role="radio"
aria-checked={active}
onClick={() => onNodeViewModeChange(mode)}
title={`${label} node view`}
className={cn(
"inline-flex h-7 min-w-8 items-center justify-center gap-1.5 rounded-md px-2 text-xs font-semibold transition md:min-w-[68px]",
active ? "bg-white text-claude-700 shadow-sm" : "text-ink-500 hover:text-ink-900"
)}
>
<Icon className="h-3.5 w-3.5 shrink-0" />
<span className="hidden md:inline">{label}</span>
</button>
);
})}
</div>
<PlaybackControls
isPlaying={isPlaying}
disabled={!hasTree}
onReplay={onReplay}
onPause={onPause}
onStep={onStep}
/>
<Button onClick={onFitView} disabled={!hasTree} title="Fit the tree into view">
<Crosshair className="h-4 w-4" />
Fit View
</Button>
<div className="hidden items-center gap-2 rounded-lg border border-paper-300 bg-paper-50 px-3 py-2 text-xs text-ink-500 lg:flex">
<FileInput className="h-4 w-4 text-claude-500" />
{isDirty ? "unsaved edits" : "workspace synced"}
</div>
</div>
</header>
);
}
|