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

View File

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

View File

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