@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.
- package/LICENSE +21 -0
- package/README.md +259 -0
- package/components.json +16 -0
- package/package.json +78 -0
- package/src/components/fleet/agent-card.tsx +97 -0
- package/src/components/fleet/fleet-overview.tsx +139 -0
- package/src/components/fleet/provider-icon.tsx +61 -0
- package/src/components/fleet/stat-card.tsx +77 -0
- package/src/components/fleet/status-dot.tsx +35 -0
- package/src/components/layout/bottom-bar.tsx +58 -0
- package/src/components/layout/shell.tsx +65 -0
- package/src/components/layout/sidebar.tsx +97 -0
- package/src/components/layout/top-bar.tsx +63 -0
- package/src/components/pipeline/pipeline-card.tsx +65 -0
- package/src/components/pipeline/pipeline-column.tsx +44 -0
- package/src/components/pipeline/pipeline-view.tsx +85 -0
- package/src/components/sessions/session-detail.tsx +153 -0
- package/src/components/sessions/session-list.tsx +125 -0
- package/src/components/sessions/session-timeline.tsx +76 -0
- package/src/components/sessions/token-chart.tsx +51 -0
- package/src/components/settings/settings-view.tsx +175 -0
- package/src/components/shared/empty-state.tsx +34 -0
- package/src/components/shared/logo.tsx +37 -0
- package/src/components/ui/badge.tsx +33 -0
- package/src/components/ui/button.tsx +54 -0
- package/src/components/ui/card.tsx +57 -0
- package/src/components/ui/dropdown-menu.tsx +77 -0
- package/src/components/ui/scroll-area.tsx +45 -0
- package/src/components/ui/separator.tsx +25 -0
- package/src/components/ui/sheet.tsx +88 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/tabs.tsx +54 -0
- package/src/components/ui/tooltip.tsx +29 -0
- package/src/hooks/use-sessions.ts +13 -0
- package/src/hooks/use-stats.ts +13 -0
- package/src/hooks/use-workers.ts +17 -0
- package/src/index.ts +82 -0
- package/src/lib/format.ts +36 -0
- package/src/lib/status-config.ts +72 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/work-type-config.ts +116 -0
- package/src/pages/dashboard-page.tsx +11 -0
- package/src/pages/pipeline-page.tsx +11 -0
- package/src/pages/session-page.tsx +29 -0
- package/src/pages/settings-page.tsx +7 -0
- package/src/styles/globals.css +218 -0
- package/src/types/api.ts +48 -0
- 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
|
+
}
|