diff --git a/frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx b/frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx
index 0e5536cb..a83a5dd1 100644
--- a/frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx
+++ b/frontend/packages/swarm-ui/src/pages/ComponentsPage.tsx
@@ -219,6 +219,36 @@ function DialogSample() {
);
}
+// `plain`: a `Panel`'s own header bar/border is the dialog's
+// only card, not a second one nested inside `Dialog`'s own — pair
+// `plain` with `Panel`'s `onClose` so the close button still exists,
+// just moved into that header instead of floating over the whole thing.
+function PlainDialogSample() {
+ const [open, setOpen] = useState(false);
+ return (
+ <>
+
+
+ >
+ );
+}
+
// Own state for the same reason `TextFieldSample` needs it — a
// controlled sample has to actually respond to interaction.
function MultiselectFilterSample() {
@@ -458,6 +488,9 @@ export function ComponentsPage() {
+
+
+
diff --git a/frontend/packages/swarm-ui/src/pages/CreateAgentForm.tsx b/frontend/packages/swarm-ui/src/pages/CreateAgentForm.tsx
index 768b5513..0f234f70 100644
--- a/frontend/packages/swarm-ui/src/pages/CreateAgentForm.tsx
+++ b/frontend/packages/swarm-ui/src/pages/CreateAgentForm.tsx
@@ -72,7 +72,14 @@ type SubmitState =
// exception report on this page.
const NAME_PATTERN = "[a-z0-9\\-]{1,63}";
-export function CreateAgentForm() {
+export function CreateAgentForm({
+ onClose,
+}: {
+ /** Rendered as the form panel's own close button — this form always
+ * mounts inside a `plain` `Dialog` (see `AgentsPage`), which no longer
+ * floats its own. */
+ onClose?: () => void;
+}) {
const [name, setName] = useState("");
const [hive, setHive] = useState("");
// `null` = still loading, `[]` = loaded but empty (a real, if unusual,
@@ -132,7 +139,7 @@ export function CreateAgentForm() {
return (
-
+
Create a new agent's swarm-level identity. This only queues the job
— check jobs to watch it settle.
diff --git a/frontend/packages/swarm-ui/src/pages/LinkMatrixAccountForm.tsx b/frontend/packages/swarm-ui/src/pages/LinkMatrixAccountForm.tsx
index 081c0b81..de08cdbe 100644
--- a/frontend/packages/swarm-ui/src/pages/LinkMatrixAccountForm.tsx
+++ b/frontend/packages/swarm-ui/src/pages/LinkMatrixAccountForm.tsx
@@ -63,9 +63,14 @@ const MODE_OPTIONS = [
export function LinkMatrixAccountForm({
hive,
agent,
+ onClose,
}: {
hive: string;
agent: string;
+ /** Rendered as the panel header's own close button — this form always
+ * mounts inside a `plain` `Dialog` (see `AgentsPage`), which no longer
+ * floats its own. */
+ onClose?: () => void;
}) {
const [account, setAccount] = useState("");
const [mode, setMode] = useState("token");
@@ -127,7 +132,11 @@ export function LinkMatrixAccountForm({
}
return (
-
+
Writes the credential to the swarm secret store and notifies{" "}
{hive} to deliver it. The agent's own matrix daemon
diff --git a/frontend/packages/swarm-ui/src/pages/agents/AgentsPage.tsx b/frontend/packages/swarm-ui/src/pages/agents/AgentsPage.tsx
index 6ed2fb62..4370d6bf 100644
--- a/frontend/packages/swarm-ui/src/pages/agents/AgentsPage.tsx
+++ b/frontend/packages/swarm-ui/src/pages/agents/AgentsPage.tsx
@@ -592,13 +592,15 @@ export function AgentsPage() {
open={createOpen}
onClose={() => setCreateOpen(false)}
label="create agent"
+ plain
>
-
+ setCreateOpen(false)} />
diff --git a/frontend/packages/swarm-ui/src/ui/dialog/Dialog.css b/frontend/packages/swarm-ui/src/ui/dialog/Dialog.css
index 49706dca..2c82b413 100644
--- a/frontend/packages/swarm-ui/src/ui/dialog/Dialog.css
+++ b/frontend/packages/swarm-ui/src/ui/dialog/Dialog.css
@@ -32,6 +32,16 @@
.ui-dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
}
+/* `plain` (see Dialog.tsx's file-top comment): drop this dialog's own
+ card look entirely — sizing/backdrop/scroll behavior above are the
+ only things left that are actually this component's job once the
+ content supplies its own bordered card (a `Panel`). */
+.ui-dialog-plain {
+ border: none;
+ border-radius: 0;
+ background: none;
+ padding: 0;
+}
.ui-dialog-close {
position: absolute;
top: 0.75em;
@@ -60,3 +70,9 @@
render directly under the button instead of beside it. */
padding-right: 3.25em;
}
+/* `plain` has no floating close button of its own to clear — the
+ content's own `Panel` header carries one instead (see `.ui-panel-close`
+ in Panel.css). */
+.ui-dialog-body-plain {
+ padding-right: 0;
+}
diff --git a/frontend/packages/swarm-ui/src/ui/dialog/Dialog.tsx b/frontend/packages/swarm-ui/src/ui/dialog/Dialog.tsx
index f699a6d2..bf98cb81 100644
--- a/frontend/packages/swarm-ui/src/ui/dialog/Dialog.tsx
+++ b/frontend/packages/swarm-ui/src/ui/dialog/Dialog.tsx
@@ -10,6 +10,18 @@
// from its former life as a standalone route — reusing that unchanged
// is simpler and lower-risk than re-deriving a modal-specific layout
// for content mara has already reviewed).
+//
+// `plain` (mara, screenshot of the matrix-account dialog: "the outer
+// card does not serve a purpose. move the close btn into the bar and
+// drop one layer"): content that's already a `Panel` — a bordered
+// card with its own header bar — nested inside this dialog's own
+// bordered card produced two concentric boxes in the same `--bg-elev`,
+// one serving no purpose. `plain` drops this dialog's own card chrome
+// (border/background/padding) and its floating close button, so a
+// `Panel` (with its own `onClose` wired into its header) becomes the
+// dialog's sole visible card instead of a second one nested inside it.
+// Escape and backdrop-click still close it either way — those are the
+// native `