react-f0rm 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -199,6 +199,57 @@ update(1, 'B'); // overwrite one value, keeping that row's id — no k
199
199
 
200
200
  `replace(values)` is the refetch shape — a server response replaces the whole list — while `update(index, value)` rewrites a single row in place.
201
201
 
202
+ ### `useFieldArrayItem`
203
+
204
+ Per-row subscription for large arrays — the counterpart of TanStack Form's field api that `useFieldArray` alone cannot offer. `useFieldArray` subscribes to the whole branch, so any row's edit re-renders the component holding the array (and, without memoization, every row). `useFieldArrayItem` gives one row — identified by the stable `id` from `fields[i].id` — a subscription of its own:
205
+
206
+ ```jsx
207
+ import {useFieldArray, useFieldArrayItem} from 'react-f0rm';
208
+
209
+ const Row = React.memo(function Row({id}) {
210
+ const item = useFieldArrayItem({name: 'tags', id});
211
+ return (
212
+ <div>
213
+ <input
214
+ value={item.value ?? ''}
215
+ onChange={e => item.setValue(e.target.value)}
216
+ />
217
+ {item.error && <span>{item.error}</span>}
218
+ </div>
219
+ );
220
+ });
221
+
222
+ function Tags() {
223
+ const {fields, append, remove} = useFieldArray({name: 'tags'});
224
+ return (
225
+ <div>
226
+ {fields.map(field => (
227
+ <div key={field.id}>
228
+ <Row id={field.id} />
229
+ <button type="button" onClick={() => remove(field.index)}>
230
+ Remove
231
+ </button>
232
+ </div>
233
+ ))}
234
+ <button type="button" onClick={() => append('')}>
235
+ Add Tag
236
+ </button>
237
+ </div>
238
+ );
239
+ }
240
+ ```
241
+
242
+ Editing row K re-renders only row K, and a whole-array rewrite (`update`, `append`) re-renders only rows whose value actually changed — untouched rows' renders stay at zero. Two requirements make that hold:
243
+
244
+ - a `useFieldArray({name})` must be mounted at the same path — it publishes the id table rows resolve against;
245
+ - the row component must be `React.memo` with stable props (`id`, optionally `form`): everything else comes from the hook, so the array component's own re-render cannot drag the rows along.
246
+
247
+ Rows whose index migrates — `remove`/`move`/`swap`/`insert` reshuffles — re-render by design: the row's path contains the index, exactly like TanStack Form's per-field api. `replace` regenerates every id, so every row remounts. The win is single-row edits staying single-row.
248
+
249
+ The hook returns `{value, setValue, errors, error, name, index, form}` — the `useField`-style shape plus `index` and `name` (the row's current path key, e.g. `["tags",0]`) for building nested fields. Value reads and writes live on the array layer — the same layer every `useFieldArray` operation touches — so `value`, `setValue` and `update`/`append`/… always agree with each other; editing through a leaf-path `useField({name: ['tags', i]})` writes a different layer and does not flow into `item.value`.
250
+
251
+ Without a paired `useFieldArray` the row is inert rather than broken: `index` is `-1`, `value` is `undefined`, and `setValue` is a no-op.
252
+
202
253
  ### `createFormContext`
203
254
 
204
255
  The module-level context serves one form per subtree; nesting two forms (or reusing a component under a different form) makes them fight over it. `createFormContext` builds an isolated bundle of bindings, typed against your values shape:
@@ -220,7 +271,7 @@ function NameField() {
220
271
  }
221
272
  ```
222
273
 
223
- Each call returns `{context, FormProvider, useFormContext, useField, useFieldArray}` bound to a private React context — pass the form via `<ProfileForm.FormProvider form={form}>`, and providers from separate instances never see each other's forms.
274
+ Each call returns `{context, FormProvider, useFormContext, useField, useFieldArray, useFieldArrayItem}` bound to a private React context — pass the form via `<ProfileForm.FormProvider form={form}>`, and providers from separate instances never see each other's forms.
224
275
 
225
276
  The bundle also carries its raw React context, so `<Form>` can provide into it while keeping its full submit machinery — validation, submit handling, focus-on-error — instead of you wiring `<FormProvider>` + `handleSubmit` by hand:
226
277
 
@@ -1,2 +1,2 @@
1
- "use strict";var e=require("react"),t=require("../form-DZK8HgZ7.cjs.js");function n(e){var t=Object.create(null);return e&&Object.keys(e).forEach(function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}}),t.default=e,Object.freeze(t)}var r,o=n(e),a={exports:{}},l={};var s,i,d={};var c=(i||(i=1,"production"===process.env.NODE_ENV?a.exports=function(){if(r)return l;r=1;var t=e,n="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=t.useState,a=t.useEffect,s=t.useLayoutEffect,i=t.useDebugValue;function d(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!n(e,r)}catch(e){return!0}}var c="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),r=o({inst:{value:n,getSnapshot:t}}),l=r[0].inst,c=r[1];return s(function(){l.value=n,l.getSnapshot=t,d(l)&&c({inst:l})},[e,n,t]),a(function(){return d(l)&&c({inst:l}),e(function(){d(l)&&c({inst:l})})},[e]),i(n),n};return l.useSyncExternalStore=void 0!==t.useSyncExternalStore?t.useSyncExternalStore:c,l}():a.exports=(s||(s=1,"production"!==process.env.NODE_ENV&&function(){function t(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!r(e,n)}catch(e){return!0}}"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var n=e,r="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=n.useState,a=n.useEffect,l=n.useLayoutEffect,s=n.useDebugValue,i=!1,c=!1,f="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,d){i||void 0===n.startTransition||(i=!0,console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));var f=d();if(!c){var p=d();r(f,p)||(console.error("The result of getSnapshot should be cached to avoid an infinite loop"),c=!0)}var u=(p=o({inst:{value:f,getSnapshot:d}}))[0].inst,m=p[1];return l(function(){u.value=f,u.getSnapshot=d,t(u)&&m({inst:u})},[e,f,d]),a(function(){return t(u)&&m({inst:u}),e(function(){t(u)&&m({inst:u})})},[e]),s(f),f};d.useSyncExternalStore=void 0!==n.useSyncExternalStore?n.useSyncExternalStore:f,"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())}()),d)),a.exports);function f(n,r,o){return function(t,n){const r=e.useRef(null);null===r.current&&(r.current={hasValue:!1});const o=r.current,a=e.useRef(n);a.current=n;const l=e.useCallback(()=>(o.hasValue||(o.value=a.current(),o.hasValue=!0),o.value),[o]),s=e.useCallback(e=>(o.hasValue=!1,t(()=>{o.hasValue=!1,e()})),[t,o]);return c.useSyncExternalStore(s,l,l)}(e.useCallback(e=>t.on(n,r,e),[n,r]),o)}const p=e.createContext(null);p.Provider;e.createContext(null).Provider;function u({name:t,value:n,depth:r=0}){const[a,l]=e.useState(r<=1),s=void 0===t?null:o.createElement(o.Fragment,null,o.createElement("span",{className:"rf0-dt-key"},String(t)),o.createElement("span",{className:"rf0-dt-punct"},": "));if(null!==n&&"object"==typeof n){const e=Array.isArray(n),t=e?n.map((e,t)=>[t,e]):Object.entries(n),i=e?"[":"{",d=e?"]":"}",c=a?"":`${i}…${d} ${t.length}`;return o.createElement("div",{className:"rf0-dt-row",style:{paddingLeft:12*r}},o.createElement("button",{type:"button",className:"rf0-dt-node-toggle","aria-expanded":a,onClick:()=>l(!a)},o.createElement("span",{className:"rf0-dt-caret"},a?"▾":"▸"),s,o.createElement("span",{className:"rf0-dt-punct"},a?i:c)),a&&o.createElement(o.Fragment,null,t.map(([e,t])=>o.createElement(u,{key:String(e),name:e,value:t,depth:r+1})),o.createElement("span",{className:"rf0-dt-punct",style:{paddingLeft:12*r}},d)))}return o.createElement("span",{className:"rf0-dt-row",style:{paddingLeft:12*r,display:"block"}},s,o.createElement(m,{value:n}))}function m({value:e}){return void 0===e?o.createElement("span",{className:"rf0-dt-null"},"undefined"):null===e?o.createElement("span",{className:"rf0-dt-null"},"null"):"string"==typeof e?o.createElement("span",{className:"rf0-dt-string"},'"',e,'"'):"boolean"==typeof e?o.createElement("span",{className:"rf0-dt-boolean"},String(e)):o.createElement("span",{className:"rf0-dt-number"},String(e))}const b="react-f0rm-devtools-style";!function(){if("undefined"==typeof document)return;if(document.getElementById(b))return;const e=document.createElement("style");e.id=b,e.textContent="\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n",document.head.appendChild(e)}();const g=["values","errors","touched","dirty"];function x(e){if(void 0!==e)return e?"rf0-dt-ok":"rf0-dt-err"}function h(e){if(null===e||"object"!=typeof e)return 1;let t=0;for(const n of Object.values(e))t+=h(n);return t}exports.Devtools=function({form:n,position:r="top-right"}){const a=e.useContext(p),l=n??a;if(!l)throw new Error("<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.");const[s,i]=e.useState(!0),[d,c]=e.useState("values"),m=e.useId().replace(/[^a-zA-Z0-9-]/g,""),b=f(l.emitter,"change",t.getValues.bind(null,l)),y=f(l.emitter,"errors",t.getErrors.bind(null,l)),E=function(e){return f(e.emitter,"touched",t.getTouchedFields.bind(null,e))}(l),v=function(e){return f(e.emitter,"change",t.getDirtyFields.bind(null,e))}(l),w=function(e){return f(e.emitter,"submitting",()=>e.isSubmitting)}(l),k=function(e){return f(e.emitter,"submitCount",()=>e.submitCount)}(l),S=f(l.emitter,"submitSuccessful",()=>l.isSubmitSuccessful);if(!s)return o.createElement("button",{type:"button",className:`rf0-dt-badge rf0-dt-badge--${r}${y.length>0?" rf0-dt-badge--has-errors":""}`,"aria-expanded":!1,"aria-label":`Open react-f0rm devtools (${y.length} errors)`,onClick:()=>i(!0)},"f0",o.createElement("span",{className:"rf0-dt-dot"}));const O={values:h(b),errors:y.length,touched:E.length,dirty:Object.keys(v).length};return o.createElement("section",{className:`rf0-dt rf0-dt--${r}`,"aria-label":"react-f0rm devtools"},o.createElement("header",{className:"rf0-dt-header"},o.createElement("span",{className:"rf0-dt-title"},"react-f0rm"),o.createElement("button",{type:"button",className:"rf0-dt-headerbtn","aria-label":"Collapse devtools",onClick:()=>i(!1)},"–")),o.createElement("div",{className:"rf0-dt-tablist",role:"tablist","aria-label":"Form state",tabIndex:-1,onKeyDown:e=>{const t={ArrowRight:1,ArrowLeft:-1}[e.key];if(!t)return;e.preventDefault();const n=g[(g.indexOf(d)+t+g.length)%g.length];c(n),document.getElementById(`${m}-tab-${n}`)?.focus()}},g.map(e=>o.createElement("button",{key:e,id:`${m}-tab-${e}`,type:"button",role:"tab",className:"rf0-dt-tab"+("errors"===e?" rf0-dt-tab--danger":""),"aria-selected":d===e,"aria-controls":`${m}-panel-${e}`,tabIndex:d===e?0:-1,onClick:()=>c(e)},e,o.createElement("span",{className:"rf0-dt-tab-count"},O[e])))),o.createElement("div",{id:`${m}-panel-${d}`,role:"tabpanel","aria-labelledby":`${m}-tab-${d}`,className:"rf0-dt-panel"},"values"===d&&o.createElement(u,{value:b}),"errors"===d&&(0===y.length?o.createElement("p",{className:"rf0-dt-empty"},"no errors"):y.map(({path:e,type:t,message:n},r)=>o.createElement("div",{key:`${e}:${r}`,className:"rf0-dt-item"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg"},n),o.createElement("span",{className:"rf0-dt-item-tag"},t)))),"touched"===d&&(0===E.length?o.createElement("p",{className:"rf0-dt-empty"},"no touched fields"):E.map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--touched"},o.createElement("span",{className:"rf0-dt-item-path"},e)))),"dirty"===d&&(0===Object.keys(v).length?o.createElement("p",{className:"rf0-dt-empty"},"no dirty fields"):Object.keys(v).map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--dirty"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg rf0-dt-item-msg--ok"},"changed"))))),o.createElement("p",{className:"rf0-dt-status","aria-live":"polite"},o.createElement("span",{className:w?"rf0-dt-on":void 0},"submitting ",o.createElement("b",null,String(w))),o.createElement("span",null,"submits ",o.createElement("b",null,k)),o.createElement("span",{className:x(S)},"ok"," ",o.createElement("b",null,void 0===S?"–":String(S)))),o.createElement("div",{className:"rf0-dt-actions"},o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.reset(l,l.initialValues)},"Reset"),o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.trigger(l)},"Validate")))};
1
+ "use strict";var e=require("react"),t=require("../form-CpN0eh8a.cjs.js");function n(e){var t=Object.create(null);return e&&Object.keys(e).forEach(function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}}),t.default=e,Object.freeze(t)}var r,o=n(e),a={exports:{}},l={};var s,i,d={};var c=(i||(i=1,"production"===process.env.NODE_ENV?a.exports=function(){if(r)return l;r=1;var t=e,n="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=t.useState,a=t.useEffect,s=t.useLayoutEffect,i=t.useDebugValue;function d(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!n(e,r)}catch(e){return!0}}var c="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),r=o({inst:{value:n,getSnapshot:t}}),l=r[0].inst,c=r[1];return s(function(){l.value=n,l.getSnapshot=t,d(l)&&c({inst:l})},[e,n,t]),a(function(){return d(l)&&c({inst:l}),e(function(){d(l)&&c({inst:l})})},[e]),i(n),n};return l.useSyncExternalStore=void 0!==t.useSyncExternalStore?t.useSyncExternalStore:c,l}():a.exports=(s||(s=1,"production"!==process.env.NODE_ENV&&function(){function t(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!r(e,n)}catch(e){return!0}}"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var n=e,r="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=n.useState,a=n.useEffect,l=n.useLayoutEffect,s=n.useDebugValue,i=!1,c=!1,f="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,d){i||void 0===n.startTransition||(i=!0,console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));var f=d();if(!c){var p=d();r(f,p)||(console.error("The result of getSnapshot should be cached to avoid an infinite loop"),c=!0)}var u=(p=o({inst:{value:f,getSnapshot:d}}))[0].inst,m=p[1];return l(function(){u.value=f,u.getSnapshot=d,t(u)&&m({inst:u})},[e,f,d]),a(function(){return t(u)&&m({inst:u}),e(function(){t(u)&&m({inst:u})})},[e]),s(f),f};d.useSyncExternalStore=void 0!==n.useSyncExternalStore?n.useSyncExternalStore:f,"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())}()),d)),a.exports);function f(n,r,o){return function(t,n){const r=e.useRef(null);null===r.current&&(r.current={hasValue:!1});const o=r.current,a=e.useRef(n);a.current=n;const l=e.useCallback(()=>(o.hasValue||(o.value=a.current(),o.hasValue=!0),o.value),[o]),s=e.useCallback(e=>(o.hasValue=!1,t(()=>{o.hasValue=!1,e()})),[t,o]);return c.useSyncExternalStore(s,l,l)}(e.useCallback(e=>t.on(n,r,e),[n,r]),o)}const p=e.createContext(null);p.Provider;e.createContext(null).Provider;function u({name:t,value:n,depth:r=0}){const[a,l]=e.useState(r<=1),s=void 0===t?null:o.createElement(o.Fragment,null,o.createElement("span",{className:"rf0-dt-key"},String(t)),o.createElement("span",{className:"rf0-dt-punct"},": "));if(null!==n&&"object"==typeof n){const e=Array.isArray(n),t=e?n.map((e,t)=>[t,e]):Object.entries(n),i=e?"[":"{",d=e?"]":"}",c=a?"":`${i}…${d} ${t.length}`;return o.createElement("div",{className:"rf0-dt-row",style:{paddingLeft:12*r}},o.createElement("button",{type:"button",className:"rf0-dt-node-toggle","aria-expanded":a,onClick:()=>l(!a)},o.createElement("span",{className:"rf0-dt-caret"},a?"▾":"▸"),s,o.createElement("span",{className:"rf0-dt-punct"},a?i:c)),a&&o.createElement(o.Fragment,null,t.map(([e,t])=>o.createElement(u,{key:String(e),name:e,value:t,depth:r+1})),o.createElement("span",{className:"rf0-dt-punct",style:{paddingLeft:12*r}},d)))}return o.createElement("span",{className:"rf0-dt-row",style:{paddingLeft:12*r,display:"block"}},s,o.createElement(m,{value:n}))}function m({value:e}){return void 0===e?o.createElement("span",{className:"rf0-dt-null"},"undefined"):null===e?o.createElement("span",{className:"rf0-dt-null"},"null"):"string"==typeof e?o.createElement("span",{className:"rf0-dt-string"},'"',e,'"'):"boolean"==typeof e?o.createElement("span",{className:"rf0-dt-boolean"},String(e)):o.createElement("span",{className:"rf0-dt-number"},String(e))}const b="react-f0rm-devtools-style";!function(){if("undefined"==typeof document)return;if(document.getElementById(b))return;const e=document.createElement("style");e.id=b,e.textContent="\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n",document.head.appendChild(e)}();const g=["values","errors","touched","dirty"];function x(e){if(void 0!==e)return e?"rf0-dt-ok":"rf0-dt-err"}function h(e){if(null===e||"object"!=typeof e)return 1;let t=0;for(const n of Object.values(e))t+=h(n);return t}exports.Devtools=function({form:n,position:r="top-right"}){const a=e.useContext(p),l=n??a;if(!l)throw new Error("<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.");const[s,i]=e.useState(!0),[d,c]=e.useState("values"),m=e.useId().replace(/[^a-zA-Z0-9-]/g,""),b=f(l.emitter,"change",t.getValues.bind(null,l)),y=f(l.emitter,"errors",t.getErrors.bind(null,l)),E=function(e){return f(e.emitter,"touched",t.getTouchedFields.bind(null,e))}(l),v=function(e){return f(e.emitter,"change",t.getDirtyFields.bind(null,e))}(l),w=function(e){return f(e.emitter,"submitting",()=>e.isSubmitting)}(l),k=function(e){return f(e.emitter,"submitCount",()=>e.submitCount)}(l),S=f(l.emitter,"submitSuccessful",()=>l.isSubmitSuccessful);if(!s)return o.createElement("button",{type:"button",className:`rf0-dt-badge rf0-dt-badge--${r}${y.length>0?" rf0-dt-badge--has-errors":""}`,"aria-expanded":!1,"aria-label":`Open react-f0rm devtools (${y.length} errors)`,onClick:()=>i(!0)},"f0",o.createElement("span",{className:"rf0-dt-dot"}));const O={values:h(b),errors:y.length,touched:E.length,dirty:Object.keys(v).length};return o.createElement("section",{className:`rf0-dt rf0-dt--${r}`,"aria-label":"react-f0rm devtools"},o.createElement("header",{className:"rf0-dt-header"},o.createElement("span",{className:"rf0-dt-title"},"react-f0rm"),o.createElement("button",{type:"button",className:"rf0-dt-headerbtn","aria-label":"Collapse devtools",onClick:()=>i(!1)},"–")),o.createElement("div",{className:"rf0-dt-tablist",role:"tablist","aria-label":"Form state",tabIndex:-1,onKeyDown:e=>{const t={ArrowRight:1,ArrowLeft:-1}[e.key];if(!t)return;e.preventDefault();const n=g[(g.indexOf(d)+t+g.length)%g.length];c(n),document.getElementById(`${m}-tab-${n}`)?.focus()}},g.map(e=>o.createElement("button",{key:e,id:`${m}-tab-${e}`,type:"button",role:"tab",className:"rf0-dt-tab"+("errors"===e?" rf0-dt-tab--danger":""),"aria-selected":d===e,"aria-controls":`${m}-panel-${e}`,tabIndex:d===e?0:-1,onClick:()=>c(e)},e,o.createElement("span",{className:"rf0-dt-tab-count"},O[e])))),o.createElement("div",{id:`${m}-panel-${d}`,role:"tabpanel","aria-labelledby":`${m}-tab-${d}`,className:"rf0-dt-panel"},"values"===d&&o.createElement(u,{value:b}),"errors"===d&&(0===y.length?o.createElement("p",{className:"rf0-dt-empty"},"no errors"):y.map(({path:e,type:t,message:n},r)=>o.createElement("div",{key:`${e}:${r}`,className:"rf0-dt-item"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg"},n),o.createElement("span",{className:"rf0-dt-item-tag"},t)))),"touched"===d&&(0===E.length?o.createElement("p",{className:"rf0-dt-empty"},"no touched fields"):E.map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--touched"},o.createElement("span",{className:"rf0-dt-item-path"},e)))),"dirty"===d&&(0===Object.keys(v).length?o.createElement("p",{className:"rf0-dt-empty"},"no dirty fields"):Object.keys(v).map(e=>o.createElement("div",{key:e,className:"rf0-dt-item rf0-dt-item--dirty"},o.createElement("span",{className:"rf0-dt-item-path"},e),o.createElement("span",{className:"rf0-dt-item-msg rf0-dt-item-msg--ok"},"changed"))))),o.createElement("p",{className:"rf0-dt-status","aria-live":"polite"},o.createElement("span",{className:w?"rf0-dt-on":void 0},"submitting ",o.createElement("b",null,String(w))),o.createElement("span",null,"submits ",o.createElement("b",null,k)),o.createElement("span",{className:x(S)},"ok"," ",o.createElement("b",null,void 0===S?"–":String(S)))),o.createElement("div",{className:"rf0-dt-actions"},o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.reset(l,l.initialValues)},"Reset"),o.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>t.trigger(l)},"Validate")))};
2
2
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/shim/index.js","../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.js","../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js","../../src/hooks/form.tsx","../../src/context.ts","../../src/devtools/JsonTree.tsx","../../src/devtools/styles.ts","../../src/devtools/Devtools.tsx"],"sourcesContent":["'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('../cjs/use-sync-external-store-shim.production.js');\n} else {\n module.exports = require('../cjs/use-sync-external-store-shim.development.js');\n}\n","/**\n * @license React\n * use-sync-external-store-shim.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar React = require(\"react\");\nfunction is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n}\nvar objectIs = \"function\" === typeof Object.is ? Object.is : is,\n useState = React.useState,\n useEffect = React.useEffect,\n useLayoutEffect = React.useLayoutEffect,\n useDebugValue = React.useDebugValue;\nfunction useSyncExternalStore$2(subscribe, getSnapshot) {\n var value = getSnapshot(),\n _useState = useState({ inst: { value: value, getSnapshot: getSnapshot } }),\n inst = _useState[0].inst,\n forceUpdate = _useState[1];\n useLayoutEffect(\n function () {\n inst.value = value;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n },\n [subscribe, value, getSnapshot]\n );\n useEffect(\n function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n });\n },\n [subscribe]\n );\n useDebugValue(value);\n return value;\n}\nfunction checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n}\nfunction useSyncExternalStore$1(subscribe, getSnapshot) {\n return getSnapshot();\n}\nvar shim =\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ? useSyncExternalStore$1\n : useSyncExternalStore$2;\nexports.useSyncExternalStore =\n void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;\n","/**\n * @license React\n * use-sync-external-store-shim.development.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\n\"production\" !== process.env.NODE_ENV &&\n (function () {\n function is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n }\n function useSyncExternalStore$2(subscribe, getSnapshot) {\n didWarnOld18Alpha ||\n void 0 === React.startTransition ||\n ((didWarnOld18Alpha = !0),\n console.error(\n \"You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release.\"\n ));\n var value = getSnapshot();\n if (!didWarnUncachedGetSnapshot) {\n var cachedValue = getSnapshot();\n objectIs(value, cachedValue) ||\n (console.error(\n \"The result of getSnapshot should be cached to avoid an infinite loop\"\n ),\n (didWarnUncachedGetSnapshot = !0));\n }\n cachedValue = useState({\n inst: { value: value, getSnapshot: getSnapshot }\n });\n var inst = cachedValue[0].inst,\n forceUpdate = cachedValue[1];\n useLayoutEffect(\n function () {\n inst.value = value;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n },\n [subscribe, value, getSnapshot]\n );\n useEffect(\n function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n });\n },\n [subscribe]\n );\n useDebugValue(value);\n return value;\n }\n function checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n }\n function useSyncExternalStore$1(subscribe, getSnapshot) {\n return getSnapshot();\n }\n \"undefined\" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&\n \"function\" ===\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart &&\n __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());\n var React = require(\"react\"),\n objectIs = \"function\" === typeof Object.is ? Object.is : is,\n useState = React.useState,\n useEffect = React.useEffect,\n useLayoutEffect = React.useLayoutEffect,\n useDebugValue = React.useDebugValue,\n didWarnOld18Alpha = !1,\n didWarnUncachedGetSnapshot = !1,\n shim =\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ? useSyncExternalStore$1\n : useSyncExternalStore$2;\n exports.useSyncExternalStore =\n void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;\n \"undefined\" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&\n \"function\" ===\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&\n __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());\n })();\n","import {useState, useEffect, useCallback, useRef} from 'react';\nimport {useSyncExternalStore} from 'use-sync-external-store/shim';\nimport {on} from '@for-fun/event-emitter';\nimport type {EventEmitter} from '@for-fun/event-emitter';\nimport {onKeyEvent, onPathEvent} from '../subscribe';\nimport createForm, {\n getErrorByPath,\n getFieldErrorsByPath,\n getValueByPath,\n hasTouchedByPath,\n hasErrors,\n isDirty,\n getDirtyFields,\n getTouchedFields,\n setInitialValues\n} from '../form';\nimport type {FieldError, Form, Options, Name} from '../form';\nimport type {FieldPath, PathValueOf} from '../types';\nimport createPath from '../path';\nimport type {Path} from '../path';\nimport {isEqual} from '../util';\n\n/**\n * Create a form instance bound to this component.\n *\n * Beyond {@link Options}, the optional `values` object enables controlled\n * usage: when it genuinely changes it is re-synced into the form with\n * setInitialValues semantics -- uncommitted user edits are discarded\n * (master-detail semantics: selecting another record replaces the draft),\n * while touched flags and errors survive. Change detection is\n * reference-first with a structural fallback, so re-renders that pass an\n * inline literal with equal content never re-sync -- the user's\n * in-progress typing is never clobbered.\n */\nexport default function useForm<T extends Record<string, any> = any>(\n options?: Options<T> & {values?: T}\n): Form<T> {\n // Lazy initialization: createForm runs once per mount and the returned\n // instance is stable across re-renders (and StrictMode double renders),\n // without writing to refs during render. A provided `values` object is\n // seeded synchronously here (createForm does the same for initialValues)\n // so the first paint and SSR already reflect the controlled values.\n const [form] = useState(() => {\n const created = createForm<T>(options);\n if (options && options.values !== undefined) {\n setInitialValues(created, options.values);\n }\n return created;\n });\n const initialValues = options && options.initialValues;\n const values = options && options.values;\n\n // Track which initialValues source object the form was last seeded from.\n // Inline options create a fresh object every render, and re-seeding\n // clears the values Map (setInitialValues semantics), which would revert\n // every committed edit right after each re-render -- on the client and\n // after hydration alike. Memoized callers are covered by the reference\n // check; inline literals by the structural one, so only genuinely new\n // content re-seeds.\n const seededRef = useRef<{done: boolean; source: any} | null>(null);\n if (seededRef.current === null)\n seededRef.current = {done: false, source: undefined};\n\n useEffect(() => {\n const seeded = seededRef.current!;\n if (\n seeded.done &&\n (seeded.source === initialValues || isEqual(seeded.source, initialValues))\n ) {\n return;\n }\n seeded.done = true;\n seeded.source = initialValues;\n setInitialValues(form, initialValues);\n }, [form, initialValues]);\n\n // Controlled values: re-sync only when the incoming object genuinely\n // differs from what the form was last seeded from. The reference check\n // is the fast path (memoized callers); inline literals get a fresh\n // object identity every render, so without the structural comparison\n // each re-render would clear the values Map (setInitialValues\n // semantics) and revert the user's uncommitted edits -- same hazard the\n // initialValues seed guard above protects against. Master-detail\n // semantics still apply whenever the content actually changed.\n const controlledRef = useRef<{done: boolean; source: any} | null>(null);\n if (controlledRef.current === null) {\n controlledRef.current = {done: false, source: undefined};\n }\n\n useEffect(() => {\n if (values === undefined) return;\n const seeded = controlledRef.current!;\n if (\n seeded.done &&\n (seeded.source === values || isEqual(seeded.source, values))\n ) {\n return;\n }\n seeded.done = true;\n seeded.source = values;\n setInitialValues(form, values);\n }, [form, values]);\n\n return form;\n}\n\n/** Per-hook snapshot cache for {@link useWatch}. */\ninterface WatchCache<T> {\n hasValue: boolean;\n value?: T;\n}\n\n/**\n * Shared core of {@link useWatch} and the path-scoped hooks: a\n * useSyncExternalStore binding over a custom event subscription.\n * `subscribeFactory` receives the invalidate callback (drop the snapshot\n * cache, then notify React) and returns its unsubscribe function, so the\n * core stays identical whether the subscription is global or scoped to\n * one path.\n */\nfunction useWatchCore<T>(\n subscribeFactory: (invalidate: () => void) => () => void,\n getter: () => T\n): T {\n // useSyncExternalStore requires getSnapshot to return the same reference\n // until the store actually changed, otherwise React warns and loops.\n // Cache the snapshot per hook instance and recompute it only on the first\n // read and after the watched event fired.\n const cacheRef = useRef<WatchCache<T> | null>(null);\n if (cacheRef.current === null) cacheRef.current = {hasValue: false};\n const cache = cacheRef.current;\n\n // Hold the latest getter in a ref so getSnapshot keeps a stable identity\n // (callers pass a freshly bound function on every render) while still\n // recomputing with the most recent getter when the cache is invalid.\n const getterRef = useRef(getter);\n getterRef.current = getter;\n\n const getSnapshot = useCallback(() => {\n if (!cache.hasValue) {\n cache.value = getterRef.current();\n cache.hasValue = true;\n }\n return cache.value as T;\n }, [cache]);\n\n const subscribe = useCallback(\n (notify: () => void) => {\n // The form may have changed between render and this subscription, and\n // those events were missed: drop the cache. React's consistency check\n // right after subscribing recomputes and re-renders only when the\n // fresh value differs from the committed snapshot.\n cache.hasValue = false;\n const invalidate = () => {\n cache.hasValue = false;\n notify();\n };\n return subscribeFactory(invalidate);\n },\n [subscribeFactory, cache]\n );\n\n // Form state lives entirely in synchronously readable Map/Set structures\n // seeded from initialValues/values during the lazy useState initializer,\n // so the server snapshot is computed exactly like the client's first\n // render -- pass getSnapshot itself as getServerSnapshot and hydration\n // matches.\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Subscribe to a form event and keep the component's snapshot of `getter()`\n * in sync with the form state.\n *\n * Built on useSyncExternalStore, so snapshots taken while React renders are\n * guaranteed consistent (no tearing under concurrent rendering) and changes\n * emitted before the subscription effect runs are still picked up.\n */\nexport function useWatch<T>(\n emitter: EventEmitter,\n event: string,\n getter: () => T\n): T {\n const subscribeFactory = useCallback(\n (invalidate: () => void) => on(emitter, event, invalidate),\n [emitter, event]\n );\n return useWatchCore(subscribeFactory, getter);\n}\n\n/**\n * Get field value state\n */\nexport function useValue<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): PathValueOf<T, P> {\n return useValueByPath(form, createPath(name));\n}\n\n/**\n * Get field value state by path\n */\nexport function useValueByPath(form: Form, path: Path): any {\n const {emitter} = form;\n const {key} = path;\n // 'leaf' scope: a leaf read depends only on its own key and its\n // ancestors' (getValueByPath fallback chain), so writes elsewhere --\n // siblings, descendants, string-prefix lookalikes ('[\"a\",\"bX\"]') -- never\n // invalidate the snapshot. Payload-less broadcasts (reset, removeField,\n // setInitialValues) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) =>\n onPathEvent(emitter, 'change', path, 'leaf', invalidate),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- deps are `key` on purpose: useValue creates a fresh Path per render, so the object must stay out of the deps while the key string pins the subscription\n [emitter, key]\n );\n return useWatchCore(subscribeFactory, getValueByPath.bind(null, form, path));\n}\n\n/**\n * Get field touched state\n */\nexport function useTouched<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): boolean {\n return useTouchedByPath(form, createPath(name));\n}\n\n/**\n * Get field touched state by path\n */\nexport function useTouchedByPath(form: Form, path: Path): boolean {\n const {emitter} = form;\n const {key} = path;\n // Touched is stored per exact key, so only this field's own setTouched\n // (now emitted with its path) matters; payload-less broadcasts (reset,\n // removeField) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'touched', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(\n subscribeFactory,\n hasTouchedByPath.bind(null, form, path)\n );\n}\n\n/**\n * Get field error message state\n * @return current error's message string (display text), or undefined\n */\nexport function useError<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): string | undefined {\n return useErrorByPath(form, createPath(name))?.message;\n}\n\n/**\n * Get field error state by path\n * @return current FieldError object ({type, message}), or undefined\n */\nexport function useErrorByPath(form: Form, path: Path): FieldError | undefined {\n const {emitter} = form;\n const {key} = path;\n // Errors are stored per exact key, so only writes to this field's error\n // (setErrorByPath now emits with its path) matter; payload-less\n // broadcasts (clearErrors, reset, removeField) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'errors', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(subscribeFactory, getErrorByPath.bind(null, form, path));\n}\n\n/**\n * Get all field errors\n * @return every error registered for the field (insertion order); an empty\n * array when the field has none\n */\nexport function useFieldErrors<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): FieldError[] {\n return useFieldErrorsByPath(form, createPath(name));\n}\n\n/**\n * Get all field errors by path\n * @return every error registered for the field (insertion order); an empty\n * array when the field has none\n */\nexport function useFieldErrorsByPath(form: Form, path: Path): FieldError[] {\n const {emitter} = form;\n const {key} = path;\n // Same exact-key subscription and snapshot rules as useErrorByPath: the\n // getter returns the shared empty constant when clean and the stored\n // array by reference otherwise, so the useSyncExternalStore snapshot is\n // reference-stable between unrelated events.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'errors', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(\n subscribeFactory,\n getFieldErrorsByPath.bind(null, form, path)\n );\n}\n\nexport function useIsDirty(form: Form): boolean {\n // Dirty state is driven by value changes, not touch state: subscribe to\n // 'change' so typing flips this immediately, even before a blur.\n return useWatch(form.emitter, 'change', isDirty.bind(null, form));\n}\n\n/**\n * Get dirty fields state -- object mapping each dirty field's user-facing\n * dotted path ('a.b', 'a.0.c') to true; recalculated after 'change' events\n */\nexport function useDirtyFields(form: Form): Record<string, boolean> {\n return useWatch(form.emitter, 'change', getDirtyFields.bind(null, form));\n}\n\n/**\n * Get touched fields state -- array of touched fields' user-facing dotted\n * paths ('a.b', 'a.0.c'); recalculated after 'touched' events\n */\nexport function useTouchedFields(form: Form): string[] {\n return useWatch(form.emitter, 'touched', getTouchedFields.bind(null, form));\n}\n\nexport function useHasErrors(form: Form): boolean {\n return useWatch(form.emitter, 'errors', hasErrors.bind(null, form));\n}\n\nexport function useIsSubmitting(form: Form): boolean {\n return useWatch(form.emitter, 'submitting', () => form.isSubmitting);\n}\n\n/**\n * Get whether the form accepts a submit right now:\n * `!isSubmitting && !hasErrors`. This is the single flag a submit\n * button's `disabled` prop wants — it is `false` for the whole async\n * `onSubmit` span (not just the validation pass) and whenever any field\n * holds an error (client validation or server backfill), replacing the\n * hand-rolled `useHasErrors(form) || useIsSubmitting(form)` pair.\n * Deliberately no dirty or validating semantics: an untouched-but-clean\n * form can submit.\n */\nexport function useCanSubmit(form: Form): boolean {\n const {emitter} = form;\n // canSubmit folds two events into one boolean: error writes\n // ('errors') and submit-state flips ('submitting'). useWatch subscribes\n // to a single event, so subscribe to both through useWatchCore — the\n // snapshot recomputes on either wake and re-renders only when the\n // boolean itself flips, so unrelated single-field error churn costs no\n // extra render (the same granularity useHasErrors already has).\n const subscribeFactory = useCallback(\n (invalidate: () => void) => {\n const offErrors = on(emitter, 'errors', invalidate);\n const offSubmitting = on(emitter, 'submitting', invalidate);\n return () => {\n offErrors();\n offSubmitting();\n };\n },\n [emitter]\n );\n return useWatchCore(\n subscribeFactory,\n () => !form.isSubmitting && !hasErrors(form)\n );\n}\n\nexport function useSubmitCount(form: Form): number {\n return useWatch(form.emitter, 'submitCount', () => form.submitCount);\n}\n","import {createContext, createElement, useContext, type ReactNode} from 'react';\nimport {\n useFieldCore,\n type UseFieldOptions,\n type UseFieldResult\n} from './hooks/field';\nimport {useFieldArrayCore, type UseFieldArrayResult} from './hooks/fieldArray';\nimport type {Form} from './form';\nimport type {Name} from './path';\nimport type {FieldPath} from './types';\n\nexport const FormContext = createContext<any>(null);\n\nexport const FormProvider = FormContext.Provider;\n\n/**\n * Read the form from the module-level {@link FormContext}. Pass the values\n * shape — `useFormContext<Values>()` — to get a fully typed `Form<Values>`\n * headless API; the `any` default keeps untyped call sites compiling.\n *\n * For multiple forms in one subtree use {@link createFormContext} instead.\n *\n * @throws when no `<FormProvider>` is mounted above the call site.\n */\nexport function useFormContext<T extends Record<string, any> = any>(): Form<T> {\n const form = useContext(FormContext);\n if (!form) throw new Error('no form provided');\n return form;\n}\n\n/**\n * Create an isolated bundle of form-context bindings: its own React context\n * plus `useField` / `useFieldArray` / `useFormContext` hooks that resolve\n * their form from it.\n *\n * Why: the module-level {@link FormContext} works fine for a single form per\n * subtree, but nesting two forms (or reusing a component inside a different\n * form) makes them fight over one context. Calling this factory once per app\n * area — `const Ctx = createFormContext<Values>()` — fixes the value shape\n * (`Ctx.useField({name: 'user.name'})` gets its `name` constrained by\n * `FieldPath<Values>` and its `value` typed accordingly), so call sites stop\n * hand-writing generics, and each instance's Provider scopes a strictly\n * separate form. The bundle also carries its raw React context\n * (`Ctx.context`) so `<Form context={Ctx.context}>` can provide into it.\n */\nexport function createFormContext<TValues extends Record<string, any> = any>() {\n const Context = createContext<Form<TValues> | null>(null);\n\n // A `form`-prop wrapper instead of exposing Context.Provider directly:\n // callers shouldn't have to know about the raw `value` prop shape.\n function FormProvider({\n form,\n children\n }: {\n form: Form<TValues>;\n children: ReactNode;\n }): ReactNode {\n return createElement(Context.Provider, {value: form}, children);\n }\n\n function useFormContext(): Form<TValues> {\n const form = useContext(Context);\n if (!form) throw new Error('no form provided');\n return form;\n }\n\n function useField<TPath extends FieldPath<TValues> | Name = Name>(\n // The bare `{name: TPath}` member keeps `name` a direct inference site\n // for TPath instead of routing it through the mapped Omit type.\n // `form` is omitted on purpose — the form always comes from this\n // factory's own Context.\n options: {name: TPath} & Omit<UseFieldOptions<TValues, TPath>, 'form'>\n ): UseFieldResult<TValues, TPath> {\n return useFieldCore(options as UseFieldOptions<TValues, TPath>, Context);\n }\n\n function useFieldArray(options: {\n name: FieldPath<TValues> | Name;\n }): UseFieldArrayResult {\n return useFieldArrayCore(options as {name: Name}, Context);\n }\n\n // The raw React context, for `<Form context={...}>`: the component keeps\n // its submit machinery while providing into this instance's private\n // context, so the bound hooks above resolve the form it manages.\n return {\n context: Context,\n FormProvider,\n useFormContext,\n useField,\n useFieldArray\n };\n}\n\nexport const CheckboxGroupContext = createContext<any>(null);\n\nexport const CheckboxGroupProvider = CheckboxGroupContext.Provider;\n\nexport function useCheckboxGroupContext(): any {\n const group = useContext(CheckboxGroupContext);\n if (!group) throw new Error('no group provided');\n return group;\n}\n","import * as React from 'react';\nimport {useState} from 'react';\nimport type {ReactNode} from 'react';\n\n/** Nodes deeper than this start collapsed. */\nconst DEFAULT_OPEN_DEPTH = 1;\n\n/**\n * Read-only inspection: the tree never mutates form state, so structural\n * sharing of the inspected value is safe and re-renders stay cheap.\n */\ninterface JsonNodeProps {\n /** Property name (or array index) rendering before the value. */\n name?: string | number;\n /** Value to render. */\n value: unknown;\n /** Current nesting depth (root is 0). */\n depth?: number;\n}\n\n/**\n * One line of the tree: either a collapsible container row\n * (`▸ key: {`) or a leaf (`key: value`).\n */\nfunction JsonNode({name, value, depth = 0}: JsonNodeProps) {\n const [open, setOpen] = useState(depth <= DEFAULT_OPEN_DEPTH);\n\n const label =\n name === undefined ? null : (\n <>\n <span className=\"rf0-dt-key\">{String(name)}</span>\n <span className=\"rf0-dt-punct\">: </span>\n </>\n );\n\n if (value !== null && typeof value === 'object') {\n const isArray = Array.isArray(value);\n const entries: Array<[string | number, unknown]> = isArray\n ? (value as unknown[]).map((v, i) => [i, v])\n : Object.entries(value as Record<string, unknown>);\n const openBracket = isArray ? '[' : '{';\n const closeBracket = isArray ? ']' : '}';\n const summary = open\n ? ''\n : `${openBracket}…${closeBracket} ${entries.length}`;\n\n return (\n <div className=\"rf0-dt-row\" style={{paddingLeft: depth * 12}}>\n <button\n type=\"button\"\n className=\"rf0-dt-node-toggle\"\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n <span className=\"rf0-dt-caret\">{open ? '▾' : '▸'}</span>\n {label}\n <span className=\"rf0-dt-punct\">{open ? openBracket : summary}</span>\n </button>\n {open && (\n <>\n {entries.map(([k, v]) => (\n <JsonNode key={String(k)} name={k} value={v} depth={depth + 1} />\n ))}\n <span className=\"rf0-dt-punct\" style={{paddingLeft: depth * 12}}>\n {closeBracket}\n </span>\n </>\n )}\n </div>\n );\n }\n\n return (\n <span\n className=\"rf0-dt-row\"\n style={{paddingLeft: depth * 12, display: 'block'}}\n >\n {label}\n <Primitive value={value} />\n </span>\n );\n}\n\n/** Render a primitive leaf with terminal-style type coloring. */\nfunction Primitive({value}: {value: unknown}): ReactNode {\n if (value === undefined)\n return <span className=\"rf0-dt-null\">undefined</span>;\n if (value === null) return <span className=\"rf0-dt-null\">null</span>;\n if (typeof value === 'string')\n return <span className=\"rf0-dt-string\">&quot;{value}&quot;</span>;\n if (typeof value === 'boolean')\n return <span className=\"rf0-dt-boolean\">{String(value)}</span>;\n return <span className=\"rf0-dt-number\">{String(value)}</span>;\n}\n\nexport default JsonNode;\n","/**\n * Stylesheet for the Devtools panel.\n *\n * Zero runtime dependencies by design: a single CSS string injected once\n * into <head> (idempotent across module reloads and multiple bundles).\n *\n * Aesthetic: instrument panel / terminal — near-black layers, monospace\n * stack, dense rows, hairline borders. Semantic colors only: error red,\n * success green, neutral gray, with one dim amber accent for the active\n * tab indicator and the collapsed badge.\n */\n\nconst CSS = `\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n`;\n\nconst STYLE_ID = 'react-f0rm-devtools-style';\n\n/**\n * Inject the panel stylesheet into <head>. Idempotent: repeated calls\n * (module reloads, HMR, multiple Devtools mounts) never duplicate the\n * <style> element. No-ops outside a DOM environment (SSR).\n */\nexport function injectDevtoolsStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = CSS;\n document.head.appendChild(style);\n}\n\ninjectDevtoolsStyles();\n","import * as React from 'react';\nimport {useContext, useId, useState} from 'react';\nimport type {KeyboardEvent} from 'react';\nimport {FormContext} from '../context';\nimport {getErrors, getValues, reset, trigger} from '../form';\nimport type {FieldErrorEntry, Form} from '../form';\nimport {\n useDirtyFields,\n useIsSubmitting,\n useSubmitCount,\n useTouchedFields,\n useWatch\n} from '../hooks/form';\nimport JsonTree from './JsonTree';\nimport './styles';\n\n/** Corner the panel docks to. */\nexport type DevtoolsPosition =\n 'top-right' | 'bottom-right' | 'top-left' | 'bottom-left';\n\n/** Props for {@link Devtools}. */\nexport interface DevtoolsProps<T extends Record<string, any> = any> {\n /**\n * Form instance to inspect. When omitted, the panel reads the closest\n * `<Form>` / FormProvider ancestor and throws if there is none.\n */\n form?: Form<T>;\n /** Corner to dock the panel in. Defaults to `'top-right'`. */\n position?: DevtoolsPosition;\n}\n\ntype TabId = 'values' | 'errors' | 'touched' | 'dirty';\n\nconst TABS: TabId[] = ['values', 'errors', 'touched', 'dirty'];\n\n/** Status chip class for the submit-successful indicator. */\nfunction submitStatusClass(\n isSubmitSuccessful: boolean | undefined\n): string | undefined {\n if (isSubmitSuccessful === undefined) return undefined;\n return isSubmitSuccessful ? 'rf0-dt-ok' : 'rf0-dt-err';\n}\n\n/** Count primitive leaves of an inspected value tree. */\nfunction countLeaves(value: unknown): number {\n if (value === null || typeof value !== 'object') return 1;\n let count = 0;\n for (const v of Object.values(value as Record<string, unknown>)) {\n count += countLeaves(v);\n }\n return count;\n}\n\n/**\n * Live form inspector — a floating instrument panel for development.\n *\n * Renders four tabs (values / errors / touched / dirty), a submit status\n * strip (isSubmitting, submitCount, isSubmitSuccessful) and two actions:\n * Reset and Validate (full `trigger`). All state is read through the\n * library's own watch hooks, so the panel updates in real time without\n * participating in validation or submit flows. Docked at a corner,\n * collapsible to a small badge; fully keyboard operable.\n *\n * Ship it from the dedicated `react-f0rm/devtools` entry — it is never\n * re-exported by the main entry, so production bundles stay untouched.\n */\nexport default function Devtools<T extends Record<string, any> = any>({\n form,\n position = 'top-right'\n}: DevtoolsProps<T>) {\n const contextForm = useContext(FormContext);\n const f = form ?? contextForm;\n if (!f) {\n throw new Error(\n '<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.'\n );\n }\n\n const [open, setOpen] = useState(true);\n const [tab, setTab] = useState<TabId>('values');\n const idPrefix = useId().replace(/[^a-zA-Z0-9-]/g, '');\n\n // Live snapshots, straight through the public watch surface.\n const values = useWatch(f.emitter, 'change', getValues.bind(null, f));\n const errors = useWatch<FieldErrorEntry[]>(\n f.emitter,\n 'errors',\n getErrors.bind(null, f)\n );\n const touched = useTouchedFields(f);\n const dirty = useDirtyFields(f);\n const isSubmitting = useIsSubmitting(f);\n const submitCount = useSubmitCount(f);\n const isSubmitSuccessful = useWatch(\n f.emitter,\n 'submitSuccessful',\n () => f.isSubmitSuccessful\n );\n\n if (!open) {\n return (\n <button\n type=\"button\"\n className={`rf0-dt-badge rf0-dt-badge--${position}${\n errors.length > 0 ? ' rf0-dt-badge--has-errors' : ''\n }`}\n aria-expanded={false}\n aria-label={`Open react-f0rm devtools (${errors.length} errors)`}\n onClick={() => setOpen(true)}\n >\n f0\n <span className=\"rf0-dt-dot\" />\n </button>\n );\n }\n\n const counts: Record<TabId, number> = {\n values: countLeaves(values),\n errors: errors.length,\n touched: touched.length,\n dirty: Object.keys(dirty).length\n };\n\n /** Arrow-key tab navigation (buttons stay click/Enter/Space operable). */\n const onTabKeyDown = (e: KeyboardEvent) => {\n const deltas: Record<string, number> = {\n ArrowRight: 1,\n ArrowLeft: -1\n };\n const delta = deltas[e.key];\n if (!delta) return;\n e.preventDefault();\n const next = TABS[(TABS.indexOf(tab) + delta + TABS.length) % TABS.length];\n setTab(next);\n document.getElementById(`${idPrefix}-tab-${next}`)?.focus();\n };\n\n return (\n <section\n className={`rf0-dt rf0-dt--${position}`}\n aria-label=\"react-f0rm devtools\"\n >\n <header className=\"rf0-dt-header\">\n <span className=\"rf0-dt-title\">react-f0rm</span>\n <button\n type=\"button\"\n className=\"rf0-dt-headerbtn\"\n aria-label=\"Collapse devtools\"\n onClick={() => setOpen(false)}\n >\n –\n </button>\n </header>\n\n <div\n className=\"rf0-dt-tablist\"\n role=\"tablist\"\n aria-label=\"Form state\"\n tabIndex={-1}\n onKeyDown={onTabKeyDown}\n >\n {TABS.map(id => (\n <button\n key={id}\n id={`${idPrefix}-tab-${id}`}\n type=\"button\"\n role=\"tab\"\n className={`rf0-dt-tab${id === 'errors' ? ' rf0-dt-tab--danger' : ''}`}\n aria-selected={tab === id}\n aria-controls={`${idPrefix}-panel-${id}`}\n tabIndex={tab === id ? 0 : -1}\n onClick={() => setTab(id)}\n >\n {id}\n <span className=\"rf0-dt-tab-count\">{counts[id]}</span>\n </button>\n ))}\n </div>\n\n <div\n id={`${idPrefix}-panel-${tab}`}\n role=\"tabpanel\"\n aria-labelledby={`${idPrefix}-tab-${tab}`}\n className=\"rf0-dt-panel\"\n >\n {tab === 'values' && <JsonTree value={values} />}\n {tab === 'errors' &&\n (errors.length === 0 ? (\n <p className=\"rf0-dt-empty\">no errors</p>\n ) : (\n errors.map(({path, type, message}, index) => (\n // Same path can hold several errors now; index keeps keys\n // unique without changing what is rendered (messages may\n // legitimately repeat for one path).\n\n <div key={`${path}:${index}`} className=\"rf0-dt-item\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n <span className=\"rf0-dt-item-msg\">{message}</span>\n <span className=\"rf0-dt-item-tag\">{type}</span>\n </div>\n ))\n ))}\n {tab === 'touched' &&\n (touched.length === 0 ? (\n <p className=\"rf0-dt-empty\">no touched fields</p>\n ) : (\n touched.map(path => (\n <div key={path} className=\"rf0-dt-item rf0-dt-item--touched\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n </div>\n ))\n ))}\n {tab === 'dirty' &&\n (Object.keys(dirty).length === 0 ? (\n <p className=\"rf0-dt-empty\">no dirty fields</p>\n ) : (\n Object.keys(dirty).map(path => (\n <div key={path} className=\"rf0-dt-item rf0-dt-item--dirty\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n <span className=\"rf0-dt-item-msg rf0-dt-item-msg--ok\">\n changed\n </span>\n </div>\n ))\n ))}\n </div>\n\n <p className=\"rf0-dt-status\" aria-live=\"polite\">\n <span className={isSubmitting ? 'rf0-dt-on' : undefined}>\n submitting <b>{String(isSubmitting)}</b>\n </span>\n <span>\n submits <b>{submitCount}</b>\n </span>\n <span className={submitStatusClass(isSubmitSuccessful)}>\n ok{' '}\n <b>\n {isSubmitSuccessful === undefined\n ? '–'\n : String(isSubmitSuccessful)}\n </b>\n </span>\n </p>\n\n <div className=\"rf0-dt-actions\">\n <button\n type=\"button\"\n className=\"rf0-dt-action\"\n onClick={() => reset(f, f.initialValues)}\n >\n Reset\n </button>\n <button\n type=\"button\"\n className=\"rf0-dt-action\"\n onClick={() => trigger(f)}\n >\n Validate\n </button>\n </div>\n </section>\n );\n}\n"],"names":["process","env","NODE_ENV","shimModule","exports","React","require$$0","objectIs","Object","is","x","y","useState","useEffect","useLayoutEffect","useDebugValue","checkIfSnapshotChanged","inst","latestGetSnapshot","getSnapshot","value","nextValue","error","shim","window","document","createElement","subscribe","_useState","forceUpdate","useSyncExternalStoreShim_production","useSyncExternalStore","__REACT_DEVTOOLS_GLOBAL_HOOK__","registerInternalModuleStart","Error","didWarnOld18Alpha","didWarnUncachedGetSnapshot","startTransition","console","cachedValue","useSyncExternalStoreShim_development","registerInternalModuleStop","useWatch","emitter","event","getter","subscribeFactory","cacheRef","useRef","current","hasValue","cache","getterRef","useCallback","notify","useWatchCore","invalidate","on","FormContext","createContext","Provider","JsonNode","name","depth","open","setOpen","label","Fragment","className","String","isArray","Array","entries","map","v","i","openBracket","closeBracket","summary","length","style","paddingLeft","type","onClick","k","key","display","Primitive","STYLE_ID","getElementById","id","textContent","head","appendChild","injectDevtoolsStyles","TABS","submitStatusClass","isSubmitSuccessful","countLeaves","count","values","form","position","contextForm","useContext","f","tab","setTab","idPrefix","useId","replace","getValues","bind","errors","getErrors","touched","getTouchedFields","useTouchedFields","dirty","getDirtyFields","useDirtyFields","isSubmitting","useIsSubmitting","submitCount","useSubmitCount","counts","keys","role","tabIndex","onKeyDown","e","delta","ArrowRight","ArrowLeft","preventDefault","next","indexOf","focus","JsonTree","path","message","index","reset","initialValues","trigger"],"mappings":"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","x_google_ignoreList":[0,1,2]}
1
+ {"version":3,"file":"index.cjs.js","sources":["../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/shim/index.js","../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.js","../../node_modules/.pnpm/use-sync-external-store@1.6.0_react@19.2.8/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js","../../src/hooks/form.tsx","../../src/context.ts","../../src/devtools/JsonTree.tsx","../../src/devtools/styles.ts","../../src/devtools/Devtools.tsx"],"sourcesContent":["'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('../cjs/use-sync-external-store-shim.production.js');\n} else {\n module.exports = require('../cjs/use-sync-external-store-shim.development.js');\n}\n","/**\n * @license React\n * use-sync-external-store-shim.production.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\nvar React = require(\"react\");\nfunction is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n}\nvar objectIs = \"function\" === typeof Object.is ? Object.is : is,\n useState = React.useState,\n useEffect = React.useEffect,\n useLayoutEffect = React.useLayoutEffect,\n useDebugValue = React.useDebugValue;\nfunction useSyncExternalStore$2(subscribe, getSnapshot) {\n var value = getSnapshot(),\n _useState = useState({ inst: { value: value, getSnapshot: getSnapshot } }),\n inst = _useState[0].inst,\n forceUpdate = _useState[1];\n useLayoutEffect(\n function () {\n inst.value = value;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n },\n [subscribe, value, getSnapshot]\n );\n useEffect(\n function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n });\n },\n [subscribe]\n );\n useDebugValue(value);\n return value;\n}\nfunction checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n}\nfunction useSyncExternalStore$1(subscribe, getSnapshot) {\n return getSnapshot();\n}\nvar shim =\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ? useSyncExternalStore$1\n : useSyncExternalStore$2;\nexports.useSyncExternalStore =\n void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;\n","/**\n * @license React\n * use-sync-external-store-shim.development.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\n\"production\" !== process.env.NODE_ENV &&\n (function () {\n function is(x, y) {\n return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);\n }\n function useSyncExternalStore$2(subscribe, getSnapshot) {\n didWarnOld18Alpha ||\n void 0 === React.startTransition ||\n ((didWarnOld18Alpha = !0),\n console.error(\n \"You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release.\"\n ));\n var value = getSnapshot();\n if (!didWarnUncachedGetSnapshot) {\n var cachedValue = getSnapshot();\n objectIs(value, cachedValue) ||\n (console.error(\n \"The result of getSnapshot should be cached to avoid an infinite loop\"\n ),\n (didWarnUncachedGetSnapshot = !0));\n }\n cachedValue = useState({\n inst: { value: value, getSnapshot: getSnapshot }\n });\n var inst = cachedValue[0].inst,\n forceUpdate = cachedValue[1];\n useLayoutEffect(\n function () {\n inst.value = value;\n inst.getSnapshot = getSnapshot;\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n },\n [subscribe, value, getSnapshot]\n );\n useEffect(\n function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n return subscribe(function () {\n checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });\n });\n },\n [subscribe]\n );\n useDebugValue(value);\n return value;\n }\n function checkIfSnapshotChanged(inst) {\n var latestGetSnapshot = inst.getSnapshot;\n inst = inst.value;\n try {\n var nextValue = latestGetSnapshot();\n return !objectIs(inst, nextValue);\n } catch (error) {\n return !0;\n }\n }\n function useSyncExternalStore$1(subscribe, getSnapshot) {\n return getSnapshot();\n }\n \"undefined\" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&\n \"function\" ===\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart &&\n __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());\n var React = require(\"react\"),\n objectIs = \"function\" === typeof Object.is ? Object.is : is,\n useState = React.useState,\n useEffect = React.useEffect,\n useLayoutEffect = React.useLayoutEffect,\n useDebugValue = React.useDebugValue,\n didWarnOld18Alpha = !1,\n didWarnUncachedGetSnapshot = !1,\n shim =\n \"undefined\" === typeof window ||\n \"undefined\" === typeof window.document ||\n \"undefined\" === typeof window.document.createElement\n ? useSyncExternalStore$1\n : useSyncExternalStore$2;\n exports.useSyncExternalStore =\n void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;\n \"undefined\" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&\n \"function\" ===\n typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&\n __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());\n })();\n","import {useState, useEffect, useCallback, useRef} from 'react';\nimport {useSyncExternalStore} from 'use-sync-external-store/shim';\nimport {on} from '@for-fun/event-emitter';\nimport type {EventEmitter} from '@for-fun/event-emitter';\nimport {onKeyEvent, onPathEvent} from '../subscribe';\nimport createForm, {\n getErrorByPath,\n getFieldErrorsByPath,\n getValueByPath,\n hasTouchedByPath,\n hasErrors,\n isDirty,\n getDirtyFields,\n getTouchedFields,\n setInitialValues\n} from '../form';\nimport type {FieldError, Form, Options, Name} from '../form';\nimport type {FieldPath, PathValueOf} from '../types';\nimport createPath from '../path';\nimport type {Path} from '../path';\nimport {isEqual} from '../util';\n\n/**\n * Create a form instance bound to this component.\n *\n * Beyond {@link Options}, the optional `values` object enables controlled\n * usage: when it genuinely changes it is re-synced into the form with\n * setInitialValues semantics -- uncommitted user edits are discarded\n * (master-detail semantics: selecting another record replaces the draft),\n * while touched flags and errors survive. Change detection is\n * reference-first with a structural fallback, so re-renders that pass an\n * inline literal with equal content never re-sync -- the user's\n * in-progress typing is never clobbered.\n */\nexport default function useForm<T extends Record<string, any> = any>(\n options?: Options<T> & {values?: T}\n): Form<T> {\n // Lazy initialization: createForm runs once per mount and the returned\n // instance is stable across re-renders (and StrictMode double renders),\n // without writing to refs during render. A provided `values` object is\n // seeded synchronously here (createForm does the same for initialValues)\n // so the first paint and SSR already reflect the controlled values.\n const [form] = useState(() => {\n const created = createForm<T>(options);\n if (options && options.values !== undefined) {\n setInitialValues(created, options.values);\n }\n return created;\n });\n const initialValues = options && options.initialValues;\n const values = options && options.values;\n\n // Track which initialValues source object the form was last seeded from.\n // Inline options create a fresh object every render, and re-seeding\n // clears the values Map (setInitialValues semantics), which would revert\n // every committed edit right after each re-render -- on the client and\n // after hydration alike. Memoized callers are covered by the reference\n // check; inline literals by the structural one, so only genuinely new\n // content re-seeds.\n const seededRef = useRef<{done: boolean; source: any} | null>(null);\n if (seededRef.current === null)\n seededRef.current = {done: false, source: undefined};\n\n useEffect(() => {\n const seeded = seededRef.current!;\n if (\n seeded.done &&\n (seeded.source === initialValues || isEqual(seeded.source, initialValues))\n ) {\n return;\n }\n seeded.done = true;\n seeded.source = initialValues;\n setInitialValues(form, initialValues);\n }, [form, initialValues]);\n\n // Controlled values: re-sync only when the incoming object genuinely\n // differs from what the form was last seeded from. The reference check\n // is the fast path (memoized callers); inline literals get a fresh\n // object identity every render, so without the structural comparison\n // each re-render would clear the values Map (setInitialValues\n // semantics) and revert the user's uncommitted edits -- same hazard the\n // initialValues seed guard above protects against. Master-detail\n // semantics still apply whenever the content actually changed.\n const controlledRef = useRef<{done: boolean; source: any} | null>(null);\n if (controlledRef.current === null) {\n controlledRef.current = {done: false, source: undefined};\n }\n\n useEffect(() => {\n if (values === undefined) return;\n const seeded = controlledRef.current!;\n if (\n seeded.done &&\n (seeded.source === values || isEqual(seeded.source, values))\n ) {\n return;\n }\n seeded.done = true;\n seeded.source = values;\n setInitialValues(form, values);\n }, [form, values]);\n\n return form;\n}\n\n/** Per-hook snapshot cache for {@link useWatch}. */\ninterface WatchCache<T> {\n hasValue: boolean;\n value?: T;\n}\n\n/**\n * Shared core of {@link useWatch} and the path-scoped hooks: a\n * useSyncExternalStore binding over a custom event subscription.\n * `subscribeFactory` receives the invalidate callback (drop the snapshot\n * cache, then notify React) and returns its unsubscribe function, so the\n * core stays identical whether the subscription is global or scoped to\n * one path.\n */\nfunction useWatchCore<T>(\n subscribeFactory: (invalidate: () => void) => () => void,\n getter: () => T\n): T {\n // useSyncExternalStore requires getSnapshot to return the same reference\n // until the store actually changed, otherwise React warns and loops.\n // Cache the snapshot per hook instance and recompute it only on the first\n // read and after the watched event fired.\n const cacheRef = useRef<WatchCache<T> | null>(null);\n if (cacheRef.current === null) cacheRef.current = {hasValue: false};\n const cache = cacheRef.current;\n\n // Hold the latest getter in a ref so getSnapshot keeps a stable identity\n // (callers pass a freshly bound function on every render) while still\n // recomputing with the most recent getter when the cache is invalid.\n const getterRef = useRef(getter);\n getterRef.current = getter;\n\n const getSnapshot = useCallback(() => {\n if (!cache.hasValue) {\n cache.value = getterRef.current();\n cache.hasValue = true;\n }\n return cache.value as T;\n }, [cache]);\n\n const subscribe = useCallback(\n (notify: () => void) => {\n // The form may have changed between render and this subscription, and\n // those events were missed: drop the cache. React's consistency check\n // right after subscribing recomputes and re-renders only when the\n // fresh value differs from the committed snapshot.\n cache.hasValue = false;\n const invalidate = () => {\n cache.hasValue = false;\n notify();\n };\n return subscribeFactory(invalidate);\n },\n [subscribeFactory, cache]\n );\n\n // Form state lives entirely in synchronously readable Map/Set structures\n // seeded from initialValues/values during the lazy useState initializer,\n // so the server snapshot is computed exactly like the client's first\n // render -- pass getSnapshot itself as getServerSnapshot and hydration\n // matches.\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/**\n * Subscribe to a form event and keep the component's snapshot of `getter()`\n * in sync with the form state.\n *\n * Built on useSyncExternalStore, so snapshots taken while React renders are\n * guaranteed consistent (no tearing under concurrent rendering) and changes\n * emitted before the subscription effect runs are still picked up.\n */\nexport function useWatch<T>(\n emitter: EventEmitter,\n event: string,\n getter: () => T\n): T {\n const subscribeFactory = useCallback(\n (invalidate: () => void) => on(emitter, event, invalidate),\n [emitter, event]\n );\n return useWatchCore(subscribeFactory, getter);\n}\n\n/**\n * Get field value state\n */\nexport function useValue<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): PathValueOf<T, P> {\n return useValueByPath(form, createPath(name));\n}\n\n/**\n * Get field value state by path\n */\nexport function useValueByPath(form: Form, path: Path): any {\n const {emitter} = form;\n const {key} = path;\n // 'leaf' scope: a leaf read depends only on its own key and its\n // ancestors' (getValueByPath fallback chain), so writes elsewhere --\n // siblings, descendants, string-prefix lookalikes ('[\"a\",\"bX\"]') -- never\n // invalidate the snapshot. Payload-less broadcasts (reset, removeField,\n // setInitialValues) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) =>\n onPathEvent(emitter, 'change', path, 'leaf', invalidate),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- deps are `key` on purpose: useValue creates a fresh Path per render, so the object must stay out of the deps while the key string pins the subscription\n [emitter, key]\n );\n return useWatchCore(subscribeFactory, getValueByPath.bind(null, form, path));\n}\n\n/**\n * Get field touched state\n */\nexport function useTouched<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): boolean {\n return useTouchedByPath(form, createPath(name));\n}\n\n/**\n * Get field touched state by path\n */\nexport function useTouchedByPath(form: Form, path: Path): boolean {\n const {emitter} = form;\n const {key} = path;\n // Touched is stored per exact key, so only this field's own setTouched\n // (now emitted with its path) matters; payload-less broadcasts (reset,\n // removeField) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'touched', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(\n subscribeFactory,\n hasTouchedByPath.bind(null, form, path)\n );\n}\n\n/**\n * Get field error message state\n * @return current error's message string (display text), or undefined\n */\nexport function useError<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): string | undefined {\n return useErrorByPath(form, createPath(name))?.message;\n}\n\n/**\n * Get field error state by path\n * @return current FieldError object ({type, message}), or undefined\n */\nexport function useErrorByPath(form: Form, path: Path): FieldError | undefined {\n const {emitter} = form;\n const {key} = path;\n // Errors are stored per exact key, so only writes to this field's error\n // (setErrorByPath now emits with its path) matter; payload-less\n // broadcasts (clearErrors, reset, removeField) still sync everything.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'errors', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(subscribeFactory, getErrorByPath.bind(null, form, path));\n}\n\n/**\n * Get all field errors\n * @return every error registered for the field (insertion order); an empty\n * array when the field has none\n */\nexport function useFieldErrors<\n T extends Record<string, any> = any,\n P extends FieldPath<T> | Name = Name\n>(form: Form<T>, name: P): FieldError[] {\n return useFieldErrorsByPath(form, createPath(name));\n}\n\n/**\n * Get all field errors by path\n * @return every error registered for the field (insertion order); an empty\n * array when the field has none\n */\nexport function useFieldErrorsByPath(form: Form, path: Path): FieldError[] {\n const {emitter} = form;\n const {key} = path;\n // Same exact-key subscription and snapshot rules as useErrorByPath: the\n // getter returns the shared empty constant when clean and the stored\n // array by reference otherwise, so the useSyncExternalStore snapshot is\n // reference-stable between unrelated events.\n const subscribeFactory = useCallback(\n (invalidate: () => void) => onKeyEvent(emitter, 'errors', key, invalidate),\n [emitter, key]\n );\n return useWatchCore(\n subscribeFactory,\n getFieldErrorsByPath.bind(null, form, path)\n );\n}\n\nexport function useIsDirty(form: Form): boolean {\n // Dirty state is driven by value changes, not touch state: subscribe to\n // 'change' so typing flips this immediately, even before a blur.\n return useWatch(form.emitter, 'change', isDirty.bind(null, form));\n}\n\n/**\n * Get dirty fields state -- object mapping each dirty field's user-facing\n * dotted path ('a.b', 'a.0.c') to true; recalculated after 'change' events\n */\nexport function useDirtyFields(form: Form): Record<string, boolean> {\n return useWatch(form.emitter, 'change', getDirtyFields.bind(null, form));\n}\n\n/**\n * Get touched fields state -- array of touched fields' user-facing dotted\n * paths ('a.b', 'a.0.c'); recalculated after 'touched' events\n */\nexport function useTouchedFields(form: Form): string[] {\n return useWatch(form.emitter, 'touched', getTouchedFields.bind(null, form));\n}\n\nexport function useHasErrors(form: Form): boolean {\n return useWatch(form.emitter, 'errors', hasErrors.bind(null, form));\n}\n\nexport function useIsSubmitting(form: Form): boolean {\n return useWatch(form.emitter, 'submitting', () => form.isSubmitting);\n}\n\n/**\n * Get whether the form accepts a submit right now:\n * `!isSubmitting && !hasErrors`. This is the single flag a submit\n * button's `disabled` prop wants — it is `false` for the whole async\n * `onSubmit` span (not just the validation pass) and whenever any field\n * holds an error (client validation or server backfill), replacing the\n * hand-rolled `useHasErrors(form) || useIsSubmitting(form)` pair.\n * Deliberately no dirty or validating semantics: an untouched-but-clean\n * form can submit.\n */\nexport function useCanSubmit(form: Form): boolean {\n const {emitter} = form;\n // canSubmit folds two events into one boolean: error writes\n // ('errors') and submit-state flips ('submitting'). useWatch subscribes\n // to a single event, so subscribe to both through useWatchCore — the\n // snapshot recomputes on either wake and re-renders only when the\n // boolean itself flips, so unrelated single-field error churn costs no\n // extra render (the same granularity useHasErrors already has).\n const subscribeFactory = useCallback(\n (invalidate: () => void) => {\n const offErrors = on(emitter, 'errors', invalidate);\n const offSubmitting = on(emitter, 'submitting', invalidate);\n return () => {\n offErrors();\n offSubmitting();\n };\n },\n [emitter]\n );\n return useWatchCore(\n subscribeFactory,\n () => !form.isSubmitting && !hasErrors(form)\n );\n}\n\nexport function useSubmitCount(form: Form): number {\n return useWatch(form.emitter, 'submitCount', () => form.submitCount);\n}\n","import {createContext, createElement, useContext, type ReactNode} from 'react';\nimport {\n useFieldCore,\n type UseFieldOptions,\n type UseFieldResult\n} from './hooks/field';\nimport {\n useFieldArrayCore,\n useFieldArrayItemCore,\n type UseFieldArrayResult,\n type UseFieldArrayItemResult\n} from './hooks/fieldArray';\nimport type {Form} from './form';\nimport type {Name} from './path';\nimport type {FieldPath} from './types';\n\nexport const FormContext = createContext<any>(null);\n\nexport const FormProvider = FormContext.Provider;\n\n/**\n * Read the form from the module-level {@link FormContext}. Pass the values\n * shape — `useFormContext<Values>()` — to get a fully typed `Form<Values>`\n * headless API; the `any` default keeps untyped call sites compiling.\n *\n * For multiple forms in one subtree use {@link createFormContext} instead.\n *\n * @throws when no `<FormProvider>` is mounted above the call site.\n */\nexport function useFormContext<T extends Record<string, any> = any>(): Form<T> {\n const form = useContext(FormContext);\n if (!form) throw new Error('no form provided');\n return form;\n}\n\n/**\n * Create an isolated bundle of form-context bindings: its own React context\n * plus `useField` / `useFieldArray` / `useFieldArrayItem` /\n * `useFormContext` hooks that resolve their form from it.\n *\n * Why: the module-level {@link FormContext} works fine for a single form per\n * subtree, but nesting two forms (or reusing a component inside a different\n * form) makes them fight over one context. Calling this factory once per app\n * area — `const Ctx = createFormContext<Values>()` — fixes the value shape\n * (`Ctx.useField({name: 'user.name'})` gets its `name` constrained by\n * `FieldPath<Values>` and its `value` typed accordingly), so call sites stop\n * hand-writing generics, and each instance's Provider scopes a strictly\n * separate form. The bundle also carries its raw React context\n * (`Ctx.context`) so `<Form context={Ctx.context}>` can provide into it.\n */\nexport function createFormContext<TValues extends Record<string, any> = any>() {\n const Context = createContext<Form<TValues> | null>(null);\n\n // A `form`-prop wrapper instead of exposing Context.Provider directly:\n // callers shouldn't have to know about the raw `value` prop shape.\n function FormProvider({\n form,\n children\n }: {\n form: Form<TValues>;\n children: ReactNode;\n }): ReactNode {\n return createElement(Context.Provider, {value: form}, children);\n }\n\n function useFormContext(): Form<TValues> {\n const form = useContext(Context);\n if (!form) throw new Error('no form provided');\n return form;\n }\n\n function useField<TPath extends FieldPath<TValues> | Name = Name>(\n // The bare `{name: TPath}` member keeps `name` a direct inference site\n // for TPath instead of routing it through the mapped Omit type.\n // `form` is omitted on purpose — the form always comes from this\n // factory's own Context.\n options: {name: TPath} & Omit<UseFieldOptions<TValues, TPath>, 'form'>\n ): UseFieldResult<TValues, TPath> {\n return useFieldCore(options as UseFieldOptions<TValues, TPath>, Context);\n }\n\n function useFieldArray(options: {\n name: FieldPath<TValues> | Name;\n }): UseFieldArrayResult {\n return useFieldArrayCore(options as {name: Name}, Context);\n }\n\n function useFieldArrayItem<TValue = any>(options: {\n name: FieldPath<TValues> | Name;\n id: string;\n }): UseFieldArrayItemResult<TValue> {\n return useFieldArrayItemCore(options as {name: Name; id: string}, Context);\n }\n\n // The raw React context, for `<Form context={...}>`: the component keeps\n // its submit machinery while providing into this instance's private\n // context, so the bound hooks above resolve the form it manages.\n return {\n context: Context,\n FormProvider,\n useFormContext,\n useField,\n useFieldArray,\n useFieldArrayItem\n };\n}\n\nexport const CheckboxGroupContext = createContext<any>(null);\n\nexport const CheckboxGroupProvider = CheckboxGroupContext.Provider;\n\nexport function useCheckboxGroupContext(): any {\n const group = useContext(CheckboxGroupContext);\n if (!group) throw new Error('no group provided');\n return group;\n}\n","import * as React from 'react';\nimport {useState} from 'react';\nimport type {ReactNode} from 'react';\n\n/** Nodes deeper than this start collapsed. */\nconst DEFAULT_OPEN_DEPTH = 1;\n\n/**\n * Read-only inspection: the tree never mutates form state, so structural\n * sharing of the inspected value is safe and re-renders stay cheap.\n */\ninterface JsonNodeProps {\n /** Property name (or array index) rendering before the value. */\n name?: string | number;\n /** Value to render. */\n value: unknown;\n /** Current nesting depth (root is 0). */\n depth?: number;\n}\n\n/**\n * One line of the tree: either a collapsible container row\n * (`▸ key: {`) or a leaf (`key: value`).\n */\nfunction JsonNode({name, value, depth = 0}: JsonNodeProps) {\n const [open, setOpen] = useState(depth <= DEFAULT_OPEN_DEPTH);\n\n const label =\n name === undefined ? null : (\n <>\n <span className=\"rf0-dt-key\">{String(name)}</span>\n <span className=\"rf0-dt-punct\">: </span>\n </>\n );\n\n if (value !== null && typeof value === 'object') {\n const isArray = Array.isArray(value);\n const entries: Array<[string | number, unknown]> = isArray\n ? (value as unknown[]).map((v, i) => [i, v])\n : Object.entries(value as Record<string, unknown>);\n const openBracket = isArray ? '[' : '{';\n const closeBracket = isArray ? ']' : '}';\n const summary = open\n ? ''\n : `${openBracket}…${closeBracket} ${entries.length}`;\n\n return (\n <div className=\"rf0-dt-row\" style={{paddingLeft: depth * 12}}>\n <button\n type=\"button\"\n className=\"rf0-dt-node-toggle\"\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n <span className=\"rf0-dt-caret\">{open ? '▾' : '▸'}</span>\n {label}\n <span className=\"rf0-dt-punct\">{open ? openBracket : summary}</span>\n </button>\n {open && (\n <>\n {entries.map(([k, v]) => (\n <JsonNode key={String(k)} name={k} value={v} depth={depth + 1} />\n ))}\n <span className=\"rf0-dt-punct\" style={{paddingLeft: depth * 12}}>\n {closeBracket}\n </span>\n </>\n )}\n </div>\n );\n }\n\n return (\n <span\n className=\"rf0-dt-row\"\n style={{paddingLeft: depth * 12, display: 'block'}}\n >\n {label}\n <Primitive value={value} />\n </span>\n );\n}\n\n/** Render a primitive leaf with terminal-style type coloring. */\nfunction Primitive({value}: {value: unknown}): ReactNode {\n if (value === undefined)\n return <span className=\"rf0-dt-null\">undefined</span>;\n if (value === null) return <span className=\"rf0-dt-null\">null</span>;\n if (typeof value === 'string')\n return <span className=\"rf0-dt-string\">&quot;{value}&quot;</span>;\n if (typeof value === 'boolean')\n return <span className=\"rf0-dt-boolean\">{String(value)}</span>;\n return <span className=\"rf0-dt-number\">{String(value)}</span>;\n}\n\nexport default JsonNode;\n","/**\n * Stylesheet for the Devtools panel.\n *\n * Zero runtime dependencies by design: a single CSS string injected once\n * into <head> (idempotent across module reloads and multiple bundles).\n *\n * Aesthetic: instrument panel / terminal — near-black layers, monospace\n * stack, dense rows, hairline borders. Semantic colors only: error red,\n * success green, neutral gray, with one dim amber accent for the active\n * tab indicator and the collapsed badge.\n */\n\nconst CSS = `\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n`;\n\nconst STYLE_ID = 'react-f0rm-devtools-style';\n\n/**\n * Inject the panel stylesheet into <head>. Idempotent: repeated calls\n * (module reloads, HMR, multiple Devtools mounts) never duplicate the\n * <style> element. No-ops outside a DOM environment (SSR).\n */\nexport function injectDevtoolsStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = CSS;\n document.head.appendChild(style);\n}\n\ninjectDevtoolsStyles();\n","import * as React from 'react';\nimport {useContext, useId, useState} from 'react';\nimport type {KeyboardEvent} from 'react';\nimport {FormContext} from '../context';\nimport {getErrors, getValues, reset, trigger} from '../form';\nimport type {FieldErrorEntry, Form} from '../form';\nimport {\n useDirtyFields,\n useIsSubmitting,\n useSubmitCount,\n useTouchedFields,\n useWatch\n} from '../hooks/form';\nimport JsonTree from './JsonTree';\nimport './styles';\n\n/** Corner the panel docks to. */\nexport type DevtoolsPosition =\n 'top-right' | 'bottom-right' | 'top-left' | 'bottom-left';\n\n/** Props for {@link Devtools}. */\nexport interface DevtoolsProps<T extends Record<string, any> = any> {\n /**\n * Form instance to inspect. When omitted, the panel reads the closest\n * `<Form>` / FormProvider ancestor and throws if there is none.\n */\n form?: Form<T>;\n /** Corner to dock the panel in. Defaults to `'top-right'`. */\n position?: DevtoolsPosition;\n}\n\ntype TabId = 'values' | 'errors' | 'touched' | 'dirty';\n\nconst TABS: TabId[] = ['values', 'errors', 'touched', 'dirty'];\n\n/** Status chip class for the submit-successful indicator. */\nfunction submitStatusClass(\n isSubmitSuccessful: boolean | undefined\n): string | undefined {\n if (isSubmitSuccessful === undefined) return undefined;\n return isSubmitSuccessful ? 'rf0-dt-ok' : 'rf0-dt-err';\n}\n\n/** Count primitive leaves of an inspected value tree. */\nfunction countLeaves(value: unknown): number {\n if (value === null || typeof value !== 'object') return 1;\n let count = 0;\n for (const v of Object.values(value as Record<string, unknown>)) {\n count += countLeaves(v);\n }\n return count;\n}\n\n/**\n * Live form inspector — a floating instrument panel for development.\n *\n * Renders four tabs (values / errors / touched / dirty), a submit status\n * strip (isSubmitting, submitCount, isSubmitSuccessful) and two actions:\n * Reset and Validate (full `trigger`). All state is read through the\n * library's own watch hooks, so the panel updates in real time without\n * participating in validation or submit flows. Docked at a corner,\n * collapsible to a small badge; fully keyboard operable.\n *\n * Ship it from the dedicated `react-f0rm/devtools` entry — it is never\n * re-exported by the main entry, so production bundles stay untouched.\n */\nexport default function Devtools<T extends Record<string, any> = any>({\n form,\n position = 'top-right'\n}: DevtoolsProps<T>) {\n const contextForm = useContext(FormContext);\n const f = form ?? contextForm;\n if (!f) {\n throw new Error(\n '<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.'\n );\n }\n\n const [open, setOpen] = useState(true);\n const [tab, setTab] = useState<TabId>('values');\n const idPrefix = useId().replace(/[^a-zA-Z0-9-]/g, '');\n\n // Live snapshots, straight through the public watch surface.\n const values = useWatch(f.emitter, 'change', getValues.bind(null, f));\n const errors = useWatch<FieldErrorEntry[]>(\n f.emitter,\n 'errors',\n getErrors.bind(null, f)\n );\n const touched = useTouchedFields(f);\n const dirty = useDirtyFields(f);\n const isSubmitting = useIsSubmitting(f);\n const submitCount = useSubmitCount(f);\n const isSubmitSuccessful = useWatch(\n f.emitter,\n 'submitSuccessful',\n () => f.isSubmitSuccessful\n );\n\n if (!open) {\n return (\n <button\n type=\"button\"\n className={`rf0-dt-badge rf0-dt-badge--${position}${\n errors.length > 0 ? ' rf0-dt-badge--has-errors' : ''\n }`}\n aria-expanded={false}\n aria-label={`Open react-f0rm devtools (${errors.length} errors)`}\n onClick={() => setOpen(true)}\n >\n f0\n <span className=\"rf0-dt-dot\" />\n </button>\n );\n }\n\n const counts: Record<TabId, number> = {\n values: countLeaves(values),\n errors: errors.length,\n touched: touched.length,\n dirty: Object.keys(dirty).length\n };\n\n /** Arrow-key tab navigation (buttons stay click/Enter/Space operable). */\n const onTabKeyDown = (e: KeyboardEvent) => {\n const deltas: Record<string, number> = {\n ArrowRight: 1,\n ArrowLeft: -1\n };\n const delta = deltas[e.key];\n if (!delta) return;\n e.preventDefault();\n const next = TABS[(TABS.indexOf(tab) + delta + TABS.length) % TABS.length];\n setTab(next);\n document.getElementById(`${idPrefix}-tab-${next}`)?.focus();\n };\n\n return (\n <section\n className={`rf0-dt rf0-dt--${position}`}\n aria-label=\"react-f0rm devtools\"\n >\n <header className=\"rf0-dt-header\">\n <span className=\"rf0-dt-title\">react-f0rm</span>\n <button\n type=\"button\"\n className=\"rf0-dt-headerbtn\"\n aria-label=\"Collapse devtools\"\n onClick={() => setOpen(false)}\n >\n –\n </button>\n </header>\n\n <div\n className=\"rf0-dt-tablist\"\n role=\"tablist\"\n aria-label=\"Form state\"\n tabIndex={-1}\n onKeyDown={onTabKeyDown}\n >\n {TABS.map(id => (\n <button\n key={id}\n id={`${idPrefix}-tab-${id}`}\n type=\"button\"\n role=\"tab\"\n className={`rf0-dt-tab${id === 'errors' ? ' rf0-dt-tab--danger' : ''}`}\n aria-selected={tab === id}\n aria-controls={`${idPrefix}-panel-${id}`}\n tabIndex={tab === id ? 0 : -1}\n onClick={() => setTab(id)}\n >\n {id}\n <span className=\"rf0-dt-tab-count\">{counts[id]}</span>\n </button>\n ))}\n </div>\n\n <div\n id={`${idPrefix}-panel-${tab}`}\n role=\"tabpanel\"\n aria-labelledby={`${idPrefix}-tab-${tab}`}\n className=\"rf0-dt-panel\"\n >\n {tab === 'values' && <JsonTree value={values} />}\n {tab === 'errors' &&\n (errors.length === 0 ? (\n <p className=\"rf0-dt-empty\">no errors</p>\n ) : (\n errors.map(({path, type, message}, index) => (\n // Same path can hold several errors now; index keeps keys\n // unique without changing what is rendered (messages may\n // legitimately repeat for one path).\n\n <div key={`${path}:${index}`} className=\"rf0-dt-item\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n <span className=\"rf0-dt-item-msg\">{message}</span>\n <span className=\"rf0-dt-item-tag\">{type}</span>\n </div>\n ))\n ))}\n {tab === 'touched' &&\n (touched.length === 0 ? (\n <p className=\"rf0-dt-empty\">no touched fields</p>\n ) : (\n touched.map(path => (\n <div key={path} className=\"rf0-dt-item rf0-dt-item--touched\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n </div>\n ))\n ))}\n {tab === 'dirty' &&\n (Object.keys(dirty).length === 0 ? (\n <p className=\"rf0-dt-empty\">no dirty fields</p>\n ) : (\n Object.keys(dirty).map(path => (\n <div key={path} className=\"rf0-dt-item rf0-dt-item--dirty\">\n <span className=\"rf0-dt-item-path\">{path}</span>\n <span className=\"rf0-dt-item-msg rf0-dt-item-msg--ok\">\n changed\n </span>\n </div>\n ))\n ))}\n </div>\n\n <p className=\"rf0-dt-status\" aria-live=\"polite\">\n <span className={isSubmitting ? 'rf0-dt-on' : undefined}>\n submitting <b>{String(isSubmitting)}</b>\n </span>\n <span>\n submits <b>{submitCount}</b>\n </span>\n <span className={submitStatusClass(isSubmitSuccessful)}>\n ok{' '}\n <b>\n {isSubmitSuccessful === undefined\n ? '–'\n : String(isSubmitSuccessful)}\n </b>\n </span>\n </p>\n\n <div className=\"rf0-dt-actions\">\n <button\n type=\"button\"\n className=\"rf0-dt-action\"\n onClick={() => reset(f, f.initialValues)}\n >\n Reset\n </button>\n <button\n type=\"button\"\n className=\"rf0-dt-action\"\n onClick={() => trigger(f)}\n >\n Validate\n </button>\n </div>\n </section>\n );\n}\n"],"names":["process","env","NODE_ENV","shimModule","exports","React","require$$0","objectIs","Object","is","x","y","useState","useEffect","useLayoutEffect","useDebugValue","checkIfSnapshotChanged","inst","latestGetSnapshot","getSnapshot","value","nextValue","error","shim","window","document","createElement","subscribe","_useState","forceUpdate","useSyncExternalStoreShim_production","useSyncExternalStore","__REACT_DEVTOOLS_GLOBAL_HOOK__","registerInternalModuleStart","Error","didWarnOld18Alpha","didWarnUncachedGetSnapshot","startTransition","console","cachedValue","useSyncExternalStoreShim_development","registerInternalModuleStop","useWatch","emitter","event","getter","subscribeFactory","cacheRef","useRef","current","hasValue","cache","getterRef","useCallback","notify","useWatchCore","invalidate","on","FormContext","createContext","Provider","JsonNode","name","depth","open","setOpen","label","Fragment","className","String","isArray","Array","entries","map","v","i","openBracket","closeBracket","summary","length","style","paddingLeft","type","onClick","k","key","display","Primitive","STYLE_ID","getElementById","id","textContent","head","appendChild","injectDevtoolsStyles","TABS","submitStatusClass","isSubmitSuccessful","countLeaves","count","values","form","position","contextForm","useContext","f","tab","setTab","idPrefix","useId","replace","getValues","bind","errors","getErrors","touched","getTouchedFields","useTouchedFields","dirty","getDirtyFields","useDirtyFields","isSubmitting","useIsSubmitting","submitCount","useSubmitCount","counts","keys","role","tabIndex","onKeyDown","e","delta","ArrowRight","ArrowLeft","preventDefault","next","indexOf","focus","JsonTree","path","message","index","reset","initialValues","trigger"],"mappings":"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","x_google_ignoreList":[0,1,2]}
@@ -1,2 +1,2 @@
1
- import*as e from"react";import t,{useCallback as n,useRef as r,createContext as o,useState as a,useContext as l,useId as i}from"react";import{o as s,g as d,a as c,b as f,c as p,r as u,t as m}from"../form-BQsb2CuG.mjs";var b,g={exports:{}},x={};var h,E,y={};var v=(E||(E=1,"production"===process.env.NODE_ENV?g.exports=function(){if(b)return x;b=1;var e=t,n="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},r=e.useState,o=e.useEffect,a=e.useLayoutEffect,l=e.useDebugValue;function i(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!n(e,r)}catch(e){return!0}}var s="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),s=r({inst:{value:n,getSnapshot:t}}),d=s[0].inst,c=s[1];return a(function(){d.value=n,d.getSnapshot=t,i(d)&&c({inst:d})},[e,n,t]),o(function(){return i(d)&&c({inst:d}),e(function(){i(d)&&c({inst:d})})},[e]),l(n),n};return x.useSyncExternalStore=void 0!==e.useSyncExternalStore?e.useSyncExternalStore:s,x}():g.exports=(h||(h=1,"production"!==process.env.NODE_ENV&&function(){function e(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!r(e,n)}catch(e){return!0}}"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var n=t,r="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=n.useState,a=n.useEffect,l=n.useLayoutEffect,i=n.useDebugValue,s=!1,d=!1,c="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(t,c){s||void 0===n.startTransition||(s=!0,console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));var f=c();if(!d){var p=c();r(f,p)||(console.error("The result of getSnapshot should be cached to avoid an infinite loop"),d=!0)}var u=(p=o({inst:{value:f,getSnapshot:c}}))[0].inst,m=p[1];return l(function(){u.value=f,u.getSnapshot=c,e(u)&&m({inst:u})},[t,f,c]),a(function(){return e(u)&&m({inst:u}),t(function(){e(u)&&m({inst:u})})},[t]),i(f),f};y.useSyncExternalStore=void 0!==n.useSyncExternalStore?n.useSyncExternalStore:c,"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())}()),y)),g.exports);function w(e,t,o){return function(e,t){const o=r(null);null===o.current&&(o.current={hasValue:!1});const a=o.current,l=r(t);l.current=t;const i=n(()=>(a.hasValue||(a.value=l.current(),a.hasValue=!0),a.value),[a]),s=n(t=>(a.hasValue=!1,e(()=>{a.hasValue=!1,t()})),[e,a]);return v.useSyncExternalStore(s,i,i)}(n(n=>s(e,t,n),[e,t]),o)}const S=o(null);S.Provider;o(null).Provider;function k({name:t,value:n,depth:r=0}){const[o,l]=a(r<=1),i=void 0===t?null:e.createElement(e.Fragment,null,e.createElement("span",{className:"rf0-dt-key"},String(t)),e.createElement("span",{className:"rf0-dt-punct"},": "));if(null!==n&&"object"==typeof n){const t=Array.isArray(n),a=t?n.map((e,t)=>[t,e]):Object.entries(n),s=t?"[":"{",d=t?"]":"}",c=o?"":`${s}…${d} ${a.length}`;return e.createElement("div",{className:"rf0-dt-row",style:{paddingLeft:12*r}},e.createElement("button",{type:"button",className:"rf0-dt-node-toggle","aria-expanded":o,onClick:()=>l(!o)},e.createElement("span",{className:"rf0-dt-caret"},o?"▾":"▸"),i,e.createElement("span",{className:"rf0-dt-punct"},o?s:c)),o&&e.createElement(e.Fragment,null,a.map(([t,n])=>e.createElement(k,{key:String(t),name:t,value:n,depth:r+1})),e.createElement("span",{className:"rf0-dt-punct",style:{paddingLeft:12*r}},d)))}return e.createElement("span",{className:"rf0-dt-row",style:{paddingLeft:12*r,display:"block"}},i,e.createElement(N,{value:n}))}function N({value:t}){return void 0===t?e.createElement("span",{className:"rf0-dt-null"},"undefined"):null===t?e.createElement("span",{className:"rf0-dt-null"},"null"):"string"==typeof t?e.createElement("span",{className:"rf0-dt-string"},'"',t,'"'):"boolean"==typeof t?e.createElement("span",{className:"rf0-dt-boolean"},String(t)):e.createElement("span",{className:"rf0-dt-number"},String(t))}const _="react-f0rm-devtools-style";!function(){if("undefined"==typeof document)return;if(document.getElementById(_))return;const e=document.createElement("style");e.id=_,e.textContent="\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n",document.head.appendChild(e)}();const O=["values","errors","touched","dirty"];function L(e){if(void 0!==e)return e?"rf0-dt-ok":"rf0-dt-err"}function C(e){if(null===e||"object"!=typeof e)return 1;let t=0;for(const n of Object.values(e))t+=C(n);return t}function $({form:t,position:n="top-right"}){const r=l(S),o=t??r;if(!o)throw new Error("<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.");const[s,b]=a(!0),[g,x]=a("values"),h=i().replace(/[^a-zA-Z0-9-]/g,""),E=w(o.emitter,"change",f.bind(null,o)),y=w(o.emitter,"errors",p.bind(null,o)),v=function(e){return w(e.emitter,"touched",d.bind(null,e))}(o),N=function(e){return w(e.emitter,"change",c.bind(null,e))}(o),_=function(e){return w(e.emitter,"submitting",()=>e.isSubmitting)}(o),$=function(e){return w(e.emitter,"submitCount",()=>e.submitCount)}(o),A=w(o.emitter,"submitSuccessful",()=>o.isSubmitSuccessful);if(!s)return e.createElement("button",{type:"button",className:`rf0-dt-badge rf0-dt-badge--${n}${y.length>0?" rf0-dt-badge--has-errors":""}`,"aria-expanded":!1,"aria-label":`Open react-f0rm devtools (${y.length} errors)`,onClick:()=>b(!0)},"f0",e.createElement("span",{className:"rf0-dt-dot"}));const V={values:C(E),errors:y.length,touched:v.length,dirty:Object.keys(N).length};return e.createElement("section",{className:`rf0-dt rf0-dt--${n}`,"aria-label":"react-f0rm devtools"},e.createElement("header",{className:"rf0-dt-header"},e.createElement("span",{className:"rf0-dt-title"},"react-f0rm"),e.createElement("button",{type:"button",className:"rf0-dt-headerbtn","aria-label":"Collapse devtools",onClick:()=>b(!1)},"–")),e.createElement("div",{className:"rf0-dt-tablist",role:"tablist","aria-label":"Form state",tabIndex:-1,onKeyDown:e=>{const t={ArrowRight:1,ArrowLeft:-1}[e.key];if(!t)return;e.preventDefault();const n=O[(O.indexOf(g)+t+O.length)%O.length];x(n),document.getElementById(`${h}-tab-${n}`)?.focus()}},O.map(t=>e.createElement("button",{key:t,id:`${h}-tab-${t}`,type:"button",role:"tab",className:"rf0-dt-tab"+("errors"===t?" rf0-dt-tab--danger":""),"aria-selected":g===t,"aria-controls":`${h}-panel-${t}`,tabIndex:g===t?0:-1,onClick:()=>x(t)},t,e.createElement("span",{className:"rf0-dt-tab-count"},V[t])))),e.createElement("div",{id:`${h}-panel-${g}`,role:"tabpanel","aria-labelledby":`${h}-tab-${g}`,className:"rf0-dt-panel"},"values"===g&&e.createElement(k,{value:E}),"errors"===g&&(0===y.length?e.createElement("p",{className:"rf0-dt-empty"},"no errors"):y.map(({path:t,type:n,message:r},o)=>e.createElement("div",{key:`${t}:${o}`,className:"rf0-dt-item"},e.createElement("span",{className:"rf0-dt-item-path"},t),e.createElement("span",{className:"rf0-dt-item-msg"},r),e.createElement("span",{className:"rf0-dt-item-tag"},n)))),"touched"===g&&(0===v.length?e.createElement("p",{className:"rf0-dt-empty"},"no touched fields"):v.map(t=>e.createElement("div",{key:t,className:"rf0-dt-item rf0-dt-item--touched"},e.createElement("span",{className:"rf0-dt-item-path"},t)))),"dirty"===g&&(0===Object.keys(N).length?e.createElement("p",{className:"rf0-dt-empty"},"no dirty fields"):Object.keys(N).map(t=>e.createElement("div",{key:t,className:"rf0-dt-item rf0-dt-item--dirty"},e.createElement("span",{className:"rf0-dt-item-path"},t),e.createElement("span",{className:"rf0-dt-item-msg rf0-dt-item-msg--ok"},"changed"))))),e.createElement("p",{className:"rf0-dt-status","aria-live":"polite"},e.createElement("span",{className:_?"rf0-dt-on":void 0},"submitting ",e.createElement("b",null,String(_))),e.createElement("span",null,"submits ",e.createElement("b",null,$)),e.createElement("span",{className:L(A)},"ok"," ",e.createElement("b",null,void 0===A?"–":String(A)))),e.createElement("div",{className:"rf0-dt-actions"},e.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>u(o,o.initialValues)},"Reset"),e.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>m(o)},"Validate")))}export{$ as Devtools};
1
+ import*as e from"react";import t,{useCallback as n,useRef as r,createContext as o,useState as a,useContext as l,useId as i}from"react";import{o as s,g as d,a as c,b as f,c as p,r as u,t as m}from"../form-_6GaGvln.mjs";var b,g={exports:{}},x={};var h,E,y={};var v=(E||(E=1,"production"===process.env.NODE_ENV?g.exports=function(){if(b)return x;b=1;var e=t,n="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},r=e.useState,o=e.useEffect,a=e.useLayoutEffect,l=e.useDebugValue;function i(e){var t=e.getSnapshot;e=e.value;try{var r=t();return!n(e,r)}catch(e){return!0}}var s="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(e,t){var n=t(),s=r({inst:{value:n,getSnapshot:t}}),d=s[0].inst,c=s[1];return a(function(){d.value=n,d.getSnapshot=t,i(d)&&c({inst:d})},[e,n,t]),o(function(){return i(d)&&c({inst:d}),e(function(){i(d)&&c({inst:d})})},[e]),l(n),n};return x.useSyncExternalStore=void 0!==e.useSyncExternalStore?e.useSyncExternalStore:s,x}():g.exports=(h||(h=1,"production"!==process.env.NODE_ENV&&function(){function e(e){var t=e.getSnapshot;e=e.value;try{var n=t();return!r(e,n)}catch(e){return!0}}"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());var n=t,r="function"==typeof Object.is?Object.is:function(e,t){return e===t&&(0!==e||1/e==1/t)||e!=e&&t!=t},o=n.useState,a=n.useEffect,l=n.useLayoutEffect,i=n.useDebugValue,s=!1,d=!1,c="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(e,t){return t()}:function(t,c){s||void 0===n.startTransition||(s=!0,console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));var f=c();if(!d){var p=c();r(f,p)||(console.error("The result of getSnapshot should be cached to avoid an infinite loop"),d=!0)}var u=(p=o({inst:{value:f,getSnapshot:c}}))[0].inst,m=p[1];return l(function(){u.value=f,u.getSnapshot=c,e(u)&&m({inst:u})},[t,f,c]),a(function(){return e(u)&&m({inst:u}),t(function(){e(u)&&m({inst:u})})},[t]),i(f),f};y.useSyncExternalStore=void 0!==n.useSyncExternalStore?n.useSyncExternalStore:c,"undefined"!=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__&&"function"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop&&__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error())}()),y)),g.exports);function w(e,t,o){return function(e,t){const o=r(null);null===o.current&&(o.current={hasValue:!1});const a=o.current,l=r(t);l.current=t;const i=n(()=>(a.hasValue||(a.value=l.current(),a.hasValue=!0),a.value),[a]),s=n(t=>(a.hasValue=!1,e(()=>{a.hasValue=!1,t()})),[e,a]);return v.useSyncExternalStore(s,i,i)}(n(n=>s(e,t,n),[e,t]),o)}const S=o(null);S.Provider;o(null).Provider;function k({name:t,value:n,depth:r=0}){const[o,l]=a(r<=1),i=void 0===t?null:e.createElement(e.Fragment,null,e.createElement("span",{className:"rf0-dt-key"},String(t)),e.createElement("span",{className:"rf0-dt-punct"},": "));if(null!==n&&"object"==typeof n){const t=Array.isArray(n),a=t?n.map((e,t)=>[t,e]):Object.entries(n),s=t?"[":"{",d=t?"]":"}",c=o?"":`${s}…${d} ${a.length}`;return e.createElement("div",{className:"rf0-dt-row",style:{paddingLeft:12*r}},e.createElement("button",{type:"button",className:"rf0-dt-node-toggle","aria-expanded":o,onClick:()=>l(!o)},e.createElement("span",{className:"rf0-dt-caret"},o?"▾":"▸"),i,e.createElement("span",{className:"rf0-dt-punct"},o?s:c)),o&&e.createElement(e.Fragment,null,a.map(([t,n])=>e.createElement(k,{key:String(t),name:t,value:n,depth:r+1})),e.createElement("span",{className:"rf0-dt-punct",style:{paddingLeft:12*r}},d)))}return e.createElement("span",{className:"rf0-dt-row",style:{paddingLeft:12*r,display:"block"}},i,e.createElement(N,{value:n}))}function N({value:t}){return void 0===t?e.createElement("span",{className:"rf0-dt-null"},"undefined"):null===t?e.createElement("span",{className:"rf0-dt-null"},"null"):"string"==typeof t?e.createElement("span",{className:"rf0-dt-string"},'"',t,'"'):"boolean"==typeof t?e.createElement("span",{className:"rf0-dt-boolean"},String(t)):e.createElement("span",{className:"rf0-dt-number"},String(t))}const _="react-f0rm-devtools-style";!function(){if("undefined"==typeof document)return;if(document.getElementById(_))return;const e=document.createElement("style");e.id=_,e.textContent="\n.rf0-dt {\n position: fixed;\n z-index: 2147483000;\n box-sizing: border-box;\n width: 308px;\n max-width: calc(100vw - 16px);\n max-height: min(70vh, 560px);\n display: flex;\n flex-direction: column;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 11px;\n line-height: 1.45;\n color: #c7d0dc;\n background: #0c1017;\n border: 1px solid #1f2735;\n border-radius: 4px;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);\n}\n.rf0-dt *,\n.rf0-dt-badge * {\n box-sizing: border-box;\n}\n.rf0-dt--top-right { top: 8px; right: 8px; }\n.rf0-dt--bottom-right { bottom: 8px; right: 8px; }\n.rf0-dt--top-left { top: 8px; left: 8px; }\n.rf0-dt--bottom-left { bottom: 8px; left: 8px; }\n\n/* ---- header -------------------------------------------------------- */\n.rf0-dt-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 5px 6px 5px 9px;\n border-bottom: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-title {\n flex: 1;\n min-width: 0;\n color: #8b96a5;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.14em;\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.rf0-dt-title::before {\n content: '';\n display: inline-block;\n width: 6px;\n height: 6px;\n margin-right: 6px;\n border-radius: 50%;\n background: #e2b93b;\n vertical-align: 1px;\n}\n.rf0-dt-headerbtn {\n border: 1px solid transparent;\n border-radius: 3px;\n padding: 1px 5px;\n color: #8b96a5;\n background: transparent;\n font: inherit;\n font-size: 10px;\n cursor: pointer;\n}\n.rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }\n.rf0-dt-headerbtn:focus-visible,\n.rf0-dt-tab:focus-visible,\n.rf0-dt-action:focus-visible,\n.rf0-dt-badge:focus-visible,\n.rf0-dt-node-toggle:focus-visible {\n outline: 1px solid #e2b93b;\n outline-offset: 1px;\n}\n\n/* ---- tabs ---------------------------------------------------------- */\n.rf0-dt-tablist {\n display: flex;\n border-bottom: 1px solid #1f2735;\n background: #0e131b;\n}\n.rf0-dt-tab {\n flex: 1;\n padding: 4px 2px 5px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: transparent;\n color: #6b7686;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n white-space: nowrap;\n}\n.rf0-dt-tab:hover { color: #aab5c4; }\n.rf0-dt-tab[aria-selected='true'] {\n color: #e7edf4;\n border-bottom-color: #e2b93b;\n}\n.rf0-dt-tab-count {\n margin-left: 3px;\n color: inherit;\n opacity: 0.75;\n}\n.rf0-dt-tab--danger[aria-selected='true'] {\n border-bottom-color: #f0647c;\n}\n\n/* ---- panels -------------------------------------------------------- */\n.rf0-dt-panel {\n flex: 1;\n min-height: 84px;\n overflow: auto;\n padding: 6px 8px;\n scrollbar-width: thin;\n scrollbar-color: #2a3547 transparent;\n}\n.rf0-dt-empty {\n padding: 10px 2px;\n color: #4d5766;\n font-style: italic;\n}\n\n/* json tree */\n.rf0-dt-row {\n display: block;\n white-space: pre;\n tab-size: 2;\n}\n.rf0-dt-node-toggle {\n border: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n white-space: pre;\n}\n.rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }\n.rf0-dt-caret {\n display: inline-block;\n width: 1.2em;\n color: #4d5766;\n}\n.rf0-dt-key { color: #8b96a5; }\n.rf0-dt-punct { color: #4d5766; }\n.rf0-dt-string { color: #8fd68a; }\n.rf0-dt-number { color: #e2b93b; }\n.rf0-dt-boolean { color: #6fb3d9; }\n.rf0-dt-null { color: #55607080; font-style: italic; }\n\n/* errors / touched / dirty lists */\n.rf0-dt-item {\n padding: 3px 2px;\n border-bottom: 1px dotted #1a2230;\n display: flex;\n gap: 8px;\n align-items: baseline;\n}\n.rf0-dt-item:last-child { border-bottom: 0; }\n.rf0-dt-item-path {\n color: #aab5c4;\n word-break: break-all;\n}\n.rf0-dt-item-msg {\n color: #f0647c;\n word-break: break-word;\n}\n.rf0-dt-item-msg--ok { color: #8fd68a; }\n.rf0-dt-item-tag {\n flex: none;\n color: #4d5766;\n font-size: 10px;\n}\n.rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }\n.rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }\n\n/* ---- submit status + actions --------------------------------------- */\n.rf0-dt-status {\n display: flex;\n gap: 10px;\n padding: 4px 9px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n color: #6b7686;\n font-size: 10px;\n letter-spacing: 0.04em;\n white-space: nowrap;\n overflow: hidden;\n}\n.rf0-dt-status b { color: #aab5c4; font-weight: 400; }\n.rf0-dt-status .rf0-dt-on { color: #e2b93b; }\n.rf0-dt-status .rf0-dt-ok { color: #8fd68a; }\n.rf0-dt-status .rf0-dt-err { color: #f0647c; }\n.rf0-dt-actions {\n display: flex;\n gap: 6px;\n padding: 6px 8px;\n border-top: 1px solid #1f2735;\n background: #10151e;\n}\n.rf0-dt-action {\n flex: 1;\n padding: 3px 0;\n border: 1px solid #2a3547;\n border-radius: 3px;\n background: #151b26;\n color: #c7d0dc;\n font: inherit;\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n cursor: pointer;\n}\n.rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }\n.rf0-dt-action:active { transform: translateY(1px); }\n\n/* ---- collapsed badge ----------------------------------------------- */\n.rf0-dt-badge {\n position: fixed;\n z-index: 2147483000;\n width: 26px;\n height: 26px;\n border: 1px solid #2a3547;\n border-radius: 50%;\n background: #0c1017;\n color: #e2b93b;\n font: inherit;\n font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',\n Menlo, Consolas, 'Liberation Mono', monospace;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.02em;\n cursor: pointer;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);\n}\n.rf0-dt-badge:hover { border-color: #e2b93b; }\n.rf0-dt-badge--top-right { top: 10px; right: 10px; }\n.rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }\n.rf0-dt-badge--top-left { top: 10px; left: 10px; }\n.rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }\n.rf0-dt-badge .rf0-dt-dot {\n position: absolute;\n top: 3px;\n right: 3px;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: #f0647c;\n display: none;\n}\n.rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }\n",document.head.appendChild(e)}();const O=["values","errors","touched","dirty"];function L(e){if(void 0!==e)return e?"rf0-dt-ok":"rf0-dt-err"}function C(e){if(null===e||"object"!=typeof e)return 1;let t=0;for(const n of Object.values(e))t+=C(n);return t}function $({form:t,position:n="top-right"}){const r=l(S),o=t??r;if(!o)throw new Error("<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider.");const[s,b]=a(!0),[g,x]=a("values"),h=i().replace(/[^a-zA-Z0-9-]/g,""),E=w(o.emitter,"change",f.bind(null,o)),y=w(o.emitter,"errors",p.bind(null,o)),v=function(e){return w(e.emitter,"touched",d.bind(null,e))}(o),N=function(e){return w(e.emitter,"change",c.bind(null,e))}(o),_=function(e){return w(e.emitter,"submitting",()=>e.isSubmitting)}(o),$=function(e){return w(e.emitter,"submitCount",()=>e.submitCount)}(o),A=w(o.emitter,"submitSuccessful",()=>o.isSubmitSuccessful);if(!s)return e.createElement("button",{type:"button",className:`rf0-dt-badge rf0-dt-badge--${n}${y.length>0?" rf0-dt-badge--has-errors":""}`,"aria-expanded":!1,"aria-label":`Open react-f0rm devtools (${y.length} errors)`,onClick:()=>b(!0)},"f0",e.createElement("span",{className:"rf0-dt-dot"}));const V={values:C(E),errors:y.length,touched:v.length,dirty:Object.keys(N).length};return e.createElement("section",{className:`rf0-dt rf0-dt--${n}`,"aria-label":"react-f0rm devtools"},e.createElement("header",{className:"rf0-dt-header"},e.createElement("span",{className:"rf0-dt-title"},"react-f0rm"),e.createElement("button",{type:"button",className:"rf0-dt-headerbtn","aria-label":"Collapse devtools",onClick:()=>b(!1)},"–")),e.createElement("div",{className:"rf0-dt-tablist",role:"tablist","aria-label":"Form state",tabIndex:-1,onKeyDown:e=>{const t={ArrowRight:1,ArrowLeft:-1}[e.key];if(!t)return;e.preventDefault();const n=O[(O.indexOf(g)+t+O.length)%O.length];x(n),document.getElementById(`${h}-tab-${n}`)?.focus()}},O.map(t=>e.createElement("button",{key:t,id:`${h}-tab-${t}`,type:"button",role:"tab",className:"rf0-dt-tab"+("errors"===t?" rf0-dt-tab--danger":""),"aria-selected":g===t,"aria-controls":`${h}-panel-${t}`,tabIndex:g===t?0:-1,onClick:()=>x(t)},t,e.createElement("span",{className:"rf0-dt-tab-count"},V[t])))),e.createElement("div",{id:`${h}-panel-${g}`,role:"tabpanel","aria-labelledby":`${h}-tab-${g}`,className:"rf0-dt-panel"},"values"===g&&e.createElement(k,{value:E}),"errors"===g&&(0===y.length?e.createElement("p",{className:"rf0-dt-empty"},"no errors"):y.map(({path:t,type:n,message:r},o)=>e.createElement("div",{key:`${t}:${o}`,className:"rf0-dt-item"},e.createElement("span",{className:"rf0-dt-item-path"},t),e.createElement("span",{className:"rf0-dt-item-msg"},r),e.createElement("span",{className:"rf0-dt-item-tag"},n)))),"touched"===g&&(0===v.length?e.createElement("p",{className:"rf0-dt-empty"},"no touched fields"):v.map(t=>e.createElement("div",{key:t,className:"rf0-dt-item rf0-dt-item--touched"},e.createElement("span",{className:"rf0-dt-item-path"},t)))),"dirty"===g&&(0===Object.keys(N).length?e.createElement("p",{className:"rf0-dt-empty"},"no dirty fields"):Object.keys(N).map(t=>e.createElement("div",{key:t,className:"rf0-dt-item rf0-dt-item--dirty"},e.createElement("span",{className:"rf0-dt-item-path"},t),e.createElement("span",{className:"rf0-dt-item-msg rf0-dt-item-msg--ok"},"changed"))))),e.createElement("p",{className:"rf0-dt-status","aria-live":"polite"},e.createElement("span",{className:_?"rf0-dt-on":void 0},"submitting ",e.createElement("b",null,String(_))),e.createElement("span",null,"submits ",e.createElement("b",null,$)),e.createElement("span",{className:L(A)},"ok"," ",e.createElement("b",null,void 0===A?"–":String(A)))),e.createElement("div",{className:"rf0-dt-actions"},e.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>u(o,o.initialValues)},"Reset"),e.createElement("button",{type:"button",className:"rf0-dt-action",onClick:()=>m(o)},"Validate")))}export{$ as Devtools};
2
2
  //# sourceMappingURL=index.mjs.map