the universal sandbox runtime for agents and humans. pocketenv.io
sandbox openclaw agent claude-code vercel-sandbox deno-sandbox cloudflare-sandbox atproto sprites daytona
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

Reset forms and show validation states in modals

Call reset() when closing modals and on Escape to clear form state
for AddEnvironmentVariable, AddFile, AddSecret, and AddVolume.
Apply is-invalid/input-error classes to inputs and textareas,
replace text-error with helper-text, and add a .is-invalid CSS rule

+42 -20
+7 -4
apps/web/src/components/contextmenu/AddEnvironmentVariableModal/AddEnvironmentVariableModal.tsx
··· 50 50 const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => { 51 51 e.stopPropagation(); 52 52 if (e.target === e.currentTarget) { 53 + reset(); 53 54 onClose(); 54 55 } 55 56 }; ··· 59 60 }; 60 61 61 62 const handleCloseButton = (e: React.MouseEvent<HTMLButtonElement>) => { 63 + reset(); 62 64 e.stopPropagation(); 63 65 onClose(); 64 66 }; ··· 71 73 value: data.value, 72 74 }); 73 75 setIsLoading(false); 76 + reset(); 74 77 reset(); 75 78 onClose(); 76 79 }; ··· 116 119 <input 117 120 type="text" 118 121 placeholder="YOUR_VARIABLE_NAME" 119 - className="grow" 122 + className={`grow ${errors.name ? "is-invalid" : ""}`} 120 123 autoComplete="off" 121 124 data-1p-ignore 122 125 data-lpignore="true" ··· 125 128 /> 126 129 </div> 127 130 {errors.name && ( 128 - <span className="text-error text-[12px] mt-1"> 131 + <span className="helper-text text-[12px] mt-1"> 129 132 {errors.name.message} 130 133 </span> 131 134 )} ··· 136 139 </span> 137 140 </label> 138 141 <textarea 139 - className="textarea max-w-full h-[250px] text-[14px] font-semibold" 142 + className={`textarea max-w-full h-[250px] text-[14px] font-semibold ${errors.value ? "is-invalid" : ""}`} 140 143 aria-label="Textarea" 141 144 placeholder="Variable Value" 142 145 {...register("value")} 143 146 ></textarea> 144 147 {errors.value && ( 145 - <span className="text-error text-[12px] mt-1 block"> 148 + <span className="helper-text text-[12px] mt-1 block"> 146 149 {errors.value.message} 147 150 </span> 148 151 )}
+7 -4
apps/web/src/components/contextmenu/AddFileModal/AddFileModal.tsx
··· 33 33 useEffect(() => { 34 34 const handleEscapeKey = (event: KeyboardEvent) => { 35 35 if (event.key === "Escape" && isOpen) { 36 + reset(); 36 37 onClose(); 37 38 } 38 39 }; ··· 41 42 return () => { 42 43 document.removeEventListener("keydown", handleEscapeKey); 43 44 }; 44 - }, [isOpen, onClose]); 45 + }, [isOpen, onClose, reset]); 45 46 46 47 const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => { 47 48 e.stopPropagation(); 48 49 if (e.target === e.currentTarget) { 50 + reset(); 49 51 onClose(); 50 52 } 51 53 }; ··· 55 57 }; 56 58 57 59 const handleCloseButton = (e: React.MouseEvent<HTMLButtonElement>) => { 60 + reset(); 58 61 e.stopPropagation(); 59 62 onClose(); 60 63 }; ··· 111 114 <input 112 115 type="text" 113 116 placeholder="File Mount Path, e.g /root/.openclaw/openclaw.json" 114 - className="grow" 117 + className={`grow ${errors.path ? "is-invalid" : ""}`} 115 118 autoComplete="off" 116 119 data-1p-ignore 117 120 data-lpignore="true" ··· 131 134 </span> 132 135 </label> 133 136 <textarea 134 - className="textarea max-w-full h-[250px] text-[14px] font-semibold" 137 + className={`textarea max-w-full h-[250px] text-[14px] font-semibold ${errors.content ? "is-invalid" : ""}`} 135 138 aria-label="Textarea" 136 139 placeholder="File Content" 137 140 {...register("content")} 138 141 ></textarea> 139 142 {errors.content && ( 140 - <span className="text-error text-[12px] mt-1 block"> 143 + <span className="helper-text text-[12px] mt-1 block"> 141 144 {errors.content.message} 142 145 </span> 143 146 )}
+11 -6
apps/web/src/components/contextmenu/AddSecretModal/AddSecretModal.tsx
··· 33 33 useEffect(() => { 34 34 const handleEscapeKey = (event: KeyboardEvent) => { 35 35 if (event.key === "Escape" && isOpen) { 36 + reset(); 36 37 onClose(); 37 38 } 38 39 }; ··· 41 42 return () => { 42 43 document.removeEventListener("keydown", handleEscapeKey); 43 44 }; 44 - }, [isOpen, onClose]); 45 + }, [isOpen, onClose, reset]); 45 46 46 47 const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => { 47 48 e.stopPropagation(); 48 49 if (e.target === e.currentTarget) { 50 + reset(); 49 51 onClose(); 50 52 } 51 53 }; ··· 56 58 57 59 const handleCloseButton = (e: React.MouseEvent<HTMLButtonElement>) => { 58 60 e.stopPropagation(); 61 + reset(); 59 62 onClose(); 60 63 }; 61 64 ··· 108 111 Name 109 112 </span> 110 113 </label> 111 - <div className="input input-bordered w-full input-lg text-[15px] font-semibold bg-transparent"> 114 + <div 115 + className={`input input-bordered w-full input-lg text-[15px] font-semibold bg-transparent ${errors.name ? "input-error" : ""}`} 116 + > 112 117 <input 113 118 type="text" 114 119 placeholder="YOUR_SECRET_NAME" 115 - className="grow" 120 + className={`grow ${errors.name ? "is-invalid" : ""}`} 116 121 autoComplete="off" 117 122 data-1p-ignore 118 123 data-lpignore="true" ··· 121 126 /> 122 127 </div> 123 128 {errors.name && ( 124 - <span className="text-error text-[12px] mt-1"> 129 + <span className="helper-text text-[12px] mt-1"> 125 130 {errors.name.message} 126 131 </span> 127 132 )} ··· 132 137 </span> 133 138 </label> 134 139 <textarea 135 - className="textarea max-w-full h-[250px] text-[14px] font-semibold" 140 + className={`textarea max-w-full h-[250px] text-[14px] font-semibold ${errors.value ? "is-invalid" : ""}`} 136 141 aria-label="Textarea" 137 142 placeholder="Secret Value" 138 143 {...register("value")} 139 144 ></textarea> 140 145 {errors.value && ( 141 - <span className="text-error text-[12px] mt-1 block"> 146 + <span className="helper-text text-[12px] mt-1 block"> 142 147 {errors.value.message} 143 148 </span> 144 149 )}
+13 -6
apps/web/src/components/contextmenu/AddVolumeModal/AddVolumeModal.tsx
··· 33 33 useEffect(() => { 34 34 const handleEscapeKey = (event: KeyboardEvent) => { 35 35 if (event.key === "Escape" && isOpen) { 36 + reset(); 36 37 onClose(); 37 38 } 38 39 }; ··· 41 42 return () => { 42 43 document.removeEventListener("keydown", handleEscapeKey); 43 44 }; 44 - }, [isOpen, onClose]); 45 + }, [isOpen, onClose, reset]); 45 46 46 47 const handleBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => { 47 48 e.stopPropagation(); 48 49 if (e.target === e.currentTarget) { 50 + reset(); 49 51 onClose(); 50 52 } 51 53 }; ··· 56 58 57 59 const handleCloseButton = (e: React.MouseEvent<HTMLButtonElement>) => { 58 60 e.stopPropagation(); 61 + reset(); 59 62 onClose(); 60 63 }; 61 64 ··· 108 111 Name 109 112 </span> 110 113 </label> 111 - <div className="input input-bordered w-full input-lg text-[15px] font-semibold bg-transparent"> 114 + <div 115 + className={`input input-bordered w-full input-lg text-[15px] font-semibold bg-transparent ${errors.name ? "is-invalid" : ""}`} 116 + > 112 117 <input 113 118 type="text" 114 119 placeholder="Your Volume Name" 115 - className="grow" 120 + className={`grow ${errors.name ? "is-invalid" : ""}`} 116 121 autoComplete="off" 117 122 data-1p-ignore 118 123 data-lpignore="true" ··· 131 136 Path 132 137 </span> 133 138 </label> 134 - <div className="input input-bordered w-full input-lg text-[15px] font-semibold bg-transparent"> 139 + <div 140 + className={`input input-bordered w-full input-lg text-[15px] font-semibold bg-transparent ${errors.path ? "is-invalid" : ""}`} 141 + > 135 142 <input 136 143 type="text" 137 144 placeholder="Mount Path, e.g /data" 138 - className="grow" 145 + className={`grow ${errors.path ? "is-invalid" : ""}`} 139 146 autoComplete="off" 140 147 data-1p-ignore 141 148 data-lpignore="true" ··· 144 151 /> 145 152 </div> 146 153 {errors.path && ( 147 - <span className="text-error text-[12px] mt-1 block"> 154 + <span className="helper-text text-[12px] mt-1 block"> 148 155 {errors.path.message} 149 156 </span> 150 157 )}
+4
apps/web/src/index.css
··· 167 167 .modal-footer .btn { 168 168 border-color: rgba(229, 229, 229, 0.2); 169 169 } 170 + 171 + .is-invalid { 172 + border-color: var(--color-error) !important; 173 + }