feat: prefill agent-edit modal, Ctrl+Enter submits modals, improve README
- AgentFormModal: sync form state to current agent values when the modal opens (useEffect on open/agent), so edit always shows the agent's current name, description, website, icon URL, and max-pending value. - Modal: add optional onSubmit prop; Ctrl+Enter (or ⌘+Enter) fires it, covering AgentFormModal, DecisionModal, and both Settings modals (disable 2FA, delete account) without breaking textarea newlines or text-input behaviour. - README: substantially expanded — purpose, capabilities, architecture, quick-start with Docker Compose, dev setup, testing, agent integration (OpenClaw, generic MCP, REST), and privacy notes. - TODO.md: remove three completed items (prefill modal, Ctrl+Enter, human readme). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import { agents as agentsApi } from "../api/client";
|
||||
import type { Agent } from "../api/types";
|
||||
@@ -27,6 +27,15 @@ export function AgentFormModal({
|
||||
const [maxPending, setMaxPending] = useState(agent?.max_pending_requests ?? 5);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
setName(agent?.name ?? "");
|
||||
setDescription(agent?.description ?? "");
|
||||
setWebsite(agent?.website ?? "");
|
||||
setIconUrl(agent?.icon_url ?? "");
|
||||
setMaxPending(agent?.max_pending_requests ?? 5);
|
||||
}, [open, agent]);
|
||||
|
||||
const submit = async () => {
|
||||
if (!name.trim()) return toast.error("Name is required");
|
||||
setLoading(true);
|
||||
@@ -55,6 +64,7 @@ export function AgentFormModal({
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onSubmit={submit}
|
||||
title={editing ? "Edit agent" : "New agent"}
|
||||
footer={
|
||||
<>
|
||||
|
||||
@@ -75,6 +75,7 @@ export function DecisionModal({
|
||||
<Modal
|
||||
open={!!decision}
|
||||
onClose={onClose}
|
||||
onSubmit={submit}
|
||||
title={m.title}
|
||||
footer={
|
||||
<>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ReactNode, useEffect } from "react";
|
||||
export function Modal({
|
||||
open,
|
||||
onClose,
|
||||
onSubmit,
|
||||
title,
|
||||
children,
|
||||
footer,
|
||||
@@ -10,6 +11,7 @@ export function Modal({
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit?: () => void;
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
footer?: ReactNode;
|
||||
@@ -19,6 +21,7 @@ export function Modal({
|
||||
if (!open) return;
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") onClose();
|
||||
if (e.key === "Enter" && (e.ctrlKey || e.metaKey) && onSubmit) onSubmit();
|
||||
};
|
||||
window.addEventListener("keydown", onKey);
|
||||
document.body.style.overflow = "hidden";
|
||||
@@ -26,7 +29,7 @@ export function Modal({
|
||||
window.removeEventListener("keydown", onKey);
|
||||
document.body.style.overflow = "";
|
||||
};
|
||||
}, [open, onClose]);
|
||||
}, [open, onClose, onSubmit]);
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
|
||||
@@ -223,6 +223,7 @@ export function SettingsPage() {
|
||||
<Modal
|
||||
open={disable2faOpen}
|
||||
onClose={() => setDisable2faOpen(false)}
|
||||
onSubmit={disable2fa}
|
||||
title="Disable 2FA"
|
||||
footer={
|
||||
<>
|
||||
@@ -246,6 +247,7 @@ export function SettingsPage() {
|
||||
<Modal
|
||||
open={deleteOpen}
|
||||
onClose={() => setDeleteOpen(false)}
|
||||
onSubmit={deleteAccount}
|
||||
title="Delete account"
|
||||
footer={
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user