mirror of https://github.com/kortix-ai/suna.git
Add agent configurator and UI component updates
This commit is contained in:
parent
b647889a7d
commit
70e89441d5
|
@ -1,4 +1,3 @@
|
|||
|
||||
from agentpress.tool import ToolResult, openapi_schema, xml_schema
|
||||
from sandbox.tool_base import SandboxToolsBase
|
||||
from utils.files_utils import should_exclude_file, clean_path
|
||||
|
|
|
@ -15,21 +15,21 @@
|
|||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.11",
|
||||
"@radix-ui/react-avatar": "^1.1.4",
|
||||
"@radix-ui/react-checkbox": "^1.1.5",
|
||||
"@radix-ui/react-checkbox": "^1.3.2",
|
||||
"@radix-ui/react-collapsible": "^1.1.4",
|
||||
"@radix-ui/react-dialog": "^1.1.10",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.11",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-label": "^2.1.4",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.5",
|
||||
"@radix-ui/react-popover": "^1.1.7",
|
||||
"@radix-ui/react-progress": "^1.1.6",
|
||||
"@radix-ui/react-radio-group": "^1.3.3",
|
||||
"@radix-ui/react-scroll-area": "^1.2.4",
|
||||
"@radix-ui/react-select": "^2.1.7",
|
||||
"@radix-ui/react-separator": "^1.1.3",
|
||||
"@radix-ui/react-separator": "^1.1.7",
|
||||
"@radix-ui/react-slider": "^1.3.2",
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-switch": "^1.2.4",
|
||||
"@radix-ui/react-tabs": "^1.1.8",
|
||||
"@radix-ui/react-tooltip": "^1.2.3",
|
||||
|
@ -2382,6 +2382,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-arrow": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.3.tgz",
|
||||
|
@ -2432,16 +2450,17 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-checkbox": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.5.tgz",
|
||||
"integrity": "sha512-B0gYIVxl77KYDR25AY9EGe/G//ef85RVBIxQvK+m5pxAC7XihAc/8leMHhDvjvhDu02SBSb6BuytlWr/G7F3+g==",
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz",
|
||||
"integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.2",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-presence": "1.1.3",
|
||||
"@radix-ui/react-primitive": "2.0.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.1",
|
||||
"@radix-ui/react-presence": "1.1.4",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"@radix-ui/react-use-previous": "1.1.1",
|
||||
"@radix-ui/react-use-size": "1.1.1"
|
||||
},
|
||||
|
@ -2460,6 +2479,72 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-presence": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz",
|
||||
"integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
"integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-effect-event": "0.0.2",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-collapsible": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.4.tgz",
|
||||
|
@ -2516,6 +2601,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||
|
@ -2699,6 +2802,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
|
@ -2809,6 +2930,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
|
@ -2895,11 +3034,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-label": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.4.tgz",
|
||||
"integrity": "sha512-wy3dqizZnZVV4ja0FNnUhIWNwWdoldXrneEyUcVtLYDAt8ovGS4ridtMAOGgXBBIfggL4BOveVWsjXDORdGEQg==",
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz",
|
||||
"integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.0"
|
||||
"@radix-ui/react-primitive": "2.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
@ -2917,11 +3057,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz",
|
||||
"integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==",
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.0"
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
@ -3150,6 +3291,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-navigation-menu": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.6.tgz",
|
||||
|
@ -3223,6 +3382,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popper": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.3.tgz",
|
||||
|
@ -3326,6 +3503,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-progress": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.6.tgz",
|
||||
|
@ -3441,6 +3636,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-radio-group/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
|
@ -3536,6 +3749,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
|
@ -3627,12 +3858,54 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-separator": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.3.tgz",
|
||||
"integrity": "sha512-2omrWKJvxR0U/tkIXezcc1nFMwtLU0+b/rDK40gnzJqTLWQ/TD/D5IYVefp9sC3QWfeQbpSbEA6op9MQKyaALQ==",
|
||||
"node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.0.3"
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-separator": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz",
|
||||
"integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
|
||||
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
|
@ -3728,6 +4001,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
|
@ -3747,9 +4038,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
|
@ -3903,6 +4195,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
|
@ -4078,6 +4388,24 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz",
|
||||
"integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
|
||||
|
|
|
@ -18,21 +18,21 @@
|
|||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.11",
|
||||
"@radix-ui/react-avatar": "^1.1.4",
|
||||
"@radix-ui/react-checkbox": "^1.1.5",
|
||||
"@radix-ui/react-checkbox": "^1.3.2",
|
||||
"@radix-ui/react-collapsible": "^1.1.4",
|
||||
"@radix-ui/react-dialog": "^1.1.10",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.11",
|
||||
"@radix-ui/react-icons": "^1.3.2",
|
||||
"@radix-ui/react-label": "^2.1.4",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.5",
|
||||
"@radix-ui/react-popover": "^1.1.7",
|
||||
"@radix-ui/react-progress": "^1.1.6",
|
||||
"@radix-ui/react-radio-group": "^1.3.3",
|
||||
"@radix-ui/react-scroll-area": "^1.2.4",
|
||||
"@radix-ui/react-select": "^2.1.7",
|
||||
"@radix-ui/react-separator": "^1.1.3",
|
||||
"@radix-ui/react-separator": "^1.1.7",
|
||||
"@radix-ui/react-slider": "^1.3.2",
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-switch": "^1.2.4",
|
||||
"@radix-ui/react-tabs": "^1.1.8",
|
||||
"@radix-ui/react-tooltip": "^1.2.3",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import React, { useState, Suspense, useEffect, useRef } from 'react';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Menu } from 'lucide-react';
|
||||
import { Menu, ChevronDown, Edit } from 'lucide-react';
|
||||
import {
|
||||
ChatInput,
|
||||
ChatInputHandles,
|
||||
|
@ -31,14 +31,48 @@ import { useAccounts } from '@/hooks/use-accounts';
|
|||
import { isLocalMode, config } from '@/lib/config';
|
||||
import { toast } from 'sonner';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { AgentConfigurator } from '@/components/agent-configurator';
|
||||
|
||||
// Constant for localStorage key to ensure consistency
|
||||
const PENDING_PROMPT_KEY = 'pendingAgentPrompt';
|
||||
|
||||
// Agent definitions
|
||||
const AGENTS = [
|
||||
{
|
||||
id: 'suna',
|
||||
name: 'Suna',
|
||||
description: 'General-purpose AI assistant for coding, writing, and problem-solving',
|
||||
},
|
||||
{
|
||||
id: 'code-expert',
|
||||
name: 'Code Expert',
|
||||
description: 'Specialized in software development, debugging, and code reviews',
|
||||
},
|
||||
{
|
||||
id: 'writer',
|
||||
name: 'Writer',
|
||||
description: 'Focused on content creation, copywriting, and documentation',
|
||||
},
|
||||
{
|
||||
id: 'analyst',
|
||||
name: 'Analyst',
|
||||
description: 'Data analysis, research, and business intelligence specialist',
|
||||
},
|
||||
];
|
||||
|
||||
function DashboardContent() {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [autoSubmit, setAutoSubmit] = useState(false);
|
||||
const [selectedAgent, setSelectedAgent] = useState('suna');
|
||||
const [showAgentConfigurator, setShowAgentConfigurator] = useState(false);
|
||||
const { billingError, handleBillingError, clearBillingError } =
|
||||
useBillingError();
|
||||
const router = useRouter();
|
||||
|
@ -51,6 +85,8 @@ function DashboardContent() {
|
|||
const secondaryGradient =
|
||||
'bg-gradient-to-r from-blue-500 to-blue-500 bg-clip-text text-transparent';
|
||||
|
||||
const selectedAgentData = AGENTS.find(agent => agent.id === selectedAgent) || AGENTS[0];
|
||||
|
||||
const handleSubmit = async (
|
||||
message: string,
|
||||
options?: {
|
||||
|
@ -128,6 +164,48 @@ function DashboardContent() {
|
|||
}
|
||||
};
|
||||
|
||||
const handleAgentChange = (value: string) => {
|
||||
if (value === 'create-new') {
|
||||
setShowAgentConfigurator(true);
|
||||
} else {
|
||||
setSelectedAgent(value);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAgentConfigSave = (config: {
|
||||
id?: string;
|
||||
name: string;
|
||||
systemInstructions: string;
|
||||
selectedTools: string[];
|
||||
isEdit: boolean;
|
||||
}) => {
|
||||
console.log('Agent configuration saved:', config);
|
||||
|
||||
if (config.isEdit && config.id) {
|
||||
// Update existing agent
|
||||
toast.success(`Agent "${config.name}" updated successfully!`);
|
||||
} else {
|
||||
// Create new agent
|
||||
const newAgent = {
|
||||
id: config.name.toLowerCase().replace(/\s+/g, '-'),
|
||||
name: config.name,
|
||||
description: config.systemInstructions || 'Custom agent',
|
||||
systemInstructions: config.systemInstructions,
|
||||
selectedTools: config.selectedTools,
|
||||
};
|
||||
|
||||
setSelectedAgent(newAgent.id);
|
||||
toast.success(`Agent "${config.name}" created successfully!`);
|
||||
}
|
||||
|
||||
setShowAgentConfigurator(false);
|
||||
};
|
||||
|
||||
const handleEditAgent = (e: React.MouseEvent) => {
|
||||
e.stopPropagation(); // Prevent the select from opening
|
||||
setShowAgentConfigurator(true);
|
||||
};
|
||||
|
||||
// Check for pending prompt in localStorage on mount
|
||||
useEffect(() => {
|
||||
// Use a small delay to ensure we're fully mounted
|
||||
|
@ -178,25 +256,65 @@ function DashboardContent() {
|
|||
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-[650px] max-w-[90%]">
|
||||
<div className="flex flex-col items-center text-center mb-2 w-full">
|
||||
<h1 className={cn('tracking-tight text-4xl font-semibold leading-tight')}>
|
||||
Hey
|
||||
</h1>
|
||||
<p className="tracking-tight text-3xl font-normal text-muted-foreground/80 mt-2 flex items-center gap-2">
|
||||
What would you like Suna to do today?
|
||||
</p>
|
||||
<div className="tracking-tight text-3xl font-normal text-muted-foreground/80 mt-2 flex flex-col items-center gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span>Hey, I am</span>
|
||||
<div className="flex items-center gap-1">
|
||||
<Select value={selectedAgent} onValueChange={handleAgentChange}>
|
||||
<SelectTrigger className="w-auto h-auto p-0 border-none bg-transparent hover:bg-muted/50 transition-colors rounded-md px-2 py-1 inline-flex items-center gap-1">
|
||||
<SelectValue asChild>
|
||||
<span className="text-foreground font-semibold text-3xl underline decoration-dashed underline-offset-4 decoration-muted-foreground/40">
|
||||
{selectedAgentData.name}
|
||||
</span>
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent className="w-64">
|
||||
{AGENTS.map((agent) => (
|
||||
<SelectItem key={agent.id} value={agent.id} className="p-3">
|
||||
<div className="font-medium">{agent.name}</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
<div className="border-t mt-1 pt-1">
|
||||
<SelectItem value="create-new" className="text-muted-foreground">
|
||||
+ Create new agent
|
||||
</SelectItem>
|
||||
</div>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-6 w-6 hover:bg-muted/50 transition-colors"
|
||||
onClick={handleEditAgent}
|
||||
>
|
||||
<Edit className="h-3 w-3 text-muted-foreground" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<span>What would you like me to do today?</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ChatInput
|
||||
ref={chatInputRef}
|
||||
onSubmit={handleSubmit}
|
||||
loading={isSubmitting}
|
||||
placeholder="Describe what you need help with..."
|
||||
placeholder={`Describe what you need help with using ${selectedAgentData.name}...`}
|
||||
value={inputValue}
|
||||
onChange={setInputValue}
|
||||
hideAttachments={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Agent Configurator Modal */}
|
||||
<AgentConfigurator
|
||||
isOpen={showAgentConfigurator}
|
||||
onClose={() => setShowAgentConfigurator(false)}
|
||||
onSave={handleAgentConfigSave}
|
||||
availableAgents={AGENTS}
|
||||
currentAgentId={selectedAgent}
|
||||
/>
|
||||
|
||||
{/* Billing Error Alert */}
|
||||
<BillingErrorAlert
|
||||
message={billingError?.message}
|
||||
|
|
|
@ -0,0 +1,421 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Search, Check } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
|
||||
interface Tool {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
category: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
interface Agent {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
systemInstructions?: string;
|
||||
selectedTools?: string[];
|
||||
}
|
||||
|
||||
const AVAILABLE_TOOLS: Tool[] = [
|
||||
{
|
||||
id: 'llm',
|
||||
name: 'LLM',
|
||||
description: 'Advanced language model capabilities',
|
||||
category: 'AI',
|
||||
icon: '🔮',
|
||||
},
|
||||
{
|
||||
id: 'python-code',
|
||||
name: 'Python Code',
|
||||
description: 'Execute Python code and scripts',
|
||||
category: 'Code',
|
||||
icon: '🐍',
|
||||
},
|
||||
{
|
||||
id: 'javascript-code',
|
||||
name: 'JavaScript Code',
|
||||
description: 'Execute JavaScript code',
|
||||
category: 'Code',
|
||||
icon: '📜',
|
||||
},
|
||||
{
|
||||
id: 'api',
|
||||
name: 'API',
|
||||
description: 'Make HTTP requests to external APIs',
|
||||
category: 'Integration',
|
||||
icon: '🔗',
|
||||
},
|
||||
{
|
||||
id: 'google-search',
|
||||
name: 'Google Search',
|
||||
description: 'Search the web using Google',
|
||||
category: 'Search',
|
||||
icon: '🔍',
|
||||
},
|
||||
{
|
||||
id: 'file-handler',
|
||||
name: 'File Handler',
|
||||
description: 'Read, write, and process files',
|
||||
category: 'File',
|
||||
icon: '📁',
|
||||
},
|
||||
{
|
||||
id: 'data-scraper',
|
||||
name: 'Data Scraper',
|
||||
description: 'Extract data from websites',
|
||||
category: 'Data',
|
||||
icon: '🕷️',
|
||||
},
|
||||
{
|
||||
id: 'email',
|
||||
name: 'Email',
|
||||
description: 'Send and manage emails',
|
||||
category: 'Communication',
|
||||
icon: '📧',
|
||||
},
|
||||
];
|
||||
|
||||
const CATEGORIES = ['All', 'AI', 'Code', 'Integration', 'Search', 'File', 'Data', 'Communication'];
|
||||
|
||||
interface AgentConfiguratorProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSave: (config: {
|
||||
id?: string;
|
||||
name: string;
|
||||
systemInstructions: string;
|
||||
selectedTools: string[];
|
||||
isEdit: boolean;
|
||||
}) => void;
|
||||
availableAgents: Agent[];
|
||||
currentAgentId?: string;
|
||||
}
|
||||
|
||||
export function AgentConfigurator({
|
||||
isOpen,
|
||||
onClose,
|
||||
onSave,
|
||||
availableAgents,
|
||||
currentAgentId
|
||||
}: AgentConfiguratorProps) {
|
||||
const [selectedAgentId, setSelectedAgentId] = useState<string>('create-new');
|
||||
const [agentName, setAgentName] = useState('');
|
||||
const [systemInstructions, setSystemInstructions] = useState('');
|
||||
const [selectedTools, setSelectedTools] = useState<string[]>([]);
|
||||
const [selectedCategory, setSelectedCategory] = useState('All');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [originalData, setOriginalData] = useState<{
|
||||
name: string;
|
||||
systemInstructions: string;
|
||||
selectedTools: string[];
|
||||
} | null>(null);
|
||||
|
||||
const isEditing = selectedAgentId !== 'create-new';
|
||||
const currentAgent = availableAgents.find(agent => agent.id === selectedAgentId);
|
||||
|
||||
// Load agent data when selection changes
|
||||
useEffect(() => {
|
||||
if (selectedAgentId === 'create-new') {
|
||||
const newData = { name: '', systemInstructions: '', selectedTools: [] };
|
||||
setAgentName(newData.name);
|
||||
setSystemInstructions(newData.systemInstructions);
|
||||
setSelectedTools(newData.selectedTools);
|
||||
setOriginalData(newData);
|
||||
} else {
|
||||
const agent = availableAgents.find(a => a.id === selectedAgentId);
|
||||
if (agent) {
|
||||
const agentData = {
|
||||
name: agent.name,
|
||||
systemInstructions: agent.systemInstructions || agent.description,
|
||||
selectedTools: agent.selectedTools || [],
|
||||
};
|
||||
setAgentName(agentData.name);
|
||||
setSystemInstructions(agentData.systemInstructions);
|
||||
setSelectedTools(agentData.selectedTools);
|
||||
setOriginalData(agentData);
|
||||
}
|
||||
}
|
||||
setHasChanges(false);
|
||||
}, [selectedAgentId, availableAgents]);
|
||||
|
||||
// Set initial agent when modal opens
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setSelectedAgentId(currentAgentId || 'create-new');
|
||||
}
|
||||
}, [isOpen, currentAgentId]);
|
||||
|
||||
// Track changes
|
||||
useEffect(() => {
|
||||
if (originalData) {
|
||||
const hasDataChanges =
|
||||
agentName !== originalData.name ||
|
||||
systemInstructions !== originalData.systemInstructions ||
|
||||
JSON.stringify(selectedTools.sort()) !== JSON.stringify(originalData.selectedTools.sort());
|
||||
setHasChanges(hasDataChanges);
|
||||
}
|
||||
}, [agentName, systemInstructions, selectedTools, originalData]);
|
||||
|
||||
const filteredTools = AVAILABLE_TOOLS.filter(tool => {
|
||||
const matchesCategory = selectedCategory === 'All' || tool.category === selectedCategory;
|
||||
const matchesSearch = tool.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
tool.description.toLowerCase().includes(searchQuery.toLowerCase());
|
||||
return matchesCategory && matchesSearch;
|
||||
});
|
||||
|
||||
const handleToolToggle = (toolId: string) => {
|
||||
setSelectedTools(prev =>
|
||||
prev.includes(toolId)
|
||||
? prev.filter(id => id !== toolId)
|
||||
: [...prev, toolId]
|
||||
);
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
if (!agentName.trim()) return;
|
||||
|
||||
console.log('Saving agent configuration:', {
|
||||
id: isEditing ? selectedAgentId : undefined,
|
||||
name: agentName,
|
||||
systemInstructions,
|
||||
selectedTools,
|
||||
isEdit: isEditing,
|
||||
});
|
||||
|
||||
onSave({
|
||||
id: isEditing ? selectedAgentId : undefined,
|
||||
name: agentName,
|
||||
systemInstructions,
|
||||
selectedTools,
|
||||
isEdit: isEditing,
|
||||
});
|
||||
|
||||
setHasChanges(false);
|
||||
};
|
||||
|
||||
const handleDiscard = () => {
|
||||
if (originalData) {
|
||||
setAgentName(originalData.name);
|
||||
setSystemInstructions(originalData.systemInstructions);
|
||||
setSelectedTools(originalData.selectedTools);
|
||||
setHasChanges(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
// Reset everything when closing
|
||||
setSelectedAgentId('create-new');
|
||||
setAgentName('');
|
||||
setSystemInstructions('');
|
||||
setSelectedTools([]);
|
||||
setSearchQuery('');
|
||||
setSelectedCategory('All');
|
||||
setHasChanges(false);
|
||||
setOriginalData(null);
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={handleClose}>
|
||||
<DialogContent className="max-w-5xl max-h-[90vh] p-0 gap-0 bg-white border border-gray-200">
|
||||
{/* Header */}
|
||||
<div className="border-b border-gray-100 px-8 py-6">
|
||||
<DialogTitle className="text-xl font-medium text-gray-900 mb-1">
|
||||
{isEditing ? 'Edit Agent' : 'Create Agent'}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-sm text-gray-600">
|
||||
{isEditing
|
||||
? 'Modify your agent configuration and tools'
|
||||
: 'Configure your custom agent with specific tools and instructions'
|
||||
}
|
||||
</DialogDescription>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex h-[600px]">
|
||||
{/* Left Panel - Configuration */}
|
||||
<div className="w-2/5 border-r border-gray-100 flex flex-col">
|
||||
<div className="p-8 flex-1 flex flex-col gap-6">
|
||||
{/* Agent Selection */}
|
||||
<div className="space-y-3">
|
||||
<Label className="text-sm font-medium text-gray-900">Agent</Label>
|
||||
<Select value={selectedAgentId} onValueChange={setSelectedAgentId}>
|
||||
<SelectTrigger className="h-10 border-gray-200 focus:border-gray-900 transition-colors">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="border-gray-200">
|
||||
<SelectItem value="create-new" className="py-2.5">
|
||||
<span className="text-gray-600">+ Create New Agent</span>
|
||||
</SelectItem>
|
||||
{availableAgents.map((agent) => (
|
||||
<SelectItem key={agent.id} value={agent.id} className="py-2.5">
|
||||
<span className="font-medium">{agent.name}</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Agent Name */}
|
||||
<div className="space-y-3">
|
||||
<Label htmlFor="agent-name" className="text-sm font-medium text-gray-900">
|
||||
Name
|
||||
</Label>
|
||||
<Input
|
||||
id="agent-name"
|
||||
placeholder="e.g., Research Assistant"
|
||||
value={agentName}
|
||||
onChange={(e) => setAgentName(e.target.value)}
|
||||
className="h-10 border-gray-200 focus:border-gray-900 transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* System Instructions */}
|
||||
<div className="space-y-3 flex-1 flex flex-col">
|
||||
<Label htmlFor="system-instructions" className="text-sm font-medium text-gray-900">
|
||||
Instructions
|
||||
</Label>
|
||||
<Textarea
|
||||
id="system-instructions"
|
||||
placeholder="Describe the agent's role, behavior, and expertise..."
|
||||
value={systemInstructions}
|
||||
onChange={(e) => setSystemInstructions(e.target.value)}
|
||||
className="flex-1 min-h-0 border-gray-200 focus:border-gray-900 transition-colors resize-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Actions */}
|
||||
<div className="border-t border-gray-100 p-6">
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={!agentName.trim() || !hasChanges}
|
||||
className="flex-1 h-10 bg-black hover:bg-gray-800 text-white border-0"
|
||||
>
|
||||
{isEditing ? 'Save Changes' : 'Create Agent'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={hasChanges ? handleDiscard : handleClose}
|
||||
className="h-10 border-gray-200 hover:bg-gray-50 text-gray-700"
|
||||
>
|
||||
{hasChanges ? 'Discard' : 'Cancel'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{hasChanges && (
|
||||
<p className="text-xs text-gray-500 text-center mt-3">
|
||||
You have unsaved changes
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Panel - Tools */}
|
||||
<div className="flex-1 flex flex-col">
|
||||
{/* Tools Header */}
|
||||
<div className="p-8 pb-6 border-b border-gray-100">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h3 className="text-lg font-medium text-gray-900">Tools</h3>
|
||||
<span className="text-sm text-gray-500 bg-gray-100 px-2.5 py-1 rounded-full">
|
||||
{selectedTools.length} selected
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Search */}
|
||||
<div className="relative mb-4">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
|
||||
<Input
|
||||
placeholder="Search tools..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-10 h-10 border-gray-200 focus:border-gray-900 transition-colors"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Categories */}
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{CATEGORIES.map((category) => (
|
||||
<button
|
||||
key={category}
|
||||
onClick={() => setSelectedCategory(category)}
|
||||
className={cn(
|
||||
"px-3 py-1.5 text-xs font-medium rounded-md transition-colors",
|
||||
selectedCategory === category
|
||||
? "bg-gray-900 text-white"
|
||||
: "bg-gray-100 text-gray-600 hover:bg-gray-200"
|
||||
)}
|
||||
>
|
||||
{category}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tools List */}
|
||||
<div className="flex-1 overflow-y-auto px-8 py-4">
|
||||
<div className="space-y-2">
|
||||
{filteredTools.map((tool) => (
|
||||
<div
|
||||
key={tool.id}
|
||||
onClick={() => handleToolToggle(tool.id)}
|
||||
className={cn(
|
||||
"group flex items-center gap-4 p-4 rounded-lg border cursor-pointer transition-all",
|
||||
selectedTools.includes(tool.id)
|
||||
? "border-gray-900 bg-gray-50"
|
||||
: "border-gray-200 hover:border-gray-300 hover:bg-gray-50"
|
||||
)}
|
||||
>
|
||||
<div className={cn(
|
||||
"flex items-center justify-center w-5 h-5 rounded border-2 transition-colors",
|
||||
selectedTools.includes(tool.id)
|
||||
? "border-gray-900 bg-gray-900"
|
||||
: "border-gray-300 group-hover:border-gray-400"
|
||||
)}>
|
||||
{selectedTools.includes(tool.id) && (
|
||||
<Check className="h-3 w-3 text-white" />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0">
|
||||
<span className="text-lg flex-shrink-0">{tool.icon}</span>
|
||||
<div className="min-w-0">
|
||||
<div className="font-medium text-gray-900 truncate">{tool.name}</div>
|
||||
<div className="text-sm text-gray-500 truncate">{tool.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
|
@ -1,38 +1,38 @@
|
|||
import * as React from 'react';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import { cva, type VariantProps } from 'class-variance-authority';
|
||||
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 { cn } from "@/lib/utils"
|
||||
|
||||
const badgeVariants = cva(
|
||||
'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
|
||||
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
|
||||
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
||||
secondary:
|
||||
'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
|
||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
||||
destructive:
|
||||
'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
||||
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
|
||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
function Badge({
|
||||
className,
|
||||
variant,
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<'span'> &
|
||||
}: React.ComponentProps<"span"> &
|
||||
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
||||
const Comp = asChild ? Slot : 'span';
|
||||
const Comp = asChild ? Slot : "span"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
|
@ -40,7 +40,7 @@ function Badge({
|
|||
className={cn(badgeVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export { Badge, badgeVariants };
|
||||
export { Badge, badgeVariants }
|
||||
|
|
|
@ -1,84 +1,84 @@
|
|||
import * as React from 'react';
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Card({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card"
|
||||
className={cn(
|
||||
'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
|
||||
className,
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-header"
|
||||
className={cn(
|
||||
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
|
||||
className,
|
||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-title"
|
||||
className={cn('leading-none font-semibold', className)}
|
||||
className={cn("leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-description"
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-action"
|
||||
className={cn(
|
||||
'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
|
||||
className,
|
||||
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-content"
|
||||
className={cn('px-6', className)}
|
||||
className={cn("px-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-footer"
|
||||
className={cn('flex items-center px-6 [.border-t]:pt-6', className)}
|
||||
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
|
@ -89,4 +89,4 @@ export {
|
|||
CardAction,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
'use client';
|
||||
"use client"
|
||||
|
||||
import * as React from 'react';
|
||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
||||
import { CheckIcon } from 'lucide-react';
|
||||
import * as React from "react"
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
||||
import { CheckIcon } from "lucide-react"
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Checkbox({
|
||||
className,
|
||||
|
@ -14,8 +14,8 @@ function Checkbox({
|
|||
<CheckboxPrimitive.Root
|
||||
data-slot="checkbox"
|
||||
className={cn(
|
||||
'peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
|
@ -26,7 +26,7 @@ function Checkbox({
|
|||
<CheckIcon className="size-3.5" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export { Checkbox };
|
||||
export { Checkbox }
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
'use client';
|
||||
"use client"
|
||||
|
||||
import * as React from 'react';
|
||||
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||
import * as React from "react"
|
||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Label({
|
||||
className,
|
||||
|
@ -13,12 +13,12 @@ function Label({
|
|||
<LabelPrimitive.Root
|
||||
data-slot="label"
|
||||
className={cn(
|
||||
'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
|
||||
className,
|
||||
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export { Label };
|
||||
export { Label }
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
'use client';
|
||||
"use client"
|
||||
|
||||
import * as React from 'react';
|
||||
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
||||
import * as React from "react"
|
||||
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Separator({
|
||||
className,
|
||||
orientation = 'horizontal',
|
||||
orientation = "horizontal",
|
||||
decorative = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
|
||||
|
@ -17,12 +17,12 @@ function Separator({
|
|||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
'bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px',
|
||||
className,
|
||||
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export { Separator };
|
||||
export { Separator }
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import * as React from 'react';
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
|
||||
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
return (
|
||||
<textarea
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
'border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className,
|
||||
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
export { Textarea };
|
||||
export { Textarea }
|
||||
|
|
Loading…
Reference in New Issue