mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
improvement(cleanup): remove unused old ui components (#3589)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
|
||||
export function ChatLoadingState() {
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import AuthBackground from '@/app/(auth)/components/auth-background'
|
||||
|
||||
export function FormLoadingState() {
|
||||
|
||||
@@ -27,8 +27,8 @@ import {
|
||||
PopoverContent,
|
||||
PopoverItem,
|
||||
PopoverTrigger,
|
||||
Skeleton,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { VerifiedBadge } from '@/components/ui/verified-badge'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
+1
-1
@@ -23,9 +23,9 @@ import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Skeleton,
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import {
|
||||
getCanonicalScopesForProvider,
|
||||
|
||||
+1
-1
@@ -19,8 +19,8 @@ import {
|
||||
ModalTabsContent,
|
||||
ModalTabsList,
|
||||
ModalTabsTrigger,
|
||||
Skeleton,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { CONNECTOR_REGISTRY } from '@/connectors/registry'
|
||||
import type { ConnectorConfig } from '@/connectors/types'
|
||||
import type { ConnectorData } from '@/hooks/queries/kb/connectors'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Skeleton } from '@/components/emcn'
|
||||
import { formatLatency } from '@/app/workspace/[workspaceId]/logs/utils'
|
||||
import type { DashboardStatsResponse, WorkflowStats } from '@/hooks/queries/logs'
|
||||
import { useFilterStore } from '@/stores/logs/filters/store'
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Copy as CopyIcon, Search as SearchIcon } from '@/components/emcn/icons'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { BASE_EXECUTION_CHARGE } from '@/lib/billing/constants'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { formatDuration } from '@/lib/core/utils/formatting'
|
||||
@@ -396,7 +395,7 @@ export const LogDetails = memo(function LogDetails({
|
||||
</div>
|
||||
|
||||
{/* Content - Scrollable */}
|
||||
<ScrollArea className='mt-[20px] h-full w-full overflow-y-auto' ref={scrollAreaRef}>
|
||||
<div className='mt-[20px] h-full w-full overflow-y-auto' ref={scrollAreaRef}>
|
||||
<div className='flex flex-col gap-[10px] pb-[16px]'>
|
||||
{/* Timestamp & Workflow Row */}
|
||||
<div className='flex min-w-0 items-center gap-[16px] px-[1px]'>
|
||||
@@ -632,7 +631,7 @@ export const LogDetails = memo(function LogDetails({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
+1
-2
@@ -2,8 +2,7 @@
|
||||
|
||||
import { useMemo } from 'react'
|
||||
import { X } from 'lucide-react'
|
||||
import { Badge, Combobox, type ComboboxOption, Label } from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { Badge, Combobox, type ComboboxOption, Label, Skeleton } from '@/components/emcn'
|
||||
import { useWorkflows } from '@/hooks/queries/workflows'
|
||||
|
||||
interface WorkflowSelectorProps {
|
||||
|
||||
+1
-1
@@ -18,11 +18,11 @@ import {
|
||||
ModalTabsContent,
|
||||
ModalTabsList,
|
||||
ModalTabsTrigger,
|
||||
Skeleton,
|
||||
TagInput,
|
||||
type TagItem,
|
||||
} from '@/components/emcn'
|
||||
import { SlackIcon } from '@/components/icons'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { dollarsToCredits } from '@/lib/billing/credits/conversion'
|
||||
import { getTriggerOptions } from '@/lib/logs/get-trigger-options'
|
||||
import { quickValidateEmail } from '@/lib/messaging/email/validation'
|
||||
|
||||
@@ -11,10 +11,11 @@ import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Skeleton,
|
||||
Switch,
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Input, Skeleton } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { formatDate } from '@/lib/core/utils/formatting'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
|
||||
+2
-1
@@ -17,11 +17,12 @@ import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Skeleton,
|
||||
TagInput,
|
||||
type TagItem,
|
||||
} from '@/components/emcn'
|
||||
import { GmailIcon, OutlookIcon } from '@/components/icons'
|
||||
import { Input as BaseInput, Skeleton } from '@/components/ui'
|
||||
import { Input as BaseInput } from '@/components/ui'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { getSubscriptionStatus } from '@/lib/billing/client'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
+2
-1
@@ -17,11 +17,12 @@ import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Skeleton,
|
||||
Textarea,
|
||||
Tooltip,
|
||||
Trash,
|
||||
} from '@/components/emcn'
|
||||
import { Input, Skeleton } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import {
|
||||
clearPendingCredentialCreateRequest,
|
||||
|
||||
+2
-1
@@ -17,10 +17,11 @@ import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Skeleton,
|
||||
Textarea,
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton, Input as UiInput } from '@/components/ui'
|
||||
import { Input as UiInput } from '@/components/ui'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import {
|
||||
clearPendingCredentialCreateRequest,
|
||||
|
||||
+1
-1
@@ -14,10 +14,10 @@ import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Skeleton,
|
||||
Switch,
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { useSession, useSubscription } from '@/lib/auth/auth-client'
|
||||
import { USAGE_THRESHOLDS } from '@/lib/billing/client/consts'
|
||||
import { useSubscriptionUpgrade } from '@/lib/billing/client/upgrade'
|
||||
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
import { Badge, Button } from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Badge, Button, Skeleton } from '@/components/emcn'
|
||||
import { checkEnterprisePlan } from '@/lib/billing/subscriptions/utils'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
|
||||
+1
-2
@@ -2,8 +2,7 @@
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { TagItem } from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { Skeleton, type TagItem } from '@/components/emcn'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { getPlanTierCredits, getPlanTierDollars } from '@/lib/billing/plan-helpers'
|
||||
import { checkEnterprisePlan } from '@/lib/billing/subscriptions/utils'
|
||||
|
||||
+1
-2
@@ -4,9 +4,8 @@ import { useEffect, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { Camera, Globe, Linkedin, Mail } from 'lucide-react'
|
||||
import Image from 'next/image'
|
||||
import { Button, Combobox, Input, Textarea } from '@/components/emcn'
|
||||
import { Button, Combobox, Input, Skeleton, Textarea } from '@/components/emcn'
|
||||
import { AgentIcon, xIcon as XIcon } from '@/components/icons'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { quickValidateEmail } from '@/lib/messaging/email/validation'
|
||||
import type { CreatorProfileDetails } from '@/app/_types/creator-profile'
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@ import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Skeleton,
|
||||
SModalTabs,
|
||||
SModalTabsBody,
|
||||
SModalTabsContent,
|
||||
@@ -27,7 +28,7 @@ import {
|
||||
Textarea,
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Input, Skeleton } from '@/components/ui'
|
||||
import { Input } from '@/components/ui'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
import { useApiKeys } from '@/hooks/queries/api-keys'
|
||||
|
||||
+1
-1
@@ -12,11 +12,11 @@ import {
|
||||
Code,
|
||||
Input,
|
||||
Label,
|
||||
Skeleton,
|
||||
TagInput,
|
||||
Textarea,
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import type { AgentAuthentication, AgentCapabilities } from '@/lib/a2a/types'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { normalizeInputFormatValue } from '@/lib/workflows/input-format'
|
||||
|
||||
+1
-1
@@ -9,9 +9,9 @@ import {
|
||||
Code,
|
||||
Combobox,
|
||||
Label,
|
||||
Skeleton,
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { OutputSelect } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/chat/components/output-select/output-select'
|
||||
|
||||
interface WorkflowDeploymentInfo {
|
||||
|
||||
+2
-1
@@ -14,12 +14,13 @@ import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Skeleton,
|
||||
TagInput,
|
||||
type TagItem,
|
||||
Textarea,
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Alert, AlertDescription, Skeleton } from '@/components/ui'
|
||||
import { Alert, AlertDescription } from '@/components/ui'
|
||||
import { getEnv, isTruthy } from '@/lib/core/config/env'
|
||||
import { generatePassword } from '@/lib/core/security/encryption'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
+1
-1
@@ -8,12 +8,12 @@ import {
|
||||
ButtonGroupItem,
|
||||
Input,
|
||||
Label,
|
||||
Skeleton,
|
||||
TagInput,
|
||||
type TagItem,
|
||||
Textarea,
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { isDev } from '@/lib/core/config/feature-flags'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { getBaseUrl, getEmailDomain } from '@/lib/core/utils/urls'
|
||||
|
||||
+1
-1
@@ -9,9 +9,9 @@ import {
|
||||
PopoverContent,
|
||||
PopoverItem,
|
||||
PopoverTrigger,
|
||||
Skeleton,
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { formatDateTime } from '@/lib/core/utils/formatting'
|
||||
import type { WorkflowDeploymentVersionResponse } from '@/lib/workflows/persistence/utils'
|
||||
import { useUpdateDeploymentVersion } from '@/hooks/queries/deployments'
|
||||
|
||||
+1
-1
@@ -13,9 +13,9 @@ import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Skeleton,
|
||||
Tooltip,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import type { WorkflowDeploymentVersionResponse } from '@/lib/workflows/persistence/utils'
|
||||
import { Preview, PreviewWorkflow } from '@/app/workspace/[workspaceId]/w/components/preview'
|
||||
import { useDeploymentVersionState, useRevertToVersion } from '@/hooks/queries/workflows'
|
||||
|
||||
+1
-1
@@ -10,9 +10,9 @@ import {
|
||||
type ComboboxOption,
|
||||
Input,
|
||||
Label,
|
||||
Skeleton,
|
||||
Textarea,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { generateToolInputSchema, sanitizeToolName } from '@/lib/mcp/workflow-tool-schema'
|
||||
import { normalizeInputFormatValue } from '@/lib/workflows/input-format'
|
||||
import { isInputDefinitionTrigger } from '@/lib/workflows/triggers/input-definition-triggers'
|
||||
|
||||
+1
-1
@@ -12,10 +12,10 @@ import {
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Skeleton,
|
||||
TagInput,
|
||||
Textarea,
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { captureAndUploadOGImage, OG_IMAGE_HEIGHT, OG_IMAGE_WIDTH } from '@/lib/og'
|
||||
|
||||
+1
-2
@@ -3,8 +3,7 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { Badge, Skeleton } from '@/components/emcn'
|
||||
import { USAGE_PILL_COLORS, USAGE_THRESHOLDS } from '@/lib/billing/client/consts'
|
||||
import { useSubscriptionUpgrade } from '@/lib/billing/client/upgrade'
|
||||
import {
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { Loader2, RotateCw, X } from 'lucide-react'
|
||||
import { Badge, Button, Tooltip } from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { Badge, Button, Skeleton, Tooltip } from '@/components/emcn'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import type { PermissionType } from '@/lib/workspaces/permissions/utils'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import * as React from 'react'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('rounded-lg border bg-card text-card-foreground shadow-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
Card.displayName = 'Card'
|
||||
|
||||
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
|
||||
)
|
||||
)
|
||||
CardHeader.displayName = 'CardHeader'
|
||||
|
||||
const CardTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('font-semibold text-2xl leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
CardTitle.displayName = 'CardTitle'
|
||||
|
||||
const CardDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('text-muted-foreground text-sm', className)} {...props} />
|
||||
)
|
||||
)
|
||||
CardDescription.displayName = 'CardDescription'
|
||||
|
||||
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
||||
)
|
||||
)
|
||||
CardContent.displayName = 'CardContent'
|
||||
|
||||
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
|
||||
)
|
||||
)
|
||||
CardFooter.displayName = 'CardFooter'
|
||||
|
||||
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
||||
@@ -1,11 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import * as CollapsiblePrimitive from '@radix-ui/react-collapsible'
|
||||
|
||||
const Collapsible = CollapsiblePrimitive.Root
|
||||
|
||||
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
|
||||
|
||||
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
|
||||
|
||||
export { Collapsible, CollapsibleTrigger, CollapsibleContent }
|
||||
@@ -1,184 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
|
||||
import { Check, ChevronRight, Circle } from 'lucide-react'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root
|
||||
|
||||
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
||||
|
||||
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
||||
|
||||
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
||||
|
||||
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
||||
|
||||
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
||||
|
||||
const DropdownMenuSubTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({ className, inset, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRight className='ml-auto' />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
))
|
||||
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName
|
||||
|
||||
const DropdownMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'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 z-50 min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=closed]:animate-out data-[state=open]:animate-in',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName
|
||||
|
||||
const DropdownMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'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 z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] origin-[--radix-dropdown-menu-content-transform-origin] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
))
|
||||
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
|
||||
|
||||
const DropdownMenuItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center gap-2 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 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
|
||||
|
||||
const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
||||
>(({ className, children, checked, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Check className='h-4 w-4' />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
))
|
||||
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<Circle className='h-2 w-2 fill-current' />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
))
|
||||
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
|
||||
|
||||
const DropdownMenuLabel = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
}
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn('px-2 py-1.5 font-semibold text-sm', inset && 'pl-8', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
|
||||
|
||||
const DropdownMenuSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
|
||||
|
||||
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return <span className={cn('ml-auto text-xs tracking-widest opacity-60', className)} {...props} />
|
||||
}
|
||||
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuRadioGroup,
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
export { Alert, AlertDescription, AlertTitle } from './alert'
|
||||
export { Button, buttonVariants } from './button'
|
||||
export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from './card'
|
||||
export { Collapsible, CollapsibleContent, CollapsibleTrigger } from './collapsible'
|
||||
export {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
@@ -14,28 +12,10 @@ export {
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from './dialog'
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubContent,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from './dropdown-menu'
|
||||
export { Input } from './input'
|
||||
export { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from './input-otp'
|
||||
export { Label } from './label'
|
||||
export { Progress } from './progress'
|
||||
export { ScrollArea, ScrollBar } from './scroll-area'
|
||||
export { SearchHighlight } from './search-highlight'
|
||||
export {
|
||||
Select,
|
||||
@@ -49,7 +29,3 @@ export {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from './select'
|
||||
export { Separator } from './separator'
|
||||
export { Skeleton } from './skeleton'
|
||||
export { TagInput } from './tag-input'
|
||||
export { ToolCallCompletion, ToolCallExecution } from './tool-call'
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
const ScrollArea = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root> & {
|
||||
hideScrollbar?: boolean
|
||||
}
|
||||
>(({ className, children, hideScrollbar = false, ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn('relative overflow-hidden', className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className='h-full w-full rounded-[inherit]'>
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar hidden={hideScrollbar} />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
))
|
||||
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
||||
|
||||
const ScrollBar = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar> & {
|
||||
hidden?: boolean
|
||||
}
|
||||
>(({ className, orientation = 'vertical', hidden = false, ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
ref={ref}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
'flex touch-none select-none transition-colors',
|
||||
orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]',
|
||||
orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-[1px]',
|
||||
hidden && 'pointer-events-none w-0 border-0 p-0 opacity-0',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb
|
||||
className={cn('relative flex-1 rounded-full bg-border', hidden && 'hidden')}
|
||||
/>
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
))
|
||||
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
|
||||
|
||||
export { ScrollArea, ScrollBar }
|
||||
@@ -1,25 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import * as SeparatorPrimitive from '@radix-ui/react-separator'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
const Separator = React.forwardRef<
|
||||
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
||||
>(({ className, orientation = 'horizontal', decorative = true, ...props }, ref) => (
|
||||
<SeparatorPrimitive.Root
|
||||
ref={ref}
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
'shrink-0 bg-border',
|
||||
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
Separator.displayName = SeparatorPrimitive.Root.displayName
|
||||
|
||||
export { Separator }
|
||||
@@ -1,7 +0,0 @@
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return <div className={cn('animate-pulse rounded-md bg-muted', className)} {...props} />
|
||||
}
|
||||
|
||||
export { Skeleton }
|
||||
@@ -1,112 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { type KeyboardEvent, useRef, useState } from 'react'
|
||||
import { X } from 'lucide-react'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
|
||||
interface TagInputProps {
|
||||
value: string[]
|
||||
onChange: (tags: string[]) => void
|
||||
placeholder?: string
|
||||
maxTags?: number
|
||||
disabled?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function TagInput({
|
||||
value = [],
|
||||
onChange,
|
||||
placeholder = 'Type and press Enter',
|
||||
maxTags = 10,
|
||||
disabled = false,
|
||||
className,
|
||||
}: TagInputProps) {
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const addTag = (tag: string) => {
|
||||
const trimmedTag = tag.trim()
|
||||
if (trimmedTag && !value.includes(trimmedTag) && value.length < maxTags) {
|
||||
onChange([...value, trimmedTag])
|
||||
setInputValue('')
|
||||
}
|
||||
}
|
||||
|
||||
const removeTag = (tagToRemove: string) => {
|
||||
onChange(value.filter((tag) => tag !== tagToRemove))
|
||||
}
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter' && inputValue.trim()) {
|
||||
e.preventDefault()
|
||||
addTag(inputValue)
|
||||
} else if (e.key === 'Backspace' && !inputValue && value.length > 0) {
|
||||
removeTag(value[value.length - 1])
|
||||
}
|
||||
}
|
||||
|
||||
const handleBlur = () => {
|
||||
if (inputValue.trim()) {
|
||||
addTag(inputValue)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'scrollbar-hide flex max-h-32 min-h-9 flex-wrap items-center gap-x-[8px] gap-y-[4px] overflow-y-auto rounded-[4px] border border-[var(--border-1)] bg-[var(--surface-5)] px-[6px] py-[4px] focus-within:outline-none',
|
||||
disabled && 'cursor-not-allowed opacity-50',
|
||||
className
|
||||
)}
|
||||
onClick={() => !disabled && inputRef.current?.focus()}
|
||||
>
|
||||
{value.map((tag) => (
|
||||
<Tag key={tag} value={tag} onRemove={() => removeTag(tag)} disabled={disabled} />
|
||||
))}
|
||||
{!disabled && value.length < maxTags && (
|
||||
<Input
|
||||
ref={inputRef}
|
||||
type='text'
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={handleBlur}
|
||||
placeholder={value.length === 0 ? placeholder : ''}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
'h-6 min-w-[180px] flex-1 border-none bg-transparent p-0 font-medium font-sans text-sm placeholder:text-[var(--text-muted)] focus-visible:ring-0 focus-visible:ring-offset-0',
|
||||
value.length > 0 ? 'pl-[4px]' : 'pl-[4px]'
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface TagProps {
|
||||
value: string
|
||||
onRemove: () => void
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
function Tag({ value, onRemove, disabled }: TagProps) {
|
||||
return (
|
||||
<div className='flex w-auto items-center gap-[4px] rounded-[4px] border border-[var(--border-1)] bg-[var(--surface-4)] px-[6px] py-[2px] text-[12px] text-[var(--text-secondary)] hover:text-[var(--text-primary)]'>
|
||||
<span className='max-w-[200px] truncate'>{value}</span>
|
||||
{!disabled && (
|
||||
<button
|
||||
type='button'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onRemove()
|
||||
}}
|
||||
className='flex-shrink-0 text-[var(--text-tertiary)] transition-colors hover:text-[var(--text-primary)] focus:outline-none'
|
||||
aria-label={`Remove ${value}`}
|
||||
>
|
||||
<X className='h-[12px] w-[12px] translate-y-[0.2px]' />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { CheckCircle, ChevronDown, ChevronRight, Loader2, Settings, XCircle } from 'lucide-react'
|
||||
import { Badge } from '@/components/emcn'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { formatDuration } from '@/lib/core/utils/formatting'
|
||||
|
||||
interface ToolCallState {
|
||||
id: string
|
||||
name: string
|
||||
displayName?: string
|
||||
parameters?: Record<string, unknown>
|
||||
state:
|
||||
| 'detecting'
|
||||
| 'pending'
|
||||
| 'executing'
|
||||
| 'completed'
|
||||
| 'error'
|
||||
| 'rejected'
|
||||
| 'applied'
|
||||
| 'ready_for_review'
|
||||
| 'aborted'
|
||||
| 'skipped'
|
||||
| 'background'
|
||||
startTime?: number
|
||||
endTime?: number
|
||||
duration?: number
|
||||
result?: unknown
|
||||
error?: string
|
||||
progress?: string
|
||||
}
|
||||
|
||||
interface ToolCallGroup {
|
||||
id: string
|
||||
toolCalls: ToolCallState[]
|
||||
status: 'pending' | 'in_progress' | 'completed' | 'error'
|
||||
startTime?: number
|
||||
endTime?: number
|
||||
summary?: string
|
||||
}
|
||||
|
||||
interface ToolCallProps {
|
||||
toolCall: ToolCallState
|
||||
isCompact?: boolean
|
||||
}
|
||||
|
||||
interface ToolCallGroupProps {
|
||||
group: ToolCallGroup
|
||||
isCompact?: boolean
|
||||
}
|
||||
|
||||
interface ToolCallIndicatorProps {
|
||||
type: 'status' | 'thinking' | 'execution'
|
||||
content: string
|
||||
toolNames?: string[]
|
||||
}
|
||||
|
||||
// Detection State Component
|
||||
export function ToolCallDetection({ content }: { content: string }) {
|
||||
return (
|
||||
<div className='flex min-w-0 items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 px-3 py-2 text-sm dark:border-blue-800 dark:bg-blue-950'>
|
||||
<Loader2 className='h-4 w-4 shrink-0 animate-spin text-blue-600 dark:text-blue-400' />
|
||||
<span className='min-w-0 truncate text-blue-800 dark:text-blue-200'>{content}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Execution State Component
|
||||
export function ToolCallExecution({ toolCall, isCompact = false }: ToolCallProps) {
|
||||
const [isExpanded, setIsExpanded] = useState(!isCompact)
|
||||
|
||||
return (
|
||||
<div className='min-w-0 rounded-lg border border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-950'>
|
||||
<Collapsible open={isExpanded} onOpenChange={setIsExpanded}>
|
||||
<CollapsibleTrigger asChild>
|
||||
<Button
|
||||
variant='ghost'
|
||||
className='w-full min-w-0 justify-between px-3 py-4 hover:bg-amber-100 dark:hover:bg-amber-900'
|
||||
>
|
||||
<div className='flex min-w-0 items-center gap-2 overflow-hidden'>
|
||||
<Settings className='h-4 w-4 shrink-0 animate-pulse text-amber-600 dark:text-amber-400' />
|
||||
<span className='min-w-0 truncate font-mono text-amber-800 text-xs dark:text-amber-200'>
|
||||
{toolCall.displayName || toolCall.name}
|
||||
</span>
|
||||
{toolCall.progress && (
|
||||
<Badge
|
||||
variant='outline'
|
||||
className='shrink-0 text-amber-700 text-xs dark:text-amber-300'
|
||||
>
|
||||
{toolCall.progress}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className='h-4 w-4 shrink-0 text-amber-600 dark:text-amber-400' />
|
||||
) : (
|
||||
<ChevronRight className='h-4 w-4 shrink-0 text-amber-600 dark:text-amber-400' />
|
||||
)}
|
||||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className='min-w-0 max-w-full px-3 pb-3'>
|
||||
<div className='min-w-0 max-w-full space-y-2'>
|
||||
<div className='flex items-center gap-2 text-amber-700 text-xs dark:text-amber-300'>
|
||||
<Loader2 className='h-3 w-3 shrink-0 animate-spin' />
|
||||
<span>Executing...</span>
|
||||
</div>
|
||||
{toolCall.parameters &&
|
||||
Object.keys(toolCall.parameters).length > 0 &&
|
||||
(toolCall.name === 'make_api_request' ||
|
||||
toolCall.name === 'set_environment_variables' ||
|
||||
toolCall.name === 'set_global_workflow_variables') && (
|
||||
<div className='min-w-0 max-w-full rounded border border-amber-200 bg-amber-50 p-2 dark:border-amber-800 dark:bg-amber-950'>
|
||||
{toolCall.name === 'make_api_request' ? (
|
||||
<div className='w-full overflow-hidden rounded border border-muted bg-card'>
|
||||
<div className='grid grid-cols-2 gap-0 border-muted/60 border-b bg-muted/40 px-2 py-1.5'>
|
||||
<div className='font-medium text-[10px] text-muted-foreground uppercase tracking-wide'>
|
||||
Method
|
||||
</div>
|
||||
<div className='font-medium text-[10px] text-muted-foreground uppercase tracking-wide'>
|
||||
Endpoint
|
||||
</div>
|
||||
</div>
|
||||
<div className='grid grid-cols-[auto_1fr] items-center gap-2 px-2 py-2'>
|
||||
<div>
|
||||
<span className='inline-flex rounded bg-muted px-1.5 py-0.5 font-mono text-muted-foreground text-xs'>
|
||||
{String((toolCall.parameters as any).method || '').toUpperCase() ||
|
||||
'GET'}
|
||||
</span>
|
||||
</div>
|
||||
<div className='min-w-0'>
|
||||
<span
|
||||
className='block overflow-x-auto whitespace-nowrap font-mono text-foreground text-xs'
|
||||
title={String((toolCall.parameters as any).url || '')}
|
||||
>
|
||||
{String((toolCall.parameters as any).url || '') || 'URL not provided'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{toolCall.name === 'set_environment_variables'
|
||||
? (() => {
|
||||
const variables =
|
||||
(toolCall.parameters as any).variables &&
|
||||
typeof (toolCall.parameters as any).variables === 'object'
|
||||
? (toolCall.parameters as any).variables
|
||||
: {}
|
||||
const entries = Object.entries(variables)
|
||||
return (
|
||||
<div className='w-full overflow-hidden rounded border border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-950'>
|
||||
<div className='grid grid-cols-2 gap-0 border-amber-200/60 border-b px-2 py-1.5 dark:border-amber-800/60'>
|
||||
<div className='font-medium text-[10px] text-amber-700 uppercase tracking-wide dark:text-amber-300'>
|
||||
Name
|
||||
</div>
|
||||
<div className='font-medium text-[10px] text-amber-700 uppercase tracking-wide dark:text-amber-300'>
|
||||
Value
|
||||
</div>
|
||||
</div>
|
||||
{entries.length === 0 ? (
|
||||
<div className='px-2 py-2 text-muted-foreground text-xs'>
|
||||
No variables provided
|
||||
</div>
|
||||
) : (
|
||||
<div className='divide-y divide-amber-200 dark:divide-amber-800'>
|
||||
{entries.map(([k, v]) => (
|
||||
<div
|
||||
key={k}
|
||||
className='grid grid-cols-[auto_1fr] items-center gap-2 px-2 py-1.5'
|
||||
>
|
||||
<div className='truncate font-medium text-amber-800 text-xs dark:text-amber-200'>
|
||||
{k}
|
||||
</div>
|
||||
<div className='min-w-0'>
|
||||
<span className='block overflow-x-auto whitespace-nowrap font-mono text-amber-700 text-xs dark:text-amber-300'>
|
||||
{String(v)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})()
|
||||
: null}
|
||||
|
||||
{toolCall.name === 'set_global_workflow_variables'
|
||||
? (() => {
|
||||
const ops = Array.isArray((toolCall.parameters as any).operations)
|
||||
? ((toolCall.parameters as any).operations as any[])
|
||||
: []
|
||||
return (
|
||||
<div className='w-full overflow-hidden rounded border border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-amber-950'>
|
||||
<div className='grid grid-cols-3 gap-0 border-amber-200/60 border-b px-2 py-1.5 dark:border-amber-800/60'>
|
||||
<div className='font-medium text-[10px] text-amber-700 uppercase tracking-wide dark:text-amber-300'>
|
||||
Name
|
||||
</div>
|
||||
<div className='font-medium text-[10px] text-amber-700 uppercase tracking-wide dark:text-amber-300'>
|
||||
Type
|
||||
</div>
|
||||
<div className='font-medium text-[10px] text-amber-700 uppercase tracking-wide dark:text-amber-300'>
|
||||
Value
|
||||
</div>
|
||||
</div>
|
||||
{ops.length === 0 ? (
|
||||
<div className='px-2 py-2 text-muted-foreground text-xs'>
|
||||
No operations provided
|
||||
</div>
|
||||
) : (
|
||||
<div className='divide-y divide-amber-200 dark:divide-amber-800'>
|
||||
{ops.map((op, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className='grid grid-cols-3 items-center gap-0 px-2 py-1.5'
|
||||
>
|
||||
<div className='min-w-0'>
|
||||
<span className='truncate text-amber-800 text-xs dark:text-amber-200'>
|
||||
{String(op.name || '')}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className='rounded border px-1 py-0.5 text-[10px] text-muted-foreground'>
|
||||
{String(op.type || '')}
|
||||
</span>
|
||||
</div>
|
||||
<div className='min-w-0'>
|
||||
{op.value !== undefined ? (
|
||||
<span className='block overflow-x-auto whitespace-nowrap font-mono text-amber-700 text-xs dark:text-amber-300'>
|
||||
{String(op.value)}
|
||||
</span>
|
||||
) : (
|
||||
<span className='text-muted-foreground text-xs'>—</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})()
|
||||
: null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function ToolCallCompletion({ toolCall, isCompact = false }: ToolCallProps) {
|
||||
const [isExpanded, setIsExpanded] = useState(false)
|
||||
const isSuccess = toolCall.state === 'completed'
|
||||
const isError = toolCall.state === 'error'
|
||||
const isAborted = toolCall.state === 'aborted'
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'min-w-0 rounded-lg border',
|
||||
isSuccess && 'border-green-200 bg-green-50 dark:border-green-800 dark:bg-green-950',
|
||||
isError && 'border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950',
|
||||
isAborted && 'border-orange-200 bg-orange-50 dark:border-orange-800 dark:bg-orange-950'
|
||||
)}
|
||||
>
|
||||
<Collapsible open={isExpanded} onOpenChange={setIsExpanded}>
|
||||
<CollapsibleTrigger asChild>
|
||||
<Button
|
||||
variant='ghost'
|
||||
className={cn(
|
||||
'w-full min-w-0 justify-between px-3 py-4',
|
||||
isSuccess && 'hover:bg-green-100 dark:hover:bg-green-900',
|
||||
isError && 'hover:bg-red-100 dark:hover:bg-red-900',
|
||||
isAborted && 'hover:bg-orange-100 dark:hover:bg-orange-900'
|
||||
)}
|
||||
>
|
||||
<div className='flex min-w-0 items-center gap-2 overflow-hidden'>
|
||||
{isSuccess && (
|
||||
<CheckCircle className='h-4 w-4 shrink-0 text-green-600 dark:text-green-400' />
|
||||
)}
|
||||
{isError && <XCircle className='h-4 w-4 shrink-0 text-red-600 dark:text-red-400' />}
|
||||
{isAborted && (
|
||||
<XCircle className='h-4 w-4 shrink-0 text-orange-600 dark:text-orange-400' />
|
||||
)}
|
||||
<span
|
||||
className={cn(
|
||||
'min-w-0 truncate font-mono text-xs',
|
||||
isSuccess && 'text-green-800 dark:text-green-200',
|
||||
isError && 'text-red-800 dark:text-red-200',
|
||||
isAborted && 'text-orange-800 dark:text-orange-200'
|
||||
)}
|
||||
>
|
||||
{toolCall.displayName || toolCall.name}
|
||||
</span>
|
||||
{toolCall.duration && (
|
||||
<Badge
|
||||
variant='outline'
|
||||
className={cn(
|
||||
'shrink-0 text-xs',
|
||||
isSuccess && 'text-green-700 dark:text-green-300',
|
||||
isError && 'text-red-700 dark:text-red-300',
|
||||
isAborted && 'text-orange-700 dark:text-orange-300'
|
||||
)}
|
||||
style={{ fontSize: '0.625rem' }}
|
||||
>
|
||||
{toolCall.duration ? formatDuration(toolCall.duration, { precision: 1 }) : ''}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className='flex shrink-0 items-center'>
|
||||
{isExpanded ? (
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
'h-4 w-4',
|
||||
isSuccess && 'text-green-600 dark:text-green-400',
|
||||
isError && 'text-red-600 dark:text-red-400'
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<ChevronRight
|
||||
className={cn(
|
||||
'h-4 w-4',
|
||||
isSuccess && 'text-green-600 dark:text-green-400',
|
||||
isError && 'text-red-600 dark:text-red-400'
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className='min-w-0 max-w-full px-3 pb-3'>
|
||||
<div className='min-w-0 max-w-full space-y-2'>
|
||||
{toolCall.parameters &&
|
||||
Object.keys(toolCall.parameters).length > 0 &&
|
||||
(toolCall.name === 'make_api_request' ||
|
||||
toolCall.name === 'set_environment_variables') && (
|
||||
<div
|
||||
className={cn(
|
||||
'min-w-0 max-w-full rounded p-2',
|
||||
isSuccess && 'bg-green-100 dark:bg-green-900',
|
||||
isError && 'bg-red-100 dark:bg-red-900'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'mb-1 font-medium text-xs',
|
||||
isSuccess && 'text-green-800 dark:text-green-200',
|
||||
isError && 'text-red-800 dark:text-red-200'
|
||||
)}
|
||||
>
|
||||
Parameters:
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
'min-w-0 max-w-full break-all font-mono text-xs',
|
||||
isSuccess && 'text-green-700 dark:text-green-300',
|
||||
isError && 'text-red-700 dark:text-red-300'
|
||||
)}
|
||||
>
|
||||
{JSON.stringify(toolCall.parameters, null, 2)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{toolCall.error && (
|
||||
<div className='min-w-0 max-w-full rounded bg-red-100 p-2 dark:bg-red-900'>
|
||||
<div className='mb-1 font-medium text-red-800 text-xs dark:text-red-200'>
|
||||
Error:
|
||||
</div>
|
||||
<div className='min-w-0 max-w-full break-all font-mono text-red-700 text-xs dark:text-red-300'>
|
||||
{toolCall.error}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Group Component for Multiple Tool Calls
|
||||
export function ToolCallGroupComponent({ group, isCompact = false }: ToolCallGroupProps) {
|
||||
const [isExpanded, setIsExpanded] = useState(true)
|
||||
|
||||
const completedCount = group.toolCalls.filter((t) => t.state === 'completed').length
|
||||
const totalCount = group.toolCalls.length
|
||||
const isAllCompleted = completedCount === totalCount
|
||||
const hasErrors = group.toolCalls.some((t) => t.state === 'error')
|
||||
|
||||
return (
|
||||
<div className='min-w-0 space-y-2'>
|
||||
{group.summary && (
|
||||
<div className='flex min-w-0 items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 px-3 py-2 text-sm dark:border-blue-800 dark:bg-blue-950'>
|
||||
<Settings className='h-4 w-4 shrink-0 text-blue-600 dark:text-blue-400' />
|
||||
<span className='min-w-0 truncate text-blue-800 dark:text-blue-200'>{group.summary}</span>
|
||||
{!isAllCompleted && (
|
||||
<Badge variant='outline' className='shrink-0 text-blue-700 text-xs dark:text-blue-300'>
|
||||
{completedCount}/{totalCount}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Collapsible open={isExpanded} onOpenChange={setIsExpanded}>
|
||||
<CollapsibleTrigger asChild>
|
||||
<Button
|
||||
variant='ghost'
|
||||
className='w-full min-w-0 justify-between px-3 py-3 text-sm hover:bg-muted'
|
||||
>
|
||||
<div className='flex min-w-0 items-center gap-2 overflow-hidden'>
|
||||
<span className='min-w-0 truncate text-muted-foreground'>
|
||||
{isAllCompleted ? 'Completed' : 'In Progress'} ({completedCount}/{totalCount})
|
||||
</span>
|
||||
{hasErrors && (
|
||||
<Badge variant='red' className='shrink-0 text-xs'>
|
||||
Errors
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{isExpanded ? (
|
||||
<ChevronDown className='h-4 w-4 shrink-0 text-muted-foreground' />
|
||||
) : (
|
||||
<ChevronRight className='h-4 w-4 shrink-0 text-muted-foreground' />
|
||||
)}
|
||||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent className='min-w-0 max-w-full space-y-2'>
|
||||
{group.toolCalls.map((toolCall) => (
|
||||
<div key={toolCall.id} className='min-w-0 max-w-full'>
|
||||
{toolCall.state === 'executing' && (
|
||||
<ToolCallExecution toolCall={toolCall} isCompact={isCompact} />
|
||||
)}
|
||||
{(toolCall.state === 'completed' || toolCall.state === 'error') && (
|
||||
<ToolCallCompletion toolCall={toolCall} isCompact={isCompact} />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Status Indicator Component
|
||||
export function ToolCallIndicator({ type, content, toolNames }: ToolCallIndicatorProps) {
|
||||
if (type === 'status' && toolNames) {
|
||||
return (
|
||||
<div className='flex min-w-0 items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 px-3 py-2 text-sm dark:border-blue-800 dark:bg-blue-950'>
|
||||
<Loader2 className='h-4 w-4 shrink-0 animate-spin text-blue-600 dark:text-blue-400' />
|
||||
<span className='min-w-0 truncate text-blue-800 dark:text-blue-200'>
|
||||
🔄 {toolNames.join(' • ')}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex min-w-0 items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 px-3 py-2 text-sm dark:border-blue-800 dark:bg-blue-950'>
|
||||
<Loader2 className='h-4 w-4 shrink-0 animate-spin text-blue-600 dark:text-blue-400' />
|
||||
<span className='min-w-0 truncate text-blue-800 dark:text-blue-200'>{content}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -20,9 +20,10 @@ import {
|
||||
ModalTabsContent,
|
||||
ModalTabsList,
|
||||
ModalTabsTrigger,
|
||||
Skeleton,
|
||||
Switch,
|
||||
} from '@/components/emcn'
|
||||
import { Input as BaseInput, Skeleton } from '@/components/ui'
|
||||
import { Input as BaseInput } from '@/components/ui'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { getSubscriptionStatus } from '@/lib/billing/client'
|
||||
import type { PermissionGroupConfig } from '@/lib/permission-groups/types'
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
import { useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { Check, ChevronDown, Clipboard, Eye, EyeOff } from 'lucide-react'
|
||||
import { Button, Combobox, Input, Switch, Textarea } from '@/components/emcn'
|
||||
import { Skeleton } from '@/components/ui'
|
||||
import { Button, Combobox, Input, Skeleton, Switch, Textarea } from '@/components/emcn'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { getSubscriptionStatus } from '@/lib/billing/client/utils'
|
||||
import { isBillingEnabled } from '@/lib/core/config/feature-flags'
|
||||
|
||||
Reference in New Issue
Block a user