Fix formatting

This commit is contained in:
Ola Hungerford
2025-02-14 07:33:37 -07:00
parent f6860a88f9
commit 133b785f79
3 changed files with 104 additions and 78 deletions

View File

@@ -4,10 +4,16 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import JsonEditor from "./JsonEditor";
export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
export type JsonValue =
| string
| number
| boolean
| null
| JsonValue[]
| { [key: string]: JsonValue };
export type JsonSchemaType = {
type: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object';
type: "string" | "number" | "integer" | "boolean" | "array" | "object";
description?: string;
properties?: Record<string, JsonSchemaType>;
items?: JsonSchemaType;
@@ -24,32 +30,32 @@ interface DynamicJsonFormProps {
const formatFieldLabel = (key: string): string => {
return key
.replace(/([A-Z])/g, ' $1') // Insert space before capital letters
.replace(/_/g, ' ') // Replace underscores with spaces
.replace(/^\w/, c => c.toUpperCase()); // Capitalize first letter
.replace(/([A-Z])/g, " $1") // Insert space before capital letters
.replace(/_/g, " ") // Replace underscores with spaces
.replace(/^\w/, (c) => c.toUpperCase()); // Capitalize first letter
};
const DynamicJsonForm = ({
schema,
value,
onChange,
maxDepth = 3
maxDepth = 3,
}: DynamicJsonFormProps) => {
const [isJsonMode, setIsJsonMode] = useState(false);
const [jsonError, setJsonError] = useState<string>();
const generateDefaultValue = (propSchema: JsonSchemaType): JsonValue => {
switch (propSchema.type) {
case 'string':
return '';
case 'number':
case 'integer':
case "string":
return "";
case "number":
case "integer":
return 0;
case 'boolean':
case "boolean":
return false;
case 'array':
case "array":
return [];
case 'object': {
case "object": {
const obj: JsonObject = {};
if (propSchema.properties) {
Object.entries(propSchema.properties).forEach(([key, prop]) => {
@@ -67,20 +73,27 @@ const DynamicJsonForm = ({
propSchema: JsonSchemaType,
currentValue: JsonValue,
path: string[] = [],
depth: number = 0
depth: number = 0,
) => {
if (depth >= maxDepth && (propSchema.type === 'object' || propSchema.type === 'array')) {
if (
depth >= maxDepth &&
(propSchema.type === "object" || propSchema.type === "array")
) {
// Render as JSON editor when max depth is reached
return (
<JsonEditor
value={JSON.stringify(currentValue ?? generateDefaultValue(propSchema), null, 2)}
value={JSON.stringify(
currentValue ?? generateDefaultValue(propSchema),
null,
2,
)}
onChange={(newValue) => {
try {
const parsed = JSON.parse(newValue);
handleFieldChange(path, parsed);
setJsonError(undefined);
} catch (err) {
setJsonError(err instanceof Error ? err.message : 'Invalid JSON');
setJsonError(err instanceof Error ? err.message : "Invalid JSON");
}
}}
error={jsonError}
@@ -89,20 +102,25 @@ const DynamicJsonForm = ({
}
switch (propSchema.type) {
case 'string':
case 'number':
case 'integer':
case "string":
case "number":
case "integer":
return (
<Input
type={propSchema.type === 'string' ? 'text' : 'number'}
value={(currentValue as string | number) ?? ''}
onChange={(e) => handleFieldChange(path,
propSchema.type === 'string' ? e.target.value : Number(e.target.value)
)}
type={propSchema.type === "string" ? "text" : "number"}
value={(currentValue as string | number) ?? ""}
onChange={(e) =>
handleFieldChange(
path,
propSchema.type === "string"
? e.target.value
: Number(e.target.value),
)
}
placeholder={propSchema.description}
/>
);
case 'boolean':
case "boolean":
return (
<Input
type="checkbox"
@@ -111,7 +129,7 @@ const DynamicJsonForm = ({
className="w-4 h-4"
/>
);
case 'object':
case "object":
if (!propSchema.properties) return null;
return (
<div className="space-y-4 border rounded-md p-4">
@@ -122,13 +140,13 @@ const DynamicJsonForm = ({
prop,
(currentValue as JsonObject)?.[key],
[...path, key],
depth + 1
depth + 1,
)}
</div>
))}
</div>
);
case 'array': {
case "array": {
const arrayValue = Array.isArray(currentValue) ? currentValue : [];
if (!propSchema.items) return null;
return (
@@ -136,7 +154,7 @@ const DynamicJsonForm = ({
{propSchema.description && (
<p className="text-sm text-gray-600">{propSchema.description}</p>
)}
{propSchema.items?.description && (
<p className="text-sm text-gray-500">
Items: {propSchema.items.description}
@@ -145,36 +163,40 @@ const DynamicJsonForm = ({
<div className="space-y-2">
{arrayValue.map((item, index) => (
<div key={index} className="flex items-center gap-2">
{renderFormFields(
propSchema.items as JsonSchemaType,
item,
[...path, index.toString()],
depth + 1
)}
<Button
variant="outline"
size="sm"
onClick={() => {
const newArray = [...arrayValue];
newArray.splice(index, 1);
handleFieldChange(path, newArray);
}}
>
Remove
</Button>
</div>
))}
<div key={index} className="flex items-center gap-2">
{renderFormFields(
propSchema.items as JsonSchemaType,
item,
[...path, index.toString()],
depth + 1,
)}
<Button
variant="outline"
size="sm"
onClick={() => {
const newArray = [...arrayValue];
newArray.splice(index, 1);
handleFieldChange(path, newArray);
}}
>
Remove
</Button>
</div>
))}
<Button
variant="outline"
size="sm"
onClick={() => {
handleFieldChange(
path,
[...arrayValue, generateDefaultValue(propSchema.items as JsonSchemaType)]
);
handleFieldChange(path, [
...arrayValue,
generateDefaultValue(propSchema.items as JsonSchemaType),
]);
}}
title={propSchema.items?.description ? `Add new ${propSchema.items.description}` : 'Add new item'}
title={
propSchema.items?.description
? `Add new ${propSchema.items.description}`
: "Add new item"
}
>
Add Item
</Button>
@@ -193,9 +215,13 @@ const DynamicJsonForm = ({
return;
}
const newValue = { ...(typeof value === 'object' && value !== null && !Array.isArray(value) ? value : {}) } as JsonObject;
const newValue = {
...(typeof value === "object" && value !== null && !Array.isArray(value)
? value
: {}),
} as JsonObject;
let current: JsonObject = newValue;
for (let i = 0; i < path.length - 1; i++) {
const key = path[i];
if (!(key in current)) {
@@ -203,7 +229,7 @@ const DynamicJsonForm = ({
}
current = current[key] as JsonObject;
}
current[path[path.length - 1]] = fieldValue;
onChange(newValue);
};
@@ -228,7 +254,7 @@ const DynamicJsonForm = ({
onChange(JSON.parse(newValue));
setJsonError(undefined);
} catch (err) {
setJsonError(err instanceof Error ? err.message : 'Invalid JSON');
setJsonError(err instanceof Error ? err.message : "Invalid JSON");
}
}}
error={jsonError}

View File

@@ -1,7 +1,7 @@
import Editor from 'react-simple-code-editor';
import Prism from 'prismjs';
import 'prismjs/components/prism-json';
import 'prismjs/themes/prism.css';
import Editor from "react-simple-code-editor";
import Prism from "prismjs";
import "prismjs/components/prism-json";
import "prismjs/themes/prism.css";
import { Button } from "@/components/ui/button";
interface JsonEditorProps {
@@ -32,28 +32,26 @@ const JsonEditor = ({ value, onChange, error }: JsonEditorProps) => {
</div>
<div
className={`border rounded-md ${
error ? 'border-red-500' : 'border-gray-200 dark:border-gray-800'
error ? "border-red-500" : "border-gray-200 dark:border-gray-800"
}`}
>
<Editor
value={value}
onValueChange={onChange}
highlight={code =>
Prism.highlight(code, Prism.languages.json, 'json')
highlight={(code) =>
Prism.highlight(code, Prism.languages.json, "json")
}
padding={10}
style={{
fontFamily: '"Fira code", "Fira Mono", monospace',
fontSize: 14,
backgroundColor: 'transparent',
minHeight: '100px',
backgroundColor: "transparent",
minHeight: "100px",
}}
className="w-full"
/>
</div>
{error && (
<p className="text-sm text-red-500 mt-1">{error}</p>
)}
{error && <p className="text-sm text-red-500 mt-1">{error}</p>}
</div>
);
};

View File

@@ -192,12 +192,14 @@ const ToolsTab = ({
) : prop.type === "object" ? (
<div className="mt-1">
<DynamicJsonForm
schema={{
type: 'object',
properties: prop.properties,
description: prop.description,
} as JsonSchemaType}
value={params[key] as JsonValue ?? {}}
schema={
{
type: "object",
properties: prop.properties,
description: prop.description,
} as JsonSchemaType
}
value={(params[key] as JsonValue) ?? {}}
onChange={(newValue: JsonValue) => {
setParams({
...params,
@@ -226,7 +228,7 @@ const ToolsTab = ({
)}
</div>
);
}
},
)}
<Button onClick={() => callTool(selectedTool.name, params)}>
<Send className="w-4 h-4 mr-2" />