Add agent configurator and UI component updates

This commit is contained in:
marko-kraemer 2025-05-23 11:48:40 +02:00
parent b647889a7d
commit 70e89441d5
11 changed files with 981 additions and 115 deletions

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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}

View File

@ -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>
);
}

View File

@ -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 }

View File

@ -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,
};
}

View File

@ -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 }

View File

@ -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 }

View File

@ -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 }

View File

@ -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 }