{
  "id": "json-inspector",
  "name": "JSON Inspector",
  "category": "data",
  "tags": ["json", "formatter", "data", "viewer"],
  "description": "JSON formatter and tree viewer for structured data.",
  "triggers": ["json viewer", "json inspector", "format json", "parse json"],
  "defaultSize": { "w": 4, "h": 4 },
  "source": "function JsonInspector() {\n  const [input, setInput] = React.useState('{\\n  \"users\": [\\n    { \"id\": 1, \"name\": \"Alice\", \"active\": true },\\n    { \"id\": 2, \"name\": \"Bob\", \"active\": false }\\n  ],\\n  \"count\": 2,\\n  \"meta\": { \"version\": \"1.0\" }\\n}');\n  const [error, setError] = React.useState(null);\n\n  let parsed = null;\n  try {\n    parsed = JSON.parse(input);\n  } catch (e) {\n    // leave parsed as null\n  }\n\n  React.useEffect(() => {\n    try {\n      JSON.parse(input);\n      setError(null);\n    } catch (e) {\n      setError(e.message);\n    }\n  }, [input]);\n\n  function formatJson() {\n    try {\n      const obj = JSON.parse(input);\n      setInput(JSON.stringify(obj, null, 2));\n      setError(null);\n    } catch (e) {\n      setError(e.message);\n    }\n  }\n\n  function minifyJson() {\n    try {\n      const obj = JSON.parse(input);\n      setInput(JSON.stringify(obj));\n      setError(null);\n    } catch (e) {\n      setError(e.message);\n    }\n  }\n\n  function TreeNode({ label, value, depth }) {\n    const [expanded, setExpanded] = React.useState(true);\n    const indent = depth * 16;\n    const type = Array.isArray(value) ? 'array' : typeof value === 'object' && value !== null ? 'object' : 'primitive';\n    const isExpandable = type === 'object' || type === 'array';\n    const keys = isExpandable ? Object.keys(value) : [];\n    const count = keys.length;\n\n    if (type === 'primitive') {\n      let color = '#1a1a1a';\n      if (typeof value === 'string') color = '#16a34a';\n      if (typeof value === 'number') color = '#2563eb';\n      if (typeof value === 'boolean') color = '#d97706';\n      if (value === null) color = '#9ca3af';\n      return (\n        <div style={{ paddingLeft: indent, fontSize: 13, fontFamily: 'monospace', lineHeight: '20px' }}>\n          <span style={{ color: '#6b7280' }}>{label ? label + ': ' : ''}</span>\n          <span style={{ color }}>{typeof value === 'string' ? '\"' + value + '\"' : String(value)}</span>\n        </div>\n      );\n    }\n\n    return (\n      <div>\n        <div\n          style={{ paddingLeft: indent, fontSize: 13, fontFamily: 'monospace', lineHeight: '20px', cursor: isExpandable ? 'pointer' : 'default', userSelect: 'none' }}\n          onClick={() => isExpandable && setExpanded(!expanded)}\n        >\n          <span style={{ color: '#6b7280', marginRight: 4 }}>{expanded ? '\\u25bc' : '\\u25b6'}</span>\n          <span style={{ color: '#6b7280' }}>{label ? label + ': ' : ''}</span>\n          <span style={{ color: '#6b7280' }}>{type === 'array' ? '[' + count + ']' : '{' + count + '}'}</span>\n        </div>\n        {expanded && keys.map((k, i) => (\n          <TreeNode key={i} label={type === 'array' ? i : k} value={value[k]} depth={depth + 1} />\n        ))}\n      </div>\n    );\n  }\n\n  return (\n    <div style={{ fontFamily: 'system-ui, sans-serif', padding: 16, background: '#fff', borderRadius: 12, height: '100%', boxSizing: 'border-box', display: 'flex', flexDirection: 'column' }}>\n      <h2 style={{ margin: '0 0 12px 0', fontSize: 18, color: '#1a1a1a' }}>JSON Inspector</h2>\n      <div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>\n        <button onClick={formatJson} style={{ padding: '6px 14px', borderRadius: 6, border: 'none', background: '#2563eb', color: '#fff', fontSize: 13, cursor: 'pointer' }}>Format</button>\n        <button onClick={minifyJson} style={{ padding: '6px 14px', borderRadius: 6, border: '1px solid #ddd', background: '#fff', fontSize: 13, cursor: 'pointer' }}>Minify</button>\n      </div>\n      <textarea\n        value={input}\n        onChange={e => setInput(e.target.value)}\n        style={{ width: '100%', height: 100, padding: 10, borderRadius: 6, border: error ? '1px solid #ef4444' : '1px solid #ddd', fontSize: 12, fontFamily: 'monospace', resize: 'none', boxSizing: 'border-box', marginBottom: 12 }}\n        placeholder=\"Paste JSON here...\"\n      />\n      {error && <div style={{ color: '#ef4444', fontSize: 12, marginBottom: 8 }}>{error}</div>}\n      <div style={{ flex: 1, overflow: 'auto', border: '1px solid #e5e7eb', borderRadius: 6, padding: 10, background: '#f9fafb' }}>\n        {parsed !== null ? <TreeNode value={parsed} depth={0} /> : <div style={{ color: '#999', fontSize: 13 }}>Invalid JSON</div>}\n      </div>\n    </div>\n  );\n}\nrender(<JsonInspector/>);",
  "placeholders": []
}
