Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: edit a notification flow on a canvas

A custom flow opens at /notifications/<id>: its nodes drawn left to right
(source, then matches, then todos), and a panel to edit the picked node: the
source's events, a match's conditions, a todo's recipients, op, key and text.
Add a match or todo below a node, move a branch under another match, or remove
it; an edit the controller would refuse (a todo with children, a second
parent, a cycle, over the node or depth cap) is turned away with the reason.

Edits stay in a draft until save, which sends the whole document at the
revision read. A stale revision is told apart from a refused document: the
editor offers to reload or to keep the draft on top of the latest. The form
choices come from the controller's schema when it sends one.

The list page gains edit and duplicate on custom flows, duplicate on the
built-ins, and a new-flow box; a new flow's id avoids the built-ins' too.
This commit is contained in:
iris 2026-10-24 14:00:44 +02:00 • committed by mara
commit f28e1e9063

View file

@ -44,3 +44,132 @@
.notify-off {
opacity: 0.6;
}
/* The flow list's new-flow row and each flow's actions. */
.notify-new {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: 0.5em;
}
.notify-flow-head > * + * {
margin-left: 0.5em;
}
/* FlowEditorPage — the head row, the canvas and the inspector beside it. */
.notify-editor-head {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: 0.75em;
margin-bottom: 0.5em;
}
.notify-editor-body {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
gap: 1em;
}
.notify-canvas {
flex: 1 1 24em;
min-width: 0;
max-height: 70vh;
overflow: auto;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg);
}
.notify-edge {
fill: none;
stroke: var(--muted);
stroke-width: 1.5;
}
.notify-box {
cursor: pointer;
}
.notify-box rect {
fill: var(--bg-elev);
stroke: var(--border);
stroke-width: 1.5;
}
.notify-box-source rect {
stroke: var(--blue);
}
.notify-box-match rect {
stroke: var(--purple);
}
.notify-box-todo rect {
stroke: var(--green);
}
.notify-box:focus {
outline: none;
}
.notify-box:focus rect,
.notify-box-selected rect {
stroke: var(--fg);
stroke-width: 2.5;
}
.notify-box text {
fill: var(--fg);
font-size: 12px;
pointer-events: none;
}
.notify-box .notify-box-kind {
fill: var(--muted);
font-size: 10px;
}
.notify-inspector {
flex: 0 1 22em;
min-width: 16em;
}
.notify-inspector fieldset {
margin: 0 0 0.75em;
}
.notify-condition {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: 0.5em;
margin-bottom: 0.4em;
}
.notify-inspector-actions {
display: flex;
flex-wrap: wrap;
gap: 0.5em;
margin-bottom: 0.75em;
}
.notify-node-id,
.notify-hint {
color: var(--muted);
font-size: 0.85em;
}
.notify-stale,
.notify-notice {
margin-bottom: 0.5em;
padding: 0.4em 0.6em;
border: 1px solid var(--red);
border-radius: 6px;
}
.notify-name {
flex: 0 1 24em;
}