mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
Added prettier, added husky pre-commit hook to ensure formatting
This commit is contained in:
Executable
+1
@@ -0,0 +1 @@
|
||||
npx lint-staged
|
||||
@@ -0,0 +1,37 @@
|
||||
# Build output
|
||||
.next
|
||||
out
|
||||
dist
|
||||
build
|
||||
|
||||
# Dependencies
|
||||
node_modules
|
||||
|
||||
# Cache
|
||||
.cache
|
||||
.npm
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# Debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Local env files
|
||||
.env*.local
|
||||
.env
|
||||
|
||||
# Vercel
|
||||
.vercel
|
||||
|
||||
# Coverage
|
||||
coverage
|
||||
|
||||
# PWA files
|
||||
**/public/sw.js
|
||||
**/public/workbox-*.js
|
||||
**/public/worker-*.js
|
||||
**/public/fallback-*.js
|
||||
@@ -0,0 +1,22 @@
|
||||
module.exports = {
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
trailingComma: 'es5',
|
||||
printWidth: 100,
|
||||
bracketSpacing: true,
|
||||
arrowParens: 'always',
|
||||
endOfLine: 'lf',
|
||||
plugins: ['prettier-plugin-tailwindcss', '@trivago/prettier-plugin-sort-imports'],
|
||||
importOrder: [
|
||||
'^(react/(.*)$)|^(react$)',
|
||||
'^(next/(.*)$)|^(next$)',
|
||||
'<THIRD_PARTY_MODULES>',
|
||||
'^@/components/(.*)$',
|
||||
'^@/lib/(.*)$',
|
||||
'^@/stores/(.*)$',
|
||||
'^@/(.*)$',
|
||||
'^[./]',
|
||||
],
|
||||
importOrderSortSpecifiers: true,
|
||||
}
|
||||
@@ -29,6 +29,7 @@ All icons are available as React components and can be imported from `components
|
||||
import { ApiIcon, CodeIcon, ErrorIcon } from '@/components/icons'
|
||||
|
||||
// Usage
|
||||
|
||||
;<ApiIcon className="w-6 h-6" />
|
||||
```
|
||||
|
||||
|
||||
+11
-13
@@ -1,14 +1,14 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { getTool } from '@/tools'
|
||||
import { anthropicProvider } from '@/providers/anthropic'
|
||||
import { googleProvider } from '@/providers/google'
|
||||
import { openaiProvider } from '@/providers/openai'
|
||||
import { ProviderConfig } from '@/providers/types'
|
||||
import { googleProvider } from '@/providers/google'
|
||||
import { getTool } from '@/tools'
|
||||
|
||||
const providers: Record<string, ProviderConfig> = {
|
||||
'anthropic/chat': anthropicProvider,
|
||||
'openai/chat': openaiProvider,
|
||||
'google/chat': googleProvider
|
||||
'google/chat': googleProvider,
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
@@ -26,7 +26,7 @@ export async function POST(request: Request) {
|
||||
const response = await fetch(provider.baseUrl, {
|
||||
method: 'POST',
|
||||
headers: provider.headers(apiKey),
|
||||
body: JSON.stringify(restParams)
|
||||
body: JSON.stringify(restParams),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -36,7 +36,7 @@ export async function POST(request: Request) {
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: await response.json()
|
||||
output: await response.json(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,10 +62,10 @@ export async function POST(request: Request) {
|
||||
const externalResponse = await fetch(url, { method, headers, body })
|
||||
|
||||
if (!externalResponse.ok) {
|
||||
const errorContent = await externalResponse.json().catch(() => ({
|
||||
message: externalResponse.statusText
|
||||
const errorContent = await externalResponse.json().catch(() => ({
|
||||
message: externalResponse.statusText,
|
||||
}))
|
||||
|
||||
|
||||
// Use the tool's error transformer or a default message
|
||||
const error = tool.transformError
|
||||
? tool.transformError(errorContent)
|
||||
@@ -84,9 +84,7 @@ export async function POST(request: Request) {
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(
|
||||
tool.transformError
|
||||
? tool.transformError(result)
|
||||
: 'Tool returned an error'
|
||||
tool.transformError ? tool.transformError(result) : 'Tool returned an error'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -97,7 +95,7 @@ export async function POST(request: Request) {
|
||||
} catch (error: any) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error.message || 'Unknown error'
|
||||
error: error.message || 'Unknown error',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-7
@@ -4,15 +4,10 @@ import { ZoomPrevention } from './zoom-prevention'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Sim Studio',
|
||||
description:
|
||||
'Drag and drop agents to create workflows. Powered by Simplicity',
|
||||
description: 'Drag and drop agents to create workflows. Powered by Simplicity',
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { EdgeProps, getSmoothStepPath } from 'reactflow'
|
||||
import { X } from 'lucide-react'
|
||||
import { EdgeProps, getSmoothStepPath } from 'reactflow'
|
||||
|
||||
export const CustomEdge = (props: EdgeProps) => {
|
||||
const isHorizontal =
|
||||
props.sourcePosition === 'right' || props.sourcePosition === 'left'
|
||||
const isHorizontal = props.sourcePosition === 'right' || props.sourcePosition === 'left'
|
||||
|
||||
// For horizontal handles, we'll add a minimum extension to ensure the path
|
||||
// always goes outward before going up/down
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
import { ArrowLeftRight, ArrowUpDown, Circle, CircleOff, Copy, Play, Trash2 } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Trash2,
|
||||
Play,
|
||||
Circle,
|
||||
CircleOff,
|
||||
Copy,
|
||||
ArrowLeftRight,
|
||||
ArrowUpDown,
|
||||
} from 'lucide-react'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
|
||||
interface ActionBarProps {
|
||||
blockId: string
|
||||
@@ -22,16 +10,10 @@ interface ActionBarProps {
|
||||
|
||||
export function ActionBar({ blockId }: ActionBarProps) {
|
||||
const removeBlock = useWorkflowStore((state) => state.removeBlock)
|
||||
const toggleBlockEnabled = useWorkflowStore(
|
||||
(state) => state.toggleBlockEnabled
|
||||
)
|
||||
const toggleBlockHandles = useWorkflowStore(
|
||||
(state) => state.toggleBlockHandles
|
||||
)
|
||||
const toggleBlockEnabled = useWorkflowStore((state) => state.toggleBlockEnabled)
|
||||
const toggleBlockHandles = useWorkflowStore((state) => state.toggleBlockHandles)
|
||||
const duplicateBlock = useWorkflowStore((state) => state.duplicateBlock)
|
||||
const isEnabled = useWorkflowStore(
|
||||
(state) => state.blocks[blockId]?.enabled ?? true
|
||||
)
|
||||
const isEnabled = useWorkflowStore((state) => state.blocks[blockId]?.enabled ?? true)
|
||||
const horizontalHandles = useWorkflowStore(
|
||||
(state) => state.blocks[blockId]?.horizontalHandles ?? false
|
||||
)
|
||||
@@ -63,16 +45,10 @@ export function ActionBar({ blockId }: ActionBarProps) {
|
||||
onClick={() => toggleBlockEnabled(blockId)}
|
||||
className="text-gray-500"
|
||||
>
|
||||
{isEnabled ? (
|
||||
<Circle className="h-4 w-4" />
|
||||
) : (
|
||||
<CircleOff className="h-4 w-4" />
|
||||
)}
|
||||
{isEnabled ? <Circle className="h-4 w-4" /> : <CircleOff className="h-4 w-4" />}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right">
|
||||
{isEnabled ? 'Disable Block' : 'Enable Block'}
|
||||
</TooltipContent>
|
||||
<TooltipContent side="right">{isEnabled ? 'Disable Block' : 'Enable Block'}</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
|
||||
+4
-10
@@ -1,17 +1,13 @@
|
||||
import { useBlockConnections } from '@/app/w/hooks/use-block-connections'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { useBlockConnections } from '@/app/w/hooks/use-block-connections'
|
||||
|
||||
interface ConnectionBlocksProps {
|
||||
blockId: string
|
||||
setIsConnecting: (isConnecting: boolean) => void
|
||||
}
|
||||
|
||||
export function ConnectionBlocks({
|
||||
blockId,
|
||||
setIsConnecting,
|
||||
}: ConnectionBlocksProps) {
|
||||
const { incomingConnections, hasIncomingConnections } =
|
||||
useBlockConnections(blockId)
|
||||
export function ConnectionBlocks({ blockId, setIsConnecting }: ConnectionBlocksProps) {
|
||||
const { incomingConnections, hasIncomingConnections } = useBlockConnections(blockId)
|
||||
|
||||
if (!hasIncomingConnections) return null
|
||||
|
||||
@@ -51,9 +47,7 @@ export function ConnectionBlocks({
|
||||
<span className="font-medium leading-none">
|
||||
{connection.name.replace(/\s+/g, '').toLowerCase()}
|
||||
</span>
|
||||
<span className="text-muted-foreground">
|
||||
.{connection.outputType}
|
||||
</span>
|
||||
<span className="text-muted-foreground">.{connection.outputType}</span>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
|
||||
+4
-16
@@ -1,7 +1,7 @@
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
|
||||
interface CheckboxListProps {
|
||||
blockId: string
|
||||
@@ -11,21 +11,9 @@ interface CheckboxListProps {
|
||||
layout?: 'full' | 'half'
|
||||
}
|
||||
|
||||
export function CheckboxList({
|
||||
blockId,
|
||||
subBlockId,
|
||||
title,
|
||||
options,
|
||||
layout,
|
||||
}: CheckboxListProps) {
|
||||
export function CheckboxList({ blockId, subBlockId, title, options, layout }: CheckboxListProps) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'grid gap-4',
|
||||
layout === 'half' ? 'grid-cols-2' : 'grid-cols-1',
|
||||
'pt-1'
|
||||
)}
|
||||
>
|
||||
<div className={cn('grid gap-4', layout === 'half' ? 'grid-cols-2' : 'grid-cols-1', 'pt-1')}>
|
||||
{options.map((option) => {
|
||||
const [value, setValue] = useSubBlockValue(blockId, option.id)
|
||||
return (
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import Editor from 'react-simple-code-editor'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { highlight, languages } from 'prismjs'
|
||||
import 'prismjs/components/prism-javascript'
|
||||
import 'prismjs/themes/prism.css'
|
||||
import Editor from 'react-simple-code-editor'
|
||||
import { TagDropdown, checkTagTrigger } from '@/components/ui/tag-dropdown'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
import { TagDropdown, checkTagTrigger } from '@/components/ui/tag-dropdown'
|
||||
|
||||
interface CodeProps {
|
||||
blockId: string
|
||||
@@ -19,9 +19,7 @@ export function Code({ blockId, subBlockId, isConnecting }: CodeProps) {
|
||||
const [lineCount, setLineCount] = useState(1)
|
||||
const [showTags, setShowTags] = useState(false)
|
||||
const [cursorPosition, setCursorPosition] = useState(0)
|
||||
const [activeSourceBlockId, setActiveSourceBlockId] = useState<string | null>(
|
||||
null
|
||||
)
|
||||
const [activeSourceBlockId, setActiveSourceBlockId] = useState<string | null>(null)
|
||||
const editorRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Add new state for tracking visual line heights
|
||||
@@ -93,10 +91,7 @@ export function Code({ blockId, subBlockId, isConnecting }: CodeProps) {
|
||||
document.body.removeChild(container)
|
||||
setVisualLineHeights(newVisualLineHeights)
|
||||
|
||||
const totalVisualLines = newVisualLineHeights.reduce(
|
||||
(sum, height) => sum + height,
|
||||
0
|
||||
)
|
||||
const totalVisualLines = newVisualLineHeights.reduce((sum, height) => sum + height, 0)
|
||||
setLineCount(totalVisualLines)
|
||||
}
|
||||
|
||||
@@ -146,8 +141,7 @@ export function Code({ blockId, subBlockId, isConnecting }: CodeProps) {
|
||||
const dropPosition = textarea?.selectionStart ?? code.length
|
||||
|
||||
// Insert '<' at drop position to trigger the dropdown
|
||||
const newValue =
|
||||
code.slice(0, dropPosition) + '<' + code.slice(dropPosition)
|
||||
const newValue = code.slice(0, dropPosition) + '<' + code.slice(dropPosition)
|
||||
|
||||
setCode(newValue)
|
||||
setStoreValue(newValue)
|
||||
@@ -216,9 +210,7 @@ export function Code({ blockId, subBlockId, isConnecting }: CodeProps) {
|
||||
}
|
||||
}
|
||||
}}
|
||||
highlight={(code) =>
|
||||
highlight(code, languages.javascript, 'javascript')
|
||||
}
|
||||
highlight={(code) => highlight(code, languages.javascript, 'javascript')}
|
||||
padding={12}
|
||||
style={{
|
||||
fontFamily: 'inherit',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useEffect } from 'react'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -6,7 +7,6 @@ import {
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
interface DropdownProps {
|
||||
options: string[]
|
||||
@@ -15,12 +15,7 @@ interface DropdownProps {
|
||||
subBlockId: string
|
||||
}
|
||||
|
||||
export function Dropdown({
|
||||
options,
|
||||
defaultValue,
|
||||
blockId,
|
||||
subBlockId,
|
||||
}: DropdownProps) {
|
||||
export function Dropdown({ options, defaultValue, blockId, subBlockId }: DropdownProps) {
|
||||
const [value, setValue] = useSubBlockValue(blockId, subBlockId)
|
||||
|
||||
// Set the value to the first option if it's not set
|
||||
|
||||
+9
-18
@@ -1,15 +1,12 @@
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useState, useRef } from 'react'
|
||||
import { SubBlockConfig } from '@/blocks/types'
|
||||
import { useRef, useState } from 'react'
|
||||
import { EnvVarDropdown, checkEnvVarTrigger } from '@/components/ui/env-var-dropdown'
|
||||
import { formatDisplayText } from '@/components/ui/formatted-text'
|
||||
import {
|
||||
EnvVarDropdown,
|
||||
checkEnvVarTrigger,
|
||||
} from '@/components/ui/env-var-dropdown'
|
||||
import { TagDropdown, checkTagTrigger } from '@/components/ui/tag-dropdown'
|
||||
import { Textarea } from '@/components/ui/textarea'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import { SubBlockConfig } from '@/blocks/types'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
|
||||
interface LongInputProps {
|
||||
placeholder?: string
|
||||
@@ -33,9 +30,7 @@ export function LongInput({
|
||||
const [cursorPosition, setCursorPosition] = useState(0)
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null)
|
||||
const overlayRef = useRef<HTMLDivElement>(null)
|
||||
const [activeSourceBlockId, setActiveSourceBlockId] = useState<string | null>(
|
||||
null
|
||||
)
|
||||
const [activeSourceBlockId, setActiveSourceBlockId] = useState<string | null>(null)
|
||||
|
||||
// Handle input changes
|
||||
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
@@ -77,15 +72,11 @@ export function LongInput({
|
||||
if (data.type !== 'connectionBlock') return
|
||||
|
||||
// Get current cursor position or append to end
|
||||
const dropPosition =
|
||||
textareaRef.current?.selectionStart ?? value?.toString().length ?? 0
|
||||
const dropPosition = textareaRef.current?.selectionStart ?? value?.toString().length ?? 0
|
||||
|
||||
// Insert '<' at drop position to trigger the dropdown
|
||||
const currentValue = value?.toString() ?? ''
|
||||
const newValue =
|
||||
currentValue.slice(0, dropPosition) +
|
||||
'<' +
|
||||
currentValue.slice(dropPosition)
|
||||
const newValue = currentValue.slice(0, dropPosition) + '<' + currentValue.slice(dropPosition)
|
||||
|
||||
// Focus the textarea first
|
||||
textareaRef.current?.focus()
|
||||
|
||||
+9
-20
@@ -1,14 +1,11 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { EnvVarDropdown, checkEnvVarTrigger } from '@/components/ui/env-var-dropdown'
|
||||
import { formatDisplayText } from '@/components/ui/formatted-text'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
import { TagDropdown, checkTagTrigger } from '@/components/ui/tag-dropdown'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { SubBlockConfig } from '@/blocks/types'
|
||||
import { formatDisplayText } from '@/components/ui/formatted-text'
|
||||
import {
|
||||
EnvVarDropdown,
|
||||
checkEnvVarTrigger,
|
||||
} from '@/components/ui/env-var-dropdown'
|
||||
import { TagDropdown, checkTagTrigger } from '@/components/ui/tag-dropdown'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
|
||||
interface ShortInputProps {
|
||||
placeholder?: string
|
||||
@@ -39,9 +36,7 @@ export function ShortInput({
|
||||
const [cursorPosition, setCursorPosition] = useState(0)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const overlayRef = useRef<HTMLDivElement>(null)
|
||||
const [activeSourceBlockId, setActiveSourceBlockId] = useState<string | null>(
|
||||
null
|
||||
)
|
||||
const [activeSourceBlockId, setActiveSourceBlockId] = useState<string | null>(null)
|
||||
|
||||
// Use either controlled or uncontrolled value
|
||||
const value = propValue !== undefined ? propValue : storeValue
|
||||
@@ -104,15 +99,11 @@ export function ShortInput({
|
||||
if (data.type !== 'connectionBlock') return
|
||||
|
||||
// Get current cursor position or append to end
|
||||
const dropPosition =
|
||||
inputRef.current?.selectionStart ?? value?.toString().length ?? 0
|
||||
const dropPosition = inputRef.current?.selectionStart ?? value?.toString().length ?? 0
|
||||
|
||||
// Insert '<' at drop position to trigger the dropdown
|
||||
const currentValue = value?.toString() ?? ''
|
||||
const newValue =
|
||||
currentValue.slice(0, dropPosition) +
|
||||
'<' +
|
||||
currentValue.slice(dropPosition)
|
||||
const newValue = currentValue.slice(0, dropPosition) + '<' + currentValue.slice(dropPosition)
|
||||
|
||||
// Focus the input first
|
||||
inputRef.current?.focus()
|
||||
@@ -151,9 +142,7 @@ export function ShortInput({
|
||||
|
||||
// Value display logic
|
||||
const displayValue =
|
||||
password && !isFocused
|
||||
? '•'.repeat(value?.toString().length ?? 0)
|
||||
: value?.toString() ?? ''
|
||||
password && !isFocused ? '•'.repeat(value?.toString().length ?? 0) : (value?.toString() ?? '')
|
||||
|
||||
// Modify the EnvVarDropdown to use the correct setter
|
||||
const handleEnvVarSelect = (newValue: string) => {
|
||||
|
||||
+1
-3
@@ -32,9 +32,7 @@ export function SliderInput({
|
||||
<div
|
||||
className="absolute text-sm text-muted-foreground"
|
||||
style={{
|
||||
left: `clamp(0%, ${
|
||||
((Number(sliderValue) - min) / (max - min)) * 100
|
||||
}%, 100%)`,
|
||||
left: `clamp(0%, ${((Number(sliderValue) - min) / (max - min)) * 100}%, 100%)`,
|
||||
transform: `translateX(-${(() => {
|
||||
const percentage = ((Number(sliderValue) - min) / (max - min)) * 100
|
||||
const bias = -25 * Math.sin((percentage * Math.PI) / 50)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { Switch as UISwitch } from '@/components/ui/switch'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
import { Label } from '@/components/ui/label'
|
||||
|
||||
interface SwitchProps {
|
||||
blockId: string
|
||||
@@ -16,10 +16,7 @@ export function Switch({ blockId, subBlockId, title }: SwitchProps) {
|
||||
<Label className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||
{title}
|
||||
</Label>
|
||||
<UISwitch
|
||||
checked={Boolean(value)}
|
||||
onCheckedChange={(checked) => setValue(checked)}
|
||||
/>
|
||||
<UISwitch checked={Boolean(value)} onCheckedChange={(checked) => setValue(checked)} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { Trash2 } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
import { useRef, useEffect } from 'react'
|
||||
|
||||
interface TableProps {
|
||||
columns: string[]
|
||||
@@ -18,9 +18,7 @@ interface TableRow {
|
||||
|
||||
export function Table({ columns, blockId, subBlockId }: TableProps) {
|
||||
const [value, setValue] = useSubBlockValue(blockId, subBlockId)
|
||||
const activePositionRef = useRef<{ rowIndex: number; column: string } | null>(
|
||||
null
|
||||
)
|
||||
const activePositionRef = useRef<{ rowIndex: number; column: string } | null>(null)
|
||||
const inputRefs = useRef<Map<string, HTMLInputElement>>(new Map())
|
||||
|
||||
useEffect(() => {
|
||||
@@ -57,11 +55,7 @@ export function Table({ columns, blockId, subBlockId }: TableProps) {
|
||||
},
|
||||
]
|
||||
|
||||
const handleCellChange = (
|
||||
rowIndex: number,
|
||||
column: string,
|
||||
value: string
|
||||
) => {
|
||||
const handleCellChange = (rowIndex: number, column: string, value: string) => {
|
||||
const updatedRows = rows.map((row, idx) =>
|
||||
idx === rowIndex
|
||||
? {
|
||||
@@ -111,10 +105,7 @@ export function Table({ columns, blockId, subBlockId }: TableProps) {
|
||||
{columns.map((column, cellIndex) => (
|
||||
<td
|
||||
key={`${row.id}-${column}`}
|
||||
className={cn(
|
||||
'p-1',
|
||||
cellIndex < columns.length - 1 && 'border-r'
|
||||
)}
|
||||
className={cn('p-1', cellIndex < columns.length - 1 && 'border-r')}
|
||||
>
|
||||
<Input
|
||||
ref={(el) => {
|
||||
@@ -128,9 +119,7 @@ export function Table({ columns, blockId, subBlockId }: TableProps) {
|
||||
data-column={column}
|
||||
value={row.cells[column] || ''}
|
||||
placeholder={column}
|
||||
onChange={(e) =>
|
||||
handleCellChange(rowIndex, column, e.target.value)
|
||||
}
|
||||
onChange={(e) => handleCellChange(rowIndex, column, e.target.value)}
|
||||
onFocus={() => {
|
||||
activePositionRef.current = { rowIndex, column }
|
||||
}}
|
||||
|
||||
+12
-41
@@ -1,6 +1,6 @@
|
||||
import { useState } from 'react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { PlusIcon, XIcon } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
@@ -9,15 +9,11 @@ import {
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from '@/components/ui/command'
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/popover'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
import { getAllBlocks } from '@/blocks'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { getAllBlocks } from '@/blocks'
|
||||
import { getTool } from '@/tools'
|
||||
import { useSubBlockValue } from '../hooks/use-sub-block-value'
|
||||
import { ShortInput } from './short-input'
|
||||
|
||||
interface ToolInputProps {
|
||||
@@ -82,9 +78,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
const [value, setValue] = useSubBlockValue(blockId, subBlockId)
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const toolBlocks = getAllBlocks().filter(
|
||||
(block) => block.toolbar.category === 'tools'
|
||||
)
|
||||
const toolBlocks = getAllBlocks().filter((block) => block.toolbar.category === 'tools')
|
||||
|
||||
const selectedTools: StoredTool[] =
|
||||
Array.isArray(value) && value.length > 0 && typeof value[0] === 'object'
|
||||
@@ -112,11 +106,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
setValue(selectedTools.filter((tool) => tool.type !== toolType))
|
||||
}
|
||||
|
||||
const handleParamChange = (
|
||||
toolType: string,
|
||||
paramId: string,
|
||||
paramValue: string
|
||||
) => {
|
||||
const handleParamChange = (toolType: string, paramId: string, paramValue: string) => {
|
||||
setValue(
|
||||
selectedTools.map((tool) =>
|
||||
tool.type === toolType
|
||||
@@ -132,13 +122,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
)
|
||||
}
|
||||
|
||||
const IconComponent = ({
|
||||
icon: Icon,
|
||||
className,
|
||||
}: {
|
||||
icon: any
|
||||
className?: string
|
||||
}) => {
|
||||
const IconComponent = ({ icon: Icon, className }: { icon: any; className?: string }) => {
|
||||
if (!Icon) return null
|
||||
return <Icon className={className} />
|
||||
}
|
||||
@@ -177,10 +161,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
className="flex items-center justify-center w-6 h-6 rounded"
|
||||
style={{ backgroundColor: block.toolbar.bgColor }}
|
||||
>
|
||||
<IconComponent
|
||||
icon={block.toolbar.icon}
|
||||
className="w-4 h-4 text-white"
|
||||
/>
|
||||
<IconComponent icon={block.toolbar.icon} className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span>{block.toolbar.title}</span>
|
||||
</CommandItem>
|
||||
@@ -193,9 +174,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
) : (
|
||||
<div className="flex flex-wrap gap-2 min-h-[2.5rem] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background">
|
||||
{selectedTools.map((tool) => {
|
||||
const toolBlock = toolBlocks.find(
|
||||
(block) => block.type === tool.type
|
||||
)
|
||||
const toolBlock = toolBlocks.find((block) => block.type === tool.type)
|
||||
const toolId = getToolIdFromBlock(tool.type)
|
||||
const requiredParams = toolId ? getRequiredToolParams(toolId) : []
|
||||
|
||||
@@ -235,10 +214,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
blockId={blockId}
|
||||
subBlockId={`${subBlockId}-param`}
|
||||
placeholder={param.description}
|
||||
password={
|
||||
param.id.toLowerCase().replace(/\s+/g, '') ===
|
||||
'apikey'
|
||||
}
|
||||
password={param.id.toLowerCase().replace(/\s+/g, '') === 'apikey'}
|
||||
isConnecting={false}
|
||||
config={{
|
||||
id: `${subBlockId}-param`,
|
||||
@@ -246,9 +222,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
title: param.id,
|
||||
}}
|
||||
value={tool.params[param.id] || ''}
|
||||
onChange={(value) =>
|
||||
handleParamChange(tool.type, param.id, value)
|
||||
}
|
||||
onChange={(value) => handleParamChange(tool.type, param.id, value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -286,10 +260,7 @@ export function ToolInput({ blockId, subBlockId }: ToolInputProps) {
|
||||
className="flex items-center justify-center w-6 h-6 rounded"
|
||||
style={{ backgroundColor: block.toolbar.bgColor }}
|
||||
>
|
||||
<IconComponent
|
||||
icon={block.toolbar.icon}
|
||||
className="w-4 h-4 text-white"
|
||||
/>
|
||||
<IconComponent icon={block.toolbar.icon} className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
<span>{block.toolbar.title}</span>
|
||||
</CommandItem>
|
||||
|
||||
+3
-4
@@ -1,11 +1,10 @@
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import { useCallback } from 'react'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
|
||||
export function useSubBlockValue(blockId: string, subBlockId: string) {
|
||||
const value = useWorkflowStore(
|
||||
useCallback(
|
||||
(state) =>
|
||||
(state.blocks[blockId]?.subBlocks[subBlockId]?.value) ?? null,
|
||||
(state) => state.blocks[blockId]?.subBlocks[subBlockId]?.value ?? null,
|
||||
[blockId, subBlockId]
|
||||
)
|
||||
)
|
||||
@@ -20,4 +19,4 @@ export function useSubBlockValue(blockId: string, subBlockId: string) {
|
||||
)
|
||||
|
||||
return [value, setValue] as const
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { SubBlockConfig } from '../../../../../../../blocks/types'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { ShortInput } from './components/short-input'
|
||||
import { LongInput } from './components/long-input'
|
||||
import { Dropdown } from './components/dropdown'
|
||||
import { SliderInput } from './components/slider-input'
|
||||
import { Table } from './components/table'
|
||||
import { Code } from './components/code'
|
||||
import { Switch } from './components/switch'
|
||||
import { ToolInput } from './components/tool-input'
|
||||
import { SubBlockConfig } from '../../../../../../../blocks/types'
|
||||
import { CheckboxList } from './components/checkbox-list'
|
||||
import { Code } from './components/code'
|
||||
import { Dropdown } from './components/dropdown'
|
||||
import { LongInput } from './components/long-input'
|
||||
import { ShortInput } from './components/short-input'
|
||||
import { SliderInput } from './components/slider-input'
|
||||
import { Switch } from './components/switch'
|
||||
import { Table } from './components/table'
|
||||
import { ToolInput } from './components/tool-input'
|
||||
|
||||
interface SubBlockProps {
|
||||
blockId: string
|
||||
@@ -65,35 +65,15 @@ export function SubBlock({ blockId, config, isConnecting }: SubBlockProps) {
|
||||
subBlockId={config.id}
|
||||
min={config.min}
|
||||
max={config.max}
|
||||
defaultValue={
|
||||
(config.min || 0) + ((config.max || 100) - (config.min || 0)) / 2
|
||||
}
|
||||
defaultValue={(config.min || 0) + ((config.max || 100) - (config.min || 0)) / 2}
|
||||
/>
|
||||
)
|
||||
case 'table':
|
||||
return (
|
||||
<Table
|
||||
blockId={blockId}
|
||||
subBlockId={config.id}
|
||||
columns={config.columns ?? []}
|
||||
/>
|
||||
)
|
||||
return <Table blockId={blockId} subBlockId={config.id} columns={config.columns ?? []} />
|
||||
case 'code':
|
||||
return (
|
||||
<Code
|
||||
blockId={blockId}
|
||||
subBlockId={config.id}
|
||||
isConnecting={isConnecting}
|
||||
/>
|
||||
)
|
||||
return <Code blockId={blockId} subBlockId={config.id} isConnecting={isConnecting} />
|
||||
case 'switch':
|
||||
return (
|
||||
<Switch
|
||||
blockId={blockId}
|
||||
subBlockId={config.id}
|
||||
title={config.title ?? ''}
|
||||
/>
|
||||
)
|
||||
return <Switch blockId={blockId} subBlockId={config.id} title={config.title ?? ''} />
|
||||
case 'tool-input':
|
||||
return <ToolInput blockId={blockId} subBlockId={config.id} />
|
||||
case 'checkbox-list':
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { BlockConfig, SubBlockConfig } from '../../../../../blocks/types'
|
||||
import { SubBlock } from './components/sub-block/sub-block'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Handle, Position } from 'reactflow'
|
||||
import { useUpdateNodeInternals } from 'reactflow'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Card } from '@/components/ui/card'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import { BlockConfig, SubBlockConfig } from '../../../../../blocks/types'
|
||||
import { ActionBar } from './components/action-bar/action-bar'
|
||||
import { ConnectionBlocks } from './components/connection-blocks/connection-blocks'
|
||||
import { useState, useEffect, useRef } from 'react'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { useUpdateNodeInternals } from 'reactflow'
|
||||
import { SubBlock } from './components/sub-block/sub-block'
|
||||
|
||||
interface WorkflowBlockProps {
|
||||
id: string
|
||||
@@ -24,19 +24,11 @@ interface SubBlockPosition {
|
||||
top: number
|
||||
}
|
||||
|
||||
export function WorkflowBlock({
|
||||
id,
|
||||
type,
|
||||
config,
|
||||
name,
|
||||
selected,
|
||||
}: WorkflowBlockProps) {
|
||||
export function WorkflowBlock({ id, type, config, name, selected }: WorkflowBlockProps) {
|
||||
const { toolbar, workflow } = config
|
||||
// Dragging connection state
|
||||
const [isConnecting, setIsConnecting] = useState(false)
|
||||
const isEnabled = useWorkflowStore(
|
||||
(state) => state.blocks[id]?.enabled ?? true
|
||||
)
|
||||
const isEnabled = useWorkflowStore((state) => state.blocks[id]?.enabled ?? true)
|
||||
const horizontalHandles = useWorkflowStore(
|
||||
(state) => state.blocks[id]?.horizontalHandles ?? false
|
||||
)
|
||||
@@ -44,9 +36,7 @@ export function WorkflowBlock({
|
||||
const [editedName, setEditedName] = useState('')
|
||||
const updateBlockName = useWorkflowStore((state) => state.updateBlockName)
|
||||
const blockRef = useRef<HTMLDivElement>(null)
|
||||
const [subBlockPositions, setSubBlockPositions] = useState<
|
||||
SubBlockPosition[]
|
||||
>([])
|
||||
const [subBlockPositions, setSubBlockPositions] = useState<SubBlockPosition[]>([])
|
||||
const updateNodeInternals = useUpdateNodeInternals()
|
||||
|
||||
// Calculate subblock positions when the component mounts or updates
|
||||
@@ -198,10 +188,7 @@ export function WorkflowBlock({
|
||||
)}
|
||||
</div>
|
||||
{!isEnabled && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="bg-gray-100 text-gray-500 hover:bg-gray-100"
|
||||
>
|
||||
<Badge variant="secondary" className="bg-gray-100 text-gray-500 hover:bg-gray-100">
|
||||
Disabled
|
||||
</Badge>
|
||||
)}
|
||||
@@ -213,16 +200,10 @@ export function WorkflowBlock({
|
||||
{row.map((subBlock, blockIndex) => (
|
||||
<div
|
||||
key={`${id}-${rowIndex}-${blockIndex}`}
|
||||
className={`space-y-1 ${
|
||||
subBlock.layout === 'half' ? 'flex-1' : 'w-full'
|
||||
}`}
|
||||
className={`space-y-1 ${subBlock.layout === 'half' ? 'flex-1' : 'w-full'}`}
|
||||
data-subblock-id={subBlock.id}
|
||||
>
|
||||
<SubBlock
|
||||
blockId={id}
|
||||
config={subBlock}
|
||||
isConnecting={isConnecting}
|
||||
/>
|
||||
<SubBlock blockId={id} config={subBlock} isConnecting={isConnecting} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -3,19 +3,19 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import ReactFlow, {
|
||||
Background,
|
||||
NodeTypes,
|
||||
EdgeTypes,
|
||||
useReactFlow,
|
||||
ConnectionLineType,
|
||||
EdgeTypes,
|
||||
NodeTypes,
|
||||
useReactFlow,
|
||||
} from 'reactflow'
|
||||
import { getBlock } from '../../../../../blocks'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import { useNotificationStore } from '@/stores/notifications/store'
|
||||
import { useWorkflowExecution } from '../../../hooks/use-workflow-execution'
|
||||
import { NotificationList } from '@/app/w/components/notifications/notifications'
|
||||
import { WorkflowNode } from '../workflow-node/workflow-node'
|
||||
import { CustomEdge } from '../custom-edge/custom-edge'
|
||||
import { initializeStateLogger } from '@/stores/workflow/logger'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import { NotificationList } from '@/app/w/components/notifications/notifications'
|
||||
import { getBlock } from '../../../../../blocks'
|
||||
import { useWorkflowExecution } from '../../../hooks/use-workflow-execution'
|
||||
import { CustomEdge } from '../custom-edge/custom-edge'
|
||||
import { WorkflowNode } from '../workflow-node/workflow-node'
|
||||
|
||||
// Define custom node and edge types for ReactFlow
|
||||
const nodeTypes: NodeTypes = {
|
||||
@@ -29,8 +29,7 @@ export function WorkflowCanvas() {
|
||||
const [selectedEdgeId, setSelectedEdgeId] = useState<string | null>(null)
|
||||
|
||||
// Store references and state management hooks
|
||||
const { isExecuting, executionResult, handleRunWorkflow } =
|
||||
useWorkflowExecution()
|
||||
const { isExecuting, executionResult, handleRunWorkflow } = useWorkflowExecution()
|
||||
const {
|
||||
blocks,
|
||||
edges,
|
||||
@@ -152,10 +151,7 @@ export function WorkflowCanvas() {
|
||||
// Handle keyboard shortcuts for edge deletion
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (
|
||||
(event.key === 'Delete' || event.key === 'Backspace') &&
|
||||
selectedEdgeId
|
||||
) {
|
||||
if ((event.key === 'Delete' || event.key === 'Backspace') && selectedEdgeId) {
|
||||
removeEdge(selectedEdgeId)
|
||||
setSelectedEdgeId(null)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NodeProps } from 'reactflow'
|
||||
import { WorkflowBlock } from '../workflow-block/workflow-block'
|
||||
import { BlockConfig } from '../../../../../blocks/types'
|
||||
import { WorkflowBlock } from '../workflow-block/workflow-block'
|
||||
|
||||
interface WorkflowNodeData {
|
||||
type: string
|
||||
@@ -8,13 +8,7 @@ interface WorkflowNodeData {
|
||||
name: string
|
||||
}
|
||||
|
||||
export const WorkflowNode = ({
|
||||
data,
|
||||
id,
|
||||
xPos,
|
||||
yPos,
|
||||
selected,
|
||||
}: NodeProps<WorkflowNodeData>) => (
|
||||
export const WorkflowNode = ({ data, id, xPos, yPos, selected }: NodeProps<WorkflowNodeData>) => (
|
||||
<WorkflowBlock
|
||||
id={id}
|
||||
type={data.type}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import { ReactFlowProvider } from 'reactflow'
|
||||
import 'reactflow/dist/style.css'
|
||||
import { useWorkflowRegistry } from '@/stores/workflow/registry'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import { WorkflowCanvas } from './components/workflow-canvas/workflow-canvas'
|
||||
|
||||
export default function Workflow() {
|
||||
@@ -66,14 +66,7 @@ export default function Workflow() {
|
||||
}
|
||||
|
||||
validateAndNavigate()
|
||||
}, [
|
||||
params.id,
|
||||
workflows,
|
||||
setActiveWorkflow,
|
||||
addWorkflow,
|
||||
router,
|
||||
isInitialized,
|
||||
])
|
||||
}, [params.id, workflows, setActiveWorkflow, addWorkflow, router, isInitialized])
|
||||
|
||||
// Don't render until initial data is loaded
|
||||
if (!isInitialized) {
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
import { useState, useMemo } from 'react'
|
||||
import { formatDistanceToNow, format } from 'date-fns'
|
||||
import {
|
||||
Terminal,
|
||||
Clock,
|
||||
Calendar,
|
||||
CheckCircle2,
|
||||
AlertCircle,
|
||||
AlertTriangle,
|
||||
} from 'lucide-react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { format, formatDistanceToNow } from 'date-fns'
|
||||
import { AlertCircle, AlertTriangle, Calendar, CheckCircle2, Clock, Terminal } from 'lucide-react'
|
||||
import { ConsoleEntry as ConsoleEntryType } from '@/stores/console/types'
|
||||
import { JSONView } from '../json-view/json-view'
|
||||
import { getBlock } from '@/blocks'
|
||||
import { JSONView } from '../json-view/json-view'
|
||||
|
||||
interface ConsoleEntryProps {
|
||||
entry: ConsoleEntryType
|
||||
@@ -46,20 +39,14 @@ export function ConsoleEntry({ entry, consoleWidth }: ConsoleEntryProps) {
|
||||
return (
|
||||
<div
|
||||
className={`border-b border-border transition-colors ${
|
||||
!entry.error && !entry.warning
|
||||
? 'hover:bg-accent/50 cursor-pointer'
|
||||
: ''
|
||||
!entry.error && !entry.warning ? 'hover:bg-accent/50 cursor-pointer' : ''
|
||||
}`}
|
||||
onClick={() =>
|
||||
!entry.error && !entry.warning && setIsExpanded(!isExpanded)
|
||||
}
|
||||
onClick={() => !entry.error && !entry.warning && setIsExpanded(!isExpanded)}
|
||||
>
|
||||
<div className="p-4 space-y-4">
|
||||
<div
|
||||
className={`${
|
||||
consoleWidth >= 400
|
||||
? 'flex items-center justify-between'
|
||||
: 'grid gap-4 grid-cols-1'
|
||||
consoleWidth >= 400 ? 'flex items-center justify-between' : 'grid gap-4 grid-cols-1'
|
||||
}`}
|
||||
>
|
||||
{entry.blockName && (
|
||||
@@ -113,9 +100,7 @@ export function ConsoleEntry({ entry, consoleWidth }: ConsoleEntryProps) {
|
||||
<AlertTriangle className="h-4 w-4 text-yellow-500 mt-1" />
|
||||
<div className="flex-1 break-all">
|
||||
<div className="font-medium">Warning</div>
|
||||
<pre className="text-sm whitespace-pre-wrap">
|
||||
{entry.warning}
|
||||
</pre>
|
||||
<pre className="text-sm whitespace-pre-wrap">{entry.warning}</pre>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { ChevronRight, ChevronDown } from 'lucide-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ChevronDown, ChevronRight } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
interface JSONViewProps {
|
||||
@@ -40,11 +40,7 @@ const copyToClipboard = (data: any) => {
|
||||
navigator.clipboard.writeText(stringified)
|
||||
}
|
||||
|
||||
export const JSONView = ({
|
||||
data,
|
||||
level = 0,
|
||||
initiallyExpanded = false,
|
||||
}: JSONViewProps) => {
|
||||
export const JSONView = ({ data, level = 0, initiallyExpanded = false }: JSONViewProps) => {
|
||||
const [isCollapsed, setIsCollapsed] = useState(!initiallyExpanded)
|
||||
const [contextMenuPosition, setContextMenuPosition] = useState<{
|
||||
x: number
|
||||
@@ -74,15 +70,9 @@ export const JSONView = ({
|
||||
return (
|
||||
<span
|
||||
onContextMenu={handleContextMenu}
|
||||
className={`${
|
||||
typeof data === 'string' ? 'text-success' : 'text-info'
|
||||
} break-all relative`}
|
||||
className={`${typeof data === 'string' ? 'text-success' : 'text-info'} break-all relative`}
|
||||
>
|
||||
{typeof data === 'string' ? (
|
||||
<TruncatedValue value={stringValue} />
|
||||
) : (
|
||||
stringValue
|
||||
)}
|
||||
{typeof data === 'string' ? <TruncatedValue value={stringValue} /> : stringValue}
|
||||
{contextMenuPosition && (
|
||||
<div
|
||||
className="fixed z-50 bg-popover border rounded-md shadow-md py-1 min-w-[160px]"
|
||||
@@ -105,9 +95,7 @@ export const JSONView = ({
|
||||
const isEmpty = items.length === 0
|
||||
|
||||
if (isEmpty) {
|
||||
return (
|
||||
<span className="text-muted-foreground">{isArray ? '[]' : '{}'}</span>
|
||||
)
|
||||
return <span className="text-muted-foreground">{isArray ? '[]' : '{}'}</span>
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -119,9 +107,7 @@ export const JSONView = ({
|
||||
setIsCollapsed(!isCollapsed)
|
||||
}}
|
||||
>
|
||||
<span className="text-xs leading-none mr-1">
|
||||
{isCollapsed ? '▶' : '▼'}
|
||||
</span>
|
||||
<span className="text-xs leading-none mr-1">{isCollapsed ? '▶' : '▼'}</span>
|
||||
<span>{isArray ? '[' : '{'}</span>
|
||||
{isCollapsed ? '...' : ''}
|
||||
</span>
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useMemo, useEffect } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { PanelLeftClose, Terminal } from 'lucide-react'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip'
|
||||
import { useConsoleStore } from '@/stores/console/store'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { useConsoleStore } from '@/stores/console/store'
|
||||
import { useWorkflowRegistry } from '@/stores/workflow/registry'
|
||||
import { ConsoleEntry } from './components/console-entry/console-entry'
|
||||
|
||||
|
||||
@@ -31,10 +31,7 @@ export function HistoryDropdownItem({
|
||||
onClick={onClick}
|
||||
>
|
||||
<Clock
|
||||
className={cn(
|
||||
'h-4 w-4',
|
||||
isFuture ? 'text-muted-foreground/50' : 'text-muted-foreground'
|
||||
)}
|
||||
className={cn('h-4 w-4', isFuture ? 'text-muted-foreground/50' : 'text-muted-foreground')}
|
||||
/>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -58,12 +55,7 @@ export function HistoryDropdownItem({
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p
|
||||
className={cn(
|
||||
'text-sm',
|
||||
isFuture ? 'text-muted-foreground/50' : 'text-foreground'
|
||||
)}
|
||||
>
|
||||
<p className={cn('text-sm', isFuture ? 'text-muted-foreground/50' : 'text-foreground')}>
|
||||
{action}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { formatDistanceToNow } from 'date-fns'
|
||||
import { Terminal, AlertCircle } from 'lucide-react'
|
||||
import { DropdownMenuItem } from '@/components/ui/dropdown-menu'
|
||||
import {
|
||||
NotificationType,
|
||||
NotificationStore,
|
||||
} from '@/stores/notifications/types'
|
||||
import { useNotificationStore } from '@/stores/notifications/store'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { AlertCircle, Terminal } from 'lucide-react'
|
||||
import { ErrorIcon } from '@/components/icons'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { DropdownMenuItem } from '@/components/ui/dropdown-menu'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useNotificationStore } from '@/stores/notifications/store'
|
||||
import { NotificationStore, NotificationType } from '@/stores/notifications/types'
|
||||
|
||||
interface NotificationDropdownItemProps {
|
||||
id: string
|
||||
@@ -46,8 +43,7 @@ export function NotificationDropdownItem({
|
||||
const timeAgo = formatDistanceToNow(timestamp, { addSuffix: true })
|
||||
|
||||
// Truncate message if it's too long
|
||||
const truncatedMessage =
|
||||
message.length > 50 ? `${message.slice(0, 50)}...` : message
|
||||
const truncatedMessage = message.length > 50 ? `${message.slice(0, 50)}...` : message
|
||||
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
@@ -57,9 +53,7 @@ export function NotificationDropdownItem({
|
||||
<Icon className={cn('h-4 w-4', NotificationColors[type])} />
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs font-medium">
|
||||
{type === 'error' ? 'Error' : 'Console'}
|
||||
</span>
|
||||
<span className="text-xs font-medium">{type === 'error' ? 'Error' : 'Console'}</span>
|
||||
<span className="text-xs text-muted-foreground">{timeAgo}</span>
|
||||
</div>
|
||||
<p className="text-sm text-foreground">{truncatedMessage}</p>
|
||||
|
||||
@@ -1,27 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { History, Bell, Play, Trash2, MessageSquare } from 'lucide-react'
|
||||
import { useNotificationStore } from '@/stores/notifications/store'
|
||||
import { NotificationDropdownItem } from './components/notification-dropdown-item'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import { HistoryDropdownItem } from './components/history-dropdown-item'
|
||||
import { formatDistanceToNow } from 'date-fns'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useWorkflowExecution } from '../../hooks/use-workflow-execution'
|
||||
import { useWorkflowRegistry } from '@/stores/workflow/registry'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip'
|
||||
import { formatDistanceToNow } from 'date-fns'
|
||||
import { Bell, History, MessageSquare, Play, Trash2 } from 'lucide-react'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -33,14 +15,27 @@ import {
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from '@/components/ui/alert-dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { useNotificationStore } from '@/stores/notifications/store'
|
||||
import { useWorkflowRegistry } from '@/stores/workflow/registry'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import { useWorkflowExecution } from '../../hooks/use-workflow-execution'
|
||||
import { HistoryDropdownItem } from './components/history-dropdown-item'
|
||||
import { NotificationDropdownItem } from './components/notification-dropdown-item'
|
||||
|
||||
export function ControlBar() {
|
||||
const { notifications, getWorkflowNotifications } = useNotificationStore()
|
||||
const { history, undo, redo, revertToHistoryState } = useWorkflowStore()
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [editedName, setEditedName] = useState('')
|
||||
const { workflows, updateWorkflow, activeWorkflowId, removeWorkflow } =
|
||||
useWorkflowRegistry()
|
||||
const { workflows, updateWorkflow, activeWorkflowId, removeWorkflow } = useWorkflowRegistry()
|
||||
const [, forceUpdate] = useState({})
|
||||
const { isExecuting, handleRunWorkflow } = useWorkflowExecution()
|
||||
const router = useRouter()
|
||||
@@ -167,8 +162,7 @@ export function ControlBar() {
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Delete Workflow</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Are you sure you want to delete this workflow? This action
|
||||
cannot be undone.
|
||||
Are you sure you want to delete this workflow? This action cannot be undone.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
@@ -198,10 +192,7 @@ export function ControlBar() {
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
) : (
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
className="w-60 max-h-[300px] overflow-y-auto"
|
||||
>
|
||||
<DropdownMenuContent align="end" className="w-60 max-h-[300px] overflow-y-auto">
|
||||
<>
|
||||
{[...history.future].reverse().map((entry, index) => (
|
||||
<HistoryDropdownItem
|
||||
@@ -210,9 +201,7 @@ export function ControlBar() {
|
||||
timestamp={entry.timestamp}
|
||||
onClick={() =>
|
||||
revertToHistoryState(
|
||||
history.past.length +
|
||||
1 +
|
||||
(history.future.length - 1 - index)
|
||||
history.past.length + 1 + (history.future.length - 1 - index)
|
||||
)
|
||||
}
|
||||
isFuture={true}
|
||||
@@ -230,9 +219,7 @@ export function ControlBar() {
|
||||
key={`past-${entry.timestamp}-${index}`}
|
||||
action={entry.action}
|
||||
timestamp={entry.timestamp}
|
||||
onClick={() =>
|
||||
revertToHistoryState(history.past.length - 1 - index)
|
||||
}
|
||||
onClick={() => revertToHistoryState(history.past.length - 1 - index)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
@@ -255,17 +242,11 @@ export function ControlBar() {
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
) : (
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
className="w-60 max-h-[300px] overflow-y-auto"
|
||||
>
|
||||
<DropdownMenuContent align="end" className="w-60 max-h-[300px] overflow-y-auto">
|
||||
{[...workflowNotifications]
|
||||
.sort((a, b) => b.timestamp - a.timestamp)
|
||||
.map((notification) => (
|
||||
<NotificationDropdownItem
|
||||
key={notification.id}
|
||||
{...notification}
|
||||
/>
|
||||
<NotificationDropdownItem key={notification.id} {...notification} />
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
)}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { AlertCircle, Terminal } from 'lucide-react'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { useNotificationStore } from '@/stores/notifications/store'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { ErrorIcon } from '@/components/icons'
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useNotificationStore } from '@/stores/notifications/store'
|
||||
|
||||
const NOTIFICATION_TIMEOUT = 4000
|
||||
const FADE_DURATION = 300
|
||||
@@ -22,9 +22,7 @@ const NotificationColors = {
|
||||
|
||||
export function NotificationList() {
|
||||
const { notifications, hideNotification } = useNotificationStore()
|
||||
const [fadingNotifications, setFadingNotifications] = useState<Set<string>>(
|
||||
new Set()
|
||||
)
|
||||
const [fadingNotifications, setFadingNotifications] = useState<Set<string>>(new Set())
|
||||
|
||||
// Only show visible notifications in the display
|
||||
const visibleNotifications = notifications.filter((n) => n.isVisible)
|
||||
@@ -74,9 +72,7 @@ export function NotificationList() {
|
||||
key={notification.id}
|
||||
className={cn(
|
||||
'transition-all duration-300 ease-in-out opacity-0 translate-y-[-100%]',
|
||||
isFading
|
||||
? 'animate-notification-fade-out'
|
||||
: 'animate-notification-slide',
|
||||
isFading ? 'animate-notification-fade-out' : 'animate-notification-slide',
|
||||
NotificationColors[notification.type]
|
||||
)}
|
||||
>
|
||||
@@ -89,9 +85,7 @@ export function NotificationList() {
|
||||
<AlertTitle className="ml-2">
|
||||
{notification.type === 'error' ? 'Error' : 'Console'}
|
||||
</AlertTitle>
|
||||
<AlertDescription className="ml-2">
|
||||
{notification.message}
|
||||
</AlertDescription>
|
||||
<AlertDescription className="ml-2">{notification.message}</AlertDescription>
|
||||
</Alert>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip'
|
||||
import clsx from 'clsx'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import clsx from 'clsx'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
|
||||
export function NavItem({
|
||||
href,
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -16,10 +11,10 @@ import {
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { useState, useRef, useEffect, useMemo } from 'react'
|
||||
import { useEnvironmentStore } from '@/stores/environment/store'
|
||||
import { EnvironmentVariable as StoreEnvironmentVariable } from '@/stores/environment/types'
|
||||
|
||||
@@ -39,9 +34,7 @@ const INITIAL_ENV_VAR: UIEnvironmentVariable = { key: '', value: '' }
|
||||
export function SettingsModal({ open, onOpenChange }: SettingsModalProps) {
|
||||
const { variables, setVariable, removeVariable } = useEnvironmentStore()
|
||||
const [envVars, setEnvVars] = useState<UIEnvironmentVariable[]>([])
|
||||
const [focusedValueIndex, setFocusedValueIndex] = useState<number | null>(
|
||||
null
|
||||
)
|
||||
const [focusedValueIndex, setFocusedValueIndex] = useState<number | null>(null)
|
||||
const [showUnsavedChanges, setShowUnsavedChanges] = useState(false)
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
||||
const pendingClose = useRef(false)
|
||||
@@ -111,15 +104,11 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) {
|
||||
|
||||
useEffect(() => {
|
||||
if (scrollContainerRef.current) {
|
||||
scrollContainerRef.current.scrollTop =
|
||||
scrollContainerRef.current.scrollHeight
|
||||
scrollContainerRef.current.scrollTop = scrollContainerRef.current.scrollHeight
|
||||
}
|
||||
}, [envVars])
|
||||
|
||||
const handleValueFocus = (
|
||||
index: number,
|
||||
e: React.FocusEvent<HTMLInputElement>
|
||||
) => {
|
||||
const handleValueFocus = (index: number, e: React.FocusEvent<HTMLInputElement>) => {
|
||||
setFocusedValueIndex(index)
|
||||
// Always scroll to the start of the input
|
||||
e.target.scrollLeft = 0
|
||||
@@ -137,10 +126,7 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) {
|
||||
}
|
||||
}
|
||||
|
||||
const handlePaste = (
|
||||
e: React.ClipboardEvent<HTMLInputElement>,
|
||||
index: number
|
||||
) => {
|
||||
const handlePaste = (e: React.ClipboardEvent<HTMLInputElement>, index: number) => {
|
||||
const text = e.clipboardData.getData('text').trim()
|
||||
if (!text) return
|
||||
|
||||
@@ -149,9 +135,9 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) {
|
||||
|
||||
e.preventDefault()
|
||||
|
||||
const inputType = (e.target as HTMLInputElement).getAttribute(
|
||||
'data-input-type'
|
||||
) as 'key' | 'value'
|
||||
const inputType = (e.target as HTMLInputElement).getAttribute('data-input-type') as
|
||||
| 'key'
|
||||
| 'value'
|
||||
const containsKeyValuePair = text.includes('=')
|
||||
|
||||
// Handle single value paste into specific field
|
||||
@@ -164,11 +150,7 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) {
|
||||
handleKeyValuePaste(lines)
|
||||
}
|
||||
|
||||
const handleSingleValuePaste = (
|
||||
text: string,
|
||||
index: number,
|
||||
inputType: 'key' | 'value'
|
||||
) => {
|
||||
const handleSingleValuePaste = (text: string, index: number, inputType: 'key' | 'value') => {
|
||||
const newEnvVars = [...envVars]
|
||||
newEnvVars[index][inputType] = text
|
||||
setEnvVars(newEnvVars)
|
||||
@@ -182,14 +164,14 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) {
|
||||
return {
|
||||
key: key.trim(),
|
||||
value,
|
||||
id: Date.now() + Math.random()
|
||||
id: Date.now() + Math.random(),
|
||||
}
|
||||
})
|
||||
.filter(({ key, value }) => key && value)
|
||||
|
||||
if (parsedVars.length > 0) {
|
||||
// Merge existing vars with new ones, removing any empty rows at the end
|
||||
const existingVars = envVars.filter(v => v.key || v.value)
|
||||
const existingVars = envVars.filter((v) => v.key || v.value)
|
||||
setEnvVars([...existingVars, ...parsedVars])
|
||||
}
|
||||
}
|
||||
@@ -200,11 +182,7 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) {
|
||||
setEnvVars([...envVars, newVar])
|
||||
}
|
||||
|
||||
const updateEnvVar = (
|
||||
index: number,
|
||||
field: 'key' | 'value',
|
||||
value: string
|
||||
) => {
|
||||
const updateEnvVar = (index: number, field: 'key' | 'value', value: string) => {
|
||||
const newEnvVars = [...envVars]
|
||||
newEnvVars[index][field] = value
|
||||
setEnvVars(newEnvVars)
|
||||
@@ -254,12 +232,7 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) {
|
||||
placeholder="Enter value"
|
||||
className="allow-scroll"
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => removeEnvVar(index)}
|
||||
className="h-10 w-10"
|
||||
>
|
||||
<Button variant="ghost" size="icon" onClick={() => removeEnvVar(index)} className="h-10 w-10">
|
||||
×
|
||||
</Button>
|
||||
</div>
|
||||
@@ -309,10 +282,7 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) {
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<AlertDialog
|
||||
open={showUnsavedChanges}
|
||||
onOpenChange={setShowUnsavedChanges}
|
||||
>
|
||||
<AlertDialog open={showUnsavedChanges} onOpenChange={setShowUnsavedChanges}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Unsaved Changes</AlertDialogTitle>
|
||||
@@ -321,12 +291,8 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) {
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={handleCancel}>
|
||||
Discard Changes
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleSave}>
|
||||
Save Changes
|
||||
</AlertDialogAction>
|
||||
<AlertDialogCancel onClick={handleCancel}>Discard Changes</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleSave}>Save Changes</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { NavItem } from './components/nav-item/nav-item'
|
||||
import { Settings, Plus } from 'lucide-react'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip'
|
||||
import { AgentIcon } from '@/components/icons'
|
||||
import { useWorkflowRegistry } from '@/stores/workflow/registry'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { Plus, Settings } from 'lucide-react'
|
||||
import { AgentIcon } from '@/components/icons'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { useWorkflowRegistry } from '@/stores/workflow/registry'
|
||||
import { NavItem } from './components/nav-item/nav-item'
|
||||
import { SettingsModal } from './components/settings-modal/settings-modal'
|
||||
|
||||
export function Sidebar() {
|
||||
const WORKFLOW_COLORS = [
|
||||
'#3972F6',
|
||||
'#F639DD',
|
||||
'#F6B539',
|
||||
'#8139F6',
|
||||
'#F64439',
|
||||
]
|
||||
const WORKFLOW_COLORS = ['#3972F6', '#F639DD', '#F6B539', '#8139F6', '#F64439']
|
||||
const { workflows, addWorkflow } = useWorkflowRegistry()
|
||||
const router = useRouter()
|
||||
const [showSettings, setShowSettings] = useState(false)
|
||||
@@ -33,9 +23,7 @@ export function Sidebar() {
|
||||
const lastWorkflow = workflowArray[workflowArray.length - 1]
|
||||
|
||||
// Find the index of the last used color, defaulting to first color if undefined
|
||||
const lastColorIndex = lastWorkflow?.color
|
||||
? WORKFLOW_COLORS.indexOf(lastWorkflow.color)
|
||||
: -1
|
||||
const lastColorIndex = lastWorkflow?.color ? WORKFLOW_COLORS.indexOf(lastWorkflow.color) : -1
|
||||
|
||||
// Get next color index, wrapping around to 0 if we reach the end
|
||||
const nextColorIndex = (lastColorIndex + 1) % WORKFLOW_COLORS.length
|
||||
@@ -83,11 +71,7 @@ export function Sidebar() {
|
||||
<nav className="flex-1 overflow-y-auto px-2 [&::-webkit-scrollbar]:hidden [-ms-overflow-style:'none'] [scrollbar-width:'none']">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
{Object.values(workflows).map((workflow) => (
|
||||
<NavItem
|
||||
key={workflow.id}
|
||||
href={`/w/${workflow.id}`}
|
||||
label={workflow.name}
|
||||
>
|
||||
<NavItem key={workflow.id} href={`/w/${workflow.id}`} label={workflow.name}>
|
||||
<div
|
||||
className="h-4 w-4 rounded-full"
|
||||
style={{ backgroundColor: workflow.color || '#3972F6' }}
|
||||
|
||||
@@ -6,10 +6,7 @@ export type ToolbarBlockProps = {
|
||||
|
||||
export function ToolbarBlock({ config }: ToolbarBlockProps) {
|
||||
const handleDragStart = (e: React.DragEvent) => {
|
||||
e.dataTransfer.setData(
|
||||
'application/json',
|
||||
JSON.stringify({ type: config.type })
|
||||
)
|
||||
e.dataTransfer.setData('application/json', JSON.stringify({ type: config.type }))
|
||||
e.dataTransfer.effectAllowed = 'move'
|
||||
}
|
||||
|
||||
@@ -31,9 +28,7 @@ export function ToolbarBlock({ config }: ToolbarBlockProps) {
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<h3 className="font-medium leading-none">{config.toolbar.title}</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{config.toolbar.description}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">{config.toolbar.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useRef, useEffect, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
interface ToolbarTabsProps {
|
||||
activeTab: 'blocks' | 'tools'
|
||||
@@ -19,8 +19,7 @@ export function ToolbarTabs({ activeTab, onTabChange }: ToolbarTabsProps) {
|
||||
const activeRef = activeTab === 'blocks' ? blocksRef : toolsRef
|
||||
if (activeRef.current) {
|
||||
const rect = activeRef.current.getBoundingClientRect()
|
||||
const parentRect =
|
||||
activeRef.current.parentElement?.getBoundingClientRect()
|
||||
const parentRect = activeRef.current.parentElement?.getBoundingClientRect()
|
||||
const offsetLeft = parentRect ? rect.left - parentRect.left : 0
|
||||
|
||||
setUnderlineStyle({
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useMemo } from 'react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { PanelRight, PanelRightClose, Search } from 'lucide-react'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Search, PanelRightClose, PanelRight } from 'lucide-react'
|
||||
import { ToolbarTabs } from './components/toolbar-tabs/toolbar-tabs'
|
||||
import { ToolbarBlock } from './components/toolbar-block/toolbar-block'
|
||||
import { getBlocksByCategory, getAllBlocks } from '../../../../blocks'
|
||||
import { BlockCategory, BlockConfig } from '../../../../blocks/types'
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from '@/components/ui/tooltip'
|
||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
|
||||
import { getAllBlocks, getBlocksByCategory } from '../../../../blocks'
|
||||
import { BlockCategory, BlockConfig } from '../../../../blocks/types'
|
||||
import { ToolbarBlock } from './components/toolbar-block/toolbar-block'
|
||||
import { ToolbarTabs } from './components/toolbar-tabs/toolbar-tabs'
|
||||
|
||||
export function Toolbar() {
|
||||
const [activeTab, setActiveTab] = useState<BlockCategory>('blocks')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import { shallow } from 'zustand/shallow'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
|
||||
export interface ConnectedBlock {
|
||||
id: string
|
||||
@@ -12,14 +12,14 @@ export function useBlockConnections(blockId: string) {
|
||||
const { edges, blocks } = useWorkflowStore(
|
||||
(state) => ({
|
||||
edges: state.edges,
|
||||
blocks: state.blocks
|
||||
blocks: state.blocks,
|
||||
}),
|
||||
shallow
|
||||
)
|
||||
|
||||
const incomingConnections = edges
|
||||
.filter(edge => edge.target === blockId)
|
||||
.map(edge => {
|
||||
.filter((edge) => edge.target === blockId)
|
||||
.map((edge) => {
|
||||
const sourceBlock = blocks[edge.source]
|
||||
return {
|
||||
id: sourceBlock.id,
|
||||
@@ -31,6 +31,6 @@ export function useBlockConnections(blockId: string) {
|
||||
|
||||
return {
|
||||
incomingConnections,
|
||||
hasIncomingConnections: incomingConnections.length > 0
|
||||
hasIncomingConnections: incomingConnections.length > 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import { Serializer } from '@/serializer'
|
||||
import { Executor } from '@/executor'
|
||||
import { ExecutionResult } from '@/executor/types'
|
||||
import { useNotificationStore } from '@/stores/notifications/store'
|
||||
import { useWorkflowRegistry } from '@/stores/workflow/registry'
|
||||
import { useConsoleStore } from '@/stores/console/store'
|
||||
import { useEnvironmentStore } from '@/stores/environment/store'
|
||||
import { useNotificationStore } from '@/stores/notifications/store'
|
||||
import { useWorkflowRegistry } from '@/stores/workflow/registry'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
import { Executor } from '@/executor'
|
||||
import { ExecutionResult } from '@/executor/types'
|
||||
import { Serializer } from '@/serializer'
|
||||
|
||||
export function useWorkflowExecution() {
|
||||
const [isExecuting, setIsExecuting] = useState(false)
|
||||
@@ -28,25 +28,31 @@ export function useWorkflowExecution() {
|
||||
|
||||
try {
|
||||
// Extract existing block states
|
||||
const currentBlockStates = Object.entries(blocks).reduce((acc, [id, block]) => {
|
||||
const responseValue = block.subBlocks?.response?.value
|
||||
if (responseValue !== undefined) {
|
||||
acc[id] = { response: responseValue }
|
||||
}
|
||||
return acc
|
||||
}, {} as Record<string, any>)
|
||||
const currentBlockStates = Object.entries(blocks).reduce(
|
||||
(acc, [id, block]) => {
|
||||
const responseValue = block.subBlocks?.response?.value
|
||||
if (responseValue !== undefined) {
|
||||
acc[id] = { response: responseValue }
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, any>
|
||||
)
|
||||
|
||||
// Get environment variables
|
||||
const envVars = getAllVariables()
|
||||
const envVarValues = Object.entries(envVars).reduce((acc, [key, variable]) => {
|
||||
acc[key] = variable.value
|
||||
return acc
|
||||
}, {} as Record<string, string>)
|
||||
const envVarValues = Object.entries(envVars).reduce(
|
||||
(acc, [key, variable]) => {
|
||||
acc[key] = variable.value
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, string>
|
||||
)
|
||||
|
||||
// Execute workflow
|
||||
const workflow = new Serializer().serializeWorkflow(blocks, edges)
|
||||
const executor = new Executor(workflow, currentBlockStates, envVarValues)
|
||||
|
||||
|
||||
const result = await executor.execute('my-run-id')
|
||||
setExecutionResult(result)
|
||||
|
||||
@@ -62,7 +68,7 @@ export function useWorkflowExecution() {
|
||||
workflowId: activeWorkflowId,
|
||||
timestamp: log.startedAt,
|
||||
blockName: log.blockTitle,
|
||||
blockType: log.blockType
|
||||
blockType: log.blockType,
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -75,7 +81,7 @@ export function useWorkflowExecution() {
|
||||
error: log.error,
|
||||
durationMs: log.durationMs,
|
||||
startedAt: log.startedAt,
|
||||
endedAt: log.endedAt
|
||||
endedAt: log.endedAt,
|
||||
})
|
||||
})
|
||||
console.groupEnd()
|
||||
@@ -84,7 +90,7 @@ export function useWorkflowExecution() {
|
||||
// Show execution result with workflowId
|
||||
addNotification(
|
||||
result.success ? 'console' : 'error',
|
||||
result.success
|
||||
result.success
|
||||
? 'Workflow completed successfully'
|
||||
: `Workflow execution failed: ${result.error}`,
|
||||
activeWorkflowId
|
||||
@@ -95,7 +101,7 @@ export function useWorkflowExecution() {
|
||||
success: false,
|
||||
output: { response: {} },
|
||||
error: errorMessage,
|
||||
logs: []
|
||||
logs: [],
|
||||
})
|
||||
|
||||
// Add error entry to console
|
||||
@@ -107,14 +113,23 @@ export function useWorkflowExecution() {
|
||||
endedAt: new Date().toISOString(),
|
||||
workflowId: activeWorkflowId,
|
||||
timestamp: new Date().toISOString(),
|
||||
blockName: 'Error'
|
||||
blockName: 'Error',
|
||||
})
|
||||
|
||||
addNotification('error', `Workflow execution failed: ${errorMessage}`, activeWorkflowId)
|
||||
} finally {
|
||||
setIsExecuting(false)
|
||||
}
|
||||
}, [activeWorkflowId, blocks, edges, addNotification, addConsole, isOpen, toggleConsole, getAllVariables])
|
||||
}, [
|
||||
activeWorkflowId,
|
||||
blocks,
|
||||
edges,
|
||||
addNotification,
|
||||
addConsole,
|
||||
isOpen,
|
||||
toggleConsole,
|
||||
getAllVariables,
|
||||
])
|
||||
|
||||
return { isExecuting, executionResult, handleRunWorkflow }
|
||||
}
|
||||
}
|
||||
|
||||
+4
-8
@@ -1,14 +1,10 @@
|
||||
import Providers from './providers'
|
||||
import { Toolbar } from './components/toolbar/toolbar'
|
||||
import { Console } from './components/console/console'
|
||||
import { ControlBar } from './components/control-bar/control-bar'
|
||||
import { Sidebar } from './components/sidebar/sidebar'
|
||||
import { Console } from './components/console/console'
|
||||
import { Toolbar } from './components/toolbar/toolbar'
|
||||
import Providers from './providers'
|
||||
|
||||
export default function WorkspaceLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
export default function WorkspaceLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Providers>
|
||||
<div className="flex min-h-screen w-full">
|
||||
|
||||
+29
-22
@@ -1,7 +1,7 @@
|
||||
import { BlockConfig } from '../types'
|
||||
import { AgentIcon } from '@/components/icons'
|
||||
import { MODEL_TOOLS, ModelType } from '../consts'
|
||||
import { ToolResponse } from '@/tools/types'
|
||||
import { MODEL_TOOLS, ModelType } from '../consts'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
interface AgentResponse extends ToolResponse {
|
||||
output: {
|
||||
@@ -32,7 +32,14 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
|
||||
category: 'blocks',
|
||||
},
|
||||
tools: {
|
||||
access: ['openai_chat', 'anthropic_chat', 'google_chat', 'xai_chat', 'deepseek_chat', 'deepseek_reasoner'],
|
||||
access: [
|
||||
'openai_chat',
|
||||
'anthropic_chat',
|
||||
'google_chat',
|
||||
'xai_chat',
|
||||
'deepseek_chat',
|
||||
'deepseek_reasoner',
|
||||
],
|
||||
config: {
|
||||
tool: (params: Record<string, any>) => {
|
||||
const model = params.model || 'gpt-4o'
|
||||
@@ -44,8 +51,8 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
|
||||
throw new Error(`Invalid model selected: ${model}`)
|
||||
}
|
||||
return tool
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
@@ -55,7 +62,7 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
|
||||
apiKey: { type: 'string', required: true },
|
||||
responseFormat: { type: 'json', required: false },
|
||||
temperature: { type: 'number', required: false },
|
||||
tools: { type: 'json', required: false }
|
||||
tools: { type: 'json', required: false },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
@@ -63,9 +70,9 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
|
||||
content: 'string',
|
||||
model: 'string',
|
||||
tokens: 'any',
|
||||
toolCalls: 'any'
|
||||
}
|
||||
}
|
||||
toolCalls: 'any',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -73,21 +80,21 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
|
||||
title: 'System Prompt',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter system prompt...'
|
||||
placeholder: 'Enter system prompt...',
|
||||
},
|
||||
{
|
||||
id: 'context',
|
||||
title: 'Context',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter context or user message...'
|
||||
placeholder: 'Enter context or user message...',
|
||||
},
|
||||
{
|
||||
id: 'model',
|
||||
title: 'Model',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: Object.keys(MODEL_TOOLS)
|
||||
options: Object.keys(MODEL_TOOLS),
|
||||
},
|
||||
{
|
||||
id: 'temperature',
|
||||
@@ -99,25 +106,25 @@ export const AgentBlock: BlockConfig<AgentResponse> = {
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: "API Key",
|
||||
type: "short-input",
|
||||
layout: "full",
|
||||
placeholder: "Enter your API key",
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your API key',
|
||||
password: true,
|
||||
connectionDroppable: false
|
||||
connectionDroppable: false,
|
||||
},
|
||||
{
|
||||
id: 'tools',
|
||||
title: 'Tools',
|
||||
type: 'tool-input',
|
||||
layout: 'full'
|
||||
layout: 'full',
|
||||
},
|
||||
{
|
||||
id: 'responseFormat',
|
||||
title: 'Response Format',
|
||||
type: 'code',
|
||||
layout: 'full'
|
||||
layout: 'full',
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ApiIcon } from '@/components/icons'
|
||||
import { BlockConfig } from '../types'
|
||||
import { RequestResponse } from '@/tools/http/request'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const ApiBlock: BlockConfig<RequestResponse> = {
|
||||
type: 'api',
|
||||
@@ -12,23 +12,23 @@ export const ApiBlock: BlockConfig<RequestResponse> = {
|
||||
category: 'blocks',
|
||||
},
|
||||
tools: {
|
||||
access: ['http_request']
|
||||
access: ['http_request'],
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
url: { type: 'string', required: true },
|
||||
method: { type: 'string', required: true },
|
||||
headers: { type: 'json', required: false },
|
||||
body: { type: 'json', required: false }
|
||||
body: { type: 'json', required: false },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
data: 'any',
|
||||
status: 'number',
|
||||
headers: 'json'
|
||||
}
|
||||
}
|
||||
headers: 'json',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -60,4 +60,4 @@ export const ApiBlock: BlockConfig<RequestResponse> = {
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -1,6 +1,6 @@
|
||||
import { ConditionalIcon } from '@/components/icons'
|
||||
import { BlockConfig } from '../types'
|
||||
import { CodeExecutionOutput } from '@/tools/function/execute'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const ConditionBlock: BlockConfig<CodeExecutionOutput> = {
|
||||
type: 'condition',
|
||||
@@ -12,19 +12,19 @@ export const ConditionBlock: BlockConfig<CodeExecutionOutput> = {
|
||||
category: 'blocks',
|
||||
},
|
||||
tools: {
|
||||
access: ['function_execute']
|
||||
access: ['function_execute'],
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
code: { type: 'string', required: true }
|
||||
code: { type: 'string', required: true },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
result: 'any',
|
||||
stdout: 'string'
|
||||
}
|
||||
}
|
||||
stdout: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -32,15 +32,15 @@ export const ConditionBlock: BlockConfig<CodeExecutionOutput> = {
|
||||
title: 'if',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
outputHandle: true
|
||||
outputHandle: true,
|
||||
},
|
||||
{
|
||||
id: 'elseIf',
|
||||
title: 'else if',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
outputHandle: true
|
||||
}
|
||||
outputHandle: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+14
-18
@@ -1,6 +1,6 @@
|
||||
import { BlockConfig } from '../types'
|
||||
import { CrewAIIcon } from '@/components/icons'
|
||||
import { VisionResponse } from '@/tools/crewai/vision'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const CrewAIVisionBlock: BlockConfig<VisionResponse> = {
|
||||
type: 'crewai_vision',
|
||||
@@ -9,26 +9,26 @@ export const CrewAIVisionBlock: BlockConfig<VisionResponse> = {
|
||||
description: 'Analyze images with vision models',
|
||||
bgColor: '#FF5A50',
|
||||
icon: CrewAIIcon,
|
||||
category: 'tools'
|
||||
category: 'tools',
|
||||
},
|
||||
tools: {
|
||||
access: ['crewai_vision']
|
||||
access: ['crewai_vision'],
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
apiKey: { type: 'string', required: true },
|
||||
imageUrl: { type: 'string', required: true },
|
||||
model: { type: 'string', required: false },
|
||||
prompt: { type: 'string', required: false }
|
||||
prompt: { type: 'string', required: false },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
content: 'string',
|
||||
model: 'any',
|
||||
tokens: 'any'
|
||||
}
|
||||
}
|
||||
tokens: 'any',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -36,25 +36,21 @@ export const CrewAIVisionBlock: BlockConfig<VisionResponse> = {
|
||||
title: 'Image URL',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter publicly accessible image URL'
|
||||
placeholder: 'Enter publicly accessible image URL',
|
||||
},
|
||||
{
|
||||
id: 'model',
|
||||
title: 'Vision Model',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: [
|
||||
'gpt-4o',
|
||||
'claude-3-opus-20240229',
|
||||
'claude-3-sonnet-20240229'
|
||||
]
|
||||
options: ['gpt-4o', 'claude-3-opus-20240229', 'claude-3-sonnet-20240229'],
|
||||
},
|
||||
{
|
||||
id: 'prompt',
|
||||
title: 'Prompt',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter prompt for image analysis (optional)'
|
||||
placeholder: 'Enter prompt for image analysis (optional)',
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
@@ -62,8 +58,8 @@ export const CrewAIVisionBlock: BlockConfig<VisionResponse> = {
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your API key',
|
||||
password: true
|
||||
password: true,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+13
-13
@@ -1,6 +1,6 @@
|
||||
import { BlockConfig } from '../types'
|
||||
import { FirecrawlIcon } from '@/components/icons'
|
||||
import { ScrapeResponse } from '@/tools/firecrawl/scrape'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const FirecrawlScrapeBlock: BlockConfig<ScrapeResponse> = {
|
||||
type: 'firecrawl_scrape',
|
||||
@@ -9,25 +9,25 @@ export const FirecrawlScrapeBlock: BlockConfig<ScrapeResponse> = {
|
||||
description: 'Scrape website content',
|
||||
bgColor: '#181C1E',
|
||||
icon: FirecrawlIcon,
|
||||
category: 'tools'
|
||||
category: 'tools',
|
||||
},
|
||||
tools: {
|
||||
access: ['firecrawl_scrape']
|
||||
access: ['firecrawl_scrape'],
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
apiKey: { type: 'string', required: true },
|
||||
url: { type: 'string', required: true },
|
||||
scrapeOptions: { type: 'json', required: false }
|
||||
scrapeOptions: { type: 'json', required: false },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
markdown: 'string',
|
||||
html: 'any',
|
||||
metadata: 'json'
|
||||
}
|
||||
}
|
||||
metadata: 'json',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -35,13 +35,13 @@ export const FirecrawlScrapeBlock: BlockConfig<ScrapeResponse> = {
|
||||
title: 'Website URL',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the webpage URL to scrape'
|
||||
placeholder: 'Enter the webpage URL to scrape',
|
||||
},
|
||||
{
|
||||
id: 'onlyMainContent',
|
||||
title: 'Only Main Content',
|
||||
type: 'switch',
|
||||
layout: 'half'
|
||||
layout: 'half',
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
@@ -49,8 +49,8 @@ export const FirecrawlScrapeBlock: BlockConfig<ScrapeResponse> = {
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your Firecrawl API key',
|
||||
password: true
|
||||
password: true,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { CodeIcon } from '@/components/icons'
|
||||
import { BlockConfig } from '../types'
|
||||
import { CodeExecutionOutput } from '@/tools/function/execute'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const FunctionBlock: BlockConfig<CodeExecutionOutput> = {
|
||||
type: 'function',
|
||||
@@ -12,26 +12,26 @@ export const FunctionBlock: BlockConfig<CodeExecutionOutput> = {
|
||||
category: 'blocks',
|
||||
},
|
||||
tools: {
|
||||
access: ['function_execute']
|
||||
access: ['function_execute'],
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
code: { type: 'string', required: true }
|
||||
code: { type: 'string', required: true },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
result: 'any',
|
||||
stdout: 'string'
|
||||
}
|
||||
}
|
||||
stdout: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'code',
|
||||
type: 'code',
|
||||
layout: 'full',
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { RepoInfoResponse } from '@/tools/github/repo'
|
||||
import { GithubIcon } from '../../components/icons'
|
||||
import { BlockConfig } from '../types'
|
||||
import { RepoInfoResponse } from '@/tools/github/repo'
|
||||
|
||||
export const GitHubBlock: BlockConfig<RepoInfoResponse> = {
|
||||
type: 'github_repo_info',
|
||||
@@ -12,12 +12,12 @@ export const GitHubBlock: BlockConfig<RepoInfoResponse> = {
|
||||
category: 'tools',
|
||||
},
|
||||
tools: {
|
||||
access: ['github_repoinfo']
|
||||
access: ['github_repoinfo'],
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
owner: { type: 'string', required: true },
|
||||
repo: { type: 'string', required: true }
|
||||
repo: { type: 'string', required: true },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
@@ -27,9 +27,9 @@ export const GitHubBlock: BlockConfig<RepoInfoResponse> = {
|
||||
stars: 'number',
|
||||
forks: 'number',
|
||||
openIssues: 'number',
|
||||
language: 'string'
|
||||
}
|
||||
}
|
||||
language: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -52,8 +52,8 @@ export const GitHubBlock: BlockConfig<RepoInfoResponse> = {
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter GitHub Token',
|
||||
password: true
|
||||
}
|
||||
password: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+15
-15
@@ -1,6 +1,6 @@
|
||||
import { JinaAIIcon } from '@/components/icons'
|
||||
import { BlockConfig } from '../types'
|
||||
import { ReadUrlResponse } from '@/tools/jina/reader'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const JinaBlock: BlockConfig<ReadUrlResponse> = {
|
||||
type: 'jina_reader',
|
||||
@@ -9,10 +9,10 @@ export const JinaBlock: BlockConfig<ReadUrlResponse> = {
|
||||
description: 'Convert website content into text',
|
||||
bgColor: '#333333',
|
||||
icon: JinaAIIcon,
|
||||
category: 'tools'
|
||||
category: 'tools',
|
||||
},
|
||||
tools: {
|
||||
access: ['jina_readurl']
|
||||
access: ['jina_readurl'],
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
@@ -20,14 +20,14 @@ export const JinaBlock: BlockConfig<ReadUrlResponse> = {
|
||||
useReaderLMv2: { type: 'boolean', required: false },
|
||||
gatherLinks: { type: 'boolean', required: false },
|
||||
jsonResponse: { type: 'boolean', required: false },
|
||||
apiKey: { type: 'string', required: true }
|
||||
apiKey: { type: 'string', required: true },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
content: 'string'
|
||||
}
|
||||
}
|
||||
content: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -35,7 +35,7 @@ export const JinaBlock: BlockConfig<ReadUrlResponse> = {
|
||||
title: 'URL',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter URL to extract content from'
|
||||
placeholder: 'Enter URL to extract content from',
|
||||
},
|
||||
{
|
||||
id: 'options',
|
||||
@@ -45,8 +45,8 @@ export const JinaBlock: BlockConfig<ReadUrlResponse> = {
|
||||
options: [
|
||||
{ id: 'useReaderLMv2', label: 'Use Reader LM v2' },
|
||||
{ id: 'gatherLinks', label: 'Gather Links' },
|
||||
{ id: 'jsonResponse', label: 'JSON Response' }
|
||||
]
|
||||
{ id: 'jsonResponse', label: 'JSON Response' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
@@ -54,8 +54,8 @@ export const JinaBlock: BlockConfig<ReadUrlResponse> = {
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your Jina API key',
|
||||
password: true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
password: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+16
-37
@@ -1,6 +1,6 @@
|
||||
import { BlockConfig } from '../types'
|
||||
import { SerperIcon } from '@/components/icons'
|
||||
import { SearchResponse } from '@/tools/serper/search'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const SerperBlock: BlockConfig<SearchResponse> = {
|
||||
type: 'serper_search',
|
||||
@@ -12,7 +12,7 @@ export const SerperBlock: BlockConfig<SearchResponse> = {
|
||||
category: 'tools',
|
||||
},
|
||||
tools: {
|
||||
access: ['serper_search']
|
||||
access: ['serper_search'],
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
@@ -21,14 +21,14 @@ export const SerperBlock: BlockConfig<SearchResponse> = {
|
||||
num: { type: 'number', required: false },
|
||||
gl: { type: 'string', required: false },
|
||||
hl: { type: 'string', required: false },
|
||||
type: { type: 'string', required: false }
|
||||
type: { type: 'string', required: false },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
searchResults: 'json'
|
||||
}
|
||||
}
|
||||
searchResults: 'json',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -36,56 +36,35 @@ export const SerperBlock: BlockConfig<SearchResponse> = {
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your search query...'
|
||||
placeholder: 'Enter your search query...',
|
||||
},
|
||||
{
|
||||
id: 'type',
|
||||
title: 'Search Type',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: ['search', 'news', 'places', 'images']
|
||||
options: ['search', 'news', 'places', 'images'],
|
||||
},
|
||||
{
|
||||
id: 'num',
|
||||
title: 'Number of Results',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: ['10', '20', '30', '40', '50', '100']
|
||||
options: ['10', '20', '30', '40', '50', '100'],
|
||||
},
|
||||
{
|
||||
id: 'gl',
|
||||
title: 'Country',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: [
|
||||
'US',
|
||||
'GB',
|
||||
'CA',
|
||||
'AU',
|
||||
'DE',
|
||||
'FR',
|
||||
'ES',
|
||||
'IT',
|
||||
'JP',
|
||||
'KR'
|
||||
]
|
||||
options: ['US', 'GB', 'CA', 'AU', 'DE', 'FR', 'ES', 'IT', 'JP', 'KR'],
|
||||
},
|
||||
{
|
||||
id: 'hl',
|
||||
title: 'Language',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: [
|
||||
'en',
|
||||
'es',
|
||||
'fr',
|
||||
'de',
|
||||
'it',
|
||||
'pt',
|
||||
'ja',
|
||||
'ko',
|
||||
'zh'
|
||||
]
|
||||
options: ['en', 'es', 'fr', 'de', 'it', 'pt', 'ja', 'ko', 'zh'],
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
@@ -93,8 +72,8 @@ export const SerperBlock: BlockConfig<SearchResponse> = {
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your Serper API key',
|
||||
password: true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
password: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+15
-15
@@ -1,6 +1,6 @@
|
||||
import { SlackIcon } from '@/components/icons'
|
||||
import { BlockConfig } from '../types'
|
||||
import { SlackIcon } from '@/components/icons'
|
||||
import { SlackMessageResponse } from '@/tools/slack/message'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const SlackMessageBlock: BlockConfig<SlackMessageResponse> = {
|
||||
type: 'slack_message',
|
||||
@@ -9,24 +9,24 @@ export const SlackMessageBlock: BlockConfig<SlackMessageResponse> = {
|
||||
description: 'Send a message to Slack',
|
||||
bgColor: '#611f69',
|
||||
icon: SlackIcon,
|
||||
category: 'tools'
|
||||
category: 'tools',
|
||||
},
|
||||
tools: {
|
||||
access: ['slack_message']
|
||||
access: ['slack_message'],
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
apiKey: { type: 'string', required: true },
|
||||
channel: { type: 'string', required: true },
|
||||
text: { type: 'string', required: true }
|
||||
text: { type: 'string', required: true },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
ts: 'string',
|
||||
channel: 'string'
|
||||
}
|
||||
}
|
||||
channel: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -34,14 +34,14 @@ export const SlackMessageBlock: BlockConfig<SlackMessageResponse> = {
|
||||
title: 'Channel',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter Slack channel (e.g., #general)'
|
||||
placeholder: 'Enter Slack channel (e.g., #general)',
|
||||
},
|
||||
{
|
||||
id: 'text',
|
||||
title: 'Message',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your alert message'
|
||||
placeholder: 'Enter your alert message',
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
@@ -50,8 +50,8 @@ export const SlackMessageBlock: BlockConfig<SlackMessageResponse> = {
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your Slack OAuth token',
|
||||
password: true,
|
||||
connectionDroppable: false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
connectionDroppable: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+25
-25
@@ -1,7 +1,7 @@
|
||||
import { BlockConfig } from '../types'
|
||||
import { TavilyIcon } from '@/components/icons'
|
||||
import { SearchResponse } from '@/tools/tavily/search'
|
||||
import { ExtractResponse } from '@/tools/tavily/extract'
|
||||
import { SearchResponse } from '@/tools/tavily/search'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
export const TavilySearchBlock: BlockConfig<SearchResponse> = {
|
||||
type: 'tavily_search',
|
||||
@@ -13,22 +13,22 @@ export const TavilySearchBlock: BlockConfig<SearchResponse> = {
|
||||
category: 'tools',
|
||||
},
|
||||
tools: {
|
||||
access: ['tavily_search']
|
||||
access: ['tavily_search'],
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
query: { type: 'string', required: true },
|
||||
apiKey: { type: 'string', required: true },
|
||||
max_results: { type: 'number', required: false }
|
||||
max_results: { type: 'number', required: false },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
query: 'string',
|
||||
results: 'json',
|
||||
response_time: 'number'
|
||||
}
|
||||
}
|
||||
response_time: 'number',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -36,14 +36,14 @@ export const TavilySearchBlock: BlockConfig<SearchResponse> = {
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your search query...'
|
||||
placeholder: 'Enter your search query...',
|
||||
},
|
||||
{
|
||||
id: 'max_results',
|
||||
title: 'Max Results',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: ['5', '10', '15', '20']
|
||||
options: ['5', '10', '15', '20'],
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
@@ -51,10 +51,10 @@ export const TavilySearchBlock: BlockConfig<SearchResponse> = {
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your Tavily API key',
|
||||
password: true
|
||||
}
|
||||
]
|
||||
}
|
||||
password: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export const TavilyExtractBlock: BlockConfig<ExtractResponse> = {
|
||||
@@ -67,22 +67,22 @@ export const TavilyExtractBlock: BlockConfig<ExtractResponse> = {
|
||||
category: 'tools',
|
||||
},
|
||||
tools: {
|
||||
access: ['tavily_extract']
|
||||
access: ['tavily_extract'],
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
urls: { type: 'string', required: true },
|
||||
apiKey: { type: 'string', required: true },
|
||||
extract_depth: { type: 'string', required: false }
|
||||
extract_depth: { type: 'string', required: false },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
results: 'json',
|
||||
failed_results: 'any',
|
||||
response_time: 'number'
|
||||
}
|
||||
}
|
||||
response_time: 'number',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -90,14 +90,14 @@ export const TavilyExtractBlock: BlockConfig<ExtractResponse> = {
|
||||
title: 'URL',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter URL to extract content from...'
|
||||
placeholder: 'Enter URL to extract content from...',
|
||||
},
|
||||
{
|
||||
id: 'extract_depth',
|
||||
title: 'Extract Depth',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: ['basic', 'advanced']
|
||||
options: ['basic', 'advanced'],
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
@@ -105,8 +105,8 @@ export const TavilyExtractBlock: BlockConfig<ExtractResponse> = {
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your Tavily API key',
|
||||
password: true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
password: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+25
-23
@@ -1,16 +1,18 @@
|
||||
import { TranslateIcon } from '@/components/icons'
|
||||
import { BlockConfig } from '../types'
|
||||
import { ChatResponse } from '@/tools/openai/chat'
|
||||
import { MODEL_TOOLS, ModelType } from '../consts'
|
||||
import { BlockConfig } from '../types'
|
||||
|
||||
const getTranslationPrompt = (targetLanguage: string) => `You are a highly skilled translator. Your task is to translate the given text into ${targetLanguage || 'English'} while:
|
||||
const getTranslationPrompt = (
|
||||
targetLanguage: string
|
||||
) => `You are a highly skilled translator. Your task is to translate the given text into ${targetLanguage || 'English'} while:
|
||||
1. Preserving the original meaning and nuance
|
||||
2. Maintaining appropriate formality levels
|
||||
3. Adapting idioms and cultural references appropriately
|
||||
4. Preserving formatting and special characters
|
||||
5. Handling technical terms accurately
|
||||
|
||||
Only return the translated text without any explanations or notes. The translation should be natural and fluent in ${targetLanguage || 'English'}.`
|
||||
Only return the translated text without any explanations or notes. The translation should be natural and fluent in ${targetLanguage || 'English'}.`
|
||||
|
||||
export const TranslateBlock: BlockConfig<ChatResponse> = {
|
||||
type: 'translate',
|
||||
@@ -37,25 +39,25 @@ export const TranslateBlock: BlockConfig<ChatResponse> = {
|
||||
throw new Error(`Invalid model selected: ${model}`)
|
||||
}
|
||||
|
||||
return tool
|
||||
}
|
||||
}
|
||||
return tool
|
||||
},
|
||||
},
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
context: { type: 'string', required: true },
|
||||
targetLanguage: { type: 'string', required: true },
|
||||
apiKey: { type: 'string', required: true },
|
||||
systemPrompt: { type: 'string', required: true }
|
||||
systemPrompt: { type: 'string', required: true },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
type: {
|
||||
content: 'string',
|
||||
model: 'string',
|
||||
tokens: 'any'
|
||||
}
|
||||
}
|
||||
tokens: 'any',
|
||||
},
|
||||
},
|
||||
},
|
||||
subBlocks: [
|
||||
{
|
||||
@@ -63,30 +65,30 @@ export const TranslateBlock: BlockConfig<ChatResponse> = {
|
||||
title: 'Text to Translate',
|
||||
type: 'long-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter the text you want to translate'
|
||||
placeholder: 'Enter the text you want to translate',
|
||||
},
|
||||
{
|
||||
id: 'targetLanguage',
|
||||
title: 'Translate To',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter language (e.g. Spanish, French, etc.)'
|
||||
placeholder: 'Enter language (e.g. Spanish, French, etc.)',
|
||||
},
|
||||
{
|
||||
id: 'model',
|
||||
title: 'Model',
|
||||
type: 'dropdown',
|
||||
layout: 'half',
|
||||
options: Object.keys(MODEL_TOOLS)
|
||||
options: Object.keys(MODEL_TOOLS),
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: "API Key",
|
||||
type: "short-input",
|
||||
layout: "full",
|
||||
placeholder: "Enter your API key",
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
layout: 'full',
|
||||
placeholder: 'Enter your API key',
|
||||
password: true,
|
||||
connectionDroppable: false
|
||||
connectionDroppable: false,
|
||||
},
|
||||
{
|
||||
id: 'systemPrompt',
|
||||
@@ -96,8 +98,8 @@ export const TranslateBlock: BlockConfig<ChatResponse> = {
|
||||
hidden: true,
|
||||
value: (params: Record<string, any>) => {
|
||||
return getTranslationPrompt(params.targetLanguage || 'English')
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
export const MODEL_TOOLS = {
|
||||
'gpt-4o': 'openai_chat',
|
||||
'o1': 'openai_chat',
|
||||
o1: 'openai_chat',
|
||||
'o3-mini': 'openai_chat',
|
||||
'deepseek-v3': 'deepseek_chat',
|
||||
'deepseek-r1': 'deepseek_reasoner',
|
||||
'claude-3-5-sonnet-20241022': 'anthropic_chat',
|
||||
'gemini-pro': 'google_chat',
|
||||
'grok-2-latest': 'xai_chat'
|
||||
'grok-2-latest': 'xai_chat',
|
||||
} as const
|
||||
|
||||
export type ModelType = keyof typeof MODEL_TOOLS
|
||||
export type ToolType = typeof MODEL_TOOLS[ModelType]
|
||||
export type ToolType = (typeof MODEL_TOOLS)[ModelType]
|
||||
|
||||
+28
-30
@@ -1,25 +1,24 @@
|
||||
import { BlockConfig } from './types'
|
||||
|
||||
// Import blocks
|
||||
import { AgentBlock } from './blocks/agent'
|
||||
import { ApiBlock } from './blocks/api'
|
||||
import { FunctionBlock } from './blocks/function'
|
||||
import { ConditionBlock } from './blocks/condition'
|
||||
import { CrewAIVisionBlock } from './blocks/crewai'
|
||||
import { FirecrawlScrapeBlock } from './blocks/firecrawl'
|
||||
import { JinaBlock } from './blocks/jina'
|
||||
import { TranslateBlock } from './blocks/translate'
|
||||
import { SlackMessageBlock } from './blocks/slack'
|
||||
import { FunctionBlock } from './blocks/function'
|
||||
import { GitHubBlock } from './blocks/github'
|
||||
import { ConditionBlock } from './blocks/condition'
|
||||
import { JinaBlock } from './blocks/jina'
|
||||
import { SerperBlock } from './blocks/serper'
|
||||
import { TavilySearchBlock, TavilyExtractBlock } from './blocks/tavily'
|
||||
import { SlackMessageBlock } from './blocks/slack'
|
||||
import { TavilyExtractBlock, TavilySearchBlock } from './blocks/tavily'
|
||||
import { TranslateBlock } from './blocks/translate'
|
||||
import { BlockConfig } from './types'
|
||||
|
||||
// Export blocks for ease of use
|
||||
export {
|
||||
AgentBlock,
|
||||
ApiBlock,
|
||||
FunctionBlock,
|
||||
CrewAIVisionBlock,
|
||||
export {
|
||||
AgentBlock,
|
||||
ApiBlock,
|
||||
FunctionBlock,
|
||||
CrewAIVisionBlock,
|
||||
FirecrawlScrapeBlock,
|
||||
JinaBlock,
|
||||
TranslateBlock,
|
||||
@@ -28,7 +27,7 @@ export {
|
||||
ConditionBlock,
|
||||
SerperBlock,
|
||||
TavilySearchBlock,
|
||||
TavilyExtractBlock
|
||||
TavilyExtractBlock,
|
||||
}
|
||||
|
||||
// Registry of all block configurations
|
||||
@@ -45,29 +44,28 @@ const blocks: Record<string, BlockConfig> = {
|
||||
condition: ConditionBlock,
|
||||
serper_search: SerperBlock,
|
||||
tavily_search: TavilySearchBlock,
|
||||
tavily_extract: TavilyExtractBlock
|
||||
tavily_extract: TavilyExtractBlock,
|
||||
}
|
||||
|
||||
// Build a reverse mapping of tools to block types
|
||||
const toolToBlockType = Object.entries(blocks).reduce((acc, [blockType, config]) => {
|
||||
config.tools.access.forEach(toolId => {
|
||||
acc[toolId] = blockType
|
||||
})
|
||||
return acc
|
||||
}, {} as Record<string, string>)
|
||||
const toolToBlockType = Object.entries(blocks).reduce(
|
||||
(acc, [blockType, config]) => {
|
||||
config.tools.access.forEach((toolId) => {
|
||||
acc[toolId] = blockType
|
||||
})
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, string>
|
||||
)
|
||||
|
||||
// Helper functions
|
||||
export const getBlock = (type: string): BlockConfig | undefined =>
|
||||
blocks[type]
|
||||
export const getBlock = (type: string): BlockConfig | undefined => blocks[type]
|
||||
|
||||
export const getBlocksByCategory = (category: 'blocks' | 'tools'): BlockConfig[] =>
|
||||
Object.values(blocks).filter(block => block.toolbar.category === category)
|
||||
Object.values(blocks).filter((block) => block.toolbar.category === category)
|
||||
|
||||
export const getAllBlockTypes = (): string[] =>
|
||||
Object.keys(blocks)
|
||||
export const getAllBlockTypes = (): string[] => Object.keys(blocks)
|
||||
|
||||
export const isValidBlockType = (type: string): type is string =>
|
||||
type in blocks
|
||||
export const isValidBlockType = (type: string): type is string => type in blocks
|
||||
|
||||
export const getAllBlocks = (): BlockConfig[] =>
|
||||
Object.values(blocks)
|
||||
export const getAllBlocks = (): BlockConfig[] => Object.values(blocks)
|
||||
|
||||
+28
-16
@@ -3,32 +3,44 @@ import type { JSX } from 'react'
|
||||
import { ToolResponse } from '@/tools/types'
|
||||
|
||||
// Tool output type utilities
|
||||
export type ExtractToolOutput<T> = T extends ToolResponse
|
||||
? T['output']
|
||||
: never
|
||||
export type ExtractToolOutput<T> = T extends ToolResponse ? T['output'] : never
|
||||
|
||||
export type ToolOutputToValueType<T> = T extends Record<string, any>
|
||||
? {
|
||||
[K in keyof T]: T[K] extends string ? 'string'
|
||||
: T[K] extends number ? 'number'
|
||||
: T[K] extends boolean ? 'boolean'
|
||||
: T[K] extends object ? 'json'
|
||||
: 'any'
|
||||
}
|
||||
: never
|
||||
export type ToolOutputToValueType<T> =
|
||||
T extends Record<string, any>
|
||||
? {
|
||||
[K in keyof T]: T[K] extends string
|
||||
? 'string'
|
||||
: T[K] extends number
|
||||
? 'number'
|
||||
: T[K] extends boolean
|
||||
? 'boolean'
|
||||
: T[K] extends object
|
||||
? 'json'
|
||||
: 'any'
|
||||
}
|
||||
: never
|
||||
|
||||
export type BlockIcon = (props: SVGProps<SVGSVGElement>) => JSX.Element
|
||||
export type BlockCategory = 'blocks' | 'tools'
|
||||
|
||||
export type PrimitiveValueType = 'string' | 'number' | 'boolean' | 'json' | 'any'
|
||||
|
||||
export type BlockOutput =
|
||||
| PrimitiveValueType
|
||||
export type BlockOutput =
|
||||
| PrimitiveValueType
|
||||
| { [key: string]: PrimitiveValueType | Record<string, any> }
|
||||
|
||||
export type ParamType = 'string' | 'number' | 'boolean' | 'json'
|
||||
|
||||
export type SubBlockType = 'short-input' | 'long-input' | 'dropdown' | 'slider' | 'table' | 'code' | 'switch' | 'tool-input' | 'checkbox-list'
|
||||
export type SubBlockType =
|
||||
| 'short-input'
|
||||
| 'long-input'
|
||||
| 'dropdown'
|
||||
| 'slider'
|
||||
| 'table'
|
||||
| 'code'
|
||||
| 'switch'
|
||||
| 'tool-input'
|
||||
| 'checkbox-list'
|
||||
export type SubBlockLayout = 'full' | 'half'
|
||||
|
||||
export interface ParamConfig {
|
||||
@@ -88,4 +100,4 @@ export interface OutputConfig {
|
||||
whenFilled: BlockOutput
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -46,4 +46,4 @@ export function resolveOutputType(
|
||||
}
|
||||
|
||||
return resolvedOutputs
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,4 +18,4 @@
|
||||
"hooks": "@/hooks"
|
||||
},
|
||||
"iconLibrary": "lucide"
|
||||
}
|
||||
}
|
||||
|
||||
+96
-24
@@ -902,11 +902,26 @@ export function SalesforceIcon(props: SVGProps<SVGSVGElement>) {
|
||||
export function FirecrawlIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg viewBox="0 0 642 600" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d="M301 63C299 91 303 122 298 149C295 158 289 165 283 169C274 172 266 170 261 167C253 176 248 183 244 191C230 226 226 263 226 301C216 310 203 317 192 310C179 295 175 277 174 259C161 273 153 288 146 304C141 321 138 336 137 352C140 372 145 388 152 402C161 421 174 435 187 449C181 462 165 453 157 450C158 454 161 458 165 461C195 490 231 500 268 509C240 494 211 471 195 442C179 413 172 378 180 344C191 353 200 362 211 364C223 365 232 361 236 353C247 274 299 214 323 143C322 136 327 140 329 142C354 165 367 191 375 218C387 254 381 294 379 329C393 345 413 334 424 329C429 342 432 352 429 362C427 378 417 388 413 400C422 407 433 403 440 400C432 423 419 442 404 460C383 483 358 501 335 512C379 502 420 491 449 459C443 458 427 464 428 452C443 437 464 423 472 403C482 383 485 362 484 339C482 307 472 280 458 254C459 267 452 276 445 284C434 289 426 279 424 272C415 247 424 220 418 198C415 179 405 165 397 150C370 114 336 86 303 64" fill="rgb(253,76,31)" />
|
||||
<path d="M324 141C303 214 249 273 244 354C235 359 229 364 223 366C205 367 193 357 182 347C180 350 179 353 180 357C178 374 178 390 182 403C185 421 193 434 200 448C212 465 227 480 243 491C258 500 269 513 285 512C284 508 257 485 252 468C241 450 235 433 233 414C241 415 254 420 263 412C260 387 265 363 273 343C281 323 293 306 310 295C317 289 324 285 330 282C328 307 328 331 329 355C330 368 332 379 338 389C358 394 376 384 388 370C383 386 377 401 371 415C376 414 381 411 385 408C383 421 380 431 376 441C366 467 356 491 334 510C358 499 381 483 400 461C418 442 430 423 440 403C432 404 421 410 413 404C414 386 428 377 427 360C429 349 428 340 424 332C413 336 404 341 392 339C386 338 381 334 379 330C380 292 385 248 371 214C366 195 358 180 349 165C341 155 333 145 323 140" fill="rgb(254,156,69)" />
|
||||
<path d="M330 284C309 293 289 311 279 332C267 356 261 383 265 411C256 420 242 418 235 412C237 438 245 459 258 479C269 493 281 507 295 513C288 495 265 472 265 446C272 447 281 454 288 444C296 425 303 407 309 388C317 406 321 427 336 443C346 449 358 446 363 438C355 464 348 489 334 511C344 501 352 491 357 480C370 457 379 435 385 412C380 411 376 416 371 418C376 401 382 386 387 371C379 382 369 388 358 391C348 394 337 392 334 383C324 353 328 316 330 285" fill="rgb(254,220,87)" />
|
||||
<path d="M311 389C303 407 297 426 289 445C282 454 273 450 268 445C267 472 285 492 302 512C299 514 297 514 294 514C297 514 299 514 301 514C314 515 325 512 334 513C341 495 355 467 362 443C357 446 351 448 344 447C337 446 334 441 330 438C320 422 316 406 311 391" fill="rgb(251,250,202)" />
|
||||
<path d="M187 163C188 181 167 187 164 203C158 215 158 228 159 241C172 233 183 221 188 209C193 194 192 178 188 166" fill="rgb(253,76,31)" />
|
||||
<path
|
||||
d="M301 63C299 91 303 122 298 149C295 158 289 165 283 169C274 172 266 170 261 167C253 176 248 183 244 191C230 226 226 263 226 301C216 310 203 317 192 310C179 295 175 277 174 259C161 273 153 288 146 304C141 321 138 336 137 352C140 372 145 388 152 402C161 421 174 435 187 449C181 462 165 453 157 450C158 454 161 458 165 461C195 490 231 500 268 509C240 494 211 471 195 442C179 413 172 378 180 344C191 353 200 362 211 364C223 365 232 361 236 353C247 274 299 214 323 143C322 136 327 140 329 142C354 165 367 191 375 218C387 254 381 294 379 329C393 345 413 334 424 329C429 342 432 352 429 362C427 378 417 388 413 400C422 407 433 403 440 400C432 423 419 442 404 460C383 483 358 501 335 512C379 502 420 491 449 459C443 458 427 464 428 452C443 437 464 423 472 403C482 383 485 362 484 339C482 307 472 280 458 254C459 267 452 276 445 284C434 289 426 279 424 272C415 247 424 220 418 198C415 179 405 165 397 150C370 114 336 86 303 64"
|
||||
fill="rgb(253,76,31)"
|
||||
/>
|
||||
<path
|
||||
d="M324 141C303 214 249 273 244 354C235 359 229 364 223 366C205 367 193 357 182 347C180 350 179 353 180 357C178 374 178 390 182 403C185 421 193 434 200 448C212 465 227 480 243 491C258 500 269 513 285 512C284 508 257 485 252 468C241 450 235 433 233 414C241 415 254 420 263 412C260 387 265 363 273 343C281 323 293 306 310 295C317 289 324 285 330 282C328 307 328 331 329 355C330 368 332 379 338 389C358 394 376 384 388 370C383 386 377 401 371 415C376 414 381 411 385 408C383 421 380 431 376 441C366 467 356 491 334 510C358 499 381 483 400 461C418 442 430 423 440 403C432 404 421 410 413 404C414 386 428 377 427 360C429 349 428 340 424 332C413 336 404 341 392 339C386 338 381 334 379 330C380 292 385 248 371 214C366 195 358 180 349 165C341 155 333 145 323 140"
|
||||
fill="rgb(254,156,69)"
|
||||
/>
|
||||
<path
|
||||
d="M330 284C309 293 289 311 279 332C267 356 261 383 265 411C256 420 242 418 235 412C237 438 245 459 258 479C269 493 281 507 295 513C288 495 265 472 265 446C272 447 281 454 288 444C296 425 303 407 309 388C317 406 321 427 336 443C346 449 358 446 363 438C355 464 348 489 334 511C344 501 352 491 357 480C370 457 379 435 385 412C380 411 376 416 371 418C376 401 382 386 387 371C379 382 369 388 358 391C348 394 337 392 334 383C324 353 328 316 330 285"
|
||||
fill="rgb(254,220,87)"
|
||||
/>
|
||||
<path
|
||||
d="M311 389C303 407 297 426 289 445C282 454 273 450 268 445C267 472 285 492 302 512C299 514 297 514 294 514C297 514 299 514 301 514C314 515 325 512 334 513C341 495 355 467 362 443C357 446 351 448 344 447C337 446 334 441 330 438C320 422 316 406 311 391"
|
||||
fill="rgb(251,250,202)"
|
||||
/>
|
||||
<path
|
||||
d="M187 163C188 181 167 187 164 203C158 215 158 228 159 241C172 233 183 221 188 209C193 194 192 178 188 166"
|
||||
fill="rgb(253,76,31)"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -1011,29 +1026,86 @@ export const GithubIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
|
||||
export const SerperIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg viewBox="0 0 654 600" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d="M324 38C356 37 389 36 417 47C452 56 484 72 509 94C539 118 561 145 577 176C593 205 601 238 606 271C610 343 590 403 552 452C528 482 499 507 467 523C438 539 404 547 372 552C297 556 235 534 184 492C133 449 103 392 93 330C93 292 89 255 102 224C112 189 128 158 149 132C194 78 255 46 322 38" fill="rgb(71,97,118)" />
|
||||
<path d="M326 39C286 43 250 55 217 75C185 94 156 120 137 150C100 204 87 266 95 336C107 402 142 462 198 502C249 538 309 556 378 551C415 545 449 533 477 516C511 497 535 472 557 445C592 393 611 333 605 265C595 196 563 140 511 95C484 73 452 57 419 48C390 38 359 38 327 39" fill="rgb(71,97,119)" />
|
||||
<path d="M342 40C407 42 465 61 513 103C541 126 562 155 576 184C592 217 600 251 600 288C602 357 579 416 535 465C510 493 478 515 445 528C416 541 385 546 352 546C284 548 225 523 178 481C130 436 103 379 96 313C94 244 113 186 151 138C179 103 209 80 245 64C276 50 307 44 340 41" fill="rgb(71,97,119)" />
|
||||
<path d="M344 42C309 44 277 51 247 64C209 81 180 103 153 136C114 186 95 244 96 312C104 379 131 435 177 480C225 522 284 547 351 546C385 545 416 540 443 528C478 514 509 492 533 466C578 416 601 357 600 289C599 251 591 217 576 187C561 156 541 127 515 105C466 63 409 44 346 41" fill="rgb(71,97,118)" />
|
||||
<path d="M327 81C378 78 423 89 462 114C511 144 546 196 557 248C567 306 559 363 530 406C498 457 448 492 395 503C338 513 282 506 239 477C192 450 156 402 143 351C126 296 137 235 163 190C198 130 258 89 325 82" fill="rgb(44,56,71)" />
|
||||
<path d="M329 83C260 89 199 129 165 189C138 235 127 296 144 349C157 401 193 449 237 475C282 505 338 512 393 503C448 491 497 457 529 408C558 363 566 306 557 250C545 196 511 145 464 116C424 91 380 79 330 82" fill="rgb(43,55,70)" />
|
||||
<path d="M334 87C381 83 423 94 458 117C510 148 544 201 554 258C562 317 551 370 521 412C487 460 440 491 385 500C331 507 281 499 241 473C191 444 157 394 145 339C136 284 143 227 171 186C207 129 265 91 332 87" fill="rgb(41,53,67)" />
|
||||
<path d="M335 88C267 90 208 129 173 184C144 227 137 284 145 338C158 393 191 443 240 471C281 498 331 506 384 500C439 490 487 459 519 413C550 370 561 317 554 259C543 201 509 149 460 119C424 96 383 85 337 88" fill="rgb(41,53,67)" />
|
||||
<path d="M347 166C361 164 373 169 387 168C412 180 437 193 447 221C449 232 443 243 434 248C403 245 398 204 365 207C338 206 315 210 297 228C294 238 289 257 303 260C337 280 382 276 417 292C436 300 448 314 455 330C457 349 462 373 449 385C435 408 413 418 391 427C361 429 328 436 304 421C280 413 260 392 250 370C246 356 255 343 268 343C293 360 316 398 356 389C382 390 409 380 416 357C389 295 298 335 260 276C246 256 248 233 258 214C279 184 309 167 346 167" fill="rgb(121,172,205)" />
|
||||
<path d="M349 168C312 167 280 183 259 212C249 233 247 256 260 274C299 334 390 294 422 354C409 381 382 391 357 389C316 399 293 361 272 342C255 344 247 356 251 368C260 391 280 412 302 420C328 435 361 428 389 428C412 417 434 407 447 386C461 373 456 349 456 332C428 270 351 289 304 262C288 258 293 239 295 229C314 209 338 204 363 204C398 203 403 244 431 249C443 242 448 232 449 222C436 193 412 181 388 172C374 170 363 166 350 167" fill="rgb(125,177,211)" />
|
||||
<path d="M349 169C386 169 425 185 441 220C444 231 441 240 432 243C409 237 402 209 380 206C347 200 314 201 293 226C290 238 286 256 297 262C332 283 375 281 411 295C431 304 446 317 452 337C455 360 452 383 434 396C415 415 391 421 366 426C338 430 316 422 295 413C276 402 261 385 254 366C254 353 261 343 275 348C290 381 325 398 360 394C388 395 411 382 420 360C425 342 413 334 404 327C359 304 298 318 265 276C253 254 255 235 261 214C280 187 314 173 346 170" fill="rgb(137,195,233)" />
|
||||
<path d="M349 171C316 173 281 187 263 214C256 235 254 254 266 273C300 316 359 304 401 325C413 333 426 342 422 358C412 382 388 396 363 395C326 399 290 382 278 348C262 345 254 353 253 365C261 384 277 401 292 411C316 421 338 429 365 426C390 420 415 414 432 398C451 383 454 360 453 338C445 317 430 305 413 296C375 282 332 284 299 264C285 257 288 239 291 228C304 212 319 205 336 202C378 193 403 213 423 244C438 244 443 232 441 222C425 186 388 171 352 170" fill="rgb(139,198,236)" />
|
||||
<path
|
||||
d="M324 38C356 37 389 36 417 47C452 56 484 72 509 94C539 118 561 145 577 176C593 205 601 238 606 271C610 343 590 403 552 452C528 482 499 507 467 523C438 539 404 547 372 552C297 556 235 534 184 492C133 449 103 392 93 330C93 292 89 255 102 224C112 189 128 158 149 132C194 78 255 46 322 38"
|
||||
fill="rgb(71,97,118)"
|
||||
/>
|
||||
<path
|
||||
d="M326 39C286 43 250 55 217 75C185 94 156 120 137 150C100 204 87 266 95 336C107 402 142 462 198 502C249 538 309 556 378 551C415 545 449 533 477 516C511 497 535 472 557 445C592 393 611 333 605 265C595 196 563 140 511 95C484 73 452 57 419 48C390 38 359 38 327 39"
|
||||
fill="rgb(71,97,119)"
|
||||
/>
|
||||
<path
|
||||
d="M342 40C407 42 465 61 513 103C541 126 562 155 576 184C592 217 600 251 600 288C602 357 579 416 535 465C510 493 478 515 445 528C416 541 385 546 352 546C284 548 225 523 178 481C130 436 103 379 96 313C94 244 113 186 151 138C179 103 209 80 245 64C276 50 307 44 340 41"
|
||||
fill="rgb(71,97,119)"
|
||||
/>
|
||||
<path
|
||||
d="M344 42C309 44 277 51 247 64C209 81 180 103 153 136C114 186 95 244 96 312C104 379 131 435 177 480C225 522 284 547 351 546C385 545 416 540 443 528C478 514 509 492 533 466C578 416 601 357 600 289C599 251 591 217 576 187C561 156 541 127 515 105C466 63 409 44 346 41"
|
||||
fill="rgb(71,97,118)"
|
||||
/>
|
||||
<path
|
||||
d="M327 81C378 78 423 89 462 114C511 144 546 196 557 248C567 306 559 363 530 406C498 457 448 492 395 503C338 513 282 506 239 477C192 450 156 402 143 351C126 296 137 235 163 190C198 130 258 89 325 82"
|
||||
fill="rgb(44,56,71)"
|
||||
/>
|
||||
<path
|
||||
d="M329 83C260 89 199 129 165 189C138 235 127 296 144 349C157 401 193 449 237 475C282 505 338 512 393 503C448 491 497 457 529 408C558 363 566 306 557 250C545 196 511 145 464 116C424 91 380 79 330 82"
|
||||
fill="rgb(43,55,70)"
|
||||
/>
|
||||
<path
|
||||
d="M334 87C381 83 423 94 458 117C510 148 544 201 554 258C562 317 551 370 521 412C487 460 440 491 385 500C331 507 281 499 241 473C191 444 157 394 145 339C136 284 143 227 171 186C207 129 265 91 332 87"
|
||||
fill="rgb(41,53,67)"
|
||||
/>
|
||||
<path
|
||||
d="M335 88C267 90 208 129 173 184C144 227 137 284 145 338C158 393 191 443 240 471C281 498 331 506 384 500C439 490 487 459 519 413C550 370 561 317 554 259C543 201 509 149 460 119C424 96 383 85 337 88"
|
||||
fill="rgb(41,53,67)"
|
||||
/>
|
||||
<path
|
||||
d="M347 166C361 164 373 169 387 168C412 180 437 193 447 221C449 232 443 243 434 248C403 245 398 204 365 207C338 206 315 210 297 228C294 238 289 257 303 260C337 280 382 276 417 292C436 300 448 314 455 330C457 349 462 373 449 385C435 408 413 418 391 427C361 429 328 436 304 421C280 413 260 392 250 370C246 356 255 343 268 343C293 360 316 398 356 389C382 390 409 380 416 357C389 295 298 335 260 276C246 256 248 233 258 214C279 184 309 167 346 167"
|
||||
fill="rgb(121,172,205)"
|
||||
/>
|
||||
<path
|
||||
d="M349 168C312 167 280 183 259 212C249 233 247 256 260 274C299 334 390 294 422 354C409 381 382 391 357 389C316 399 293 361 272 342C255 344 247 356 251 368C260 391 280 412 302 420C328 435 361 428 389 428C412 417 434 407 447 386C461 373 456 349 456 332C428 270 351 289 304 262C288 258 293 239 295 229C314 209 338 204 363 204C398 203 403 244 431 249C443 242 448 232 449 222C436 193 412 181 388 172C374 170 363 166 350 167"
|
||||
fill="rgb(125,177,211)"
|
||||
/>
|
||||
<path
|
||||
d="M349 169C386 169 425 185 441 220C444 231 441 240 432 243C409 237 402 209 380 206C347 200 314 201 293 226C290 238 286 256 297 262C332 283 375 281 411 295C431 304 446 317 452 337C455 360 452 383 434 396C415 415 391 421 366 426C338 430 316 422 295 413C276 402 261 385 254 366C254 353 261 343 275 348C290 381 325 398 360 394C388 395 411 382 420 360C425 342 413 334 404 327C359 304 298 318 265 276C253 254 255 235 261 214C280 187 314 173 346 170"
|
||||
fill="rgb(137,195,233)"
|
||||
/>
|
||||
<path
|
||||
d="M349 171C316 173 281 187 263 214C256 235 254 254 266 273C300 316 359 304 401 325C413 333 426 342 422 358C412 382 388 396 363 395C326 399 290 382 278 348C262 345 254 353 253 365C261 384 277 401 292 411C316 421 338 429 365 426C390 420 415 414 432 398C451 383 454 360 453 338C445 317 430 305 413 296C375 282 332 284 299 264C285 257 288 239 291 228C304 212 319 205 336 202C378 193 403 213 423 244C438 244 443 232 441 222C425 186 388 171 352 170"
|
||||
fill="rgb(139,198,236)"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export const TavilyIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg viewBox="0 0 600 600" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<path d="M432 291C415 294 418 313 417 326C380 328 342 327 306 328C316 344 312 368 301 381C339 384 377 383 414 384C419 393 415 404 419 412C424 419 431 422 437 421C554 393 539 314 425 290" fill="rgb(248,202,81)" />
|
||||
<path d="M263 87C260 88 257 89 255 93C237 121 219 147 204 174C203 184 206 191 212 195C222 198 231 196 239 197C241 238 240 277 241 316C257 307 276 309 294 308C296 273 295 234 296 199C309 196 328 200 333 183C314 149 299 103 267 83" fill="rgb(109,164,249)" />
|
||||
<path d="M314 356L316 354C386 355 457 354 527 355C504 385 469 400 440 421C431 421 424 418 421 411C415 402 420 389 416 383C384 371 284 406 312 358" fill="rgb(250,188,28)" />
|
||||
<path d="M314 356C281 405 384 369 410 384C422 388 415 402 421 409C425 417 431 420 437 420C469 400 504 384 529 360C456 355 386 356 317 355" fill="rgb(251,186,23)" />
|
||||
<path d="M264 325C271 325 290 329 283 339C236 384 186 436 139 482C133 481 133 477 131 474C133 477 133 481 135 482C174 490 213 472 250 466C261 447 246 435 235 426C254 406 271 389 289 372C303 352 287 324 266 326" fill="rgb(251,156,158)" />
|
||||
<path d="M263 327C260 328 256 328 253 330C233 348 216 367 197 384C188 381 183 371 175 368C166 367 161 369 156 372C148 409 133 447 133 482C173 430 281 366 277 323" fill="rgb(248,56,63)" />
|
||||
<path d="M258 326C235 341 218 365 198 382C186 376 176 360 161 368L160 369L157 369C149 378 150 391 146 401C150 391 149 379 157 370C174 359 185 376 195 385C219 365 238 337 262 325" fill="rgb(242,165,165)" />
|
||||
<path
|
||||
d="M432 291C415 294 418 313 417 326C380 328 342 327 306 328C316 344 312 368 301 381C339 384 377 383 414 384C419 393 415 404 419 412C424 419 431 422 437 421C554 393 539 314 425 290"
|
||||
fill="rgb(248,202,81)"
|
||||
/>
|
||||
<path
|
||||
d="M263 87C260 88 257 89 255 93C237 121 219 147 204 174C203 184 206 191 212 195C222 198 231 196 239 197C241 238 240 277 241 316C257 307 276 309 294 308C296 273 295 234 296 199C309 196 328 200 333 183C314 149 299 103 267 83"
|
||||
fill="rgb(109,164,249)"
|
||||
/>
|
||||
<path
|
||||
d="M314 356L316 354C386 355 457 354 527 355C504 385 469 400 440 421C431 421 424 418 421 411C415 402 420 389 416 383C384 371 284 406 312 358"
|
||||
fill="rgb(250,188,28)"
|
||||
/>
|
||||
<path
|
||||
d="M314 356C281 405 384 369 410 384C422 388 415 402 421 409C425 417 431 420 437 420C469 400 504 384 529 360C456 355 386 356 317 355"
|
||||
fill="rgb(251,186,23)"
|
||||
/>
|
||||
<path
|
||||
d="M264 325C271 325 290 329 283 339C236 384 186 436 139 482C133 481 133 477 131 474C133 477 133 481 135 482C174 490 213 472 250 466C261 447 246 435 235 426C254 406 271 389 289 372C303 352 287 324 266 326"
|
||||
fill="rgb(251,156,158)"
|
||||
/>
|
||||
<path
|
||||
d="M263 327C260 328 256 328 253 330C233 348 216 367 197 384C188 381 183 371 175 368C166 367 161 369 156 372C148 409 133 447 133 482C173 430 281 366 277 323"
|
||||
fill="rgb(248,56,63)"
|
||||
/>
|
||||
<path
|
||||
d="M258 326C235 341 218 365 198 382C186 376 176 360 161 368L160 369L157 369C149 378 150 391 146 401C150 391 149 379 157 370C174 359 185 376 195 385C219 365 238 337 262 325"
|
||||
fill="rgb(242,165,165)"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { buttonVariants } from "@/components/ui/button"
|
||||
import * as React from 'react'
|
||||
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const AlertDialog = AlertDialogPrimitive.Root
|
||||
|
||||
@@ -18,7 +17,7 @@ const AlertDialogOverlay = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Overlay
|
||||
className={cn(
|
||||
"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",
|
||||
'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',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -36,7 +35,7 @@ const AlertDialogContent = React.forwardRef<
|
||||
<AlertDialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -45,33 +44,18 @@ const AlertDialogContent = React.forwardRef<
|
||||
))
|
||||
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
||||
|
||||
const AlertDialogHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col space-y-2 text-center sm:text-left",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...props} />
|
||||
)
|
||||
AlertDialogHeader.displayName = "AlertDialogHeader"
|
||||
AlertDialogHeader.displayName = 'AlertDialogHeader'
|
||||
|
||||
const AlertDialogFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className
|
||||
)}
|
||||
className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
AlertDialogFooter.displayName = "AlertDialogFooter"
|
||||
AlertDialogFooter.displayName = 'AlertDialogFooter'
|
||||
|
||||
const AlertDialogTitle = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
||||
@@ -79,7 +63,7 @@ const AlertDialogTitle = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn("text-lg font-semibold", className)}
|
||||
className={cn('text-lg font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
@@ -91,22 +75,17 @@ const AlertDialogDescription = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
className={cn('text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertDialogDescription.displayName =
|
||||
AlertDialogPrimitive.Description.displayName
|
||||
AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName
|
||||
|
||||
const AlertDialogAction = React.forwardRef<
|
||||
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Action
|
||||
ref={ref}
|
||||
className={cn(buttonVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
<AlertDialogPrimitive.Action ref={ref} className={cn(buttonVariants(), className)} {...props} />
|
||||
))
|
||||
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
|
||||
|
||||
@@ -116,11 +95,7 @@ const AlertDialogCancel = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AlertDialogPrimitive.Cancel
|
||||
ref={ref}
|
||||
className={cn(
|
||||
buttonVariants({ variant: "outline" }),
|
||||
"mt-2 sm:mt-0",
|
||||
className
|
||||
)}
|
||||
className={cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
|
||||
+21
-32
@@ -1,20 +1,19 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
|
||||
'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
default: 'bg-background text-foreground',
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
variant: 'default',
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -23,37 +22,27 @@ const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
>(({ className, variant, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
<div ref={ref} role="alert" className={cn(alertVariants({ variant }), className)} {...props} />
|
||||
))
|
||||
Alert.displayName = "Alert"
|
||||
Alert.displayName = 'Alert'
|
||||
|
||||
const AlertTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
AlertTitle.displayName = "AlertTitle"
|
||||
const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn('mb-1 font-medium leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
AlertTitle.displayName = 'AlertTitle'
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
<div ref={ref} className={cn('text-sm [&_p]:leading-relaxed', className)} {...props} />
|
||||
))
|
||||
AlertDescription.displayName = "AlertDescription"
|
||||
AlertDescription.displayName = 'AlertDescription'
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription }
|
||||
|
||||
+10
-14
@@ -1,24 +1,22 @@
|
||||
import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||
default: 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
||||
outline: 'text-foreground',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
variant: 'default',
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -28,9 +26,7 @@ export interface BadgeProps
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
)
|
||||
return <div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants }
|
||||
|
||||
@@ -1,108 +1,93 @@
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import { Slot } from '@radix-ui/react-slot'
|
||||
import { ChevronRight, MoreHorizontal } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Breadcrumb = React.forwardRef<
|
||||
HTMLElement,
|
||||
React.ComponentPropsWithoutRef<"nav"> & {
|
||||
React.ComponentPropsWithoutRef<'nav'> & {
|
||||
separator?: React.ReactNode
|
||||
}
|
||||
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
|
||||
Breadcrumb.displayName = "Breadcrumb"
|
||||
Breadcrumb.displayName = 'Breadcrumb'
|
||||
|
||||
const BreadcrumbList = React.forwardRef<
|
||||
HTMLOListElement,
|
||||
React.ComponentPropsWithoutRef<"ol">
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ol
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
BreadcrumbList.displayName = "BreadcrumbList"
|
||||
const BreadcrumbList = React.forwardRef<HTMLOListElement, React.ComponentPropsWithoutRef<'ol'>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<ol
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
BreadcrumbList.displayName = 'BreadcrumbList'
|
||||
|
||||
const BreadcrumbItem = React.forwardRef<
|
||||
HTMLLIElement,
|
||||
React.ComponentPropsWithoutRef<"li">
|
||||
>(({ className, ...props }, ref) => (
|
||||
<li
|
||||
ref={ref}
|
||||
className={cn("inline-flex items-center gap-1.5", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
BreadcrumbItem.displayName = "BreadcrumbItem"
|
||||
const BreadcrumbItem = React.forwardRef<HTMLLIElement, React.ComponentPropsWithoutRef<'li'>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<li ref={ref} className={cn('inline-flex items-center gap-1.5', className)} {...props} />
|
||||
)
|
||||
)
|
||||
BreadcrumbItem.displayName = 'BreadcrumbItem'
|
||||
|
||||
const BreadcrumbLink = React.forwardRef<
|
||||
HTMLAnchorElement,
|
||||
React.ComponentPropsWithoutRef<"a"> & {
|
||||
React.ComponentPropsWithoutRef<'a'> & {
|
||||
asChild?: boolean
|
||||
}
|
||||
>(({ asChild, className, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "a"
|
||||
const Comp = asChild ? Slot : 'a'
|
||||
|
||||
return (
|
||||
<Comp
|
||||
ref={ref}
|
||||
className={cn("transition-colors hover:text-foreground", className)}
|
||||
className={cn('transition-colors hover:text-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
BreadcrumbLink.displayName = "BreadcrumbLink"
|
||||
BreadcrumbLink.displayName = 'BreadcrumbLink'
|
||||
|
||||
const BreadcrumbPage = React.forwardRef<
|
||||
HTMLSpanElement,
|
||||
React.ComponentPropsWithoutRef<"span">
|
||||
>(({ className, ...props }, ref) => (
|
||||
<span
|
||||
ref={ref}
|
||||
role="link"
|
||||
aria-disabled="true"
|
||||
aria-current="page"
|
||||
className={cn("font-normal text-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
BreadcrumbPage.displayName = "BreadcrumbPage"
|
||||
const BreadcrumbPage = React.forwardRef<HTMLSpanElement, React.ComponentPropsWithoutRef<'span'>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<span
|
||||
ref={ref}
|
||||
role="link"
|
||||
aria-disabled="true"
|
||||
aria-current="page"
|
||||
className={cn('font-normal text-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
BreadcrumbPage.displayName = 'BreadcrumbPage'
|
||||
|
||||
const BreadcrumbSeparator = ({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"li">) => (
|
||||
const BreadcrumbSeparator = ({ children, className, ...props }: React.ComponentProps<'li'>) => (
|
||||
<li
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
className={cn("[&>svg]:w-3.5 [&>svg]:h-3.5", className)}
|
||||
className={cn('[&>svg]:w-3.5 [&>svg]:h-3.5', className)}
|
||||
{...props}
|
||||
>
|
||||
{children ?? <ChevronRight />}
|
||||
</li>
|
||||
)
|
||||
BreadcrumbSeparator.displayName = "BreadcrumbSeparator"
|
||||
BreadcrumbSeparator.displayName = 'BreadcrumbSeparator'
|
||||
|
||||
const BreadcrumbEllipsis = ({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"span">) => (
|
||||
const BreadcrumbEllipsis = ({ className, ...props }: React.ComponentProps<'span'>) => (
|
||||
<span
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
className={cn("flex h-9 w-9 items-center justify-center", className)}
|
||||
className={cn('flex h-9 w-9 items-center justify-center', className)}
|
||||
{...props}
|
||||
>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
<span className="sr-only">More</span>
|
||||
</span>
|
||||
)
|
||||
BreadcrumbEllipsis.displayName = "BreadcrumbElipssis"
|
||||
BreadcrumbEllipsis.displayName = 'BreadcrumbElipssis'
|
||||
|
||||
export {
|
||||
Breadcrumb,
|
||||
|
||||
+20
-28
@@ -1,34 +1,30 @@
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import { Slot } from '@radix-ui/react-slot'
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
outline:
|
||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
||||
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline',
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
default: 'h-10 px-4 py-2',
|
||||
sm: 'h-9 rounded-md px-3',
|
||||
lg: 'h-11 rounded-md px-8',
|
||||
icon: 'h-10 w-10',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -41,16 +37,12 @@ export interface ButtonProps
|
||||
|
||||
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button"
|
||||
const Comp = asChild ? Slot : 'button'
|
||||
return (
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
<Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
|
||||
)
|
||||
}
|
||||
)
|
||||
Button.displayName = "Button"
|
||||
Button.displayName = 'Button'
|
||||
|
||||
export { Button, buttonVariants }
|
||||
|
||||
+46
-71
@@ -1,79 +1,54 @@
|
||||
import * as React from "react"
|
||||
import * as React from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
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 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 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('text-2xl font-semibold leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
)
|
||||
CardTitle.displayName = 'CardTitle'
|
||||
|
||||
const CardTitle = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-2xl font-semibold 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-sm text-muted-foreground', className)} {...props} />
|
||||
)
|
||||
)
|
||||
CardDescription.displayName = 'CardDescription'
|
||||
|
||||
const CardDescription = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", 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 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"
|
||||
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,10 +1,9 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
||||
import { Check } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox'
|
||||
import { Check } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Checkbox = React.forwardRef<
|
||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||
@@ -13,14 +12,12 @@ const Checkbox = React.forwardRef<
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||
'peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
className={cn("flex items-center justify-center text-current")}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator className={cn('flex items-center justify-center text-current')}>
|
||||
<Check className="h-4 w-4" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
|
||||
+16
-15
@@ -6,9 +6,20 @@ import * as React from 'react'
|
||||
import { type DialogProps } from '@radix-ui/react-dialog'
|
||||
import { Command as CommandPrimitive } from 'cmdk'
|
||||
import { Search } from 'lucide-react'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Dialog, DialogContent } from '@/components/ui/dialog'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
// This file is not typed correctly from shadcn, so we're disabling the type checker
|
||||
// @ts-nocheck
|
||||
|
||||
const Command = React.forwardRef<
|
||||
React.ElementRef<typeof CommandPrimitive>,
|
||||
@@ -81,11 +92,7 @@ const CommandEmpty = React.forwardRef<
|
||||
children?: React.ReactNode
|
||||
}
|
||||
>((props, ref) => (
|
||||
<CommandPrimitive.Empty
|
||||
ref={ref}
|
||||
className="py-6 text-center text-sm"
|
||||
{...props}
|
||||
/>
|
||||
<CommandPrimitive.Empty ref={ref} className="py-6 text-center text-sm" {...props} />
|
||||
))
|
||||
|
||||
CommandEmpty.displayName = CommandPrimitive.Empty.displayName
|
||||
@@ -140,16 +147,10 @@ const CommandItem = React.forwardRef<
|
||||
|
||||
CommandItem.displayName = CommandPrimitive.Item.displayName
|
||||
|
||||
const CommandShortcut = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
const CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return (
|
||||
<span
|
||||
className={cn(
|
||||
'ml-auto text-xs tracking-widest text-muted-foreground',
|
||||
className
|
||||
)}
|
||||
className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
+15
-34
@@ -1,10 +1,9 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||
import { X } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import * as DialogPrimitive from '@radix-ui/react-dialog'
|
||||
import { X } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Dialog = DialogPrimitive.Root
|
||||
|
||||
@@ -21,7 +20,7 @@ const DialogOverlay = React.forwardRef<
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"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",
|
||||
'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',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -38,7 +37,7 @@ const DialogContent = React.forwardRef<
|
||||
<DialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -53,33 +52,18 @@ const DialogContent = React.forwardRef<
|
||||
))
|
||||
DialogContent.displayName = DialogPrimitive.Content.displayName
|
||||
|
||||
const DialogHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col space-y-1.5 text-center sm:text-left",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)} {...props} />
|
||||
)
|
||||
DialogHeader.displayName = "DialogHeader"
|
||||
DialogHeader.displayName = 'DialogHeader'
|
||||
|
||||
const DialogFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className
|
||||
)}
|
||||
className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
DialogFooter.displayName = "DialogFooter"
|
||||
DialogFooter.displayName = 'DialogFooter'
|
||||
|
||||
const DialogTitle = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Title>,
|
||||
@@ -87,10 +71,7 @@ const DialogTitle = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-lg font-semibold leading-none tracking-tight",
|
||||
className
|
||||
)}
|
||||
className={cn('text-lg font-semibold leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
@@ -102,7 +83,7 @@ const DialogDescription = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
className={cn('text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
"use client"
|
||||
'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/utils"
|
||||
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/utils'
|
||||
|
||||
const DropdownMenu = DropdownMenuPrimitive.Root
|
||||
|
||||
@@ -27,8 +26,8 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default gap-2 select-none items-center 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",
|
||||
'flex cursor-default gap-2 select-none items-center 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}
|
||||
@@ -37,8 +36,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
||||
<ChevronRight className="ml-auto" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
))
|
||||
DropdownMenuSubTrigger.displayName =
|
||||
DropdownMenuPrimitive.SubTrigger.displayName
|
||||
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName
|
||||
|
||||
const DropdownMenuSubContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
||||
@@ -47,14 +45,13 @@ const DropdownMenuSubContent = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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",
|
||||
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
DropdownMenuSubContent.displayName =
|
||||
DropdownMenuPrimitive.SubContent.displayName
|
||||
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName
|
||||
|
||||
const DropdownMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
||||
@@ -65,7 +62,7 @@ const DropdownMenuContent = React.forwardRef<
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"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",
|
||||
'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',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -83,8 +80,8 @@ const DropdownMenuItem = React.forwardRef<
|
||||
<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",
|
||||
'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}
|
||||
@@ -99,7 +96,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 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}
|
||||
@@ -113,8 +110,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
))
|
||||
DropdownMenuCheckboxItem.displayName =
|
||||
DropdownMenuPrimitive.CheckboxItem.displayName
|
||||
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName
|
||||
|
||||
const DropdownMenuRadioItem = React.forwardRef<
|
||||
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
||||
@@ -123,7 +119,7 @@ const DropdownMenuRadioItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -146,11 +142,7 @@ const DropdownMenuLabel = React.forwardRef<
|
||||
>(({ className, inset, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"px-2 py-1.5 text-sm font-semibold",
|
||||
inset && "pl-8",
|
||||
className
|
||||
)}
|
||||
className={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
@@ -162,24 +154,16 @@ const DropdownMenuSeparator = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
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}
|
||||
/>
|
||||
)
|
||||
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
|
||||
return <span className={cn('ml-auto text-xs tracking-widest opacity-60', className)} {...props} />
|
||||
}
|
||||
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
|
||||
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useEnvironmentStore } from '@/stores/environment/store'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useEnvironmentStore } from '@/stores/environment/store'
|
||||
|
||||
interface EnvVarDropdownProps {
|
||||
visible: boolean
|
||||
@@ -12,20 +12,20 @@ interface EnvVarDropdownProps {
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
export const EnvVarDropdown: React.FC<EnvVarDropdownProps> = ({
|
||||
visible,
|
||||
export const EnvVarDropdown: React.FC<EnvVarDropdownProps> = ({
|
||||
visible,
|
||||
onSelect,
|
||||
searchTerm = '',
|
||||
className,
|
||||
inputValue,
|
||||
cursorPosition,
|
||||
onClose
|
||||
onClose,
|
||||
}) => {
|
||||
const envVars = useEnvironmentStore((state) => Object.keys(state.variables))
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
|
||||
// Filter env vars based on search term
|
||||
const filteredEnvVars = envVars.filter(envVar =>
|
||||
const filteredEnvVars = envVars.filter((envVar) =>
|
||||
envVar.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
)
|
||||
|
||||
@@ -38,15 +38,14 @@ export const EnvVarDropdown: React.FC<EnvVarDropdownProps> = ({
|
||||
const handleEnvVarSelect = (envVar: string) => {
|
||||
const textBeforeCursor = inputValue.slice(0, cursorPosition)
|
||||
const textAfterCursor = inputValue.slice(cursorPosition)
|
||||
|
||||
|
||||
// Find the position of the last '{{' before cursor
|
||||
const lastOpenBraces = textBeforeCursor.lastIndexOf('{{')
|
||||
if (lastOpenBraces === -1) return
|
||||
|
||||
const newValue = textBeforeCursor.slice(0, lastOpenBraces) +
|
||||
'{{' + envVar + '}}' +
|
||||
textAfterCursor
|
||||
|
||||
|
||||
const newValue =
|
||||
textBeforeCursor.slice(0, lastOpenBraces) + '{{' + envVar + '}}' + textAfterCursor
|
||||
|
||||
onSelect(newValue)
|
||||
onClose?.()
|
||||
}
|
||||
@@ -58,13 +57,11 @@ export const EnvVarDropdown: React.FC<EnvVarDropdownProps> = ({
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault()
|
||||
setSelectedIndex(prev =>
|
||||
prev < filteredEnvVars.length - 1 ? prev + 1 : prev
|
||||
)
|
||||
setSelectedIndex((prev) => (prev < filteredEnvVars.length - 1 ? prev + 1 : prev))
|
||||
break
|
||||
case 'ArrowUp':
|
||||
e.preventDefault()
|
||||
setSelectedIndex(prev => prev > 0 ? prev - 1 : prev)
|
||||
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : prev))
|
||||
break
|
||||
case 'Enter':
|
||||
e.preventDefault()
|
||||
@@ -90,7 +87,7 @@ export const EnvVarDropdown: React.FC<EnvVarDropdownProps> = ({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"absolute z-[9999] w-full mt-1 overflow-hidden bg-popover rounded-md border shadow-md",
|
||||
'absolute z-[9999] w-full mt-1 overflow-hidden bg-popover rounded-md border shadow-md',
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -104,10 +101,10 @@ export const EnvVarDropdown: React.FC<EnvVarDropdownProps> = ({
|
||||
<button
|
||||
key={envVar}
|
||||
className={cn(
|
||||
"w-full px-3 py-1.5 text-sm text-left",
|
||||
"hover:bg-accent hover:text-accent-foreground",
|
||||
"focus:bg-accent focus:text-accent-foreground focus:outline-none",
|
||||
index === selectedIndex && "bg-accent text-accent-foreground"
|
||||
'w-full px-3 py-1.5 text-sm text-left',
|
||||
'hover:bg-accent hover:text-accent-foreground',
|
||||
'focus:bg-accent focus:text-accent-foreground focus:outline-none',
|
||||
index === selectedIndex && 'bg-accent text-accent-foreground'
|
||||
)}
|
||||
onMouseEnter={() => setSelectedIndex(index)}
|
||||
onMouseDown={(e) => {
|
||||
@@ -125,7 +122,10 @@ export const EnvVarDropdown: React.FC<EnvVarDropdownProps> = ({
|
||||
}
|
||||
|
||||
// Helper function to check for '{{' trigger and get search term
|
||||
export const checkEnvVarTrigger = (text: string, cursorPosition: number): { show: boolean; searchTerm: string } => {
|
||||
export const checkEnvVarTrigger = (
|
||||
text: string,
|
||||
cursorPosition: number
|
||||
): { show: boolean; searchTerm: string } => {
|
||||
if (cursorPosition >= 2) {
|
||||
const textBeforeCursor = text.slice(0, cursorPosition)
|
||||
const match = textBeforeCursor.match(/\{\{(\w*)$/)
|
||||
@@ -134,4 +134,4 @@ export const checkEnvVarTrigger = (text: string, cursorPosition: number): { show
|
||||
}
|
||||
}
|
||||
return { show: false, searchTerm: '' }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,4 +33,4 @@ export function formatDisplayText(text: string): ReactNode[] {
|
||||
|
||||
return <span key={index}>{part}</span>
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react'
|
||||
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<'input'>>(
|
||||
|
||||
+8
-14
@@ -1,25 +1,19 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import * as LabelPrimitive from '@radix-ui/react-label'
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const labelVariants = cva(
|
||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
|
||||
)
|
||||
|
||||
const Label = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||
VariantProps<typeof labelVariants>
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> & VariantProps<typeof labelVariants>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<LabelPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(labelVariants(), className)}
|
||||
{...props}
|
||||
/>
|
||||
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
|
||||
))
|
||||
Label.displayName = LabelPrimitive.Root.displayName
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import * as PopoverPrimitive from '@radix-ui/react-popover'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Popover = PopoverPrimitive.Root
|
||||
|
||||
@@ -12,14 +11,14 @@ const PopoverTrigger = PopoverPrimitive.Trigger
|
||||
const PopoverContent = React.forwardRef<
|
||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
||||
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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",
|
||||
'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const ScrollArea = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.Root>,
|
||||
@@ -11,7 +10,7 @@ const ScrollArea = React.forwardRef<
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<ScrollAreaPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn("relative overflow-hidden", className)}
|
||||
className={cn('relative overflow-hidden', className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
|
||||
@@ -26,16 +25,14 @@ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
|
||||
const ScrollBar = React.forwardRef<
|
||||
React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
|
||||
React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
>(({ className, orientation = "vertical", ...props }, ref) => (
|
||||
>(({ className, orientation = 'vertical', ...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]",
|
||||
'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]',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
+19
-27
@@ -1,10 +1,9 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||
import { Check, ChevronDown, ChevronUp } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import * as SelectPrimitive from '@radix-ui/react-select'
|
||||
import { Check, ChevronDown, ChevronUp } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Select = SelectPrimitive.Root
|
||||
|
||||
@@ -19,7 +18,7 @@ const SelectTrigger = React.forwardRef<
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -38,10 +37,7 @@ const SelectScrollUpButton = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
className={cn('flex cursor-default items-center justify-center py-1', className)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUp className="h-4 w-4" />
|
||||
@@ -55,29 +51,25 @@ const SelectScrollDownButton = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
className={cn('flex cursor-default items-center justify-center py-1', className)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
))
|
||||
SelectScrollDownButton.displayName =
|
||||
SelectPrimitive.ScrollDownButton.displayName
|
||||
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
|
||||
|
||||
const SelectContent = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||
>(({ className, children, position = "popper", ...props }, ref) => (
|
||||
>(({ className, children, position = 'popper', ...props }, ref) => (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover 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",
|
||||
position === "popper" &&
|
||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover 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',
|
||||
position === 'popper' &&
|
||||
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
className
|
||||
)}
|
||||
position={position}
|
||||
@@ -86,9 +78,9 @@ const SelectContent = React.forwardRef<
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
"p-1",
|
||||
position === "popper" &&
|
||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
||||
'p-1',
|
||||
position === 'popper' &&
|
||||
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
@@ -105,7 +97,7 @@ const SelectLabel = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
|
||||
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
@@ -118,7 +110,7 @@ const SelectItem = React.forwardRef<
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -140,7 +132,7 @@ const SelectSeparator = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
|
||||
+23
-43
@@ -1,11 +1,10 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { X } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import * as SheetPrimitive from '@radix-ui/react-dialog'
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
import { X } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Sheet = SheetPrimitive.Root
|
||||
|
||||
@@ -21,7 +20,7 @@ const SheetOverlay = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Overlay
|
||||
className={cn(
|
||||
"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",
|
||||
'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',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -31,20 +30,20 @@ const SheetOverlay = React.forwardRef<
|
||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
|
||||
|
||||
const sheetVariants = cva(
|
||||
"fixed z-50 gap-4 bg-background 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",
|
||||
'fixed z-50 gap-4 bg-background 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',
|
||||
{
|
||||
variants: {
|
||||
side: {
|
||||
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
|
||||
top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
|
||||
bottom:
|
||||
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
|
||||
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",
|
||||
'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
|
||||
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',
|
||||
right:
|
||||
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
|
||||
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
side: "right",
|
||||
side: 'right',
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -56,14 +55,10 @@ interface SheetContentProps
|
||||
const SheetContent = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Content>,
|
||||
SheetContentProps
|
||||
>(({ side = "right", className, children, ...props }, ref) => (
|
||||
>(({ side = 'right', className, children, ...props }, ref) => (
|
||||
<SheetPortal>
|
||||
<SheetOverlay />
|
||||
<SheetPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(sheetVariants({ side }), className)}
|
||||
{...props}
|
||||
>
|
||||
<SheetPrimitive.Content ref={ref} className={cn(sheetVariants({ side }), className)} {...props}>
|
||||
{children}
|
||||
<SheetPrimitive.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">
|
||||
<X className="h-4 w-4" />
|
||||
@@ -74,33 +69,18 @@ const SheetContent = React.forwardRef<
|
||||
))
|
||||
SheetContent.displayName = SheetPrimitive.Content.displayName
|
||||
|
||||
const SheetHeader = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col space-y-2 text-center sm:text-left",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
const SheetHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div className={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...props} />
|
||||
)
|
||||
SheetHeader.displayName = "SheetHeader"
|
||||
SheetHeader.displayName = 'SheetHeader'
|
||||
|
||||
const SheetFooter = ({
|
||||
className,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
const SheetFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
|
||||
<div
|
||||
className={cn(
|
||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||
className
|
||||
)}
|
||||
className={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
SheetFooter.displayName = "SheetFooter"
|
||||
SheetFooter.displayName = 'SheetFooter'
|
||||
|
||||
const SheetTitle = React.forwardRef<
|
||||
React.ElementRef<typeof SheetPrimitive.Title>,
|
||||
@@ -108,7 +88,7 @@ const SheetTitle = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Title
|
||||
ref={ref}
|
||||
className={cn("text-lg font-semibold text-foreground", className)}
|
||||
className={cn('text-lg font-semibold text-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
@@ -120,7 +100,7 @@ const SheetDescription = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SheetPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
className={cn('text-sm text-muted-foreground', className)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as SliderPrimitive from "@radix-ui/react-slider"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import * as SliderPrimitive from '@radix-ui/react-slider'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Slider = React.forwardRef<
|
||||
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||
@@ -11,10 +10,7 @@ const Slider = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SliderPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full touch-none select-none items-center",
|
||||
className
|
||||
)}
|
||||
className={cn('relative flex w-full touch-none select-none items-center', className)}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as SwitchPrimitives from "@radix-ui/react-switch"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import * as SwitchPrimitives from '@radix-ui/react-switch'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Switch = React.forwardRef<
|
||||
React.ElementRef<typeof SwitchPrimitives.Root>,
|
||||
@@ -11,7 +10,7 @@ const Switch = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SwitchPrimitives.Root
|
||||
className={cn(
|
||||
"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
||||
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -19,7 +18,7 @@ const Switch = React.forwardRef<
|
||||
>
|
||||
<SwitchPrimitives.Thumb
|
||||
className={cn(
|
||||
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
|
||||
'pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0'
|
||||
)}
|
||||
/>
|
||||
</SwitchPrimitives.Root>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import * as TabsPrimitive from '@radix-ui/react-tabs'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const Tabs = TabsPrimitive.Root
|
||||
|
||||
@@ -14,7 +13,7 @@ const TabsList = React.forwardRef<
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
||||
'inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -29,7 +28,7 @@ const TabsTrigger = React.forwardRef<
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 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",
|
||||
'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 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',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -44,7 +43,7 @@ const TabsContent = React.forwardRef<
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||
'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useWorkflowStore } from '@/stores/workflow/store'
|
||||
|
||||
@@ -62,9 +62,7 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
const blockName = sourceBlock.name || sourceBlock.type
|
||||
|
||||
return {
|
||||
tags: outputPaths.map(
|
||||
(path) => `${blockName.replace(/\s+/g, '').toLowerCase()}.${path}`
|
||||
),
|
||||
tags: outputPaths.map((path) => `${blockName.replace(/\s+/g, '').toLowerCase()}.${path}`),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,9 +90,7 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
const outputPaths = getOutputPaths(sourceBlock.outputs)
|
||||
const blockName = sourceBlock.name || sourceBlock.type
|
||||
|
||||
return outputPaths.map(
|
||||
(path) => `${blockName.replace(/\s+/g, '').toLowerCase()}.${path}`
|
||||
)
|
||||
return outputPaths.map((path) => `${blockName.replace(/\s+/g, '').toLowerCase()}.${path}`)
|
||||
})
|
||||
|
||||
return { tags: sourceTags }
|
||||
@@ -120,12 +116,7 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
const lastOpenBracket = textBeforeCursor.lastIndexOf('<')
|
||||
if (lastOpenBracket === -1) return
|
||||
|
||||
const newValue =
|
||||
textBeforeCursor.slice(0, lastOpenBracket) +
|
||||
'<' +
|
||||
tag +
|
||||
'>' +
|
||||
textAfterCursor
|
||||
const newValue = textBeforeCursor.slice(0, lastOpenBracket) + '<' + tag + '>' + textAfterCursor
|
||||
|
||||
onSelect(newValue)
|
||||
onClose?.()
|
||||
@@ -138,9 +129,7 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
switch (e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault()
|
||||
setSelectedIndex((prev) =>
|
||||
prev < filteredTags.length - 1 ? prev + 1 : prev
|
||||
)
|
||||
setSelectedIndex((prev) => (prev < filteredTags.length - 1 ? prev + 1 : prev))
|
||||
break
|
||||
case 'ArrowUp':
|
||||
e.preventDefault()
|
||||
@@ -177,9 +166,7 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
>
|
||||
<div className="py-1">
|
||||
{filteredTags.length === 0 ? (
|
||||
<div className="px-3 py-2 text-sm text-muted-foreground">
|
||||
No matching tags found
|
||||
</div>
|
||||
<div className="px-3 py-2 text-sm text-muted-foreground">No matching tags found</div>
|
||||
) : (
|
||||
filteredTags.map((tag, index) => (
|
||||
<button
|
||||
@@ -206,20 +193,14 @@ export const TagDropdown: React.FC<TagDropdownProps> = ({
|
||||
}
|
||||
|
||||
// Helper function to check for '<' trigger
|
||||
export const checkTagTrigger = (
|
||||
text: string,
|
||||
cursorPosition: number
|
||||
): { show: boolean } => {
|
||||
export const checkTagTrigger = (text: string, cursorPosition: number): { show: boolean } => {
|
||||
if (cursorPosition >= 1) {
|
||||
const textBeforeCursor = text.slice(0, cursorPosition)
|
||||
const lastOpenBracket = textBeforeCursor.lastIndexOf('<')
|
||||
const lastCloseBracket = textBeforeCursor.lastIndexOf('>')
|
||||
|
||||
// Show if we have an unclosed '<' that's not part of a completed tag
|
||||
if (
|
||||
lastOpenBracket !== -1 &&
|
||||
(lastCloseBracket === -1 || lastCloseBracket < lastOpenBracket)
|
||||
) {
|
||||
if (lastOpenBracket !== -1 && (lastCloseBracket === -1 || lastCloseBracket < lastOpenBracket)) {
|
||||
return { show: true }
|
||||
}
|
||||
}
|
||||
|
||||
+17
-19
@@ -1,22 +1,20 @@
|
||||
import * as React from "react"
|
||||
import * as React from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Textarea = React.forwardRef<
|
||||
HTMLTextAreaElement,
|
||||
React.ComponentProps<"textarea">
|
||||
>(({ className, ...props }, ref) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
})
|
||||
Textarea.displayName = "Textarea"
|
||||
const Textarea = React.forwardRef<HTMLTextAreaElement, React.ComponentProps<'textarea'>>(
|
||||
({ className, ...props }, ref) => {
|
||||
return (
|
||||
<textarea
|
||||
className={cn(
|
||||
'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)
|
||||
Textarea.displayName = 'Textarea'
|
||||
|
||||
export { Textarea }
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"use client"
|
||||
'use client'
|
||||
|
||||
import * as React from "react"
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import * as React from 'react'
|
||||
import * as TooltipPrimitive from '@radix-ui/react-tooltip'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const TooltipProvider = TooltipPrimitive.Provider
|
||||
|
||||
@@ -19,7 +18,7 @@ const TooltipContent = React.forwardRef<
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm 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",
|
||||
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm 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',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
+479
-455
File diff suppressed because it is too large
Load Diff
+121
-110
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* "Executor" for running agentic workflows in parallel.
|
||||
*
|
||||
*
|
||||
* Notes & Features:
|
||||
* • Uses a layered topological sort to allow parallel block execution for blocks with no remaining dependencies.
|
||||
* • Each block's inputs are resolved through a template mechanism (e.g., <blockId.property>).
|
||||
@@ -8,19 +8,13 @@
|
||||
* • Maintains robust error handling (if a block fails, throws an error for the entire workflow).
|
||||
* • Returns per-block logs that can be displayed in the UI for better trace/debug.
|
||||
*/
|
||||
|
||||
import { SerializedWorkflow, SerializedBlock } from '@/serializer/types'
|
||||
import { BlockOutput } from '@/blocks/types'
|
||||
import {
|
||||
Tool,
|
||||
ExecutionContext,
|
||||
ExecutionResult,
|
||||
BlockLog
|
||||
} from './types'
|
||||
import { tools, executeTool, getTool } from '@/tools'
|
||||
import { executeProviderRequest } from '@/providers/service'
|
||||
import { getAllBlocks } from '@/blocks'
|
||||
import { BlockOutput } from '@/blocks/types'
|
||||
import { BlockConfig } from '@/blocks/types'
|
||||
import { executeProviderRequest } from '@/providers/service'
|
||||
import { SerializedBlock, SerializedWorkflow } from '@/serializer/types'
|
||||
import { executeTool, getTool, tools } from '@/tools'
|
||||
import { BlockLog, ExecutionContext, ExecutionResult, Tool } from './types'
|
||||
|
||||
export class Executor {
|
||||
constructor(
|
||||
@@ -42,9 +36,9 @@ export class Executor {
|
||||
blockStates: new Map<string, BlockOutput>(),
|
||||
blockLogs: [],
|
||||
metadata: {
|
||||
startTime: startTime.toISOString()
|
||||
startTime: startTime.toISOString(),
|
||||
},
|
||||
environmentVariables: this.environmentVariables
|
||||
environmentVariables: this.environmentVariables,
|
||||
}
|
||||
|
||||
// Pre-populate block states if initialBlockStates exist
|
||||
@@ -105,9 +99,7 @@ export class Executor {
|
||||
}
|
||||
|
||||
// Start with all blocks that have inDegree = 0
|
||||
let layer = blocks
|
||||
.filter((b) => (inDegree.get(b.id) || 0) === 0)
|
||||
.map((b) => b.id)
|
||||
let layer = blocks.filter((b) => (inDegree.get(b.id) || 0) === 0).map((b) => b.id)
|
||||
|
||||
// Track the final output from the "last" block or set of blocks
|
||||
let lastOutput: BlockOutput = { response: {} }
|
||||
@@ -156,7 +148,7 @@ export class Executor {
|
||||
// Compute the end time and duration
|
||||
const end = new Date()
|
||||
blockLog.endedAt = end.toISOString()
|
||||
|
||||
|
||||
if (blockLog.startedAt) {
|
||||
const started = new Date(blockLog.startedAt).getTime()
|
||||
blockLog.durationMs = end.getTime() - started
|
||||
@@ -216,76 +208,87 @@ export class Executor {
|
||||
// Special handling for agent blocks that use providers
|
||||
if (block.metadata?.type === 'agent') {
|
||||
const model = inputs.model || 'gpt-4o'
|
||||
const providerId = model.startsWith('gpt') || model.startsWith('o1')
|
||||
? 'openai'
|
||||
: model.startsWith('claude')
|
||||
? 'anthropic'
|
||||
: model.startsWith('gemini')
|
||||
? 'google'
|
||||
: model.startsWith('grok')
|
||||
? 'xai'
|
||||
: 'deepseek'
|
||||
const providerId =
|
||||
model.startsWith('gpt') || model.startsWith('o1')
|
||||
? 'openai'
|
||||
: model.startsWith('claude')
|
||||
? 'anthropic'
|
||||
: model.startsWith('gemini')
|
||||
? 'google'
|
||||
: model.startsWith('grok')
|
||||
? 'xai'
|
||||
: 'deepseek'
|
||||
|
||||
// Format tools if they exist
|
||||
const tools = Array.isArray(inputs.tools) ? inputs.tools.map((tool: any) => {
|
||||
// Get the tool ID from the block type
|
||||
const block = getAllBlocks().find((b: BlockConfig) => b.type === tool.type)
|
||||
const toolId = block?.tools.access[0]
|
||||
if (!toolId) return null
|
||||
const tools = Array.isArray(inputs.tools)
|
||||
? inputs.tools
|
||||
.map((tool: any) => {
|
||||
// Get the tool ID from the block type
|
||||
const block = getAllBlocks().find((b: BlockConfig) => b.type === tool.type)
|
||||
const toolId = block?.tools.access[0]
|
||||
if (!toolId) return null
|
||||
|
||||
// Get the tool configuration
|
||||
const toolConfig = getTool(toolId)
|
||||
if (!toolConfig) return null
|
||||
// Get the tool configuration
|
||||
const toolConfig = getTool(toolId)
|
||||
if (!toolConfig) return null
|
||||
|
||||
// Resolve environment variables in tool parameters
|
||||
const resolvedParams = Object.entries(tool.params || {}).reduce((acc, [key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
// Handle environment variables with {{}} syntax
|
||||
const envMatches = value.match(/\{\{([^}]+)\}\}/g)
|
||||
if (envMatches) {
|
||||
let resolvedValue = value
|
||||
for (const match of envMatches) {
|
||||
const envKey = match.slice(2, -2) // remove {{ and }}
|
||||
const envValue = context.environmentVariables?.[envKey]
|
||||
|
||||
if (envValue === undefined) {
|
||||
throw new Error(`Environment variable "${envKey}" was not found.`)
|
||||
}
|
||||
// Resolve environment variables in tool parameters
|
||||
const resolvedParams = Object.entries(tool.params || {}).reduce(
|
||||
(acc, [key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
// Handle environment variables with {{}} syntax
|
||||
const envMatches = value.match(/\{\{([^}]+)\}\}/g)
|
||||
if (envMatches) {
|
||||
let resolvedValue = value
|
||||
for (const match of envMatches) {
|
||||
const envKey = match.slice(2, -2) // remove {{ and }}
|
||||
const envValue = context.environmentVariables?.[envKey]
|
||||
|
||||
resolvedValue = resolvedValue.replace(match, envValue)
|
||||
if (envValue === undefined) {
|
||||
throw new Error(`Environment variable "${envKey}" was not found.`)
|
||||
}
|
||||
|
||||
resolvedValue = resolvedValue.replace(match, envValue)
|
||||
}
|
||||
acc[key] = resolvedValue
|
||||
} else {
|
||||
acc[key] = value
|
||||
}
|
||||
} else {
|
||||
acc[key] = value
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, any>
|
||||
)
|
||||
|
||||
// Return the tool configuration with resolved parameters
|
||||
return {
|
||||
id: toolConfig.id,
|
||||
name: toolConfig.name,
|
||||
description: toolConfig.description,
|
||||
params: resolvedParams,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: Object.entries(toolConfig.params).reduce(
|
||||
(acc, [key, config]) => ({
|
||||
...acc,
|
||||
[key]: {
|
||||
type: config.type === 'json' ? 'object' : config.type,
|
||||
description: config.description || '',
|
||||
...(key in resolvedParams && { default: resolvedParams[key] }),
|
||||
},
|
||||
}),
|
||||
{}
|
||||
),
|
||||
required: Object.entries(toolConfig.params)
|
||||
.filter(([_, config]) => config.required)
|
||||
.map(([key]) => key),
|
||||
},
|
||||
}
|
||||
acc[key] = resolvedValue
|
||||
} else {
|
||||
acc[key] = value
|
||||
}
|
||||
} else {
|
||||
acc[key] = value
|
||||
}
|
||||
return acc
|
||||
}, {} as Record<string, any>)
|
||||
|
||||
// Return the tool configuration with resolved parameters
|
||||
return {
|
||||
id: toolConfig.id,
|
||||
name: toolConfig.name,
|
||||
description: toolConfig.description,
|
||||
params: resolvedParams,
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: Object.entries(toolConfig.params).reduce((acc, [key, config]) => ({
|
||||
...acc,
|
||||
[key]: {
|
||||
type: config.type === 'json' ? 'object' : config.type,
|
||||
description: config.description || '',
|
||||
...(key in resolvedParams && { default: resolvedParams[key] })
|
||||
}
|
||||
}), {}),
|
||||
required: Object.entries(toolConfig.params)
|
||||
.filter(([_, config]) => config.required)
|
||||
.map(([key]) => key)
|
||||
}
|
||||
}
|
||||
}).filter((t): t is NonNullable<typeof t> => t !== null) : []
|
||||
})
|
||||
.filter((t): t is NonNullable<typeof t> => t !== null)
|
||||
: []
|
||||
|
||||
const requestPayload = {
|
||||
model,
|
||||
@@ -294,7 +297,7 @@ export class Executor {
|
||||
tools: tools.length > 0 ? tools : undefined,
|
||||
temperature: inputs.temperature,
|
||||
maxTokens: inputs.maxTokens,
|
||||
apiKey: inputs.apiKey
|
||||
apiKey: inputs.apiKey,
|
||||
}
|
||||
|
||||
// Log the request payload for debugging
|
||||
@@ -314,13 +317,13 @@ export class Executor {
|
||||
tokens: response.tokens || {
|
||||
prompt: 0,
|
||||
completion: 0,
|
||||
total: 0
|
||||
total: 0,
|
||||
},
|
||||
toolCalls: {
|
||||
list: response.toolCalls || [],
|
||||
count: response.toolCalls?.length || 0
|
||||
}
|
||||
}
|
||||
count: response.toolCalls?.length || 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +345,7 @@ export class Executor {
|
||||
|
||||
try {
|
||||
const result = await executeTool(toolId, validatedParams)
|
||||
|
||||
|
||||
if (!result.success) {
|
||||
// Ensure we have a meaningful error message
|
||||
const errorMessage = result.error || `Tool ${toolId} failed with no error message`
|
||||
@@ -360,9 +363,9 @@ export class Executor {
|
||||
error: error.message || `Tool ${toolId} failed`,
|
||||
startedAt: new Date().toISOString(),
|
||||
endedAt: new Date().toISOString(),
|
||||
durationMs: 0
|
||||
durationMs: 0,
|
||||
}
|
||||
|
||||
|
||||
// Add the log entry
|
||||
context.blockLogs.push(blockLog as BlockLog)
|
||||
|
||||
@@ -375,26 +378,26 @@ export class Executor {
|
||||
* Validates required parameters for a Tool, or uses defaults if present.
|
||||
*/
|
||||
private validateToolParams(tool: Tool, params: Record<string, any>): Record<string, any> {
|
||||
return Object.entries(tool.params).reduce((acc, [name, config]) => {
|
||||
if (name in params) {
|
||||
acc[name] = params[name]
|
||||
} else if ('default' in config) {
|
||||
acc[name] = config.default
|
||||
} else if (config.required) {
|
||||
throw new Error(`Missing required parameter '${name}'`)
|
||||
}
|
||||
return acc
|
||||
}, {} as Record<string, any>)
|
||||
return Object.entries(tool.params).reduce(
|
||||
(acc, [name, config]) => {
|
||||
if (name in params) {
|
||||
acc[name] = params[name]
|
||||
} else if ('default' in config) {
|
||||
acc[name] = config.default
|
||||
} else if (config.required) {
|
||||
throw new Error(`Missing required parameter '${name}'`)
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, any>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves any template references in a block's config params (e.g., "<someBlockId.response>"),
|
||||
* pulling from context.blockStates. This is how outputs from one block get wired as inputs to another.
|
||||
*/
|
||||
private resolveInputs(
|
||||
block: SerializedBlock,
|
||||
context: ExecutionContext
|
||||
): Record<string, any> {
|
||||
private resolveInputs(block: SerializedBlock, context: ExecutionContext): Record<string, any> {
|
||||
const inputs = { ...block.config.params }
|
||||
|
||||
// Create quick-lookup for blocks by ID and by normalized name
|
||||
@@ -402,7 +405,7 @@ export class Executor {
|
||||
const blockByName = new Map(
|
||||
this.workflow.blocks.map((b) => [
|
||||
b.metadata?.title?.toLowerCase().replace(/\s+/g, '') || '',
|
||||
b
|
||||
b,
|
||||
])
|
||||
)
|
||||
|
||||
@@ -432,19 +435,25 @@ export class Executor {
|
||||
|
||||
// Check if the referenced block is disabled.
|
||||
if (sourceBlock.enabled === false) {
|
||||
throw new Error(`Block "${sourceBlock.metadata?.title}" is disabled, and block "${block.metadata?.title}" depends on it.`)
|
||||
throw new Error(
|
||||
`Block "${sourceBlock.metadata?.title}" is disabled, and block "${block.metadata?.title}" depends on it.`
|
||||
)
|
||||
}
|
||||
|
||||
const sourceState = context.blockStates.get(sourceBlock.id)
|
||||
if (!sourceState) {
|
||||
throw new Error(`No state found for block "${sourceBlock.metadata?.title}" (ID: ${sourceBlock.id}).`)
|
||||
throw new Error(
|
||||
`No state found for block "${sourceBlock.metadata?.title}" (ID: ${sourceBlock.id}).`
|
||||
)
|
||||
}
|
||||
|
||||
// Drill into the path
|
||||
let replacementValue: any = sourceState
|
||||
for (const part of pathParts) {
|
||||
if (!replacementValue || typeof replacementValue !== 'object') {
|
||||
throw new Error(`Invalid path part "${part}" in "${path}" for block "${block.metadata?.title}".`)
|
||||
throw new Error(
|
||||
`Invalid path part "${part}" in "${path}" for block "${block.metadata?.title}".`
|
||||
)
|
||||
}
|
||||
replacementValue = replacementValue[part]
|
||||
}
|
||||
@@ -459,7 +468,9 @@ export class Executor {
|
||||
: String(replacementValue)
|
||||
)
|
||||
} else {
|
||||
throw new Error(`No value found at path "${path}" in block "${sourceBlock.metadata?.title}".`)
|
||||
throw new Error(
|
||||
`No value found at path "${path}" in block "${sourceBlock.metadata?.title}".`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -470,7 +481,7 @@ export class Executor {
|
||||
for (const match of envMatches) {
|
||||
const envKey = match.slice(2, -2) // remove {{ and }}
|
||||
const envValue = this.environmentVariables?.[envKey]
|
||||
|
||||
|
||||
if (envValue === undefined) {
|
||||
throw new Error(`Environment variable "${envKey}" was not found.`)
|
||||
}
|
||||
|
||||
+1
-1
@@ -84,4 +84,4 @@ export interface Tool<P = any, O = Record<string, any>> {
|
||||
*/
|
||||
export interface ToolRegistry {
|
||||
[key: string]: Tool
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -9,8 +9,11 @@ module.exports = {
|
||||
},
|
||||
testMatch: ['**/__tests__/**/*.test.ts'],
|
||||
transform: {
|
||||
'^.+\\.(ts|tsx)$': ['ts-jest', {
|
||||
tsconfig: 'tsconfig.json',
|
||||
}],
|
||||
}
|
||||
};
|
||||
'^.+\\.(ts|tsx)$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: 'tsconfig.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
import { type ClassValue, clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { type ClassValue, clsx } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
import { BlockState } from '@/stores/workflow/types'
|
||||
import { Edge } from 'reactflow'
|
||||
import { Serializer } from '@/serializer'
|
||||
import { BlockState } from '@/stores/workflow/types'
|
||||
import { Executor } from '@/executor'
|
||||
import { Serializer } from '@/serializer'
|
||||
|
||||
export interface WorkflowExecutionResult {
|
||||
success: boolean
|
||||
@@ -35,20 +35,20 @@ export async function executeWorkflow(
|
||||
console.log('Workflow executed successfully:', result.output)
|
||||
return {
|
||||
success: true,
|
||||
output: result.output
|
||||
output: result.output,
|
||||
}
|
||||
} else {
|
||||
console.error('Workflow execution failed:', result.error)
|
||||
return {
|
||||
success: false,
|
||||
error: result.error
|
||||
error: result.error,
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Error executing workflow:', error)
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error occurred'
|
||||
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import type { NextConfig } from "next";
|
||||
import type { NextConfig } from 'next'
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
};
|
||||
}
|
||||
|
||||
export default nextConfig;
|
||||
export default nextConfig
|
||||
|
||||
Generated
+736
-12
@@ -37,13 +37,18 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^20",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.7.0",
|
||||
"lint-staged": "^15.4.3",
|
||||
"postcss": "^8",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"ts-jest": "^29.2.5",
|
||||
"typescript": "^5.7.3"
|
||||
@@ -282,13 +287,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.26.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz",
|
||||
"integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==",
|
||||
"version": "7.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
|
||||
"integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.26.5"
|
||||
"@babel/types": "^7.26.7"
|
||||
},
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -564,17 +569,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.26.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz",
|
||||
"integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==",
|
||||
"version": "7.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz",
|
||||
"integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
"@babel/generator": "^7.26.5",
|
||||
"@babel/parser": "^7.26.5",
|
||||
"@babel/parser": "^7.26.7",
|
||||
"@babel/template": "^7.25.9",
|
||||
"@babel/types": "^7.26.5",
|
||||
"@babel/types": "^7.26.7",
|
||||
"debug": "^4.3.1",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@@ -583,9 +588,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.26.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz",
|
||||
"integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==",
|
||||
"version": "7.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz",
|
||||
"integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3101,6 +3106,41 @@
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@trivago/prettier-plugin-sort-imports": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz",
|
||||
"integrity": "sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/generator": "^7.26.5",
|
||||
"@babel/parser": "^7.26.7",
|
||||
"@babel/traverse": "^7.26.7",
|
||||
"@babel/types": "^7.26.7",
|
||||
"javascript-natural-sort": "^0.7.1",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">18.12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@vue/compiler-sfc": "3.x",
|
||||
"prettier": "2.x - 3.x",
|
||||
"prettier-plugin-svelte": "3.x",
|
||||
"svelte": "4.x || 5.x"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@vue/compiler-sfc": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-svelte": {
|
||||
"optional": true
|
||||
},
|
||||
"svelte": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
@@ -4024,6 +4064,64 @@
|
||||
"integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cli-cursor": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
|
||||
"integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"restore-cursor": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-truncate": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
|
||||
"integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"slice-ansi": "^5.0.0",
|
||||
"string-width": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-truncate/node_modules/emoji-regex": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cli-truncate/node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^10.3.0",
|
||||
"get-east-asian-width": "^1.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/client-only": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||
@@ -4675,6 +4773,13 @@
|
||||
"simple-swizzle": "^0.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/colorette": {
|
||||
"version": "2.0.20",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||
@@ -5007,6 +5112,19 @@
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/environment": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
|
||||
"integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
@@ -5058,6 +5176,13 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
||||
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/execa": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||
@@ -5284,6 +5409,19 @@
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-east-asian-width": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
|
||||
"integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-nonce": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
|
||||
@@ -5404,6 +5542,22 @@
|
||||
"node": ">=10.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/husky": {
|
||||
"version": "9.1.7",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
|
||||
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"husky": "bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/typicode"
|
||||
}
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz",
|
||||
@@ -5692,6 +5846,13 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/javascript-natural-sort": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
|
||||
"integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jest": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
|
||||
@@ -6467,6 +6628,249 @@
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lint-staged": {
|
||||
"version": "15.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.4.3.tgz",
|
||||
"integrity": "sha512-FoH1vOeouNh1pw+90S+cnuoFwRfUD9ijY2GKy5h7HS3OR7JVir2N2xrsa0+Twc1B7cW72L+88geG5cW4wIhn7g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^5.4.1",
|
||||
"commander": "^13.1.0",
|
||||
"debug": "^4.4.0",
|
||||
"execa": "^8.0.1",
|
||||
"lilconfig": "^3.1.3",
|
||||
"listr2": "^8.2.5",
|
||||
"micromatch": "^4.0.8",
|
||||
"pidtree": "^0.6.0",
|
||||
"string-argv": "^0.3.2",
|
||||
"yaml": "^2.7.0"
|
||||
},
|
||||
"bin": {
|
||||
"lint-staged": "bin/lint-staged.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/lint-staged"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/chalk": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
|
||||
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/commander": {
|
||||
"version": "13.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
|
||||
"integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/execa": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
|
||||
"integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.3",
|
||||
"get-stream": "^8.0.1",
|
||||
"human-signals": "^5.0.0",
|
||||
"is-stream": "^3.0.0",
|
||||
"merge-stream": "^2.0.0",
|
||||
"npm-run-path": "^5.1.0",
|
||||
"onetime": "^6.0.0",
|
||||
"signal-exit": "^4.1.0",
|
||||
"strip-final-newline": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/execa?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/get-stream": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
|
||||
"integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/human-signals": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
|
||||
"integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=16.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/is-stream": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
|
||||
"integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/mimic-fn": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
|
||||
"integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/npm-run-path": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
|
||||
"integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/onetime": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
|
||||
"integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mimic-fn": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/path-key": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
|
||||
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/lint-staged/node_modules/strip-final-newline": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
|
||||
"integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2": {
|
||||
"version": "8.2.5",
|
||||
"resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz",
|
||||
"integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cli-truncate": "^4.0.0",
|
||||
"colorette": "^2.0.20",
|
||||
"eventemitter3": "^5.0.1",
|
||||
"log-update": "^6.1.0",
|
||||
"rfdc": "^1.4.1",
|
||||
"wrap-ansi": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2/node_modules/emoji-regex": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/listr2/node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^10.3.0",
|
||||
"get-east-asian-width": "^1.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2/node_modules/wrap-ansi": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
|
||||
"integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.2.1",
|
||||
"string-width": "^7.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
@@ -6480,6 +6884,13 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
||||
@@ -6487,6 +6898,118 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/log-update": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
|
||||
"integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-escapes": "^7.0.0",
|
||||
"cli-cursor": "^5.0.0",
|
||||
"slice-ansi": "^7.1.0",
|
||||
"strip-ansi": "^7.1.0",
|
||||
"wrap-ansi": "^9.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/ansi-escapes": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
|
||||
"integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"environment": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/emoji-regex": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
||||
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/log-update/node_modules/is-fullwidth-code-point": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
|
||||
"integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"get-east-asian-width": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/slice-ansi": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
|
||||
"integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.2.1",
|
||||
"is-fullwidth-code-point": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/string-width": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
||||
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^10.3.0",
|
||||
"get-east-asian-width": "^1.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/log-update/node_modules/wrap-ansi": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
|
||||
"integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.2.1",
|
||||
"string-width": "^7.0.0",
|
||||
"strip-ansi": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
@@ -6586,6 +7109,19 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-function": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
|
||||
"integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||
@@ -6972,6 +7508,19 @@
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pidtree": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
|
||||
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"pidtree": "bin/pidtree.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
@@ -7146,6 +7695,101 @@
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
|
||||
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-tailwindcss": {
|
||||
"version": "0.6.11",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.11.tgz",
|
||||
"integrity": "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ianvs/prettier-plugin-sort-imports": "*",
|
||||
"@prettier/plugin-pug": "*",
|
||||
"@shopify/prettier-plugin-liquid": "*",
|
||||
"@trivago/prettier-plugin-sort-imports": "*",
|
||||
"@zackad/prettier-plugin-twig": "*",
|
||||
"prettier": "^3.0",
|
||||
"prettier-plugin-astro": "*",
|
||||
"prettier-plugin-css-order": "*",
|
||||
"prettier-plugin-import-sort": "*",
|
||||
"prettier-plugin-jsdoc": "*",
|
||||
"prettier-plugin-marko": "*",
|
||||
"prettier-plugin-multiline-arrays": "*",
|
||||
"prettier-plugin-organize-attributes": "*",
|
||||
"prettier-plugin-organize-imports": "*",
|
||||
"prettier-plugin-sort-imports": "*",
|
||||
"prettier-plugin-style-order": "*",
|
||||
"prettier-plugin-svelte": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@ianvs/prettier-plugin-sort-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"@prettier/plugin-pug": {
|
||||
"optional": true
|
||||
},
|
||||
"@shopify/prettier-plugin-liquid": {
|
||||
"optional": true
|
||||
},
|
||||
"@trivago/prettier-plugin-sort-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"@zackad/prettier-plugin-twig": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-astro": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-css-order": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-import-sort": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-jsdoc": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-marko": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-multiline-arrays": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-organize-attributes": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-organize-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-sort-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-style-order": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-svelte": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pretty-format": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
|
||||
@@ -7453,6 +8097,39 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/restore-cursor": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
|
||||
"integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"onetime": "^7.0.0",
|
||||
"signal-exit": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/restore-cursor/node_modules/onetime": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
|
||||
"integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mimic-function": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/reusify": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||
@@ -7463,6 +8140,13 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rfdc": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
||||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@@ -7662,6 +8346,36 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
|
||||
"integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.0.0",
|
||||
"is-fullwidth-code-point": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
|
||||
"integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
@@ -7720,6 +8434,16 @@
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string-argv": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
|
||||
"integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6.19"
|
||||
}
|
||||
},
|
||||
"node_modules/string-length": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
|
||||
|
||||
+14
-1
@@ -8,7 +8,10 @@
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch"
|
||||
"test:watch": "jest --watch",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-alert-dialog": "^1.1.5",
|
||||
@@ -40,15 +43,25 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^20",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.7.0",
|
||||
"lint-staged": "^15.4.3",
|
||||
"postcss": "^8",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"ts-jest": "^29.2.5",
|
||||
"typescript": "^5.7.3"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx,json,css,scss,md}": [
|
||||
"prettier --write"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -3,6 +3,6 @@ const config = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default config;
|
||||
export default config
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { ProviderConfig, FunctionCallResponse, ProviderToolConfig, ProviderRequest, Message } from '../types'
|
||||
import {
|
||||
FunctionCallResponse,
|
||||
Message,
|
||||
ProviderConfig,
|
||||
ProviderRequest,
|
||||
ProviderToolConfig,
|
||||
} from '../types'
|
||||
|
||||
export const anthropicProvider: ProviderConfig = {
|
||||
id: 'anthropic',
|
||||
@@ -7,12 +13,12 @@ export const anthropicProvider: ProviderConfig = {
|
||||
version: '1.0.0',
|
||||
models: ['claude-3-5-sonnet-20241022'],
|
||||
defaultModel: 'claude-3-5-sonnet-20241022',
|
||||
|
||||
|
||||
baseUrl: 'https://api.anthropic.com/v1/messages',
|
||||
headers: (apiKey: string) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': apiKey,
|
||||
'anthropic-version': '2023-06-01'
|
||||
'anthropic-version': '2023-06-01',
|
||||
}),
|
||||
|
||||
transformToolsToFunctions: (tools: ProviderToolConfig[]) => {
|
||||
@@ -20,18 +26,21 @@ export const anthropicProvider: ProviderConfig = {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return tools.map(tool => ({
|
||||
return tools.map((tool) => ({
|
||||
name: tool.id,
|
||||
description: tool.description,
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: tool.parameters.properties,
|
||||
required: tool.parameters.required
|
||||
}
|
||||
required: tool.parameters.required,
|
||||
},
|
||||
}))
|
||||
},
|
||||
|
||||
transformFunctionCallResponse: (response: any, tools?: ProviderToolConfig[]): FunctionCallResponse => {
|
||||
transformFunctionCallResponse: (
|
||||
response: any,
|
||||
tools?: ProviderToolConfig[]
|
||||
): FunctionCallResponse => {
|
||||
const rawResponse = response?.output || response
|
||||
if (!rawResponse?.content) {
|
||||
throw new Error('No content found in response')
|
||||
@@ -42,7 +51,7 @@ export const anthropicProvider: ProviderConfig = {
|
||||
throw new Error('No tool use found in response')
|
||||
}
|
||||
|
||||
const tool = tools?.find(t => t.id === toolUse.name)
|
||||
const tool = tools?.find((t) => t.id === toolUse.name)
|
||||
if (!tool) {
|
||||
throw new Error(`Tool not found: ${toolUse.name}`)
|
||||
}
|
||||
@@ -61,48 +70,53 @@ export const anthropicProvider: ProviderConfig = {
|
||||
name: toolUse.name,
|
||||
arguments: {
|
||||
...tool.params,
|
||||
...input
|
||||
}
|
||||
...input,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
transformRequest: (request: ProviderRequest, functions?: any) => {
|
||||
// Transform messages to Anthropic format
|
||||
const messages = request.messages?.map(msg => {
|
||||
if (msg.role === 'function') {
|
||||
return {
|
||||
role: 'user',
|
||||
content: [{
|
||||
type: 'tool_result',
|
||||
tool_use_id: msg.name,
|
||||
content: msg.content
|
||||
}]
|
||||
const messages =
|
||||
request.messages?.map((msg) => {
|
||||
if (msg.role === 'function') {
|
||||
return {
|
||||
role: 'user',
|
||||
content: [
|
||||
{
|
||||
type: 'tool_result',
|
||||
tool_use_id: msg.name,
|
||||
content: msg.content,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.function_call) {
|
||||
return {
|
||||
role: 'assistant',
|
||||
content: [{
|
||||
type: 'tool_use',
|
||||
id: msg.function_call.name,
|
||||
name: msg.function_call.name,
|
||||
input: JSON.parse(msg.function_call.arguments)
|
||||
}]
|
||||
if (msg.function_call) {
|
||||
return {
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{
|
||||
type: 'tool_use',
|
||||
id: msg.function_call.name,
|
||||
name: msg.function_call.name,
|
||||
input: JSON.parse(msg.function_call.arguments),
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
role: msg.role === 'assistant' ? 'assistant' : 'user',
|
||||
content: msg.content ? [{ type: 'text', text: msg.content }] : []
|
||||
}
|
||||
}) || []
|
||||
return {
|
||||
role: msg.role === 'assistant' ? 'assistant' : 'user',
|
||||
content: msg.content ? [{ type: 'text', text: msg.content }] : [],
|
||||
}
|
||||
}) || []
|
||||
|
||||
// Add context if provided
|
||||
if (request.context) {
|
||||
messages.unshift({
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: request.context }]
|
||||
content: [{ type: 'text', text: request.context }],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -113,7 +127,7 @@ export const anthropicProvider: ProviderConfig = {
|
||||
system: request.systemPrompt || '',
|
||||
max_tokens: parseInt(String(request.maxTokens)) || 1024,
|
||||
temperature: parseFloat(String(request.temperature ?? 0.7)),
|
||||
...(functions && { tools: functions })
|
||||
...(functions && { tools: functions }),
|
||||
}
|
||||
|
||||
return payload
|
||||
@@ -128,15 +142,15 @@ export const anthropicProvider: ProviderConfig = {
|
||||
|
||||
// Get the actual response content
|
||||
const rawResponse = response.output || response
|
||||
|
||||
|
||||
// Extract text content from the message
|
||||
let content = ''
|
||||
const messageContent = rawResponse?.content || rawResponse?.message?.content
|
||||
|
||||
if (Array.isArray(messageContent)) {
|
||||
content = messageContent
|
||||
.filter(item => item.type === 'text')
|
||||
.map(item => item.text)
|
||||
.filter((item) => item.type === 'text')
|
||||
.map((item) => item.text)
|
||||
.join('\n')
|
||||
} else if (typeof messageContent === 'string') {
|
||||
content = messageContent
|
||||
@@ -149,9 +163,9 @@ export const anthropicProvider: ProviderConfig = {
|
||||
tokens: {
|
||||
prompt: rawResponse.usage.input_tokens,
|
||||
completion: rawResponse.usage.output_tokens,
|
||||
total: rawResponse.usage.input_tokens + rawResponse.usage.output_tokens
|
||||
}
|
||||
})
|
||||
total: rawResponse.usage.input_tokens + rawResponse.usage.output_tokens,
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
return result
|
||||
@@ -170,5 +184,5 @@ export const anthropicProvider: ProviderConfig = {
|
||||
console.error('Error in hasFunctionCall:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
+31
-26
@@ -1,4 +1,4 @@
|
||||
import { ProviderConfig, FunctionCallResponse, ProviderToolConfig, ProviderRequest } from '../types'
|
||||
import { FunctionCallResponse, ProviderConfig, ProviderRequest, ProviderToolConfig } from '../types'
|
||||
|
||||
export const deepseekProvider: ProviderConfig = {
|
||||
id: 'deepseek',
|
||||
@@ -7,11 +7,11 @@ export const deepseekProvider: ProviderConfig = {
|
||||
version: '1.0.0',
|
||||
models: ['deepseek-chat'],
|
||||
defaultModel: 'deepseek-chat',
|
||||
|
||||
|
||||
baseUrl: 'https://api.deepseek.com/v1/chat/completions',
|
||||
headers: (apiKey: string) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${apiKey}`
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
}),
|
||||
|
||||
transformToolsToFunctions: (tools: ProviderToolConfig[]) => {
|
||||
@@ -19,23 +19,26 @@ export const deepseekProvider: ProviderConfig = {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return tools.map(tool => ({
|
||||
return tools.map((tool) => ({
|
||||
type: 'function',
|
||||
function: {
|
||||
name: tool.id,
|
||||
description: tool.description,
|
||||
parameters: tool.parameters
|
||||
}
|
||||
parameters: tool.parameters,
|
||||
},
|
||||
}))
|
||||
},
|
||||
|
||||
transformFunctionCallResponse: (response: any, tools?: ProviderToolConfig[]): FunctionCallResponse => {
|
||||
transformFunctionCallResponse: (
|
||||
response: any,
|
||||
tools?: ProviderToolConfig[]
|
||||
): FunctionCallResponse => {
|
||||
const toolCall = response.choices?.[0]?.message?.tool_calls?.[0]
|
||||
if (!toolCall || !toolCall.function) {
|
||||
throw new Error('No valid tool call found in response')
|
||||
}
|
||||
|
||||
const tool = tools?.find(t => t.id === toolCall.function.name)
|
||||
const tool = tools?.find((t) => t.id === toolCall.function.name)
|
||||
if (!tool) {
|
||||
throw new Error(`Tool not found: ${toolCall.function.name}`)
|
||||
}
|
||||
@@ -54,19 +57,19 @@ export const deepseekProvider: ProviderConfig = {
|
||||
name: toolCall.function.name,
|
||||
arguments: {
|
||||
...tool.params,
|
||||
...args
|
||||
}
|
||||
...args,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
transformRequest: (request: ProviderRequest, functions?: any) => {
|
||||
// Transform messages from internal format to Deepseek format
|
||||
const messages = (request.messages || []).map(msg => {
|
||||
const messages = (request.messages || []).map((msg) => {
|
||||
if (msg.role === 'function') {
|
||||
return {
|
||||
role: 'tool',
|
||||
content: msg.content,
|
||||
tool_call_id: msg.name
|
||||
tool_call_id: msg.name,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,14 +77,16 @@ export const deepseekProvider: ProviderConfig = {
|
||||
return {
|
||||
role: 'assistant',
|
||||
content: null,
|
||||
tool_calls: [{
|
||||
id: msg.function_call.name,
|
||||
type: 'function',
|
||||
function: {
|
||||
name: msg.function_call.name,
|
||||
arguments: msg.function_call.arguments
|
||||
}
|
||||
}]
|
||||
tool_calls: [
|
||||
{
|
||||
id: msg.function_call.name,
|
||||
type: 'function',
|
||||
function: {
|
||||
name: msg.function_call.name,
|
||||
arguments: msg.function_call.arguments,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,11 +98,11 @@ export const deepseekProvider: ProviderConfig = {
|
||||
messages: [
|
||||
{ role: 'system', content: request.systemPrompt },
|
||||
...(request.context ? [{ role: 'user', content: request.context }] : []),
|
||||
...messages
|
||||
...messages,
|
||||
],
|
||||
temperature: request.temperature || 0.7,
|
||||
max_tokens: request.maxTokens || 1024,
|
||||
...(functions && { tools: functions })
|
||||
...(functions && { tools: functions }),
|
||||
}
|
||||
|
||||
return payload
|
||||
@@ -116,13 +121,13 @@ export const deepseekProvider: ProviderConfig = {
|
||||
tokens: response.usage && {
|
||||
prompt: response.usage.prompt_tokens,
|
||||
completion: response.usage.completion_tokens,
|
||||
total: response.usage.total_tokens
|
||||
}
|
||||
total: response.usage.total_tokens,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
hasFunctionCall: (response: any) => {
|
||||
if (!response) return false
|
||||
return !!response.choices?.[0]?.message?.tool_calls?.[0]
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
+60
-47
@@ -1,18 +1,18 @@
|
||||
import { ProviderConfig, FunctionCallResponse, ProviderToolConfig, ProviderRequest } from '../types'
|
||||
import { ToolConfig } from '@/tools/types'
|
||||
import { FunctionCallResponse, ProviderConfig, ProviderRequest, ProviderToolConfig } from '../types'
|
||||
|
||||
export const googleProvider: ProviderConfig = {
|
||||
id: 'google',
|
||||
name: 'Google',
|
||||
description: 'Google\'s Gemini models',
|
||||
description: "Google's Gemini models",
|
||||
version: '1.0.0',
|
||||
models: ['gemini-pro'],
|
||||
defaultModel: 'gemini-pro',
|
||||
|
||||
|
||||
baseUrl: 'https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent',
|
||||
headers: (apiKey: string) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'x-goog-api-key': apiKey
|
||||
'x-goog-api-key': apiKey,
|
||||
}),
|
||||
|
||||
transformToolsToFunctions: (tools: ProviderToolConfig[]) => {
|
||||
@@ -34,8 +34,8 @@ export const googleProvider: ProviderConfig = {
|
||||
[key]: {
|
||||
type: 'OBJECT',
|
||||
description: value.description || '',
|
||||
properties: transformProperties(value.properties)
|
||||
}
|
||||
properties: transformProperties(value.properties),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,20 +44,19 @@ export const googleProvider: ProviderConfig = {
|
||||
...acc,
|
||||
[key]: {
|
||||
type: (value.type || 'string').toUpperCase(),
|
||||
description: value.description || ''
|
||||
}
|
||||
description: value.description || '',
|
||||
},
|
||||
}
|
||||
}, {})
|
||||
}
|
||||
|
||||
return {
|
||||
functionDeclarations: tools.map(tool => {
|
||||
functionDeclarations: tools.map((tool) => {
|
||||
// Get properties excluding complex types
|
||||
const properties = transformProperties(tool.parameters.properties)
|
||||
|
||||
|
||||
// Filter required fields to only include ones that exist in properties
|
||||
const required = (tool.parameters.required || [])
|
||||
.filter(field => field in properties)
|
||||
const required = (tool.parameters.required || []).filter((field) => field in properties)
|
||||
|
||||
return {
|
||||
name: tool.id,
|
||||
@@ -65,14 +64,17 @@ export const googleProvider: ProviderConfig = {
|
||||
parameters: {
|
||||
type: 'OBJECT',
|
||||
properties,
|
||||
required
|
||||
}
|
||||
required,
|
||||
},
|
||||
}
|
||||
})
|
||||
}),
|
||||
}
|
||||
},
|
||||
|
||||
transformFunctionCallResponse: (response: any, tools?: ProviderToolConfig[]): FunctionCallResponse => {
|
||||
transformFunctionCallResponse: (
|
||||
response: any,
|
||||
tools?: ProviderToolConfig[]
|
||||
): FunctionCallResponse => {
|
||||
// Extract function call from Gemini response
|
||||
const functionCall = response.candidates?.[0]?.content?.parts?.[0]?.functionCall
|
||||
if (!functionCall) {
|
||||
@@ -82,7 +84,7 @@ export const googleProvider: ProviderConfig = {
|
||||
// Log the function call for debugging
|
||||
console.log('Raw function call from Gemini:', JSON.stringify(functionCall, null, 2))
|
||||
|
||||
const tool = tools?.find(t => t.id === functionCall.name)
|
||||
const tool = tools?.find((t) => t.id === functionCall.name)
|
||||
if (!tool) {
|
||||
throw new Error(`Tool not found: ${functionCall.name}`)
|
||||
}
|
||||
@@ -105,57 +107,68 @@ export const googleProvider: ProviderConfig = {
|
||||
name: functionCall.name,
|
||||
arguments: {
|
||||
...functionArgs,
|
||||
apiKey: tool.params.apiKey // Always use the apiKey from tool params
|
||||
}
|
||||
apiKey: tool.params.apiKey, // Always use the apiKey from tool params
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
transformRequest: (request: ProviderRequest, functions?: any) => {
|
||||
// Combine system prompt and context into a single message if both exist
|
||||
const initialMessage = request.systemPrompt + (request.context ? `\n\n${request.context}` : '')
|
||||
|
||||
|
||||
const messages = [
|
||||
{ role: 'user', parts: [{ text: initialMessage }] },
|
||||
...(request.messages || []).map(msg => {
|
||||
...(request.messages || []).map((msg) => {
|
||||
if (msg.role === 'function') {
|
||||
return {
|
||||
role: 'user',
|
||||
parts: [{
|
||||
functionResponse: {
|
||||
name: msg.name,
|
||||
response: {
|
||||
parts: [
|
||||
{
|
||||
functionResponse: {
|
||||
name: msg.name,
|
||||
content: JSON.parse(msg.content || '{}')
|
||||
}
|
||||
}
|
||||
}]
|
||||
response: {
|
||||
name: msg.name,
|
||||
content: JSON.parse(msg.content || '{}'),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (msg.function_call) {
|
||||
return {
|
||||
role: 'model',
|
||||
parts: [{
|
||||
functionCall: {
|
||||
name: msg.function_call.name,
|
||||
args: JSON.parse(msg.function_call.arguments)
|
||||
}
|
||||
}]
|
||||
parts: [
|
||||
{
|
||||
functionCall: {
|
||||
name: msg.function_call.name,
|
||||
args: JSON.parse(msg.function_call.arguments),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
role: msg.role === 'assistant' ? 'model' : 'user',
|
||||
parts: [{ text: msg.content || '' }]
|
||||
parts: [{ text: msg.content || '' }],
|
||||
}
|
||||
})
|
||||
}),
|
||||
]
|
||||
|
||||
// Log the request for debugging
|
||||
console.log('Gemini request:', JSON.stringify({
|
||||
messages,
|
||||
tools: functions ? [{ functionDeclarations: functions.functionDeclarations }] : undefined
|
||||
}, null, 2))
|
||||
console.log(
|
||||
'Gemini request:',
|
||||
JSON.stringify(
|
||||
{
|
||||
messages,
|
||||
tools: functions ? [{ functionDeclarations: functions.functionDeclarations }] : undefined,
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
contents: messages,
|
||||
@@ -163,7 +176,7 @@ export const googleProvider: ProviderConfig = {
|
||||
generationConfig: {
|
||||
temperature: request.temperature || 0.7,
|
||||
maxOutputTokens: request.maxTokens || 1024,
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -173,12 +186,12 @@ export const googleProvider: ProviderConfig = {
|
||||
tokens: response.usageMetadata && {
|
||||
prompt: response.usageMetadata.promptTokenCount,
|
||||
completion: response.usageMetadata.candidatesTokenCount,
|
||||
total: response.usageMetadata.totalTokenCount
|
||||
}
|
||||
total: response.usageMetadata.totalTokenCount,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
hasFunctionCall: (response: any) => {
|
||||
return !!response.candidates?.[0]?.content?.parts?.[0]?.functionCall
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
+25
-20
@@ -1,5 +1,5 @@
|
||||
import { ProviderConfig, FunctionCallResponse, ProviderToolConfig, ProviderRequest } from '../types'
|
||||
import { ToolConfig } from '@/tools/types'
|
||||
import { FunctionCallResponse, ProviderConfig, ProviderRequest, ProviderToolConfig } from '../types'
|
||||
|
||||
export const openaiProvider: ProviderConfig = {
|
||||
id: 'openai',
|
||||
@@ -8,11 +8,11 @@ export const openaiProvider: ProviderConfig = {
|
||||
version: '1.0.0',
|
||||
models: ['gpt-4o', 'o1', 'o3-mini'],
|
||||
defaultModel: 'gpt-4o',
|
||||
|
||||
|
||||
baseUrl: 'https://api.openai.com/v1/chat/completions',
|
||||
headers: (apiKey: string) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${apiKey}`
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
}),
|
||||
|
||||
transformToolsToFunctions: (tools: ProviderToolConfig[]) => {
|
||||
@@ -20,34 +20,37 @@ export const openaiProvider: ProviderConfig = {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return tools.map(tool => ({
|
||||
return tools.map((tool) => ({
|
||||
name: tool.id,
|
||||
description: tool.description,
|
||||
parameters: tool.parameters
|
||||
parameters: tool.parameters,
|
||||
}))
|
||||
},
|
||||
|
||||
transformFunctionCallResponse: (response: any, tools?: ProviderToolConfig[]): FunctionCallResponse => {
|
||||
transformFunctionCallResponse: (
|
||||
response: any,
|
||||
tools?: ProviderToolConfig[]
|
||||
): FunctionCallResponse => {
|
||||
const functionCall = response.choices?.[0]?.message?.function_call
|
||||
if (!functionCall) {
|
||||
throw new Error('No function call found in response')
|
||||
}
|
||||
|
||||
const tool = tools?.find(t => t.id === functionCall.name)
|
||||
const tool = tools?.find((t) => t.id === functionCall.name)
|
||||
const toolParams = tool?.params || {}
|
||||
|
||||
return {
|
||||
name: functionCall.name,
|
||||
arguments: {
|
||||
...toolParams,
|
||||
...JSON.parse(functionCall.arguments)
|
||||
}
|
||||
...JSON.parse(functionCall.arguments),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
transformRequest: (request: ProviderRequest, functions?: any) => {
|
||||
console.log('OpenAI transformRequest - Input:', JSON.stringify(request, null, 2))
|
||||
|
||||
|
||||
const isO1Model = request.model?.startsWith('o1')
|
||||
const isO1Mini = request.model === 'o1-mini'
|
||||
|
||||
@@ -64,17 +67,19 @@ export const openaiProvider: ProviderConfig = {
|
||||
|
||||
// Add system prompt if present
|
||||
if (request.systemPrompt) {
|
||||
allMessages.push(transformMessageRole({
|
||||
role: 'system',
|
||||
content: request.systemPrompt
|
||||
}))
|
||||
allMessages.push(
|
||||
transformMessageRole({
|
||||
role: 'system',
|
||||
content: request.systemPrompt,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// Add context if present
|
||||
if (request.context) {
|
||||
allMessages.push({
|
||||
role: 'user',
|
||||
content: request.context
|
||||
content: request.context,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -86,7 +91,7 @@ export const openaiProvider: ProviderConfig = {
|
||||
// Build the request payload
|
||||
const payload: any = {
|
||||
model: request.model || 'gpt-4o',
|
||||
messages: allMessages
|
||||
messages: allMessages,
|
||||
}
|
||||
|
||||
// Only add parameters supported by the model type
|
||||
@@ -114,14 +119,14 @@ export const openaiProvider: ProviderConfig = {
|
||||
transformResponse: (response: any) => {
|
||||
const output = {
|
||||
content: response.choices?.[0]?.message?.content || '',
|
||||
tokens: undefined as any
|
||||
tokens: undefined as any,
|
||||
}
|
||||
|
||||
if (response.usage) {
|
||||
output.tokens = {
|
||||
prompt: response.usage.prompt_tokens,
|
||||
completion: response.usage.completion_tokens,
|
||||
total: response.usage.total_tokens
|
||||
total: response.usage.total_tokens,
|
||||
}
|
||||
|
||||
// Add reasoning_tokens for o1 models if available
|
||||
@@ -135,5 +140,5 @@ export const openaiProvider: ProviderConfig = {
|
||||
|
||||
hasFunctionCall: (response: any) => {
|
||||
return !!response.choices?.[0]?.message?.function_call
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
+31
-28
@@ -1,9 +1,9 @@
|
||||
import { ProviderConfig, ProviderRequest, ProviderResponse, TokenInfo } from './types'
|
||||
import { openaiProvider } from './openai'
|
||||
import { executeTool, getTool } from '@/tools'
|
||||
import { anthropicProvider } from './anthropic'
|
||||
import { googleProvider } from './google'
|
||||
import { getTool, executeTool } from '@/tools'
|
||||
import { deepseekProvider } from './deepseek'
|
||||
import { googleProvider } from './google'
|
||||
import { openaiProvider } from './openai'
|
||||
import { ProviderConfig, ProviderRequest, ProviderResponse, TokenInfo } from './types'
|
||||
import { xAIProvider } from './xai'
|
||||
|
||||
// Register providers
|
||||
@@ -12,7 +12,7 @@ const providers: Record<string, ProviderConfig> = {
|
||||
anthropic: anthropicProvider,
|
||||
google: googleProvider,
|
||||
deepseek: deepseekProvider,
|
||||
xai: xAIProvider
|
||||
xai: xAIProvider,
|
||||
}
|
||||
|
||||
export async function executeProviderRequest(
|
||||
@@ -25,9 +25,10 @@ export async function executeProviderRequest(
|
||||
}
|
||||
|
||||
// Transform tools to provider-specific function format
|
||||
const functions = request.tools && request.tools.length > 0
|
||||
? provider.transformToolsToFunctions(request.tools)
|
||||
: undefined
|
||||
const functions =
|
||||
request.tools && request.tools.length > 0
|
||||
? provider.transformToolsToFunctions(request.tools)
|
||||
: undefined
|
||||
|
||||
// Transform the request using provider-specific logic
|
||||
const payload = provider.transformRequest(request, functions)
|
||||
@@ -45,17 +46,17 @@ export async function executeProviderRequest(
|
||||
try {
|
||||
while (iterationCount < MAX_ITERATIONS) {
|
||||
console.log(`Processing iteration ${iterationCount + 1}`)
|
||||
|
||||
|
||||
// Transform the response using provider-specific logic
|
||||
const transformedResponse = provider.transformResponse(currentResponse)
|
||||
content = transformedResponse.content
|
||||
|
||||
|
||||
// Update tokens
|
||||
if (transformedResponse.tokens) {
|
||||
const newTokens: TokenInfo = {
|
||||
prompt: (tokens?.prompt ?? 0) + (transformedResponse.tokens?.prompt ?? 0),
|
||||
completion: (tokens?.completion ?? 0) + (transformedResponse.tokens?.completion ?? 0),
|
||||
total: (tokens?.total ?? 0) + (transformedResponse.tokens?.total ?? 0)
|
||||
total: (tokens?.total ?? 0) + (transformedResponse.tokens?.total ?? 0),
|
||||
}
|
||||
tokens = newTokens
|
||||
}
|
||||
@@ -63,7 +64,7 @@ export async function executeProviderRequest(
|
||||
// Check for function calls using provider-specific logic
|
||||
const hasFunctionCall = provider.hasFunctionCall(currentResponse)
|
||||
console.log('Has function call:', hasFunctionCall)
|
||||
|
||||
|
||||
if (!hasFunctionCall) {
|
||||
console.log('No function call detected, breaking loop')
|
||||
break
|
||||
@@ -94,7 +95,7 @@ export async function executeProviderRequest(
|
||||
|
||||
const result = await executeTool(functionCall.name, functionCall.arguments)
|
||||
console.log('Tool execution result:', result.success)
|
||||
|
||||
|
||||
if (!result.success) {
|
||||
console.log('Tool execution failed')
|
||||
break
|
||||
@@ -109,20 +110,23 @@ export async function executeProviderRequest(
|
||||
content: null,
|
||||
function_call: {
|
||||
name: functionCall.name,
|
||||
arguments: JSON.stringify(functionCall.arguments)
|
||||
}
|
||||
arguments: JSON.stringify(functionCall.arguments),
|
||||
},
|
||||
})
|
||||
currentMessages.push({
|
||||
role: 'function',
|
||||
name: functionCall.name,
|
||||
content: JSON.stringify(result.output)
|
||||
content: JSON.stringify(result.output),
|
||||
})
|
||||
|
||||
// Prepare the next request
|
||||
const nextPayload = provider.transformRequest({
|
||||
...request,
|
||||
messages: currentMessages
|
||||
}, functions)
|
||||
const nextPayload = provider.transformRequest(
|
||||
{
|
||||
...request,
|
||||
messages: currentMessages,
|
||||
},
|
||||
functions
|
||||
)
|
||||
|
||||
// Make the next request
|
||||
currentResponse = await makeProxyRequest(providerId, nextPayload, request.apiKey)
|
||||
@@ -142,25 +146,25 @@ export async function executeProviderRequest(
|
||||
model: currentResponse.model,
|
||||
tokens,
|
||||
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
||||
toolResults: toolResults.length > 0 ? toolResults : undefined
|
||||
toolResults: toolResults.length > 0 ? toolResults : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
async function makeProxyRequest(providerId: string, payload: any, apiKey: string) {
|
||||
console.log('Making proxy request for provider:', providerId)
|
||||
|
||||
|
||||
const response = await fetch('/api/proxy', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
toolId: `${providerId}/chat`,
|
||||
params: {
|
||||
...payload,
|
||||
apiKey
|
||||
}
|
||||
})
|
||||
apiKey,
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
@@ -171,5 +175,4 @@ async function makeProxyRequest(providerId: string, payload: any, apiKey: string
|
||||
const { output } = await response.json()
|
||||
console.log('Proxy request completed')
|
||||
return output
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
-6
@@ -18,19 +18,22 @@ export interface ProviderConfig {
|
||||
version: string
|
||||
models: string[]
|
||||
defaultModel: string
|
||||
|
||||
|
||||
// Provider-specific configuration
|
||||
baseUrl: string
|
||||
headers: (apiKey: string) => Record<string, string>
|
||||
|
||||
|
||||
// Tool calling support
|
||||
transformToolsToFunctions: (tools: ProviderToolConfig[]) => any
|
||||
transformFunctionCallResponse: (response: any, tools?: ProviderToolConfig[]) => FunctionCallResponse
|
||||
|
||||
transformFunctionCallResponse: (
|
||||
response: any,
|
||||
tools?: ProviderToolConfig[]
|
||||
) => FunctionCallResponse
|
||||
|
||||
// Provider-specific request/response transformations
|
||||
transformRequest: (request: ProviderRequest, functions?: any) => any
|
||||
transformResponse: (response: any) => TransformedResponse
|
||||
|
||||
|
||||
// Function to check if response contains a function call
|
||||
hasFunctionCall: (response: any) => boolean
|
||||
|
||||
@@ -89,4 +92,4 @@ export interface ProviderRequest {
|
||||
}
|
||||
|
||||
// Map of provider IDs to their configurations
|
||||
export const providers: Record<string, ProviderConfig> = {}
|
||||
export const providers: Record<string, ProviderConfig> = {}
|
||||
|
||||
+33
-28
@@ -1,4 +1,4 @@
|
||||
import { ProviderConfig, FunctionCallResponse, ProviderToolConfig, ProviderRequest } from '../types'
|
||||
import { FunctionCallResponse, ProviderConfig, ProviderRequest, ProviderToolConfig } from '../types'
|
||||
|
||||
export const xAIProvider: ProviderConfig = {
|
||||
id: 'xai',
|
||||
@@ -7,11 +7,11 @@ export const xAIProvider: ProviderConfig = {
|
||||
version: '1.0.0',
|
||||
models: ['grok-2-latest'],
|
||||
defaultModel: 'grok-2-latest',
|
||||
|
||||
|
||||
baseUrl: 'https://api.x.ai/v1/chat/completions',
|
||||
headers: (apiKey: string) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${apiKey}`
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
}),
|
||||
|
||||
transformToolsToFunctions: (tools: ProviderToolConfig[]) => {
|
||||
@@ -19,24 +19,27 @@ export const xAIProvider: ProviderConfig = {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return tools.map(tool => ({
|
||||
return tools.map((tool) => ({
|
||||
type: 'function',
|
||||
function: {
|
||||
name: tool.id,
|
||||
description: tool.description,
|
||||
parameters: tool.parameters
|
||||
}
|
||||
parameters: tool.parameters,
|
||||
},
|
||||
}))
|
||||
},
|
||||
|
||||
transformFunctionCallResponse: (response: any, tools?: ProviderToolConfig[]): FunctionCallResponse => {
|
||||
transformFunctionCallResponse: (
|
||||
response: any,
|
||||
tools?: ProviderToolConfig[]
|
||||
): FunctionCallResponse => {
|
||||
// xAI returns tool_calls array like OpenAI
|
||||
const toolCall = response.choices?.[0]?.message?.tool_calls?.[0]
|
||||
if (!toolCall || !toolCall.function) {
|
||||
throw new Error('No valid tool call found in response')
|
||||
}
|
||||
|
||||
const tool = tools?.find(t => t.id === toolCall.function.name)
|
||||
const tool = tools?.find((t) => t.id === toolCall.function.name)
|
||||
if (!tool) {
|
||||
throw new Error(`Tool not found: ${toolCall.function.name}`)
|
||||
}
|
||||
@@ -55,19 +58,19 @@ export const xAIProvider: ProviderConfig = {
|
||||
name: toolCall.function.name,
|
||||
arguments: {
|
||||
...tool.params,
|
||||
...args
|
||||
}
|
||||
...args,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
transformRequest: (request: ProviderRequest, functions?: any) => {
|
||||
// Convert function messages to tool messages
|
||||
const messages = (request.messages || []).map(msg => {
|
||||
const messages = (request.messages || []).map((msg) => {
|
||||
if (msg.role === 'function') {
|
||||
return {
|
||||
role: 'tool',
|
||||
content: msg.content,
|
||||
tool_call_id: msg.name // xAI expects tool_call_id for tool results
|
||||
tool_call_id: msg.name, // xAI expects tool_call_id for tool results
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,14 +78,16 @@ export const xAIProvider: ProviderConfig = {
|
||||
return {
|
||||
role: 'assistant',
|
||||
content: null,
|
||||
tool_calls: [{
|
||||
id: msg.function_call.name,
|
||||
type: 'function',
|
||||
function: {
|
||||
name: msg.function_call.name,
|
||||
arguments: msg.function_call.arguments
|
||||
}
|
||||
}]
|
||||
tool_calls: [
|
||||
{
|
||||
id: msg.function_call.name,
|
||||
type: 'function',
|
||||
function: {
|
||||
name: msg.function_call.name,
|
||||
arguments: msg.function_call.arguments,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,14 +99,14 @@ export const xAIProvider: ProviderConfig = {
|
||||
messages: [
|
||||
{ role: 'system', content: request.systemPrompt },
|
||||
...(request.context ? [{ role: 'user', content: request.context }] : []),
|
||||
...messages
|
||||
...messages,
|
||||
],
|
||||
temperature: request.temperature || 0.7,
|
||||
max_tokens: request.maxTokens || 1024,
|
||||
...(functions && {
|
||||
...(functions && {
|
||||
tools: functions,
|
||||
tool_choice: 'auto' // xAI specific parameter
|
||||
})
|
||||
tool_choice: 'auto', // xAI specific parameter
|
||||
}),
|
||||
}
|
||||
|
||||
return payload
|
||||
@@ -118,13 +123,13 @@ export const xAIProvider: ProviderConfig = {
|
||||
tokens: response.usage && {
|
||||
prompt: response.usage.prompt_tokens,
|
||||
completion: response.usage.completion_tokens,
|
||||
total: response.usage.total_tokens
|
||||
}
|
||||
total: response.usage.total_tokens,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
hasFunctionCall: (response: any) => {
|
||||
if (!response) return false
|
||||
return !!response.choices?.[0]?.message?.tool_calls?.[0]
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,101 +1,101 @@
|
||||
import { Edge } from 'reactflow'
|
||||
import { Serializer } from '../index'
|
||||
import { SerializedWorkflow } from '../types'
|
||||
import { BlockState } from '@/stores/workflow/types'
|
||||
import { BlockOutput } from '@/blocks/types'
|
||||
import { Edge } from 'reactflow'
|
||||
import { BlockState } from '@/stores/workflow/types'
|
||||
import { getBlock } from '@/blocks'
|
||||
import { BlockOutput } from '@/blocks/types'
|
||||
import { Serializer } from '../index'
|
||||
import { SerializedWorkflow } from '../types'
|
||||
|
||||
// Mock icons
|
||||
jest.mock('@/components/icons', () => ({
|
||||
AgentIcon: () => 'AgentIcon',
|
||||
ApiIcon: () => 'ApiIcon',
|
||||
CodeIcon: () => 'CodeIcon',
|
||||
}))
|
||||
}))
|
||||
|
||||
// Mock blocks
|
||||
jest.mock('@/blocks', () => ({
|
||||
getBlock: jest.fn(),
|
||||
}))
|
||||
}))
|
||||
|
||||
describe('Serializer', () => {
|
||||
let serializer: Serializer
|
||||
let serializer: Serializer
|
||||
|
||||
beforeEach(() => {
|
||||
serializer = new Serializer()
|
||||
serializer = new Serializer()
|
||||
;(getBlock as jest.Mock).mockReset()
|
||||
;(getBlock as jest.Mock).mockImplementation((type: string) => {
|
||||
if (type === 'agent') {
|
||||
return {
|
||||
tools: {
|
||||
access: ['openai.chat'],
|
||||
config: null
|
||||
config: null,
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
systemPrompt: { type: 'string', required: false },
|
||||
context: { type: 'string', required: false },
|
||||
apiKey: { type: 'string', required: false }
|
||||
apiKey: { type: 'string', required: false },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
response: {
|
||||
text: 'string',
|
||||
model: 'string',
|
||||
tokens: 'number'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
tokens: 'number',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
},
|
||||
subBlocks: [
|
||||
{ id: 'model', type: 'dropdown' },
|
||||
{ id: 'systemPrompt', type: 'long-input' },
|
||||
{ id: 'temperature', type: 'slider' },
|
||||
{ id: 'responseFormat', type: 'code' }
|
||||
]
|
||||
{ id: 'responseFormat', type: 'code' },
|
||||
],
|
||||
},
|
||||
toolbar: {
|
||||
title: 'Agent Block',
|
||||
description: 'Use any LLM',
|
||||
category: 'blocks',
|
||||
bgColor: '#7F2FFF'
|
||||
}
|
||||
bgColor: '#7F2FFF',
|
||||
},
|
||||
}
|
||||
} else if (type === 'api') {
|
||||
return {
|
||||
tools: {
|
||||
access: ['http.request'],
|
||||
config: null
|
||||
config: null,
|
||||
},
|
||||
workflow: {
|
||||
inputs: {
|
||||
url: { type: 'string', required: true },
|
||||
method: { type: 'string', required: true }
|
||||
method: { type: 'string', required: true },
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
response: {
|
||||
body: 'any',
|
||||
status: 'number',
|
||||
headers: 'json'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
headers: 'json',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
},
|
||||
subBlocks: [
|
||||
{ id: 'url', type: 'short-input' },
|
||||
{ id: 'method', type: 'dropdown' }
|
||||
]
|
||||
{ id: 'method', type: 'dropdown' },
|
||||
],
|
||||
},
|
||||
toolbar: {
|
||||
title: 'API Block',
|
||||
description: 'Make HTTP requests',
|
||||
category: '',
|
||||
bgColor: '#00FF00'
|
||||
}
|
||||
bgColor: '#00FF00',
|
||||
},
|
||||
}
|
||||
}
|
||||
return {
|
||||
tools: {
|
||||
access: ['test-tool'],
|
||||
config: null
|
||||
config: null,
|
||||
},
|
||||
workflow: {
|
||||
inputs: {},
|
||||
@@ -104,21 +104,21 @@ describe('Serializer', () => {
|
||||
response: {
|
||||
text: 'string',
|
||||
model: 'string',
|
||||
tokens: 'number'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
tokens: 'number',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
},
|
||||
subBlocks: []
|
||||
subBlocks: [],
|
||||
},
|
||||
toolbar: {
|
||||
title: 'Test Block',
|
||||
description: 'A test block',
|
||||
category: 'test',
|
||||
bgColor: '#000000'
|
||||
}
|
||||
bgColor: '#000000',
|
||||
},
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('serializeWorkflow', () => {
|
||||
it('should serialize a workflow with one tool', async () => {
|
||||
@@ -130,26 +130,26 @@ describe('Serializer', () => {
|
||||
position: { x: 0, y: 0 },
|
||||
enabled: true,
|
||||
subBlocks: {
|
||||
'model': {
|
||||
model: {
|
||||
id: 'model',
|
||||
type: 'dropdown',
|
||||
value: 'gpt-4o'
|
||||
value: 'gpt-4o',
|
||||
},
|
||||
'systemPrompt': {
|
||||
systemPrompt: {
|
||||
id: 'systemPrompt',
|
||||
type: 'long-input',
|
||||
value: 'test'
|
||||
}
|
||||
value: 'test',
|
||||
},
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
response: {
|
||||
text: 'string',
|
||||
status: 'number'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
}
|
||||
}
|
||||
status: 'number',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const workflow = serializer.serializeWorkflow(blocks, [])
|
||||
@@ -158,22 +158,22 @@ describe('Serializer', () => {
|
||||
expect(block.config.tool).toBe('openai.chat')
|
||||
expect(block.config.params).toEqual({
|
||||
model: 'gpt-4o',
|
||||
systemPrompt: 'test'
|
||||
systemPrompt: 'test',
|
||||
})
|
||||
expect(block.inputs).toEqual({
|
||||
systemPrompt: 'string',
|
||||
context: 'string',
|
||||
apiKey: 'string'
|
||||
apiKey: 'string',
|
||||
})
|
||||
expect(block.outputs).toEqual({
|
||||
response: {
|
||||
response: {
|
||||
text: 'string',
|
||||
status: 'number'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
status: 'number',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle blocks with minimal configuration', () => {
|
||||
const blocks: Record<string, BlockState> = {
|
||||
@@ -184,20 +184,20 @@ describe('Serializer', () => {
|
||||
position: { x: 0, y: 0 },
|
||||
enabled: true,
|
||||
subBlocks: {
|
||||
'model': {
|
||||
model: {
|
||||
id: 'model',
|
||||
type: 'dropdown',
|
||||
value: 'gpt-4o'
|
||||
}
|
||||
value: 'gpt-4o',
|
||||
},
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
response: {
|
||||
text: 'string'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
}
|
||||
}
|
||||
text: 'string',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const workflow = serializer.serializeWorkflow(blocks, [])
|
||||
@@ -208,16 +208,16 @@ describe('Serializer', () => {
|
||||
expect(block.inputs).toEqual({
|
||||
systemPrompt: 'string',
|
||||
context: 'string',
|
||||
apiKey: 'string'
|
||||
apiKey: 'string',
|
||||
})
|
||||
expect(block.outputs).toEqual({
|
||||
response: {
|
||||
response: {
|
||||
text: 'string'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
text: 'string',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle complex workflow with multiple blocks', () => {
|
||||
const blocks: Record<string, BlockState> = {
|
||||
@@ -228,26 +228,26 @@ describe('Serializer', () => {
|
||||
position: { x: 100, y: 100 },
|
||||
enabled: true,
|
||||
subBlocks: {
|
||||
'url': {
|
||||
url: {
|
||||
id: 'url',
|
||||
type: 'short-input',
|
||||
value: 'https://api.data.com'
|
||||
value: 'https://api.data.com',
|
||||
},
|
||||
'method': {
|
||||
method: {
|
||||
id: 'method',
|
||||
type: 'dropdown',
|
||||
value: 'GET'
|
||||
}
|
||||
value: 'GET',
|
||||
},
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
response: {
|
||||
body: 'json',
|
||||
status: 'number',
|
||||
headers: 'json'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
}
|
||||
headers: 'json',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
},
|
||||
},
|
||||
'process-1': {
|
||||
id: 'process-1',
|
||||
@@ -256,27 +256,27 @@ describe('Serializer', () => {
|
||||
position: { x: 300, y: 100 },
|
||||
enabled: true,
|
||||
subBlocks: {
|
||||
'model': {
|
||||
model: {
|
||||
id: 'model',
|
||||
type: 'dropdown',
|
||||
value: 'gpt-4o'
|
||||
value: 'gpt-4o',
|
||||
},
|
||||
'systemPrompt': {
|
||||
systemPrompt: {
|
||||
id: 'systemPrompt',
|
||||
type: 'long-input',
|
||||
value: 'Process this data'
|
||||
}
|
||||
value: 'Process this data',
|
||||
},
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
response: {
|
||||
text: 'string',
|
||||
model: 'string',
|
||||
tokens: 'number'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
}
|
||||
}
|
||||
tokens: 'number',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const connections: Edge[] = [
|
||||
@@ -285,8 +285,8 @@ describe('Serializer', () => {
|
||||
source: 'input-1',
|
||||
target: 'process-1',
|
||||
sourceHandle: 'response',
|
||||
targetHandle: 'context'
|
||||
}
|
||||
targetHandle: 'context',
|
||||
},
|
||||
]
|
||||
|
||||
const workflow = serializer.serializeWorkflow(blocks, connections)
|
||||
@@ -303,29 +303,29 @@ describe('Serializer', () => {
|
||||
expect(conn.targetHandle).toBe('context')
|
||||
|
||||
// Verify block outputs
|
||||
const inputBlock = workflow.blocks.find(b => b.id === 'input-1')
|
||||
const processBlock = workflow.blocks.find(b => b.id === 'process-1')
|
||||
const inputBlock = workflow.blocks.find((b) => b.id === 'input-1')
|
||||
const processBlock = workflow.blocks.find((b) => b.id === 'process-1')
|
||||
|
||||
expect(inputBlock?.outputs).toEqual({
|
||||
response: {
|
||||
response: {
|
||||
body: 'json',
|
||||
status: 'number',
|
||||
headers: 'json'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
headers: 'json',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
})
|
||||
expect(processBlock?.outputs).toEqual({
|
||||
response: {
|
||||
response: {
|
||||
text: 'string',
|
||||
model: 'string',
|
||||
tokens: 'number'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
tokens: 'number',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('deserializeWorkflow', () => {
|
||||
it('should deserialize a workflow back to blocks and connections', () => {
|
||||
@@ -339,34 +339,34 @@ describe('Serializer', () => {
|
||||
tool: 'openai.chat',
|
||||
params: {
|
||||
model: 'gpt-4o',
|
||||
systemPrompt: 'You are helpful'
|
||||
}
|
||||
systemPrompt: 'You are helpful',
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
systemPrompt: 'string',
|
||||
context: 'string',
|
||||
apiKey: 'string'
|
||||
apiKey: 'string',
|
||||
},
|
||||
outputs: {
|
||||
response: {
|
||||
response: {
|
||||
text: 'string',
|
||||
model: 'string',
|
||||
tokens: 'number'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
tokens: 'number',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
},
|
||||
metadata: {
|
||||
title: 'Agent Block',
|
||||
description: 'Use any LLM',
|
||||
category: 'blocks',
|
||||
color: '#7F2FFF',
|
||||
type: 'agent'
|
||||
type: 'agent',
|
||||
},
|
||||
enabled: true
|
||||
}
|
||||
enabled: true,
|
||||
},
|
||||
],
|
||||
connections: []
|
||||
connections: [],
|
||||
}
|
||||
|
||||
const { blocks } = serializer.deserializeWorkflow(workflow)
|
||||
@@ -381,10 +381,10 @@ describe('Serializer', () => {
|
||||
response: {
|
||||
text: 'string',
|
||||
model: 'string',
|
||||
tokens: 'number'
|
||||
}
|
||||
} satisfies BlockOutput
|
||||
tokens: 'number',
|
||||
},
|
||||
} satisfies BlockOutput,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user