{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "use-history",
  "title": "Use History",
  "author": "Zubeyr <https://zubeyr.dev>",
  "description": "An undo/redo hook for any state. Consecutive edits to the same field merge into one step, and Ctrl+Z / Ctrl+Shift+Z hotkeys are opt-in so typing in inputs keeps its native undo.",
  "files": [
    {
      "path": "registry/dashboard/components/use-history/use-history.ts",
      "content": "'use client'\n\nimport { useCallback, useEffect, useState } from 'react'\n\ntype State<T> = { past: T[]; present: T; future: T[]; mergeKey: string | null }\n\ntype SetOptions = { merge?: string }\n\nexport function useHistory<T>(initial: T, { hotkeys = false }: { hotkeys?: boolean } = {}) {\n  const [state, setState] = useState<State<T>>({\n    past: [],\n    present: initial,\n    future: [],\n    mergeKey: null,\n  })\n\n  const set = useCallback((next: T | ((current: T) => T), { merge }: SetOptions = {}) => {\n    setState((s) => {\n      const value = typeof next === 'function' ? (next as (current: T) => T)(s.present) : next\n      const merging = merge !== undefined && merge === s.mergeKey\n      return {\n        past: merging ? s.past : [...s.past, s.present],\n        present: value,\n        future: [],\n        mergeKey: merge ?? null,\n      }\n    })\n  }, [])\n\n  const undo = useCallback(() => {\n    setState((s) =>\n      s.past.length\n        ? {\n            past: s.past.slice(0, -1),\n            present: s.past[s.past.length - 1],\n            future: [s.present, ...s.future],\n            mergeKey: null,\n          }\n        : s,\n    )\n  }, [])\n\n  const redo = useCallback(() => {\n    setState((s) =>\n      s.future.length\n        ? {\n            past: [...s.past, s.present],\n            present: s.future[0],\n            future: s.future.slice(1),\n            mergeKey: null,\n          }\n        : s,\n    )\n  }, [])\n\n  const reset = useCallback(\n    (value?: T) =>\n      setState((s) => ({ past: [], present: value ?? s.present, future: [], mergeKey: null })),\n    [],\n  )\n\n  useEffect(() => {\n    if (!hotkeys) return\n    const onKey = (event: KeyboardEvent) => {\n      if (!(event.metaKey || event.ctrlKey) || event.key.toLowerCase() !== 'z') return\n      const target = event.target as HTMLElement\n      if (target.closest('input, textarea, [contenteditable=\"true\"]')) return\n      event.preventDefault()\n      if (event.shiftKey) redo()\n      else undo()\n    }\n    window.addEventListener('keydown', onKey)\n    return () => window.removeEventListener('keydown', onKey)\n  }, [hotkeys, undo, redo])\n\n  return {\n    value: state.present,\n    set,\n    undo,\n    redo,\n    reset,\n    canUndo: state.past.length > 0,\n    canRedo: state.future.length > 0,\n  }\n}\n",
      "type": "registry:component",
      "target": "components/dashboard/use-history.ts"
    }
  ],
  "meta": {
    "scenario": "dashboard",
    "kind": "components"
  },
  "categories": [
    "dashboard",
    "hooks",
    "state"
  ],
  "type": "registry:component"
}