diff --git a/bun.lockb b/bun.lockb index 86a90aed29..c40fe4f6dc 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/react/package.json b/packages/react/package.json index ae5eb892e7..7f55a3d84a 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -191,6 +191,7 @@ "@types/react": "18.3.5", "@types/react-dom": "18.3.0", "@vitejs/plugin-react": "4.3.1", + "@zag-js/stringify-state": "0.65.1", "globby": "14.0.2", "jsdom": "24.1.3", "lucide-react": "0.438.0", diff --git a/packages/react/src/use-debugger.ts b/packages/react/src/use-debugger.ts new file mode 100644 index 0000000000..315aa968da --- /dev/null +++ b/packages/react/src/use-debugger.ts @@ -0,0 +1,63 @@ +import { stringifyState } from '@zag-js/stringify-state' +import { useLayoutEffect } from 'react' + +interface Service { + initialContext: { id: string } + state: Record + subscribe: (callback: () => void) => () => void +} + +export const useDebugger = (service: Service) => { + useLayoutEffect(() => { + const syncPre = () => { + let group = document.getElementById('__zag-debug') + + if (!group) { + group = document.createElement('div') + group.id = '__zag-debug' + Object.assign(group.style, { + position: 'fixed', + top: '0', + right: '0', + zIndex: '9999', + display: 'flex', + flexDirection: 'column', + gap: '4px', + boxSizing: 'border-box', + }) + + document.body.appendChild(group) + } + + const id = service.initialContext.id + let pre = document.getElementById(id) + + if (!pre) { + pre = document.createElement('pre') + pre.id = id + Object.assign(pre.style, { + padding: '4px', + background: 'white', + border: '1px solid black', + borderRadius: '4px', + maxHeight: '300px', + overflow: 'auto', + }) + group.appendChild(pre) + } + + pre.textContent = stringifyState(service.state) + + return () => { + pre.remove() + } + } + + const removePre = syncPre() + const unsubscribe = service.subscribe(syncPre) + return () => { + removePre() + unsubscribe() + } + }, [service]) +}