Form, Field, and app-owned Zod validation.
@prepared911/ui for the controls and keep the schema in the feature:
Form from @prepared911/ui/form is the form element. It does not ship useZodForm.Field from @prepared911/ui/field is the labeled control. FormInput is not an export.Select from @prepared911/ui/select is the select. An empty value is null.FormLayoutStandard, FormLayoutSectioned, FormLayoutDrawer, and FormDialog are the shells.onSubmit={form.handleSubmit(fn)}.Form does not track dirty state.Input. Those belong on Field.Field inside the controller and pass the form's submit handler to Form or the layout block. @prepared911/ui does not export a Zod resolver.
FormDialog (@prepared911/ui/blocks/form-dialog) is the dialog shell. FormLayoutDrawer (@prepared911/ui/blocks/form-layout-drawer) is the drawer shell. Both follow Form's submit contract and ask before closing a dirty form. They do not use a modal provider or a drawer portal id.
If onSubmit returns a promise, the form is pending until it settles. useFormPending() from @prepared911/ui/form is the flag for the Save button's loading prop.