+
+
+
+ {
+ setNameError(validateToolName(value));
+ if (!validateToolName(value)) {
+ handleUpdate({
+ ...tool,
+ name: value
+ });
+ }
+ }}
validate={(value) => {
const error = validateToolName(value);
setNameError(error);
return { valid: !error, errorMessage: error || undefined };
}}
- onValidatedChange={(value) => {
- handleUpdate({
- ...tool,
- name: value
- });
- }}
- placeholder="Enter tool name..."
- className="w-full text-sm bg-transparent focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 transition-colors px-4 py-3"
- autoResize
+ showSaveButton={true}
+ showDiscardButton={true}
+ error={nameError}
+ className="w-full"
+ />
+
+
+
+
+
+ handleUpdate({ ...tool, description: value })}
+ multiline={true}
+ placeholder="Describe what this tool does..."
+ className="w-full"
/>
- {nameError && (
-
{nameError}
- )}
- )}
-
-
-
- {!isReadOnly && (
-
-
-
-
+
+ {/* Behavior Section */}
+
}
+ title="Behavior"
+ labelWidth="md:w-32"
+ className="mb-1"
+ >
+
+ {!isReadOnly && (
+
+
+ handleUpdate({
+ ...tool,
+ mockTool: value === "mock",
+ autoSubmitMockedResponse: value === "mock" ? true : undefined
+ })}
+ orientation="horizontal"
+ classNames={{
+ wrapper: "flex flex-col md:flex-row gap-2 md:gap-8 pl-0 md:pl-3",
+ label: "text-sm"
+ }}
+ >
+
+ Mock tool responses
+
+
+ Connect tool to your API
+
+
+
+ )}
+ {tool.mockTool && (
+
+
+ Describe the response the mock tool should return. This will be shown in the chat when the tool is called. You can also provide a JSON schema for the response.
+ handleUpdate({
+ ...tool,
+ mockInstructions: value
+ })}
+ multiline={true}
+ placeholder="Mock response instructions..."
+ className="w-full text-xs p-2 bg-white dark:bg-gray-900"
+ />
Automatically send mock response in chat
-
-
-
+ )}
- )}
-
-
-
-
- {renderParameters()}
-
-
- {!isReadOnly && (
-
+
+ {/* Parameters Section */}
+
}
+ title="Parameters"
+ labelWidth="md:w-32"
+ className="mb-1"
+ >
+
+ {tool.parameters?.properties && Object.entries(tool.parameters.properties).map(([paramName, param]) => (
+
+ ))}
+ {!isReadOnly && (
-
- )}
-
+ )}
+
+
);
diff --git a/apps/rowboat/components/common/section-card.tsx b/apps/rowboat/components/common/section-card.tsx
index 350bf536..90d7be32 100644
--- a/apps/rowboat/components/common/section-card.tsx
+++ b/apps/rowboat/components/common/section-card.tsx
@@ -2,25 +2,57 @@ import React, { useState } from "react";
import { ChevronDown, ChevronRight } from "lucide-react";
interface SectionCardProps {
+ icon?: React.ReactNode;
title: React.ReactNode;
children: React.ReactNode;
labelWidth?: string; // e.g., 'md:w-32'
className?: string;
+ style?: React.CSSProperties;
+ chevronSize?: string;
}
-export function SectionCard({ title, children, labelWidth = 'md:w-32', className = '' }: SectionCardProps) {
+export function SectionCard({ icon, title, children, labelWidth = 'md:w-32', className = '', style, chevronSize = 'w-4 h-4' }: SectionCardProps) {
const [expanded, setExpanded] = useState(true);
+ React.useEffect(() => {
+ const btn = document.getElementById(`section-card-header-${title && typeof title === 'string' ? title : ''}`);
+ if (btn) {
+ console.log('SectionCard header button:', btn, btn.getBoundingClientRect(), window.getComputedStyle(btn));
+ const chevron = btn.querySelector('svg');
+ if (chevron) {
+ console.log('Chevron:', chevron, chevron.getBoundingClientRect(), window.getComputedStyle(chevron));
+ }
+ const iconEl = btn.querySelector('.section-card-icon');
+ if (iconEl) {
+ console.log('Icon:', iconEl, iconEl.getBoundingClientRect(), window.getComputedStyle(iconEl));
+ }
+ const label = btn.querySelector('span');
+ if (label) {
+ console.log('Label:', label, label.getBoundingClientRect(), window.getComputedStyle(label));
+ }
+ }
+ }, [title]);
+
return (
-
+
void,
+ handleDelete: (name: string) => void,
+ handleRename: (oldName: string, newName: string) => void,
+ readOnly?: boolean
+}) {
+ const [expanded, setExpanded] = useState(false);
+ const [localName, setLocalName] = useState(param.name);
+
+ useEffect(() => {
+ setLocalName(param.name);
+ }, [param.name]);
+
+ return (
+
+
+
+ {expanded && (
+
+
+
+
+
+ {
+ setLocalName(value);
+ if (value && value !== param.name) {
+ handleRename(param.name, value);
+ }
+ }}
+ multiline={false}
+ showSaveButton={true}
+ showDiscardButton={true}
+ className="w-full"
+ locked={readOnly}
+ />
+
+
+
+
+
+ handleUpdate(param.name, {
+ ...param,
+ description: value
+ })}
+ multiline={true}
+ placeholder="Describe this parameter..."
+ className="w-full"
+ locked={readOnly}
+ />
+
+
+
+
+
+
+
+
+
+
handleUpdate(param.name, {
+ ...param,
+ required: !param.required
+ })}
+ isDisabled={readOnly}
+ className="mt-2"
+ >
+ Required parameter
+
+
+ )}
+
+
+ );
+}