diff --git a/.changeset/multi-type-schema-support.md b/.changeset/multi-type-schema-support.md new file mode 100644 index 00000000..78aafece --- /dev/null +++ b/.changeset/multi-type-schema-support.md @@ -0,0 +1,7 @@ +--- +"json-schema-studio": patch +--- + +Fix: Handle multi-type schemas in node type inference + +Multi-type schemas (like `["string", "null"]`) were previously failing type inference and falling back to a default "others" node style. The type inference logic has been updated to pick the first non-null type from the array to determine the node's visual style. diff --git a/src/components/CustomReactFlowNode.tsx b/src/components/CustomReactFlowNode.tsx index f612998c..5f3cffee 100644 --- a/src/components/CustomReactFlowNode.tsx +++ b/src/components/CustomReactFlowNode.tsx @@ -1,5 +1,6 @@ import { Handle } from "@xyflow/react"; import type { RFNodeData } from "../utils/processAST"; +import { neonColors } from "../utils/processAST"; import { useContext, useLayoutEffect, useRef, useState } from "react"; import { AppContext } from "../contexts/AppContext"; @@ -30,13 +31,14 @@ const CustomNode = ({ data, id, selected }: { data: RFNodeData; id: string; sele setHandleOffsets(offsets); }, [data.nodeData]); + const { color } = data.nodeStyle; + return (
@@ -64,12 +66,12 @@ const CustomNode = ({ data, id, selected }: { data: RFNodeData; id: string; sele
{data.nodeLabel} @@ -96,19 +98,28 @@ const CustomNode = ({ data, id, selected }: { data: RFNodeData; id: string; sele {isArray ? ( -
- {(keyData.value as string[]).map((item, index) => ( -
{ - rowRefs.current[`${id}-${item}`] = el; - }} - key={index} - className="px-2 py-[2px] bg-[var(--node-value-bg-color)]" - style={{ border: `1px solid ${data.nodeStyle.color}30` }} - > - {item} -
- ))} +
+ {(keyData.value as string[]).map((item, index) => { + const itemColor = key === "type" && item in neonColors + ? neonColors[item as keyof typeof neonColors] + : null; + return ( +
{ + rowRefs.current[`${id}-${item}`] = el; + }} + key={index} + className={`px-2 py-[2px] ${itemColor ? "rounded text-center font-medium shadow-sm" : "bg-[var(--node-value-bg-color)]"}`} + style={{ + border: itemColor ? `1px solid ${itemColor}` : `1px solid ${color}30`, + backgroundColor: itemColor ? `${itemColor}33` : undefined, + color: itemColor ? (theme === "dark" ? itemColor : `color-mix(in srgb, ${itemColor} 60%, black)`) : undefined, + }} + > + {item} +
+ ); + })}
) : ( { - if (typeof nodeData.type?.value === "string") { - return ["objectSchema", nodeData.type.value as string]; +export const inferSchemaType = (nodeData: GraphNode["data"]["nodeData"]): [string, string, string[]?] => { + const typeValue = nodeData.type?.value; + + if (typeof typeValue === "string") { + return ["objectSchema", typeValue]; + } + + if (Array.isArray(typeValue)) { + let primaryType = "others"; + for (const t of typeValue as string[]) { + if (t !== "null") { + primaryType = t; + break; + } + } + return ["multiType", primaryType, typeValue as string[]]; } const hasAnyKeyword = (keywords: Set) => { diff --git a/src/utils/processAST.ts b/src/utils/processAST.ts index bfd1c23f..0b06a6a6 100644 --- a/src/utils/processAST.ts +++ b/src/utils/processAST.ts @@ -32,7 +32,7 @@ export type RFNodeData = { } type NodeStyle = { - color: string + color: string; } export type GraphEdge = RFEdge & { @@ -80,7 +80,7 @@ type GenerateSourceHandles = (key: string | undefined, value: unknown, nodeId: s type UpdateNode = (node: UnpositionedGraphNode, update: UpdateNodeOptionalParameters) => void; -const neonColors = { +export const neonColors = { string: "#FF6EFF", // neon magenta number: "#00FF95", // neon mint integer: "#00FF95", // neon mint @@ -91,6 +91,7 @@ const neonColors = { booleanSchemaTrue: "#12FF4B", // neon green booleanSchemaFalse: "#FF3B3B", // neon red reference: "#FFE1BD", // soft neon cream + multiType: "#FF007F", // neon rose others: "#CCCCCC", // soft gray }; @@ -157,7 +158,8 @@ export const processAST: ProcessAST = ({ ast, schemaUri, nodes, edges, parentId, } const getColor = (nodeData: NodeData) => { - const [, definedFor] = inferSchemaType(nodeData); + const [schemaType, definedFor] = inferSchemaType(nodeData); + if (schemaType === "multiType") return neonColors.multiType; return ( neonColors[definedFor as keyof typeof neonColors] ?? neonColors.others ); @@ -179,7 +181,7 @@ export const processAST: ProcessAST = ({ ast, schemaUri, nodes, edges, parentId, updateNode( newNode, - { nodeData, nodeStyle: { color: color }, addTargetHandle: { handleId: targetHandle, position: Position.Left } } + { nodeData, nodeStyle: { color }, addTargetHandle: { handleId: targetHandle, position: Position.Left } } ); };