@renseiai/agentfactory-dashboard 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +259 -0
  3. package/components.json +16 -0
  4. package/package.json +78 -0
  5. package/src/components/fleet/agent-card.tsx +97 -0
  6. package/src/components/fleet/fleet-overview.tsx +139 -0
  7. package/src/components/fleet/provider-icon.tsx +61 -0
  8. package/src/components/fleet/stat-card.tsx +77 -0
  9. package/src/components/fleet/status-dot.tsx +35 -0
  10. package/src/components/layout/bottom-bar.tsx +58 -0
  11. package/src/components/layout/shell.tsx +65 -0
  12. package/src/components/layout/sidebar.tsx +97 -0
  13. package/src/components/layout/top-bar.tsx +63 -0
  14. package/src/components/pipeline/pipeline-card.tsx +65 -0
  15. package/src/components/pipeline/pipeline-column.tsx +44 -0
  16. package/src/components/pipeline/pipeline-view.tsx +85 -0
  17. package/src/components/sessions/session-detail.tsx +153 -0
  18. package/src/components/sessions/session-list.tsx +125 -0
  19. package/src/components/sessions/session-timeline.tsx +76 -0
  20. package/src/components/sessions/token-chart.tsx +51 -0
  21. package/src/components/settings/settings-view.tsx +175 -0
  22. package/src/components/shared/empty-state.tsx +34 -0
  23. package/src/components/shared/logo.tsx +37 -0
  24. package/src/components/ui/badge.tsx +33 -0
  25. package/src/components/ui/button.tsx +54 -0
  26. package/src/components/ui/card.tsx +57 -0
  27. package/src/components/ui/dropdown-menu.tsx +77 -0
  28. package/src/components/ui/scroll-area.tsx +45 -0
  29. package/src/components/ui/separator.tsx +25 -0
  30. package/src/components/ui/sheet.tsx +88 -0
  31. package/src/components/ui/skeleton.tsx +15 -0
  32. package/src/components/ui/tabs.tsx +54 -0
  33. package/src/components/ui/tooltip.tsx +29 -0
  34. package/src/hooks/use-sessions.ts +13 -0
  35. package/src/hooks/use-stats.ts +13 -0
  36. package/src/hooks/use-workers.ts +17 -0
  37. package/src/index.ts +82 -0
  38. package/src/lib/format.ts +36 -0
  39. package/src/lib/status-config.ts +72 -0
  40. package/src/lib/utils.ts +6 -0
  41. package/src/lib/work-type-config.ts +116 -0
  42. package/src/pages/dashboard-page.tsx +11 -0
  43. package/src/pages/pipeline-page.tsx +11 -0
  44. package/src/pages/session-page.tsx +29 -0
  45. package/src/pages/settings-page.tsx +7 -0
  46. package/src/styles/globals.css +218 -0
  47. package/src/types/api.ts +48 -0
  48. package/tailwind.config.ts +139 -0
@@ -0,0 +1,33 @@
1
+ import * as React from 'react'
2
+ import { cva, type VariantProps } from 'class-variance-authority'
3
+ import { cn } from '../../lib/utils'
4
+
5
+ const badgeVariants = cva(
6
+ 'inline-flex items-center rounded-full border px-2 py-0.5 text-2xs font-medium font-body transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: 'border-transparent bg-primary text-primary-foreground',
11
+ secondary: 'border-af-surface-border/40 bg-af-surface/40 text-af-text-secondary',
12
+ destructive: 'border-transparent bg-destructive text-destructive-foreground',
13
+ outline: 'text-foreground',
14
+ success: 'border-af-status-success/20 bg-af-status-success/10 text-af-status-success',
15
+ warning: 'border-af-status-warning/20 bg-af-status-warning/10 text-af-status-warning',
16
+ error: 'border-af-status-error/20 bg-af-status-error/10 text-af-status-error',
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ variant: 'default',
21
+ },
22
+ }
23
+ )
24
+
25
+ export interface BadgeProps
26
+ extends React.HTMLAttributes<HTMLDivElement>,
27
+ VariantProps<typeof badgeVariants> {}
28
+
29
+ function Badge({ className, variant, ...props }: BadgeProps) {
30
+ return <div className={cn(badgeVariants({ variant }), className)} {...props} />
31
+ }
32
+
33
+ export { Badge, badgeVariants }
@@ -0,0 +1,54 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Slot } from '@radix-ui/react-slot'
5
+ import { cva, type VariantProps } from 'class-variance-authority'
6
+ import { cn } from '../../lib/utils'
7
+
8
+ const buttonVariants = cva(
9
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium font-body ring-offset-background transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
10
+ {
11
+ variants: {
12
+ variant: {
13
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90 glow-soft',
14
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
15
+ outline: 'border border-af-surface-border/50 bg-transparent hover:bg-af-surface/40 hover:text-accent-foreground',
16
+ secondary: 'bg-af-surface/60 text-secondary-foreground hover:bg-af-surface/80',
17
+ ghost: 'hover:bg-af-surface/40 hover:text-accent-foreground',
18
+ link: 'text-primary underline-offset-4 hover:underline',
19
+ },
20
+ size: {
21
+ default: 'h-9 px-4 py-2',
22
+ sm: 'h-8 rounded-lg px-3',
23
+ lg: 'h-10 rounded-lg px-8',
24
+ icon: 'h-9 w-9',
25
+ },
26
+ },
27
+ defaultVariants: {
28
+ variant: 'default',
29
+ size: 'default',
30
+ },
31
+ }
32
+ )
33
+
34
+ export interface ButtonProps
35
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
36
+ VariantProps<typeof buttonVariants> {
37
+ asChild?: boolean
38
+ }
39
+
40
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
41
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
42
+ const Comp = asChild ? Slot : 'button'
43
+ return (
44
+ <Comp
45
+ className={cn(buttonVariants({ variant, size, className }))}
46
+ ref={ref}
47
+ {...props}
48
+ />
49
+ )
50
+ }
51
+ )
52
+ Button.displayName = 'Button'
53
+
54
+ export { Button, buttonVariants }
@@ -0,0 +1,57 @@
1
+ import * as React from 'react'
2
+ import { cn } from '../../lib/utils'
3
+
4
+ const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
5
+ ({ className, ...props }, ref) => (
6
+ <div
7
+ ref={ref}
8
+ className={cn(
9
+ 'rounded-xl border border-af-surface-border/40 bg-af-surface/40 text-card-foreground',
10
+ className
11
+ )}
12
+ {...props}
13
+ />
14
+ )
15
+ )
16
+ Card.displayName = 'Card'
17
+
18
+ const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
19
+ ({ className, ...props }, ref) => (
20
+ <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
21
+ )
22
+ )
23
+ CardHeader.displayName = 'CardHeader'
24
+
25
+ const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
26
+ ({ className, ...props }, ref) => (
27
+ <h3
28
+ ref={ref}
29
+ className={cn('font-display text-sm font-semibold leading-none tracking-tight', className)}
30
+ {...props}
31
+ />
32
+ )
33
+ )
34
+ CardTitle.displayName = 'CardTitle'
35
+
36
+ const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
37
+ ({ className, ...props }, ref) => (
38
+ <p ref={ref} className={cn('text-xs font-body text-muted-foreground', className)} {...props} />
39
+ )
40
+ )
41
+ CardDescription.displayName = 'CardDescription'
42
+
43
+ const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
44
+ ({ className, ...props }, ref) => (
45
+ <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
46
+ )
47
+ )
48
+ CardContent.displayName = 'CardContent'
49
+
50
+ const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
51
+ ({ className, ...props }, ref) => (
52
+ <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
53
+ )
54
+ )
55
+ CardFooter.displayName = 'CardFooter'
56
+
57
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
@@ -0,0 +1,77 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
5
+ import { cn } from '../../lib/utils'
6
+
7
+ const DropdownMenu = DropdownMenuPrimitive.Root
8
+ const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
9
+ const DropdownMenuGroup = DropdownMenuPrimitive.Group
10
+
11
+ const DropdownMenuContent = React.forwardRef<
12
+ React.ComponentRef<typeof DropdownMenuPrimitive.Content>,
13
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
14
+ >(({ className, sideOffset = 4, ...props }, ref) => (
15
+ <DropdownMenuPrimitive.Portal>
16
+ <DropdownMenuPrimitive.Content
17
+ ref={ref}
18
+ sideOffset={sideOffset}
19
+ className={cn(
20
+ 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
21
+ className
22
+ )}
23
+ {...props}
24
+ />
25
+ </DropdownMenuPrimitive.Portal>
26
+ ))
27
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
28
+
29
+ const DropdownMenuItem = React.forwardRef<
30
+ React.ComponentRef<typeof DropdownMenuPrimitive.Item>,
31
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { inset?: boolean }
32
+ >(({ className, inset, ...props }, ref) => (
33
+ <DropdownMenuPrimitive.Item
34
+ ref={ref}
35
+ className={cn(
36
+ 'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
37
+ inset && 'pl-8',
38
+ className
39
+ )}
40
+ {...props}
41
+ />
42
+ ))
43
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
44
+
45
+ const DropdownMenuSeparator = React.forwardRef<
46
+ React.ComponentRef<typeof DropdownMenuPrimitive.Separator>,
47
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
48
+ >(({ className, ...props }, ref) => (
49
+ <DropdownMenuPrimitive.Separator
50
+ ref={ref}
51
+ className={cn('-mx-1 my-1 h-px bg-muted', className)}
52
+ {...props}
53
+ />
54
+ ))
55
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
56
+
57
+ const DropdownMenuLabel = React.forwardRef<
58
+ React.ComponentRef<typeof DropdownMenuPrimitive.Label>,
59
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & { inset?: boolean }
60
+ >(({ className, inset, ...props }, ref) => (
61
+ <DropdownMenuPrimitive.Label
62
+ ref={ref}
63
+ className={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
64
+ {...props}
65
+ />
66
+ ))
67
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
68
+
69
+ export {
70
+ DropdownMenu,
71
+ DropdownMenuTrigger,
72
+ DropdownMenuContent,
73
+ DropdownMenuItem,
74
+ DropdownMenuSeparator,
75
+ DropdownMenuLabel,
76
+ DropdownMenuGroup,
77
+ }
@@ -0,0 +1,45 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
5
+ import { cn } from '../../lib/utils'
6
+
7
+ const ScrollArea = React.forwardRef<
8
+ React.ComponentRef<typeof ScrollAreaPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
10
+ >(({ className, children, ...props }, ref) => (
11
+ <ScrollAreaPrimitive.Root
12
+ ref={ref}
13
+ className={cn('relative overflow-hidden', className)}
14
+ {...props}
15
+ >
16
+ <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
17
+ {children}
18
+ </ScrollAreaPrimitive.Viewport>
19
+ <ScrollBar />
20
+ <ScrollAreaPrimitive.Corner />
21
+ </ScrollAreaPrimitive.Root>
22
+ ))
23
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
24
+
25
+ const ScrollBar = React.forwardRef<
26
+ React.ComponentRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
27
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
28
+ >(({ className, orientation = 'vertical', ...props }, ref) => (
29
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
30
+ ref={ref}
31
+ orientation={orientation}
32
+ className={cn(
33
+ 'flex touch-none select-none transition-colors',
34
+ orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]',
35
+ orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-[1px]',
36
+ className
37
+ )}
38
+ {...props}
39
+ >
40
+ <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
41
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
42
+ ))
43
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
44
+
45
+ export { ScrollArea, ScrollBar }
@@ -0,0 +1,25 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import * as SeparatorPrimitive from '@radix-ui/react-separator'
5
+ import { cn } from '../../lib/utils'
6
+
7
+ const Separator = React.forwardRef<
8
+ React.ComponentRef<typeof SeparatorPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
10
+ >(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => (
11
+ <SeparatorPrimitive.Root
12
+ ref={ref}
13
+ decorative={decorative}
14
+ orientation={orientation}
15
+ className={cn(
16
+ 'shrink-0 bg-border',
17
+ orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
18
+ className
19
+ )}
20
+ {...props}
21
+ />
22
+ ))
23
+ Separator.displayName = SeparatorPrimitive.Root.displayName
24
+
25
+ export { Separator }
@@ -0,0 +1,88 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import * as DialogPrimitive from '@radix-ui/react-dialog'
5
+ import { cva, type VariantProps } from 'class-variance-authority'
6
+ import { X } from 'lucide-react'
7
+ import { cn } from '../../lib/utils'
8
+
9
+ const Sheet = DialogPrimitive.Root
10
+ const SheetTrigger = DialogPrimitive.Trigger
11
+ const SheetClose = DialogPrimitive.Close
12
+ const SheetPortal = DialogPrimitive.Portal
13
+
14
+ const SheetOverlay = React.forwardRef<
15
+ React.ComponentRef<typeof DialogPrimitive.Overlay>,
16
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
17
+ >(({ className, ...props }, ref) => (
18
+ <DialogPrimitive.Overlay
19
+ className={cn(
20
+ 'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
21
+ className
22
+ )}
23
+ {...props}
24
+ ref={ref}
25
+ />
26
+ ))
27
+ SheetOverlay.displayName = DialogPrimitive.Overlay.displayName
28
+
29
+ const sheetVariants = cva(
30
+ 'fixed z-50 gap-4 bg-af-bg-secondary p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
31
+ {
32
+ variants: {
33
+ side: {
34
+ top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
35
+ bottom: 'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
36
+ left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
37
+ right: 'inset-y-0 right-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
38
+ },
39
+ },
40
+ defaultVariants: {
41
+ side: 'right',
42
+ },
43
+ }
44
+ )
45
+
46
+ interface SheetContentProps
47
+ extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>,
48
+ VariantProps<typeof sheetVariants> {}
49
+
50
+ const SheetContent = React.forwardRef<
51
+ React.ComponentRef<typeof DialogPrimitive.Content>,
52
+ SheetContentProps
53
+ >(({ side = 'left', className, children, ...props }, ref) => (
54
+ <SheetPortal>
55
+ <SheetOverlay />
56
+ <DialogPrimitive.Content
57
+ ref={ref}
58
+ className={cn(sheetVariants({ side }), className)}
59
+ {...props}
60
+ >
61
+ {children}
62
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
63
+ <X className="h-4 w-4" />
64
+ <span className="sr-only">Close</span>
65
+ </DialogPrimitive.Close>
66
+ </DialogPrimitive.Content>
67
+ </SheetPortal>
68
+ ))
69
+ SheetContent.displayName = DialogPrimitive.Content.displayName
70
+
71
+ const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
72
+ <div className={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...props} />
73
+ )
74
+ SheetHeader.displayName = 'SheetHeader'
75
+
76
+ const SheetTitle = React.forwardRef<
77
+ React.ComponentRef<typeof DialogPrimitive.Title>,
78
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
79
+ >(({ className, ...props }, ref) => (
80
+ <DialogPrimitive.Title
81
+ ref={ref}
82
+ className={cn('text-lg font-semibold text-foreground', className)}
83
+ {...props}
84
+ />
85
+ ))
86
+ SheetTitle.displayName = DialogPrimitive.Title.displayName
87
+
88
+ export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetTitle }
@@ -0,0 +1,15 @@
1
+ import { cn } from '../../lib/utils'
2
+
3
+ function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
4
+ return (
5
+ <div
6
+ className={cn(
7
+ 'animate-pulse rounded-lg bg-af-surface/60',
8
+ className
9
+ )}
10
+ {...props}
11
+ />
12
+ )
13
+ }
14
+
15
+ export { Skeleton }
@@ -0,0 +1,54 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import * as TabsPrimitive from '@radix-ui/react-tabs'
5
+ import { cn } from '../../lib/utils'
6
+
7
+ const Tabs = TabsPrimitive.Root
8
+
9
+ const TabsList = React.forwardRef<
10
+ React.ComponentRef<typeof TabsPrimitive.List>,
11
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
12
+ >(({ className, ...props }, ref) => (
13
+ <TabsPrimitive.List
14
+ ref={ref}
15
+ className={cn(
16
+ 'inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground',
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ ))
22
+ TabsList.displayName = TabsPrimitive.List.displayName
23
+
24
+ const TabsTrigger = React.forwardRef<
25
+ React.ComponentRef<typeof TabsPrimitive.Trigger>,
26
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
27
+ >(({ className, ...props }, ref) => (
28
+ <TabsPrimitive.Trigger
29
+ ref={ref}
30
+ className={cn(
31
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm',
32
+ className
33
+ )}
34
+ {...props}
35
+ />
36
+ ))
37
+ TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
38
+
39
+ const TabsContent = React.forwardRef<
40
+ React.ComponentRef<typeof TabsPrimitive.Content>,
41
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
42
+ >(({ className, ...props }, ref) => (
43
+ <TabsPrimitive.Content
44
+ ref={ref}
45
+ className={cn(
46
+ 'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
47
+ className
48
+ )}
49
+ {...props}
50
+ />
51
+ ))
52
+ TabsContent.displayName = TabsPrimitive.Content.displayName
53
+
54
+ export { Tabs, TabsList, TabsTrigger, TabsContent }
@@ -0,0 +1,29 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip'
5
+ import { cn } from '../../lib/utils'
6
+
7
+ const TooltipProvider = TooltipPrimitive.Provider
8
+
9
+ const Tooltip = TooltipPrimitive.Root
10
+
11
+ const TooltipTrigger = TooltipPrimitive.Trigger
12
+
13
+ const TooltipContent = React.forwardRef<
14
+ React.ComponentRef<typeof TooltipPrimitive.Content>,
15
+ React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
16
+ >(({ className, sideOffset = 4, ...props }, ref) => (
17
+ <TooltipPrimitive.Content
18
+ ref={ref}
19
+ sideOffset={sideOffset}
20
+ className={cn(
21
+ 'z-50 overflow-hidden rounded-md bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
22
+ className
23
+ )}
24
+ {...props}
25
+ />
26
+ ))
27
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName
28
+
29
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -0,0 +1,13 @@
1
+ 'use client'
2
+
3
+ import useSWR from 'swr'
4
+ import type { PublicSessionsListResponse } from '../types/api'
5
+
6
+ const fetcher = (url: string) => fetch(url).then((r) => r.json())
7
+
8
+ export function useSessions(refreshInterval = 5000) {
9
+ return useSWR<PublicSessionsListResponse>('/api/public/sessions', fetcher, {
10
+ refreshInterval,
11
+ dedupingInterval: 2000,
12
+ })
13
+ }
@@ -0,0 +1,13 @@
1
+ 'use client'
2
+
3
+ import useSWR from 'swr'
4
+ import type { PublicStatsResponse } from '../types/api'
5
+
6
+ const fetcher = (url: string) => fetch(url).then((r) => r.json())
7
+
8
+ export function useStats(refreshInterval = 5000) {
9
+ return useSWR<PublicStatsResponse>('/api/public/stats', fetcher, {
10
+ refreshInterval,
11
+ dedupingInterval: 2000,
12
+ })
13
+ }
@@ -0,0 +1,17 @@
1
+ 'use client'
2
+
3
+ import useSWR from 'swr'
4
+ import type { WorkersListResponse } from '../types/api'
5
+
6
+ const authedFetcher = async (url: string) => {
7
+ const res = await fetch(url)
8
+ if (res.status === 401) return null
9
+ return res.json()
10
+ }
11
+
12
+ export function useWorkers(refreshInterval = 10000) {
13
+ return useSWR<WorkersListResponse | null>('/api/workers', authedFetcher, {
14
+ refreshInterval,
15
+ dedupingInterval: 5000,
16
+ })
17
+ }
package/src/index.ts ADDED
@@ -0,0 +1,82 @@
1
+ // Layout
2
+ export { DashboardShell } from './components/layout/shell'
3
+ export { Sidebar } from './components/layout/sidebar'
4
+ export { TopBar } from './components/layout/top-bar'
5
+ export { BottomBar } from './components/layout/bottom-bar'
6
+
7
+ // Pages
8
+ export { DashboardPage } from './pages/dashboard-page'
9
+ export { PipelinePage } from './pages/pipeline-page'
10
+ export { SessionPage } from './pages/session-page'
11
+ export { SettingsPage } from './pages/settings-page'
12
+
13
+ // Fleet components
14
+ export { FleetOverview } from './components/fleet/fleet-overview'
15
+ export { AgentCard } from './components/fleet/agent-card'
16
+ export { StatCard } from './components/fleet/stat-card'
17
+ export { StatusDot } from './components/fleet/status-dot'
18
+ export { ProviderIcon } from './components/fleet/provider-icon'
19
+
20
+ // Pipeline components
21
+ export { PipelineView } from './components/pipeline/pipeline-view'
22
+ export { PipelineColumn } from './components/pipeline/pipeline-column'
23
+ export { PipelineCard } from './components/pipeline/pipeline-card'
24
+
25
+ // Session components
26
+ export { SessionList } from './components/sessions/session-list'
27
+ export { SessionDetail } from './components/sessions/session-detail'
28
+ export { SessionTimeline } from './components/sessions/session-timeline'
29
+ export { TokenChart } from './components/sessions/token-chart'
30
+
31
+ // Settings
32
+ export { SettingsView } from './components/settings/settings-view'
33
+
34
+ // Shared
35
+ export { Logo } from './components/shared/logo'
36
+ export { EmptyState } from './components/shared/empty-state'
37
+
38
+ // UI primitives
39
+ export { Button } from './components/ui/button'
40
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } from './components/ui/card'
41
+ export { Badge } from './components/ui/badge'
42
+ export { Skeleton } from './components/ui/skeleton'
43
+ export { Separator } from './components/ui/separator'
44
+ export { ScrollArea, ScrollBar } from './components/ui/scroll-area'
45
+ export { Tabs, TabsList, TabsTrigger, TabsContent } from './components/ui/tabs'
46
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './components/ui/tooltip'
47
+ export { Sheet, SheetTrigger, SheetClose, SheetContent, SheetHeader, SheetTitle } from './components/ui/sheet'
48
+ export {
49
+ DropdownMenu,
50
+ DropdownMenuTrigger,
51
+ DropdownMenuContent,
52
+ DropdownMenuItem,
53
+ DropdownMenuSeparator,
54
+ DropdownMenuLabel,
55
+ DropdownMenuGroup,
56
+ } from './components/ui/dropdown-menu'
57
+
58
+ // Hooks
59
+ export { useStats } from './hooks/use-stats'
60
+ export { useSessions } from './hooks/use-sessions'
61
+ export { useWorkers } from './hooks/use-workers'
62
+
63
+ // Utilities
64
+ export { cn } from './lib/utils'
65
+ export { formatDuration, formatCost, formatTokens, formatRelativeTime } from './lib/format'
66
+ export { getWorkTypeConfig } from './lib/work-type-config'
67
+ export { getStatusConfig } from './lib/status-config'
68
+
69
+ // Types
70
+ export type {
71
+ PublicStatsResponse,
72
+ PublicSessionResponse,
73
+ PublicSessionsListResponse,
74
+ SessionStatus,
75
+ WorkerResponse,
76
+ WorkersListResponse,
77
+ PipelineStatus,
78
+ } from './types/api'
79
+ export type { WorkTypeConfig } from './lib/work-type-config'
80
+ export type { StatusConfig } from './lib/status-config'
81
+ export type { TimelineEvent } from './components/sessions/session-timeline'
82
+ export type { NavItem } from './components/layout/sidebar'
@@ -0,0 +1,36 @@
1
+ export function formatDuration(seconds: number): string {
2
+ if (seconds < 60) return `${seconds}s`
3
+ if (seconds < 3600) {
4
+ const m = Math.floor(seconds / 60)
5
+ const s = seconds % 60
6
+ return s > 0 ? `${m}m ${s}s` : `${m}m`
7
+ }
8
+ const h = Math.floor(seconds / 3600)
9
+ const m = Math.floor((seconds % 3600) / 60)
10
+ return m > 0 ? `${h}h ${m}m` : `${h}h`
11
+ }
12
+
13
+ export function formatCost(usd: number | undefined | null): string {
14
+ if (usd == null || usd === 0) return '$0.00'
15
+ if (usd < 0.01) return `$${usd.toFixed(4)}`
16
+ return `$${usd.toFixed(2)}`
17
+ }
18
+
19
+ export function formatTokens(count: number | undefined | null): string {
20
+ if (count == null || count === 0) return '0'
21
+ if (count < 1_000) return count.toString()
22
+ if (count < 1_000_000) return `${(count / 1_000).toFixed(1)}k`
23
+ return `${(count / 1_000_000).toFixed(2)}M`
24
+ }
25
+
26
+ export function formatRelativeTime(isoString: string): string {
27
+ const diff = Date.now() - new Date(isoString).getTime()
28
+ const seconds = Math.floor(diff / 1000)
29
+ if (seconds < 60) return 'just now'
30
+ const minutes = Math.floor(seconds / 60)
31
+ if (minutes < 60) return `${minutes}m ago`
32
+ const hours = Math.floor(minutes / 60)
33
+ if (hours < 24) return `${hours}h ago`
34
+ const days = Math.floor(hours / 24)
35
+ return `${days}d ago`
36
+ }