"use client"; import { useState, type ReactNode } from "react"; import { CopyButton } from "../feedback/CopyButton.tsx"; export function DiffView({ before, after, beforeLabel = "Before", afterLabel = "After", className = "", }: { before: string; after: string; beforeLabel?: string; afterLabel?: string; className?: string; }) { const a = before.split("\n"), b = after.split("\n"); // ponytail: aligned-line comparison, use a diff engine when moved-line matching matters. return (

{beforeLabel}

          {a.map((line, i) => (
            
              
              {line || " "}
              {"\n"}
            
          ))}
        

{afterLabel}

          {b.map((line, i) => (
            
              
              {line || " "}
              {"\n"}
            
          ))}
        
); } function JsonNode({ value, name, depth, ancestors, }: { value: unknown; name: string; depth: number; ancestors: ReadonlySet; }): ReactNode { if (value === null || typeof value !== "object") return (
{name} {typeof value === "string" ? JSON.stringify(value) : String(value)}
); if (ancestors.has(value)) return
{name}: [Circular]
; if (depth > 8) return
{name}: [Depth limit]
; const next = new Set(ancestors); next.add(value); const entries = Object.entries(value); // ponytail: at most 200 siblings per node; paginate before inspecting large payloads. return (
{name}{" "} {Array.isArray(value) ? `[${entries.length}]` : `{${entries.length}}`}
{entries.slice(0, 200).map(([key, item]) => ( ))} {entries.length > 200 && (

Showing 200 of {entries.length} entries.

)}
); } export function DataInspector({ value, label = "Data", className = "", }: { value: unknown; label?: string; className?: string; }) { let serialized = ""; try { serialized = JSON.stringify(value, null, 2) ?? String(value); } catch { /* Circular data remains inspectable; copying is unavailable. */ } return (

{label}

{serialized && ( Copy JSON )}
); } export type TreeNode = { id: string; label: string; children?: readonly TreeNode[]; }; export function TreeView({ nodes, label = "Hierarchy", onSelect, selected, className = "", }: { nodes: readonly TreeNode[]; label?: string; onSelect?: (id: string) => void; selected?: string; className?: string; }) { const render = (items: readonly TreeNode[], depth = 0): ReactNode => ( ); return ( ); } export type Activity = { id: string; actor: string; action: string; time: string; dateTime?: string; type?: string; }; export function ActivityFeed({ items, label = "Activity feed", className = "", }: { items: readonly Activity[]; label?: string; className?: string; }) { const [filter, setFilter] = useState("All"); const types = [ ...new Set(items.map((i) => i.type).filter((t): t is string => !!t)), ]; const shown = items.filter((i) => filter === "All" || i.type === filter); return (

{label}

{types.length > 0 && ( )}
    {shown.map((item) => (
  1. {item.actor} {item.action}
  2. ))}
{!shown.length && (

No activity in this view.

)}
); }