---
name: "json-tree-explorer"
description: "Interactive JSON tree viewer with collapsible nodes, color-coded types, real-time search, and copy-to-clipboard. Paste any JSON and explore it visually."
metadata:
  version: "1.0.0"
  dependencies:
    system:
      - "browser"
  runtime:
    language: "html"
    entrypoint: "explorer.html"
    timeout_seconds: 30
---

See skill files — primary content is `explorer.html`.

---

## Files

### `explorer.html`

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSON Tree Explorer</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #0a0a1a; color: #e2e8f0; font-family: 'Segoe UI', system-ui, sans-serif; height: 100vh; display: flex; flex-direction: column; }
header { background: rgba(6,182,212,0.1); border-bottom: 1px solid rgba(6,182,212,0.2); padding: 10px 18px; display: flex; align-items: center; gap: 12px; }
header h1 { font-size: 15px; font-weight: 700; color: #22d3ee; }
.badge { background: rgba(6,182,212,0.15); border: 1px solid rgba(6,182,212,0.3); color: #06b6d4; font-size: 10px; padding: 2px 8px; border-radius: 20px; font-family: monospace; }
.hright { margin-left: auto; display: flex; gap: 8px; }
button { background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.12); color: #94a3b8; font-size: 11px; padding: 4px 12px; border-radius: 5px; cursor: pointer; font-family: monospace; transition: all 0.15s; }
button:hover { background: rgba(6,182,212,0.15); border-color: rgba(6,182,212,0.4); color: #22d3ee; }
main { display: flex; flex: 1; overflow: hidden; }
.input-pane { width: 320px; display: flex; flex-direction: column; border-right: 1px solid rgba(255,255,255,0.07); }
.pane-header { background: rgba(255,255,255,0.03); padding: 7px 14px; font-size: 11px; color: #555; border-bottom: 1px solid rgba(255,255,255,0.06); font-family: monospace; }
textarea { flex: 1; background: transparent; border: none; outline: none; padding: 14px; color: #e2e8f0; font-family: monospace; font-size: 12px; line-height: 1.6; resize: none; }
.tree-pane { flex: 1; overflow-y: auto; padding: 14px 18px; }
.node { margin: 1px 0; }
.key { color: #c084fc; font-weight: 600; }
.colon { color: #555; }
.toggle { cursor: pointer; user-select: none; color: #475569; font-size: 11px; padding: 0 4px; }
.toggle:hover { color: #94a3b8; }
.string { color: #4ade80; }
.number { color: #60a5fa; }
.boolean { color: #fb923c; }
.null { color: #f87171; font-style: italic; }
.bracket { color: #64748b; }
.count { color: #374151; font-size: 10px; font-family: monospace; }
.children { margin-left: 20px; border-left: 1px solid rgba(255,255,255,0.05); padding-left: 10px; }
.error { color: #f87171; font-family: monospace; font-size: 12px; padding: 14px; }
.row { display: flex; align-items: flex-start; gap: 4px; font-family: monospace; font-size: 12.5px; line-height: 1.8; }
.search-bar { padding: 8px 14px; border-bottom: 1px solid rgba(255,255,255,0.06); }
.search-bar input { width: 100%; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); color: #e2e8f0; font-size: 12px; padding: 5px 10px; border-radius: 5px; outline: none; font-family: monospace; }
.search-bar input:focus { border-color: rgba(6,182,212,0.4); }
.highlight { background: rgba(6,182,212,0.2); border-radius: 2px; }
::-webkit-scrollbar { width: 5px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: rgba(6,182,212,0.2); border-radius: 3px; }
</style>
</head>
<body>
<header>
  <h1>JSON Tree Explorer</h1>
  <span class="badge">SkillSlap</span>
  <div class="hright">
    <button onclick="expandAll()">Expand All</button>
    <button onclick="collapseAll()">Collapse All</button>
    <button onclick="copyFormatted()">Copy Formatted</button>
  </div>
</header>
<main>
  <div class="input-pane">
    <div class="pane-header">📋 Paste JSON</div>
    <textarea id="input" spellcheck="false" placeholder='Paste JSON here...'></textarea>
    <div class="search-bar"><input id="search" placeholder="🔍 Search keys and values..." oninput="doSearch()"></div>
  </div>
  <div class="tree-pane" id="tree"></div>
</main>
<script>
const inp = document.getElementById('input');
const treeEl = document.getElementById('tree');
const searchEl = document.getElementById('search');
let searchTerm = '';
let collapsed = new Set();
let parsed = null;

const DEFAULT_JSON = {
  "skill": {
    "id": "a1b2c3d4",
    "title": "JSON Tree Explorer",
    "version": "1.0.0",
    "tags": ["json", "visualization", "tool"],
    "verified": true,
    "score": 0.94,
    "author": {
      "username": "atapifire",
      "verified": true,
      "skills_count": 12
    },
    "metadata": {
      "runtime": {"entrypoint": "explorer.html", "language": "html"},
      "dependencies": {"system": ["browser"]}
    },
    "stats": {"slaps": 42, "tips": 7, "forks": 3},
    "description": "An interactive JSON tree explorer built in pure JavaScript.",
    "created_at": "2026-02-20T00:00:00Z",
    "featured": null
  }
};

function esc(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }

function hl(text) {
  if (!searchTerm) return esc(text);
  const re = new RegExp('(' + searchTerm.replace(/[.*+?^${}()|[\]\\]/g,'\\$&') + ')', 'gi');
  return esc(text).replace(re, '<span class="highlight">$1</span>');
}

function render(val, key, path, depth) {
  const id = path.join('.');
  const indent = depth * 20;
  const isCollapsed = collapsed.has(id);

  if (val === null) {
    return `<div class="row node" style="margin-left:${indent}px">${key ? `<span class="key">${hl(key)}</span><span class="colon">: </span>` : ''}<span class="null">null</span></div>`;
  }
  if (typeof val === 'string') {
    return `<div class="row node" style="margin-left:${indent}px">${key ? `<span class="key">${hl(key)}</span><span class="colon">: </span>` : ''}<span class="string">"${hl(val)}"</span></div>`;
  }
  if (typeof val === 'number') {
    return `<div class="row node" style="margin-left:${indent}px">${key ? `<span class="key">${hl(key)}</span><span class="colon">: </span>` : ''}<span class="number">${hl(val)}</span></div>`;
  }
  if (typeof val === 'boolean') {
    return `<div class="row node" style="margin-left:${indent}px">${key ? `<span class="key">${hl(key)}</span><span class="colon">: </span>` : ''}<span class="boolean">${val}</span></div>`;
  }
  if (Array.isArray(val)) {
    const count = val.length;
    if (count === 0) return `<div class="row node" style="margin-left:${indent}px">${key ? `<span class="key">${hl(key)}</span><span class="colon">: </span>` : ''}<span class="bracket">[]</span></div>`;
    const inner = isCollapsed ? '' : val.map((v,i) => render(v, null, [...path,i], depth+1)).join('');
    return `<div class="node"><div class="row" style="margin-left:${indent}px"><span class="toggle" onclick="toggleNode('${id}')">${isCollapsed?'▶':'▼'}</span>${key ? `<span class="key">${hl(key)}</span><span class="colon">: </span>` : ''}<span class="bracket">[</span><span class="count">${count} items</span>${isCollapsed?'<span class="bracket">]</span>':''}</div>${isCollapsed?'':'<div>'+inner+'</div><div class="row" style="margin-left:'+indent+'px"><span class="bracket">]</span></div>'}</div>`;
  }
  if (typeof val === 'object') {
    const keys = Object.keys(val);
    if (keys.length === 0) return `<div class="row node" style="margin-left:${indent}px">${key ? `<span class="key">${hl(key)}</span><span class="colon">: </span>` : ''}<span class="bracket">{}</span></div>`;
    const inner = isCollapsed ? '' : keys.map(k => render(val[k], k, [...path,k], depth+1)).join('');
    return `<div class="node"><div class="row" style="margin-left:${indent}px"><span class="toggle" onclick="toggleNode('${id}')">${isCollapsed?'▶':'▼'}</span>${key ? `<span class="key">${hl(key)}</span><span class="colon">: </span>` : ''}<span class="bracket">{</span><span class="count">${keys.length} keys</span>${isCollapsed?'<span class="bracket">}</span>':''}</div>${isCollapsed?'':'<div>'+inner+'</div><div class="row" style="margin-left:'+indent+'px"><span class="bracket">}</span></div>'}</div>`;
  }
  return '';
}

function renderTree() {
  if (!parsed) { treeEl.innerHTML = '<div class="error">No valid JSON</div>'; return; }
  treeEl.innerHTML = render(parsed, null, ['root'], 0);
}

function toggleNode(id) { collapsed.has(id) ? collapsed.delete(id) : collapsed.add(id); renderTree(); }
function expandAll() { collapsed.clear(); renderTree(); }
function collapseAll() {
  function collectIds(val, path) {
    if (val && typeof val === 'object') {
      const id = path.join('.');
      collapsed.add(id);
      const keys = Array.isArray(val) ? val.map((_,i)=>i) : Object.keys(val);
      keys.forEach(k => collectIds(val[k], [...path, k]));
    }
  }
  collectIds(parsed, ['root']);
  renderTree();
}
function copyFormatted() {
  if (parsed) navigator.clipboard?.writeText(JSON.stringify(parsed, null, 2));
}
function doSearch() { searchTerm = searchEl.value.trim(); renderTree(); }

function parseAndRender() {
  const text = inp.value.trim();
  if (!text) { parsed = DEFAULT_JSON; inp.value = JSON.stringify(DEFAULT_JSON, null, 2); renderTree(); return; }
  try { parsed = JSON.parse(text); treeEl.innerHTML = ''; renderTree(); }
  catch(e) { treeEl.innerHTML = `<div class="error">⚠ JSON parse error: ${esc(e.message)}</div>`; }
}

inp.value = JSON.stringify(DEFAULT_JSON, null, 2);
parsed = DEFAULT_JSON;
renderTree();
inp.addEventListener('input', parseAndRender);
</script>
</body>
</html>
```

### `README.md`

```md
# JSON Tree Explorer

An interactive JSON tree viewer with collapsible nodes, syntax highlighting, and search.

## Features
- Paste any JSON and instantly see a color-coded tree view
- Collapse/expand individual nodes or all at once
- Color coding: strings (green), numbers (blue), booleans (orange), null (red), keys (purple)
- Real-time search highlighting across keys and values
- Copy formatted JSON to clipboard
- Handles deeply nested structures gracefully

## Usage
Paste JSON into the left panel. Use Expand All / Collapse All buttons, or click ▶/▼ next to any node. Type in the search bar to highlight matching keys and values.

```
