Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions web/packages/agenta-entity-ui/src/testcase/TestcaseDataEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,18 @@ const MESSAGES_VIEW_OPTIONS: FieldViewModeOption[] = [
{value: "yaml", label: "YAML"},
]

const getTestcaseViewOptions = ({dataType}: {dataType: DataType}): FieldViewModeOption[] =>
dataType === "messages" ? MESSAGES_VIEW_OPTIONS : TESTCASE_VIEW_OPTIONS
// JSON/YAML only — for number and boolean fields
const CODE_ONLY_VIEW_OPTIONS: FieldViewModeOption[] = [
{value: "json", label: "JSON"},
{value: "yaml", label: "YAML"},
]

const getTestcaseViewOptions = ({dataType}: {dataType: DataType}): FieldViewModeOption[] => {
if (dataType === "messages") return MESSAGES_VIEW_OPTIONS
if (dataType === "number" || dataType === "boolean") return CODE_ONLY_VIEW_OPTIONS
return TESTCASE_VIEW_OPTIONS // strings, null → Text/Markdown/JSON/YAML
}

const renderTestcaseTypeChip = (value: unknown) => <TypeChip value={value} />

function FullPayloadCodeEditor({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {useCallback, useEffect, useMemo} from "react"

import {
detectDataType,
type DataType,
JsonEditorWithLocalState,
MessagesField,
type CoreFieldRendererProps,
Expand Down Expand Up @@ -31,20 +33,34 @@ function CodeEditor({
viewMode,
onChange,
readOnly,
lockedType,
onLockType,
}: {
editorId: string
value: unknown
displayValue: string
viewMode?: ViewMode
onChange: (value: unknown) => void
readOnly?: boolean
lockedType?: DataType
onLockType?: (type: DataType) => void
}) {
if (viewMode !== "yaml") {
return (
<JsonEditorWithLocalState
editorKey={editorId}
initialValue={displayValue}
onValidChange={(nextValue) => onChange(parseCodeEditorValue(nextValue, value))}
onValidChange={(nextRaw) => {
const parsed = parseCodeEditorValue(nextRaw, value)
onChange(parsed)
// Dynamic type casting: if the parsed type changed, update metadata
if (onLockType) {
const newType = detectDataType(parsed, "native")
if (newType !== lockedType) {
onLockType(newType)
}
}
}}
readOnly={readOnly}
/>
)
Expand All @@ -63,11 +79,16 @@ function CodeEditor({
id={editorId}
initialValue={displayValue}
value={displayValue}
handleChange={
readOnly
? undefined
: (nextValue) => onChange(parseCodeEditorValue(nextValue, value, viewMode))
}
handleChange={(nextValue) => {
const parsed = parseCodeEditorValue(nextValue, value, viewMode)
onChange(parsed)
if (onLockType) {
const newType = detectDataType(parsed, "native")
if (newType !== lockedType) {
onLockType(newType)
}
}
}}
editorType="border"
className="min-h-[60px] overflow-hidden"
disableDebounce
Expand Down Expand Up @@ -111,21 +132,38 @@ function TextEditor({
markdown,
onChange,
readOnly,
lockedType,
}: {
editorId: string
value: unknown
displayValue: string
markdown?: boolean
onChange: (value: unknown) => void
readOnly?: boolean
lockedType?: DataType
}) {
// Auto-infer native types from typed text so number / boolean values
// stop getting stored as strings. Anything that doesn't look exactly
// like a clean number or boolean literal stays a string — see
// inferPrimitiveFromText for the precise rules.
const handleChange = useCallback(
(next: string) => onChange(inferPrimitiveFromText(next)),
[onChange],
(next: string) => {
const inferred = inferPrimitiveFromText(next)
if (lockedType) {
const inferredType =
typeof inferred === "boolean"
? "boolean"
: typeof inferred === "number"
? "number"
: "string"
// Reject if type doesn't match — do NOT call onChange
if (inferredType !== lockedType) {
return
}
}
onChange(inferred)
},
[onChange, lockedType],
)

return (
Expand Down Expand Up @@ -173,6 +211,8 @@ export function TestcaseDrillInFieldRenderer({
dataType,
isRawMode,
viewMode,
lockedType,
onLockType,
}: CoreFieldRendererProps) {
const displayValue = useMemo(() => toDisplayString(value, viewMode), [value, viewMode])
const editorId = `testcase-field-${fullPathKey}`
Expand Down Expand Up @@ -200,6 +240,8 @@ export function TestcaseDrillInFieldRenderer({
viewMode={viewMode}
onChange={onChange}
readOnly={!editable}
lockedType={lockedType}
onLockType={onLockType}
/>
)
}
Expand Down Expand Up @@ -250,6 +292,7 @@ export function TestcaseDrillInFieldRenderer({
markdown
onChange={onChange}
readOnly={!editable}
lockedType={lockedType}
/>
)
}
Expand All @@ -262,6 +305,7 @@ export function TestcaseDrillInFieldRenderer({
displayValue={displayValue}
onChange={onChange}
readOnly={!editable}
lockedType={lockedType}
/>
)
}
Expand All @@ -276,6 +320,8 @@ export function TestcaseDrillInFieldRenderer({
viewMode={viewMode}
onChange={onChange}
readOnly={!editable}
lockedType={lockedType}
onLockType={onLockType}
/>
)
}
Expand All @@ -287,6 +333,7 @@ export function TestcaseDrillInFieldRenderer({
displayValue={displayValue}
onChange={onChange}
readOnly={!editable}
lockedType={lockedType}
/>
)
}
Loading