diff --git a/src/main/index.ts b/src/main/index.ts index 0ff6540..475ea8e 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -47,6 +47,7 @@ function createWindow(): void { } app.whenReady().then(async () => { + app.setName("Agent Trace"); const userDataPath = app.getPath("userData"); const updateService = createUpdateService({ currentVersion: app.getVersion(), diff --git a/src/main/ipc/register-ipc.ts b/src/main/ipc/register-ipc.ts index 57edf78..bbd8faa 100644 --- a/src/main/ipc/register-ipc.ts +++ b/src/main/ipc/register-ipc.ts @@ -87,6 +87,12 @@ export function registerIpcHandlers(deps: IpcDependencies): () => void { } await deps.proxyManager.startProfile(profileId); + + const profiles = deps.profileStore.getProfiles(); + const updated = profiles.map((p) => p.id === profileId ? { ...p, autoStart: true } : p); + deps.profileStore.saveProfiles(updated); + + broadcast(deps.getMainWindow, IPC.PROFILES_CHANGED, { profiles: updated }); broadcast(deps.getMainWindow, IPC.PROFILE_STATUS_CHANGED, { statuses: deps.proxyManager.getStatuses(), }); @@ -98,6 +104,12 @@ export function registerIpcHandlers(deps: IpcDependencies): () => void { } await deps.proxyManager.stopProfile(profileId); + + const profiles = deps.profileStore.getProfiles(); + const updated = profiles.map((p) => p.id === profileId ? { ...p, autoStart: false } : p); + deps.profileStore.saveProfiles(updated); + + broadcast(deps.getMainWindow, IPC.PROFILES_CHANGED, { profiles: updated }); broadcast(deps.getMainWindow, IPC.PROFILE_STATUS_CHANGED, { statuses: deps.proxyManager.getStatuses(), }); diff --git a/src/preload/index.ts b/src/preload/index.ts index efce7ee..d4f4c2d 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -4,6 +4,7 @@ import type { ConnectionProfile, ExchangeDetailVM, ProfileStatusChangedEvent, + ProfilesChangedEvent, SessionListFilter, SessionListItemVM, SessionTraceVM, @@ -94,6 +95,17 @@ export const electronAPI: ElectronAPI = { ipcRenderer.removeListener(IPC.PROFILE_STATUS_CHANGED, handler); }, + onProfilesChanged: ( + cb: (payload: ProfilesChangedEvent) => void, + ): (() => void) => { + const handler = ( + _e: Electron.IpcRendererEvent, + payload: ProfilesChangedEvent, + ) => cb(payload); + ipcRenderer.on(IPC.PROFILES_CHANGED, handler); + return () => ipcRenderer.removeListener(IPC.PROFILES_CHANGED, handler); + }, + onUpdateStateChanged: (cb: (state: UpdateState) => void): (() => void) => { const handler = (_e: Electron.IpcRendererEvent, state: UpdateState) => cb(state); diff --git a/src/renderer/src/components/context-chip.tsx b/src/renderer/src/components/context-chip.tsx index 8d82f0b..a9f3f81 100644 --- a/src/renderer/src/components/context-chip.tsx +++ b/src/renderer/src/components/context-chip.tsx @@ -93,7 +93,7 @@ export function ContextChip({ return (
setExpanded(!expanded)} >
diff --git a/src/renderer/src/components/conversation-view.tsx b/src/renderer/src/components/conversation-view.tsx index 6c85754..de5a63b 100644 --- a/src/renderer/src/components/conversation-view.tsx +++ b/src/renderer/src/components/conversation-view.tsx @@ -4,6 +4,7 @@ import { ArrowUp, ArrowDown } from "lucide-react"; import { cn } from "../lib/utils"; import type { SessionTimeline } from "../../../shared/contracts"; import { useTraceStore } from "../stores/trace-store"; +import { useSessionStore } from "../stores/session-store"; const SCROLL_THRESHOLD = 120; @@ -15,6 +16,7 @@ interface ConversationViewProps { export function ConversationView({ timeline, rawMode }: ConversationViewProps) { const storeTrace = useTraceStore((state) => state.trace); const storeRawMode = useTraceStore((state) => state.rawMode); + const selectedSessionId = useSessionStore((s) => s.selectedSessionId); const activeTimeline = timeline ?? storeTrace?.timeline ?? { messages: [] }; const activeRawMode = rawMode ?? storeRawMode; const messages = activeTimeline.messages; @@ -24,16 +26,68 @@ export function ConversationView({ timeline, rawMode }: ConversationViewProps) { const [showBottom, setShowBottom] = useState(false); const [hasNew, setHasNew] = useState(false); const prevCountRef = useRef(messages.length); + const scrollCache = useRef>(new Map()); + const prevSessionRef = useRef(null); + const scrollListenerAttached = useRef(false); const updateButtons = useCallback(() => { const el = viewportRef.current; if (!el) return; const { scrollTop, scrollHeight, clientHeight } = el; - setShowTop(scrollTop > SCROLL_THRESHOLD); + const isScrollable = scrollHeight > clientHeight + 1; + setShowTop(isScrollable && scrollTop > SCROLL_THRESHOLD); const distanceFromBottom = scrollHeight - scrollTop - clientHeight; - setShowBottom(distanceFromBottom > SCROLL_THRESHOLD); + setShowBottom(isScrollable && distanceFromBottom > SCROLL_THRESHOLD); }, []); + // Ref callback to setup scroll listener once + const setViewportRef = useCallback((el: HTMLDivElement | null) => { + // Cleanup old listener if ref changes + if (viewportRef.current && scrollListenerAttached.current) { + viewportRef.current.removeEventListener("scroll", handleScroll); + scrollListenerAttached.current = false; + } + + viewportRef.current = el; + + if (el && !scrollListenerAttached.current) { + el.addEventListener("scroll", handleScroll, { passive: true }); + scrollListenerAttached.current = true; + requestAnimationFrame(updateButtons); + } + }, [updateButtons]); + + const handleScroll = useCallback(() => { + updateButtons(); + const el = viewportRef.current; + if (!el) return; + const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight; + if (distanceFromBottom <= SCROLL_THRESHOLD) { + setHasNew(false); + } + }, [updateButtons]); + + // Save/restore scroll position on session switch + useEffect(() => { + const el = viewportRef.current; + if (prevSessionRef.current && el) { + scrollCache.current.set(prevSessionRef.current, el.scrollTop); + } + if (selectedSessionId && el) { + const saved = scrollCache.current.get(selectedSessionId); + requestAnimationFrame(() => { + el.scrollTo({ top: saved ?? 0 }); + updateButtons(); + }); + } + prevSessionRef.current = selectedSessionId ?? null; + }, [selectedSessionId, updateButtons]); + + // Recalculate buttons when content changes + useEffect(() => { + requestAnimationFrame(updateButtons); + }, [messages.length, updateButtons]); + // Detect new messages while not at bottom useEffect(() => { if (messages.length > prevCountRef.current) { @@ -48,41 +102,28 @@ export function ConversationView({ timeline, rawMode }: ConversationViewProps) { prevCountRef.current = messages.length; }, [messages.length]); - // Attach scroll listener to the viewport + // Watch for element size changes (visibility, content loading, etc.) useEffect(() => { const el = viewportRef.current; if (!el) return; - const onScroll = () => { + const resizeObserver = new ResizeObserver(() => { updateButtons(); - // Clear new indicator when scrolled to bottom - const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight; - if (distanceFromBottom <= SCROLL_THRESHOLD) { - setHasNew(false); - } - }; - el.addEventListener("scroll", onScroll, { passive: true }); - updateButtons(); - return () => el.removeEventListener("scroll", onScroll); - }, [updateButtons]); - - // Grab the viewport element from radix ScrollArea - const containerRef = useCallback((node: HTMLDivElement | null) => { - if (node) { - const viewport = node.querySelector("[data-slot='scroll-area-viewport']"); - viewportRef.current = viewport as HTMLDivElement | null; - updateButtons(); - } + }); + resizeObserver.observe(el); + return () => resizeObserver.disconnect(); }, [updateButtons]); const scrollToTop = () => { - viewportRef.current?.scrollTo({ top: 0, behavior: "smooth" }); + viewportRef.current?.scrollTo({ top: 0 }); + requestAnimationFrame(updateButtons); }; const scrollToBottom = () => { const el = viewportRef.current; if (el) { - el.scrollTo({ top: el.scrollHeight, behavior: "smooth" }); + el.scrollTo({ top: el.scrollHeight }); setHasNew(false); + requestAnimationFrame(updateButtons); } }; @@ -95,8 +136,8 @@ export function ConversationView({ timeline, rawMode }: ConversationViewProps) { } return ( -
-
{ viewportRef.current = el; }}> +
+
{messages.map((msg, i) => (
- {/* Scroll to top */} - {showTop && ( - - )} - - {/* Scroll to latest */} - {showBottom && ( - + )} + {showBottom && ( + )} - +
)}
); diff --git a/src/renderer/src/components/inspector-panel.tsx b/src/renderer/src/components/inspector-panel.tsx index 2f23202..883a5de 100644 --- a/src/renderer/src/components/inspector-panel.tsx +++ b/src/renderer/src/components/inspector-panel.tsx @@ -67,7 +67,7 @@ export function InspectorPanel({ + + + +
+ +
+
+ +
+ +
+
+ + + + + { + await upsertProfile(profile); + setAddOpen(false); + toast.success("Profile created"); + }} + /> + + + + ); +} diff --git a/src/renderer/src/components/profile-switcher.tsx b/src/renderer/src/components/profile-switcher.tsx index 09aab1e..7ab91ea 100644 --- a/src/renderer/src/components/profile-switcher.tsx +++ b/src/renderer/src/components/profile-switcher.tsx @@ -136,7 +136,7 @@ function ProfileRow({ profile, port, isRunning, onToggle, onEdit, onDelete }: Pr return (
setRowHovered(true)} onMouseLeave={() => setRowHovered(false)} > @@ -147,35 +147,34 @@ function ProfileRow({ profile, port, isRunning, onToggle, onEdit, onDelete }: Pr )} /> - {profile.name} - - {/* Edit / Delete — visible on row hover */} - + + {profile.name} + + {rowHovered && ( + + + + )} - > - - :{port} -
- - -
- +
{/* Search & Filter */} -
+
s.clearHistory); const [showAddForm, setShowAddForm] = useState(false); + const [editingProfile, setEditingProfile] = useState(null); + const [deletingProfile, setDeletingProfile] = useState(null); useEffect(() => { if (!open || initialized) return; @@ -81,9 +87,11 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) { return ( - - {showAddForm ? "Add Profile" : "Settings"} - + {!showAddForm && ( + + Settings + + )} {showAddForm ? ( {profiles.map((profile) => { const isRunning = statuses[profile.id]?.isRunning ?? false; + const [rowHovered, setRowHovered] = useState(false); return (
setRowHovered(true)} + onMouseLeave={() => setRowHovered(false)} >
+ {rowHovered && ( +
+ + +
+ )} :{profile.localPort} @@ -169,7 +198,7 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
Updates
-
+
Version {updateState.currentVersion || "unknown"} @@ -208,7 +237,7 @@ export function SettingsDialog({ open, onOpenChange }: SettingsDialogProps) {
)} + + {/* Edit Profile Dialog */} + !open && setEditingProfile(null)}> + + {editingProfile && ( + { + await upsertProfile(updated); + setEditingProfile(null); + toast.success("Profile updated"); + }} + /> + )} + + + + {/* Delete Confirmation Dialog */} + !open && setDeletingProfile(null)}> + + + Delete {deletingProfile?.name}? + + This will stop the proxy and remove this profile permanently. + + + + + + + + + +
); diff --git a/src/renderer/src/components/status-bar.tsx b/src/renderer/src/components/status-bar.tsx index 12dc53e..954a516 100644 --- a/src/renderer/src/components/status-bar.tsx +++ b/src/renderer/src/components/status-bar.tsx @@ -1,5 +1,6 @@ import { Settings, Github } from "lucide-react"; import { Button } from "./ui/button"; +import { ProfileDropdown } from "./profile-dropdown"; interface StatusBarProps { onSettingsClick: () => void; @@ -7,10 +8,17 @@ interface StatusBarProps { export function StatusBar({ onSettingsClick }: StatusBarProps) { return ( -
- Agent Trace +
+ {/* Left spacer — balances the right buttons so the center content stays centered */} +
-
+ {/* Center: Profiles */} + + + {/* Right: Action Buttons */} +
+ ); +} + export function SystemView() { const instructions = useTraceStore( (state) => state.trace?.instructions ?? EMPTY_INSTRUCTIONS, @@ -24,13 +47,20 @@ export function SystemView() { ); } + const copyText = rawMode ? JSON.stringify(instructions, null, 2) : text; + if (rawMode) { return (
-
-            {JSON.stringify(instructions, null, 2)}
-          
+
+
+ +
+
+              {JSON.stringify(instructions, null, 2)}
+            
+
); @@ -39,14 +69,11 @@ export function SystemView() { return (
-
- {rawMode ? ( -
-              {text}
-            
- ) : ( - - )} +
+
+ +
+
diff --git a/src/renderer/src/components/tools-view.tsx b/src/renderer/src/components/tools-view.tsx index 1fa3289..52d7be8 100644 --- a/src/renderer/src/components/tools-view.tsx +++ b/src/renderer/src/components/tools-view.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; import { ScrollArea } from "./ui/scroll-area"; -import { ChevronRight } from "lucide-react"; +import { ChevronRight, Copy, Check } from "lucide-react"; import { cn } from "../lib/utils"; import { SchemaFieldRenderer } from "./ui/schema-field-renderer"; import type { InspectorSection, NormalizedTool } from "../../../shared/contracts"; @@ -16,13 +16,39 @@ function getToolsFromInspector(): NormalizedTool[] { return section?.tools ?? []; } +function CopyButton({ text }: { text: string }) { + const [copied, setCopied] = useState(false); + + const handleCopy = (e: React.MouseEvent) => { + e.stopPropagation(); + navigator.clipboard.writeText(text); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }; + + return ( + + ); +} + function ToolItem({ tool, rawMode }: { tool: NormalizedTool; rawMode: boolean }) { const [expanded, setExpanded] = useState(false); const [descExpanded, setDescExpanded] = useState(false); + const copyText = rawMode ? JSON.stringify(tool, null, 2) : JSON.stringify(tool, null, 2); + if (rawMode) { return ( -
+
+
+ +
           {JSON.stringify(tool, null, 2)}
         
@@ -31,26 +57,42 @@ function ToolItem({ tool, rawMode }: { tool: NormalizedTool; rawMode: boolean }) } return ( -
+
setExpanded(true) : undefined} + >
setExpanded(!expanded)} + className={cn( + "flex items-center justify-between", + expanded && "sticky top-0 z-10 bg-card cursor-pointer -mx-4 -mt-4 px-4 pt-4 pb-2 border-b border-border/50 transition-colors hover:brightness-95 dark:hover:brightness-110 rounded-t-lg" + )} + onClick={expanded ? () => setExpanded(false) : undefined} > - + + {tool.name} + {!expanded && tool.description && ( + + {tool.description} + )} - /> - {tool.name} - {!expanded && tool.description && ( - - {tool.description} - - )} +
+
e.stopPropagation()}> + +
{expanded && ( -
+
{tool.description && (
svg]:pointer-events-none [&>svg]:size-3!", + "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-md border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", { variants: { variant: { diff --git a/src/renderer/src/components/ui/button.tsx b/src/renderer/src/components/ui/button.tsx index 04f0be5..5fb023f 100644 --- a/src/renderer/src/components/ui/button.tsx +++ b/src/renderer/src/components/ui/button.tsx @@ -5,7 +5,7 @@ import { Slot } from "radix-ui" import { cn } from "@/lib/utils" const buttonVariants = cva( - "group/button inline-flex shrink-0 items-center justify-center rounded-none border border-transparent bg-clip-padding text-xs font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + "group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-xs font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", { variants: { variant: { @@ -23,12 +23,12 @@ const buttonVariants = cva( size: { default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", - xs: "h-6 gap-1 rounded-none px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", - sm: "h-7 gap-1 rounded-none px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", + xs: "h-6 gap-1 rounded-sm px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", + sm: "h-7 gap-1 rounded-sm px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3", icon: "size-8", - "icon-xs": "size-6 rounded-none [&_svg:not([class*='size-'])]:size-3", - "icon-sm": "size-7 rounded-none", + "icon-xs": "size-6 rounded-sm [&_svg:not([class*='size-'])]:size-3", + "icon-sm": "size-7 rounded-sm", "icon-lg": "size-9", }, }, diff --git a/src/renderer/src/components/ui/command.tsx b/src/renderer/src/components/ui/command.tsx index 06e976e..4f831e9 100644 --- a/src/renderer/src/components/ui/command.tsx +++ b/src/renderer/src/components/ui/command.tsx @@ -21,7 +21,7 @@ function Command({ ) { + return +} + +function DropdownMenuTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuContent({ + className, + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, +} diff --git a/src/renderer/src/components/ui/input.tsx b/src/renderer/src/components/ui/input.tsx index 4a5ea1a..10c5448 100644 --- a/src/renderer/src/components/ui/input.tsx +++ b/src/renderer/src/components/ui/input.tsx @@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) { type={type} data-slot="input" className={cn( - "h-8 w-full min-w-0 rounded-none border border-input bg-transparent px-2.5 py-1 text-xs transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-xs file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 md:text-xs dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", + "h-8 w-full min-w-0 rounded-md border border-input bg-transparent px-2.5 py-1 text-xs transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-xs file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-1 aria-invalid:ring-destructive/20 md:text-xs dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className )} {...props} diff --git a/src/renderer/src/components/ui/select.tsx b/src/renderer/src/components/ui/select.tsx new file mode 100644 index 0000000..ac15477 --- /dev/null +++ b/src/renderer/src/components/ui/select.tsx @@ -0,0 +1,99 @@ +"use client" + +import * as React from "react" +import { Select as SelectPrimitive } from "radix-ui" +import { ChevronDownIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Select({ + ...props +}: React.ComponentProps) { + return +} + +function SelectTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + {children} + + + + + ) +} + +function SelectContent({ + className, + children, + position = "popper", + ...props +}: React.ComponentProps) { + return ( + + + + {children} + + + + ) +} + +function SelectItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + {children} + + ) +} + +function SelectValue({ + ...props +}: React.ComponentProps) { + return +} + +export { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} diff --git a/src/renderer/src/components/ui/tabs.tsx b/src/renderer/src/components/ui/tabs.tsx index ea35432..a8fd7cc 100644 --- a/src/renderer/src/components/ui/tabs.tsx +++ b/src/renderer/src/components/ui/tabs.tsx @@ -23,7 +23,7 @@ function Tabs({ } const tabsListVariants = cva( - "group/tabs-list inline-flex w-fit items-center justify-center rounded-none p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none", + "group/tabs-list inline-flex w-fit items-center justify-center rounded-md p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none", { variants: { variant: { @@ -61,7 +61,7 @@ function TabsTrigger({ {children} - + ) diff --git a/src/renderer/src/features/profiles/components/data-flow-diagram.tsx b/src/renderer/src/features/profiles/components/data-flow-diagram.tsx index 5383b70..937e508 100644 --- a/src/renderer/src/features/profiles/components/data-flow-diagram.tsx +++ b/src/renderer/src/features/profiles/components/data-flow-diagram.tsx @@ -13,7 +13,7 @@ export function DataFlowDiagram({ clientName, localPort, upstreamUrl }: DataFlow return (
{/* Client node */} -
+
Your Client
@@ -28,7 +28,7 @@ export function DataFlowDiagram({ clientName, localPort, upstreamUrl }: DataFlow
{/* Agent Trace node (highlighted) */} -
+
Agent Trace
@@ -43,7 +43,7 @@ export function DataFlowDiagram({ clientName, localPort, upstreamUrl }: DataFlow
{/* Upstream node */} -
+
Upstream API
diff --git a/src/renderer/src/features/profiles/components/provider-badge.tsx b/src/renderer/src/features/profiles/components/provider-badge.tsx index b2409d7..cdebe24 100644 --- a/src/renderer/src/features/profiles/components/provider-badge.tsx +++ b/src/renderer/src/features/profiles/components/provider-badge.tsx @@ -16,7 +16,7 @@ export function ProviderBadge({ providerId, className }: ProviderBadgeProps) { return ( +
{label && (
{label} @@ -27,7 +27,7 @@ export function ShellBlock({ label, code }: ShellBlockProps) { {code}