Spaces:
Running
Running
File size: 4,244 Bytes
68185ce d89db9c 68185ce d89db9c 68185ce d89db9c 68185ce d89db9c 68185ce |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
import type React from "react";
import { useState } from "react";
type JsonValue = string | number | boolean | null | JsonObject | JsonArray;
type JsonObject = { [key: string]: JsonValue };
type JsonArray = JsonValue[];
const isJsonString = (str: string): boolean => {
if (typeof str !== "string") return false;
const trimmed = str.trim();
// Check if it looks like JSON (starts with { or [)
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return false;
try {
const parsed = JSON.parse(trimmed);
// Ensure it's actually an object or array, not just a primitive
console.log("JSON parse successful:", parsed);
return typeof parsed === "object" && parsed !== null;
} catch (error) {
console.log("JSON parse failed:", error);
return false;
}
};
const JsonCollapsible: React.FC<{ data: JsonValue; isString?: boolean }> = ({
data,
isString = false,
}) => {
const [isCollapsed, setIsCollapsed] = useState(true);
let jsonData: JsonValue;
try {
jsonData = isString ? JSON.parse(data as string) : data;
} catch {
// If parsing fails, display as string
return (
<pre className="text-sm text-gray-300 whitespace-pre-wrap overflow-auto">
{String(data)}
</pre>
);
}
const isObject = typeof jsonData === "object" && jsonData !== null;
if (!isObject) {
return <span className="text-gray-300">{JSON.stringify(jsonData)}</span>;
}
const keys = Object.keys(jsonData as JsonObject);
const preview = Array.isArray(jsonData)
? `[${jsonData.length} items]`
: `{${keys.length} keys}`;
return (
<div className="json-collapsible">
<button
onClick={() => setIsCollapsed(!isCollapsed)}
className="flex items-center gap-1 text-blue-400 hover:text-blue-300 transition-colors text-sm font-mono"
>
<span
className="transform transition-transform duration-200"
style={{
transform: isCollapsed ? "rotate(-90deg)" : "rotate(0deg)",
}}
>
▼
</span>
{isCollapsed ? preview : Array.isArray(jsonData) ? "[" : "{"}
</button>
{!isCollapsed && (
<div className="ml-4 mt-1">
<pre className="text-sm text-gray-300 whitespace-pre-wrap overflow-auto">
{JSON.stringify(jsonData, null, 2)}
</pre>
<div className="text-blue-400 font-mono text-sm">
{Array.isArray(jsonData) ? "]" : "}"}
</div>
</div>
)}
</div>
);
};
const ResultBlock: React.FC<{ error?: string; result?: unknown }> = ({
error,
result,
}) => {
console.log("ResultBlock component rendered with:", {
error,
result,
type: typeof result,
});
const renderContent = () => {
console.log("ResultBlock Debug:", {
result,
type: typeof result,
isString: typeof result === "string",
isArray: Array.isArray(result),
startsWithBracket:
typeof result === "string" && result.trim().startsWith("["),
isJsonString: typeof result === "string" ? isJsonString(result) : false,
});
// Handle objects and arrays directly
if (typeof result === "object" && result !== null) {
console.log("Rendering as object/array with JsonCollapsible");
return <JsonCollapsible data={result as JsonValue} />;
}
// Handle string that might be JSON
if (typeof result === "string") {
const trimmed = result.trim();
if (isJsonString(trimmed)) {
console.log("Rendering string as JSON with JsonCollapsible");
return <JsonCollapsible data={trimmed} isString={true} />;
}
}
// Fallback to plain text display
console.log("Rendering as plain text fallback");
return (
<pre className="text-sm text-gray-300 whitespace-pre-wrap overflow-auto">
{String(result)}
</pre>
);
};
return (
<div
className={
error
? "bg-red-900 border border-red-600 rounded p-3"
: "bg-gray-700 border border-gray-600 rounded p-3"
}
>
{error ? <p className="text-red-300 text-sm">Error: {error}</p> : null}
<div className="mt-2">{renderContent()}</div>
</div>
);
};
export default ResultBlock;
|