bullpane 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server.mjs +2 -2
- package/package.json +1 -1
- package/web/assets/FlowsPage-BOHg_j5s.js +26 -0
- package/web/assets/{FlowsPage-C1DrWzZu.css → FlowsPage-BRUBQF1y.css} +1 -1
- package/web/assets/{JobTreePage-CbwbeeRl.js → JobTreePage-DWE60jpd.js} +2 -2
- package/web/assets/{McpConsentPage-CI53cgZX.js → McpConsentPage-YpDqETHG.js} +1 -1
- package/web/assets/{index-Dj9D1-XU.js → index-CIHznD1e.js} +2 -2
- package/web/assets/{style-J6Fkg5PC.js → style-B5JqANGk.js} +3 -3
- package/web/index.html +1 -1
- package/web/assets/FlowsPage-Cp6TR7Wa.js +0 -26
package/dist/server.mjs
CHANGED
|
@@ -9170,8 +9170,8 @@ function within(promise, ms) {
|
|
|
9170
9170
|
return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
|
|
9171
9171
|
}
|
|
9172
9172
|
function toDetectedMapEdge(connectionId, e) {
|
|
9173
|
-
const from = flowMapNodeId({ connectionId, queueName: e.
|
|
9174
|
-
const to = flowMapNodeId({ connectionId, queueName: e.
|
|
9173
|
+
const from = flowMapNodeId({ connectionId, queueName: e.to });
|
|
9174
|
+
const to = flowMapNodeId({ connectionId, queueName: e.from });
|
|
9175
9175
|
return { id: detectedEdgeId(from, to), from, to, source: "detected", label: null, evidence: e.evidence };
|
|
9176
9176
|
}
|
|
9177
9177
|
function toManualMapEdge(row) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bullpane",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Self-hosted dashboard for BullMQ and BullMQ Pro, on Redis or on BullMQ 6's Postgres backend: queues, jobs, failures, metrics, server health, flows and Pro groups. A bull-board alternative you can run with npx.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bullmq",
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import{c as ke,r as c,a as Dt,w as Ze,j as e,W as he,B as P,P as ue,S as Ue,C as at,b as Z,D as ge,d as Fe,T as be,f as ot,e as se,g as de,h as me,L as Re,E as Te,i as te,u as Lt,k as Pt,l as rt,m as $t,n as At,o as _e,I as De,p as Rt,q as je,s as Tt,t as R,v as H,x as it,y as _t,z as Oe,A as Ot,F as Bt,G as Ht,H as Vt,J as Qt,K as Wt,M as Gt,N as Xt,O as ve,Q as Yt,R as Zt,U as Ut,X as Be,V as Kt,Y as Le,Z as Jt,_ as lt,$ as ct,a0 as es,a1 as ts,a2 as ss,a3 as ns,a4 as as,a5 as dt,a6 as ut,a7 as os,a8 as rs,a9 as is,aa as ls}from"./index-CIHznD1e.js";import{l as we,u as cs,P as ne,g as ds,B as us,E as ms,H as Ke,R as xs,a as hs,b as mt,c as xt,d as ps,i as ht,e as pt,f as ft,C as gt,M as bt}from"./style-B5JqANGk.js";/**
|
|
2
|
+
* @license lucide-react v0.469.0 - ISC
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the ISC license.
|
|
5
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/const He=ke("ArrowRight",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"m12 5 7 7-7 7",key:"xquz4c"}]]);/**
|
|
7
|
+
* @license lucide-react v0.469.0 - ISC
|
|
8
|
+
*
|
|
9
|
+
* This source code is licensed under the ISC license.
|
|
10
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
11
|
+
*/const fs=ke("FolderInput",[["path",{d:"M2 9V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-1",key:"fm4g5t"}],["path",{d:"M2 13h10",key:"pgb2dq"}],["path",{d:"m9 16 3-3-3-3",key:"6m91ic"}]]);/**
|
|
12
|
+
* @license lucide-react v0.469.0 - ISC
|
|
13
|
+
*
|
|
14
|
+
* This source code is licensed under the ISC license.
|
|
15
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
16
|
+
*/const jt=ke("LayoutGrid",[["rect",{width:"7",height:"7",x:"3",y:"3",rx:"1",key:"1g98yp"}],["rect",{width:"7",height:"7",x:"14",y:"3",rx:"1",key:"6d4xhi"}],["rect",{width:"7",height:"7",x:"14",y:"14",rx:"1",key:"nxv5o0"}],["rect",{width:"7",height:"7",x:"3",y:"14",rx:"1",key:"1bb6yr"}]]);/**
|
|
17
|
+
* @license lucide-react v0.469.0 - ISC
|
|
18
|
+
*
|
|
19
|
+
* This source code is licensed under the ISC license.
|
|
20
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
21
|
+
*/const vt=ke("Link2",[["path",{d:"M9 17H7A5 5 0 0 1 7 7h2",key:"8i5ue5"}],["path",{d:"M15 7h2a5 5 0 1 1 0 10h-2",key:"1b9ql8"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12",key:"1jonct"}]]);/**
|
|
22
|
+
* @license lucide-react v0.469.0 - ISC
|
|
23
|
+
*
|
|
24
|
+
* This source code is licensed under the ISC license.
|
|
25
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
26
|
+
*/const gs=ke("SearchX",[["path",{d:"m13.5 8.5-5 5",key:"1cs55j"}],["path",{d:"m8.5 8.5 5 5",key:"a8mexj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]),Je=(t,s)=>t.position-s.position||t.name.localeCompare(s.name)||t.id.localeCompare(s.id);function Ve(t){const s=t.filter(o=>o.kind==="manual"),l=new Set(s.map(o=>o.id)),d=new Map;for(const o of s){const m=o.parentId&&l.has(o.parentId)&&o.parentId!==o.id?o.parentId:null;d.has(m)||d.set(m,[]),d.get(m).push(o)}const a=new Set,i=(o,m)=>(d.get(o)??[]).slice().sort(Je).filter(v=>!a.has(v.id)).map(v=>(a.add(v.id),{map:v,depth:m,children:i(v.id,m+1)})),n=i(null,0);for(const o of s.slice().sort(Je))a.has(o.id)||(a.add(o.id),n.push({map:o,depth:0,children:i(o.id,1)}));return n}function bs(t,s=new Set){const l=[],d=a=>{for(const i of a)l.push(i),s.has(i.map.id)||d(i.children)};return d(t),l}function js(t,s){var n;const l=new Map(t.map(o=>[o.id,o])),d=[],a=new Set([s]);let i=((n=l.get(s))==null?void 0:n.parentId)??null;for(;i&&!a.has(i);){const o=l.get(i);if(!o)break;d.unshift(o),a.add(i),i=o.parentId}return d}function wt(t,s){const l=Ve(t),d=[{id:null,label:"Top level",depth:0}],a=i=>{for(const n of i)n.map.id!==s&&(d.push({id:n.map.id,label:n.map.name,depth:n.depth+1}),a(n.children))};return a(l),d}function vs(t,s){const l=new Map;for(const a of t)a.kind!=="detected"||!a.connectionId||(l.has(a.connectionId)||l.set(a.connectionId,[]),l.get(a.connectionId).push(a));const d=a=>{const i=s.indexOf(a);return i===-1?Number.MAX_SAFE_INTEGER:i};return[...l.entries()].sort(([a],[i])=>d(a)-d(i)||a.localeCompare(i)).map(([a,i])=>({connectionId:a,maps:i.slice().sort((n,o)=>n.name.localeCompare(o.name))}))}function ws(t,s,l){const{nodeWidth:d,nodeHeight:a,columnGap:i=90,rowGap:n=28}=l,o=new Map,m=new Map;for(const x of t)x.x!=null&&x.y!=null&&m.set(x.id,{x:x.x,y:x.y});const v=t.filter(x=>!m.has(x.id)).map(x=>x.id);if(v.length===0)return o;const f={nodeWidth:d,nodeHeight:a,columnGap:i,rowGap:n};if(m.size===0){const x=we(t.map(r=>({id:r.id})),s,f);for(const r of v){const y=x.get(r);o.set(r,{x:y.x,y:y.y})}return o}const p=new Set(t.map(x=>x.id)),z=new Map,b=new Map;for(const x of p)z.set(x,[]),b.set(x,[]);for(const x of s)!p.has(x.from)||!p.has(x.to)||x.from===x.to||(b.get(x.from).push(x.to),z.get(x.to).push(x.from));const w=x=>{for(const r of m.values())if(Math.abs(r.x-x.x)<d+10&&Math.abs(r.y-x.y)<a+n/2)return!0;return!1},$=x=>{let r={...x};for(let y=0;y<500&&w(r);y++)r={x:r.x,y:r.y+a+n};return r},j=x=>x.reduce((r,y)=>r+y,0)/x.length;let D=v.slice().sort(),h=!0;for(;D.length&&h;){h=!1;const x=[];for(const r of D){const y=z.get(r).filter(g=>m.has(g)).map(g=>m.get(g)),q=b.get(r).filter(g=>m.has(g)).map(g=>m.get(g));let F=null;if(y.length?F={x:Math.max(...y.map(g=>g.x))+d+i,y:j(y.map(g=>g.y))}:q.length&&(F={x:Math.min(...q.map(g=>g.x))-d-i,y:j(q.map(g=>g.y))}),!F){x.push(r);continue}const M=$(F);m.set(r,M),o.set(r,M),h=!0}D=x}if(D.length){const x=new Set(D),r=we(D.map(g=>({id:g})),s.filter(g=>x.has(g.from)&&x.has(g.to)),f),y=[...m.values()].map(g=>g.x),q=[...m.values()].map(g=>g.y),F=Math.min(...y),M=Math.max(...q)+a+n*2;for(const g of D){const T=r.get(g),_=$({x:F+T.x,y:M+T.y});m.set(g,_),o.set(g,_)}}return o}const Ee=["#f778ba","#ff8c42","#a5d64c","#79c0ff","#d2a8ff","#e3b341","#56d4a8","#ff7b72"];function ze(t,s){const l=s.findIndex(a=>a.id===t);if(l!==-1)return Ee[l%Ee.length];let d=0;for(const a of t)d=d*31+a.charCodeAt(0)>>>0;return Ee[d%Ee.length]}function Ns(t,s=[]){const l=new Map;for(const a of t){const i=l.get(a.connectionId);i?i.count++:l.set(a.connectionId,{name:a.connectionName||a.connectionId,count:1})}const d=a=>{const i=s.findIndex(n=>n.id===a);return i===-1?Number.MAX_SAFE_INTEGER:i};return[...l.entries()].sort(([a],[i])=>d(a)-d(i)||a.localeCompare(i)).map(([a,i])=>{var n;return{connectionId:a,name:((n=s.find(o=>o.id===a))==null?void 0:n.name)??i.name,color:ze(a,s),count:i.count}})}function ys(t){const s=t.indexOf(":");return s<=0||s===t.length-1?null:{connectionId:t.slice(0,s),queueName:t.slice(s+1)}}const Pe="flowMaps.collapsed",ks=2400;function Cs({maps:t,loading:s,detectedComplete:l,connections:d,selection:a,canCreate:i,onSelect:n,onNewMap:o}){const[m,v]=c.useState(()=>new Set(Dt(Pe,[]))),f=h=>v(x=>{const r=new Set(x);return r.has(h)?r.delete(h):r.add(h),Ze(Pe,[...r]),r}),p=c.useMemo(()=>Ve(t),[t]),z=c.useMemo(()=>bs(p,m),[p,m]),b=c.useMemo(()=>vs(t,d.map(h=>h.id)),[t,d]),w=h=>{var x;return((x=d.find(r=>r.id===h))==null?void 0:x.name)??h},$=Es(t,s),j=(a==null?void 0:a.kind)==="map"?a.id:null,D=c.useRef(null);return c.useEffect(()=>{var y,q;if(!j||D.current===j||t.length===0)return;D.current=j;const h=new Map(t.map(F=>[F.id,F]));let x=((y=h.get(j))==null?void 0:y.parentId)??null;const r=[];for(;x&&!r.includes(x);)r.push(x),x=((q=h.get(x))==null?void 0:q.parentId)??null;r.some(F=>m.has(F))&&v(F=>{const M=new Set(F);return r.forEach(g=>M.delete(g)),Ze(Pe,[...M]),M})},[j,t,m]),e.jsxs("aside",{className:"flex w-60 shrink-0 flex-col border-r border-border bg-surface xl:w-64","aria-label":"Flow maps",children:[e.jsxs("div",{className:"flex h-11 shrink-0 items-center gap-2 border-b border-border px-3",children:[e.jsx(he,{className:"size-4 text-pro","aria-hidden":!0}),e.jsx("span",{className:"text-sm font-semibold",children:"Flows"}),i&&e.jsx(P,{size:"xs",variant:"ghost",className:"ml-auto",leftIcon:e.jsx(ue,{}),onClick:()=>o(null),title:"New flow map",children:"New map"})]}),e.jsxs("nav",{className:"min-h-0 flex-1 overflow-x-hidden overflow-y-auto px-2 pb-3",children:[e.jsx($e,{title:"Maps",children:s?e.jsxs("div",{className:"space-y-1.5 px-2 py-1",children:[e.jsx(Ue,{className:"h-4 w-3/4"}),e.jsx(Ue,{className:"h-4 w-1/2"})]}):z.length===0?e.jsxs("p",{className:"px-2 py-1 text-[11px] leading-relaxed text-fg-subtle",children:["No maps yet. A map is a named drawing of the queues one process goes through.",i&&e.jsxs(e.Fragment,{children:[" ",e.jsx("button",{type:"button",className:"text-accent hover:underline",onClick:()=>o(null),children:"New map"})]})]}):e.jsx("ul",{role:"tree","aria-label":"Maps",children:z.map(({map:h,depth:x,children:r})=>e.jsx("li",{role:"treeitem","aria-expanded":r.length?!m.has(h.id):void 0,"aria-selected":j===h.id,children:e.jsx(Ae,{depth:x,selected:j===h.id,fresh:$.has(h.id),icon:e.jsx(he,{className:"size-3.5 shrink-0 text-pro/80","aria-hidden":!0}),label:h.name,title:h.description??void 0,count:h.nodeCount,onSelect:()=>n({kind:"map",id:h.id}),expander:r.length?e.jsx("button",{type:"button",className:"flex size-4 shrink-0 items-center justify-center rounded text-fg-subtle hover:text-fg",onClick:()=>f(h.id),"aria-label":m.has(h.id)?`Expand ${h.name}`:`Collapse ${h.name}`,children:e.jsx(at,{className:Z("size-3.5 transition-transform",!m.has(h.id)&&"rotate-90")})}):e.jsx("span",{className:"size-4 shrink-0"}),action:i&&e.jsx(P,{size:"icon-xs",variant:"ghost",className:"opacity-0 group-hover:opacity-100 focus-visible:opacity-100",title:`New map inside "${h.name}"`,"aria-label":`New map inside ${h.name}`,onClick:()=>o(h.id),children:e.jsx(ue,{})})})},h.id))})}),(b.length>0||!l)&&e.jsxs($e,{title:"Detected",hint:e.jsx(be,{content:"From BullMQ FlowProducer: queues linked by the parent field of their jobs. Read-only; copy one to edit it.",side:"right",children:e.jsx("span",{className:"cursor-help normal-case tracking-normal",children:e.jsx(Ms,{})})}),right:!l&&e.jsx(Fe,{size:11,label:"sampling",className:"text-[10px] font-normal tracking-normal normal-case"}),children:[b.map(h=>e.jsxs("div",{className:"mb-1",children:[(d.length>1||b.length>1)&&e.jsxs("div",{className:"flex h-6 items-center gap-1.5 px-2 text-[11px] text-fg-subtle",children:[e.jsx(ge,{className:"size-3","aria-hidden":!0}),e.jsx("span",{className:"truncate",children:w(h.connectionId)})]}),e.jsx("ul",{children:h.maps.map(x=>e.jsx("li",{children:e.jsx(Ae,{depth:0,selected:j===x.id,fresh:$.has(x.id),icon:e.jsx(vt,{className:"size-3.5 shrink-0 text-teal","aria-hidden":!0}),label:x.name,title:"From BullMQ FlowProducer (read-only)",count:x.nodeCount,onSelect:()=>n({kind:"map",id:x.id}),expander:e.jsx("span",{className:"size-4 shrink-0"})})},x.id))})]},h.connectionId)),b.length===0&&e.jsx("p",{className:"px-2 py-1 text-[11px] text-fg-subtle",children:"Sampling jobs for parent links…"})]}),e.jsx($e,{title:"All queues",children:e.jsx("ul",{children:d.map(h=>e.jsx("li",{children:e.jsx(Ae,{depth:0,selected:(a==null?void 0:a.kind)==="connection"&&a.id===h.id,icon:e.jsx(ge,{className:"size-3.5 shrink-0 text-fg-subtle","aria-hidden":!0}),label:h.name,title:`Every queue on ${h.name}, with detected and manual edges`,onSelect:()=>n({kind:"connection",id:h.id}),expander:e.jsx("span",{className:"size-4 shrink-0"})})},h.id))})})]})]})}function Ms(){return e.jsx("span",{className:"rounded bg-teal/15 px-1 py-px text-[9px] font-medium text-teal",children:"auto"})}function $e({title:t,hint:s,right:l,children:d}){return e.jsxs("section",{className:"mt-3",children:[e.jsxs("h2",{className:"mb-1 flex h-5 items-center gap-1.5 px-2 text-[10px] font-semibold tracking-wider text-fg-subtle uppercase",children:[t,s,l&&e.jsx("span",{className:"ml-auto",children:l})]}),d]})}function Ae({depth:t,selected:s,fresh:l,icon:d,label:a,title:i,count:n,onSelect:o,expander:m,action:v}){return e.jsxs("div",{className:Z("group flex h-7 min-w-0 items-center gap-1 rounded-md pr-1 text-[13px] transition-colors",s?"bg-surface-3 text-fg":"text-fg-muted hover:bg-surface-2 hover:text-fg",l&&!s&&"fm-tree-fresh"),style:{paddingLeft:4+t*14},children:[m,e.jsxs("button",{type:"button",onClick:o,className:"flex h-full min-w-0 flex-1 items-center gap-1.5 text-left","aria-current":s||void 0,title:i,children:[d,e.jsx("span",{className:"truncate",children:a}),n!==void 0&&e.jsx("span",{className:"num ml-auto pl-1 text-[10px] text-fg-subtle group-hover:hidden",children:n})]}),v]})}function Es(t,s){const l=c.useRef(null),[d,a]=c.useState(new Set),i=c.useRef([]);return c.useEffect(()=>()=>i.current.forEach(n=>window.clearTimeout(n)),[]),c.useEffect(()=>{if(s)return;if(!l.current){l.current=new Set(t.map(o=>o.id));return}const n=t.filter(o=>!l.current.has(o.id)).map(o=>o.id);n.length!==0&&(n.forEach(o=>l.current.add(o)),a(o=>new Set([...o,...n])),i.current.push(window.setTimeout(()=>a(o=>new Set([...o].filter(m=>!n.includes(m)))),ks)))},[t,s]),d}const Ne=232,ye=92,Ss=140,qe={nodeWidth:Ne,nodeHeight:ye,columnGap:Ss,rowGap:28},Is=c.memo(function({data:s,selected:l,isConnectable:d}){const a=s.counts,i=s.missing?"missing":a.failed>0?"failed":a.active>0?"active":a.waiting+a.prioritized>0?"waiting":"idle",n={missing:"bg-fg-subtle",failed:de.failed.dotClass,active:de.active.dotClass,waiting:de.waiting.dotClass,idle:"bg-border-strong"}[i];return e.jsxs("div",{className:Z("fm-node relative flex flex-col rounded-lg border bg-surface px-3 py-2 text-fg shadow-sm",s.fresh&&"fm-fresh",l?"border-accent shadow-[0_0_0_3px_color-mix(in_srgb,var(--accent)_25%,transparent)]":"border-border-strong hover:border-fg-subtle",s.isPaused&&!l&&"border-dashed",s.missing&&"border-dashed opacity-60"),style:{width:Ne,height:ye},children:[e.jsx(Ke,{type:"target",position:ne.Left,className:"fm-handle",isConnectable:d}),e.jsxs("div",{className:"flex min-w-0 items-center gap-1.5 pr-5",children:[e.jsx("span",{className:Z("status-dot size-2",n,i==="active"&&"pulse text-state-active"),"aria-hidden":!0}),e.jsx("span",{className:"truncate text-[13px] font-semibold",title:s.queueName,children:s.queueName}),s.isPaused&&!s.missing&&e.jsx(me,{variant:"warning",size:"xs",className:"ml-auto",children:"paused"})]}),s.connectionName&&e.jsx("span",{className:"mt-1 flex min-w-0",children:e.jsx(Qe,{name:s.connectionName,color:s.connectionColor})}),s.missing?e.jsxs("div",{className:"mt-auto flex items-center gap-1.5 text-[11px] text-fg-muted",children:[e.jsx(gs,{className:"size-3.5 shrink-0","aria-hidden":!0}),e.jsx("span",{className:"truncate",children:"Queue not found on this connection"})]}):e.jsxs("div",{className:"mt-auto grid grid-cols-4 gap-1 text-[11px]",children:[e.jsx(Se,{label:"waiting",value:a.waiting+a.prioritized,tone:de.waiting.textClass}),e.jsx(Se,{label:"active",value:a.active,tone:de.active.textClass}),e.jsx(Se,{label:"delayed",value:a.delayed,tone:de.delayed.textClass}),e.jsx(Se,{label:"failed",value:a.failed,tone:de.failed.textClass})]}),!s.missing&&e.jsx(Re,{to:te.queue(s.connectionId,s.queueName),className:"nodrag absolute top-1.5 right-1.5 rounded p-1 text-fg-subtle transition-colors hover:bg-surface-2 hover:text-fg",title:"Open queue","aria-label":`Open queue ${s.queueName}`,children:e.jsx(Te,{className:"size-3"})}),e.jsx(Ke,{type:"source",position:ne.Right,className:"fm-handle",isConnectable:d})]})});function Qe({name:t,color:s,className:l}){const d=s??"var(--fg-subtle)";return e.jsxs("span",{className:Z("inline-flex h-4 min-w-0 items-center gap-1 rounded px-1 text-[10px] leading-none font-medium",l),style:{color:d,background:`color-mix(in srgb, ${d} 14%, transparent)`,border:`1px solid color-mix(in srgb, ${d} 38%, transparent)`},title:`Connection: ${t}`,children:[e.jsx(ge,{className:"size-2.5 shrink-0","aria-hidden":!0}),e.jsx("span",{className:"truncate",children:t})]})}function Se({label:t,value:s,tone:l}){return e.jsxs("span",{className:"flex min-w-0 flex-col leading-tight",children:[e.jsx("span",{className:Z("num font-semibold",s>0?l:"text-fg-subtle"),children:ot(s)}),e.jsx("span",{className:"text-[10px] text-fg-subtle",children:t})]})}const Nt={queue:Is},We=c.createContext(null),zs={manual:"var(--fm-edge-manual)",detected:"var(--fm-edge-detected)"};function qs(t,s,l,d){const a=d,i=a*.6;switch(l){case ne.Left:return`M${t-a},${s-i} L${t},${s} L${t-a},${s+i} Z`;case ne.Right:return`M${t+a},${s-i} L${t},${s} L${t+a},${s+i} Z`;case ne.Top:return`M${t-i},${s-a} L${t},${s} L${t+i},${s-a} Z`;default:return`M${t-i},${s+a} L${t},${s} L${t+i},${s+a} Z`}}function yt(t,s=null){return{id:t.id,source:t.from,target:t.to,type:"flow",data:{source:t.source,label:t.label,evidence:t.evidence,cross:s},deletable:t.source==="manual"}}const et=5,Fs=c.memo(function(s){const{id:l,sourceX:d,sourceY:a,targetX:i,targetY:n,sourcePosition:o,targetPosition:m,selected:v,data:f}=s,p=c.useContext(We),z=cs(s.target),b=z&&m===ne.Left?z.internals.positionAbsolute.x-et:z&&m===ne.Right?z.internals.positionAbsolute.x+(z.measured.width??0)+et:i,w=v?11:9,$=m===ne.Left?b-w:m===ne.Right?b+w:b,[j,D,h]=ds({sourceX:d,sourceY:a,targetX:$,targetY:n,sourcePosition:o,targetPosition:m,curvature:.35}),[x,r]=c.useState(!1);c.useEffect(()=>{const _=window.setTimeout(()=>r(!0),680);return()=>window.clearTimeout(_)},[]);const y=(f==null?void 0:f.source)??"manual",q=y==="detected",F=zs[y],M=(f==null?void 0:f.label)??null,g=(f==null?void 0:f.evidence)??0,T=(f==null?void 0:f.cross)??null;return e.jsxs(e.Fragment,{children:[e.jsx(us,{id:l,path:j,interactionWidth:22,className:Z(x?q&&"fm-edge-flow":"fm-edge-draw"),...x?{}:{pathLength:1},style:{stroke:F,strokeWidth:v?2.5:q?1.5:1.75,opacity:v?1:.9}}),x&&e.jsx("path",{d:qs(b,n,m,w),className:"fm-arrowhead",style:{fill:F}}),(M||q||T)&&e.jsx(ms,{children:e.jsx("div",{className:"nodrag nopan pointer-events-auto absolute",style:{transform:`translate(-50%, -50%) translate(${D}px, ${h}px)`},children:e.jsx("div",{className:"fm-edge-label",children:e.jsx(be,{content:q?`From BullMQ FlowProducer: ${se(g)} sampled ${g===1?"job":"jobs"} carry this parent link`:T?`Crosses connections: ${T.from.name} → ${T.to.name}`:void 0,children:e.jsxs("button",{type:"button",onClick:_=>p==null?void 0:p(l,_),className:Z("flex max-w-56 items-center gap-1 rounded-md border bg-surface px-1.5 py-0.5 text-[11px] leading-tight shadow-sm transition-colors",v?"border-accent text-fg":"border-border text-fg-muted hover:border-border-strong hover:text-fg"),children:[T&&e.jsxs("span",{className:"flex shrink-0 items-center gap-0.5","aria-label":`from ${T.from.name} to ${T.to.name}`,children:[e.jsx("span",{className:"size-1.5 rounded-full",style:{background:T.from.color??"var(--fg-subtle)"}}),e.jsx(He,{className:"size-2.5 text-fg-subtle","aria-hidden":!0}),e.jsx("span",{className:"size-1.5 rounded-full",style:{background:T.to.color??"var(--fg-subtle)"}})]}),q&&e.jsxs("span",{className:"num font-mono text-teal",children:["×",ot(g)]}),M&&e.jsx("span",{className:"truncate",children:M})]})})})})})]})}),kt={flow:Fs};function Ct({className:t}){return e.jsxs("div",{className:Z("flex flex-wrap items-center gap-x-4 gap-y-1 text-[11px] text-fg-muted",t),children:[e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx("svg",{width:"28",height:"8","aria-hidden":!0,children:e.jsx("line",{x1:"0",y1:"4",x2:"28",y2:"4",stroke:"var(--accent)",strokeWidth:"1.75"})}),"drawn"]}),e.jsx(be,{content:"From BullMQ FlowProducer: the arrow goes from the parent queue to each child queue it waits for.",children:e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx("svg",{width:"28",height:"8","aria-hidden":!0,children:e.jsx("line",{x1:"0",y1:"4",x2:"28",y2:"4",stroke:"var(--teal)",strokeWidth:"1.5",strokeDasharray:"6 5"})}),"detected"]})}),e.jsxs("span",{className:"flex items-center gap-1.5",children:[e.jsx("span",{className:"inline-block h-2.5 w-5 rounded-sm border border-dashed border-border-strong","aria-hidden":!0}),"paused / not found"]})]})}function Ds({onNodeIds:t,onAdd:s,onClose:l,connectionColor:d}){const{byConnection:a,isLoading:i}=Lt(),[n,o]=c.useState(""),[m,v]=c.useState(null),[f,p]=c.useState(0),z=c.useRef(null),b=c.useRef(null);c.useEffect(()=>{const r=y=>{z.current&&!z.current.contains(y.target)&&l()};return document.addEventListener("mousedown",r),()=>document.removeEventListener("mousedown",r)},[l]);const w=n.trim().toLowerCase(),$=c.useMemo(()=>a.filter(r=>!m||r.connection.id===m).map(({connection:r,queues:y})=>({connection:r,options:y.filter(q=>!w||q.name.toLowerCase().includes(w)||r.name.toLowerCase().includes(w)).sort((q,F)=>q.name.localeCompare(F.name)).map(q=>{const F={connectionId:r.id,queueName:q.name},M=$t(F);return{...F,id:M,connectionName:r.name,onMap:t.has(M)}})})).filter(r=>r.options.length>0),[a,w,m,t]),j=c.useMemo(()=>$.flatMap(r=>r.options),[$]),D=a.length>1;c.useEffect(()=>p(0),[w,m]),c.useEffect(()=>{var r,y;(y=(r=b.current)==null?void 0:r.querySelector(`[data-index="${f}"]`))==null||y.scrollIntoView({block:"nearest"})},[f]);const h=r=>{!r||r.onMap||s({connectionId:r.connectionId,queueName:r.queueName})};let x=-1;return e.jsxs("div",{ref:z,className:"fm-pop absolute top-full right-0 z-30 mt-1.5 w-80 overflow-hidden rounded-lg border border-border bg-surface shadow-[var(--shadow)]",role:"dialog","aria-label":"Add a queue to this map",children:[e.jsxs("div",{className:"border-b border-border p-2",children:[e.jsxs("div",{className:"relative",children:[e.jsx(Pt,{className:"pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-fg-subtle","aria-hidden":!0}),e.jsx("input",{autoFocus:!0,className:"control !h-8 pl-8",placeholder:D?"Search queues on every connection…":"Search queues…",value:n,onChange:r=>o(r.target.value),role:"combobox","aria-expanded":!0,"aria-controls":"fm-add-queue-list","aria-activedescendant":j[f]?`fm-opt-${f}`:void 0,onKeyDown:r=>{r.key==="ArrowDown"?(r.preventDefault(),p(y=>Math.min(j.length-1,y+1))):r.key==="ArrowUp"?(r.preventDefault(),p(y=>Math.max(0,y-1))):r.key==="Enter"?(r.preventDefault(),h(j[f])):r.key==="Escape"&&(r.preventDefault(),l())}})]}),D&&e.jsxs("div",{className:"mt-2 flex flex-wrap gap-1",role:"radiogroup","aria-label":"Connection",children:[e.jsx(tt,{label:"All",active:!m,onClick:()=>v(null)}),a.map(({connection:r})=>e.jsx(tt,{label:r.name,color:d(r.id),active:m===r.id,onClick:()=>v(y=>y===r.id?null:r.id)},r.id))]})]}),e.jsxs("ul",{id:"fm-add-queue-list",ref:b,role:"listbox",className:"max-h-80 overflow-y-auto p-1",children:[i&&e.jsx("li",{className:"flex justify-center py-6",children:e.jsx(Fe,{label:"Loading queues…"})}),!i&&j.length===0&&e.jsx("li",{className:"px-3 py-6 text-center text-xs text-fg-subtle",children:"No queue matches."}),$.map(r=>e.jsxs("li",{role:"presentation",children:[D&&e.jsxs("div",{className:"flex items-center gap-1.5 px-2 pt-2 pb-1 text-[10px] font-semibold tracking-wider text-fg-subtle uppercase",children:[e.jsx(ge,{className:"size-3",style:{color:d(r.connection.id)??void 0},"aria-hidden":!0}),r.connection.name]}),e.jsx("ul",{role:"presentation",children:r.options.map(y=>{x++;const q=x;return e.jsxs("li",{id:`fm-opt-${q}`,"data-index":q,role:"option","aria-selected":q===f,"aria-disabled":y.onMap||void 0,onMouseEnter:()=>p(q),onMouseDown:F=>F.preventDefault(),onClick:()=>h(y),className:Z("flex h-7 cursor-pointer items-center gap-2 rounded-md px-2 text-[13px]",q===f&&!y.onMap&&"bg-surface-2",y.onMap?"cursor-default text-fg-subtle":"text-fg"),children:[e.jsx("span",{className:"min-w-0 flex-1 truncate",children:y.queueName}),y.onMap?e.jsxs("span",{className:"flex items-center gap-1 text-[11px]",children:[e.jsx(rt,{className:"size-3.5 text-success","aria-hidden":!0})," on map"]}):q===f&&e.jsx(ue,{className:"size-3.5 text-fg-muted","aria-hidden":!0})]},y.id)})})]},r.connection.id))]})]})}function tt({label:t,active:s,color:l,onClick:d}){return e.jsxs("button",{type:"button",role:"radio","aria-checked":s,onClick:d,className:Z("inline-flex h-6 items-center gap-1 rounded-md border px-2 text-[11px] transition-colors",s?"border-accent bg-accent/15 text-fg":"border-border text-fg-muted hover:border-border-strong hover:text-fg"),children:[l!==void 0&&e.jsx("span",{className:"size-1.5 rounded-full",style:{background:l??"var(--fg-subtle)"},"aria-hidden":!0}),t]})}const Mt=t=>t>1?" ".repeat(t-1)+"└ ":"";function Ls({open:t,parentId:s,maps:l,onClose:d,onCreated:a}){const i=At(),[n,o]=c.useState(""),[m,v]=c.useState(""),[f,p]=c.useState(s??""),[z,b]=c.useState(null);c.useEffect(()=>{t&&(o(""),v(""),b(null),p(s??""))},[t,s]);const w=c.useMemo(()=>wt(l,"\0new"),[l]),$=()=>{var D;const j=Tt.safeParse({name:n,description:m.trim()||null,parentId:f||null});if(!j.success){b(((D=j.error.issues[0])==null?void 0:D.message)??"Invalid");return}i.mutate(j.data,{onSuccess:h=>{R.success(`Map "${h.name}" created`),a(h),d()},onError:h=>R.error(H(h))})};return e.jsx(_e,{open:t,onClose:d,size:"sm",title:"New flow map",description:"A named diagram of the queues one process goes through, on any connection.",footer:e.jsxs(e.Fragment,{children:[e.jsx(P,{variant:"ghost",size:"sm",onClick:d,children:"Cancel"}),e.jsx(P,{variant:"primary",size:"sm",onClick:$,loading:i.isPending,children:"Create map"})]}),children:e.jsxs("form",{className:"space-y-3",onSubmit:j=>{j.preventDefault(),$()},children:[e.jsx(De,{label:"Name",autoFocus:!0,value:n,onChange:j=>(o(j.target.value),b(null)),error:z,maxLength:80,placeholder:"e.g. Order checkout"}),e.jsx(Rt,{label:"Description (optional)",value:m,onChange:j=>v(j.target.value),maxLength:500,rows:2,placeholder:"What the process does, who owns it"}),w.length>1&&e.jsx(je,{label:"Inside",value:f,onChange:j=>p(j.target.value),options:w.map(j=>({value:j.id??"",label:j.id?Mt(j.depth)+j.label:"Top level"})),hint:"Optional. Nesting only groups maps; each map keeps its own diagram."})]})})}function Ps({open:t,map:s,maps:l,onClose:d}){const a=it(),i=c.useMemo(()=>wt(l,s.id),[l,s.id]),[n,o]=c.useState(s.parentId??"");c.useEffect(()=>{t&&o(s.parentId??"")},[t,s.parentId]);const m=(s.parentId??"")===n;return e.jsx(_e,{open:t,onClose:d,size:"sm",title:`Move "${s.name}"`,description:"Its child maps move with it.",footer:e.jsxs(e.Fragment,{children:[e.jsx(P,{variant:"ghost",size:"sm",onClick:d,children:"Cancel"}),e.jsx(P,{variant:"primary",size:"sm",disabled:m,loading:a.isPending,onClick:()=>a.mutate({id:s.id,input:{parentId:n||null}},{onSuccess:()=>(R.success("Map moved"),d()),onError:v=>R.error(H(v))}),children:"Move"})]}),children:e.jsx(je,{label:"Move to",value:n,onChange:v=>o(v.target.value),options:i.map(v=>({value:v.id??"",label:v.id?Mt(v.depth)+v.label:"Top level"})),hint:"A map cannot go inside itself or one of its own children."})})}const $s=4e3,As=15e3,Rs=400,Ts=520,st=800,_s=.5,nt=2500,Os=t=>1-Math.pow(1-t,3);function Bs(t){return e.jsx(xs,{children:e.jsx(Hs,{...t})})}function Hs({mapId:t,maps:s,canOperate:l,onSelectMap:d,onDeleted:a}){const i=_t(t),n=i.data,o=Oe(),m=hs(),v=c.useRef(null),f=(n==null?void 0:n.kind)==="detected"||t.startsWith("detected:"),p=l&&!f,z=Ot(),b=Bt(),w=Ht(),$=Vt(),j=Qt(),D=Wt(),h=Gt(),[x,r,y]=mt([]),[q,F,M]=xt([]),[g,T]=c.useState(!1),[_,ae]=c.useState(null),U=c.useRef(new Map),J=c.useRef(new Map),oe=c.useRef(new Map),re=c.useRef(new Set),le=c.useRef(new Map),xe=c.useRef(),k=c.useRef(),O=c.useRef(new Map),Q=c.useRef(!1),K=c.useRef(0),V=c.useMemo(()=>(o.data??[]).map(u=>({id:u.id,name:u.name})),[o.data]),pe=c.useMemo(()=>n?Ns(n.nodes,V):[],[n,V]),ie=pe.length>1,Y=c.useMemo(()=>new Map(((n==null?void 0:n.nodes)??[]).map(u=>[u.id,u])),[n]),Ce=c.useCallback(()=>{var B;k.current&&cancelAnimationFrame(k.current);const u=new Map(m.getNodes().map(G=>[G.id,{...G.position}]));if([...U.current].filter(([G,C])=>{const L=u.get(G);return L&&!re.current.has(G)&&(Math.abs(L.x-C.x)>.5||Math.abs(L.y-C.y)>.5)}).length===0)return;const N=(B=window.matchMedia)==null?void 0:B.call(window,"(prefers-reduced-motion: reduce)").matches,A=performance.now(),I=G=>{const C=N?1:Math.min(1,(G-A)/Ts),L=Os(C);r(S=>S.map(W=>{const X=U.current.get(W.id),ee=u.get(W.id);return!X||!ee||re.current.has(W.id)||Math.abs(ee.x-X.x)<=.5&&Math.abs(ee.y-X.y)<=.5?W:{...W,position:{x:ee.x+(X.x-ee.x)*L,y:ee.y+(X.y-ee.y)*L}}})),C<1&&(k.current=requestAnimationFrame(I))};k.current=requestAnimationFrame(I)},[m,r]),fe=c.useCallback(u=>{const E=v.current,N=[...U.current.values()];if(!E||N.length===0)return;const A=Math.min(...N.map(L=>L.x)),I=Math.min(...N.map(L=>L.y)),B=Math.max(...N.map(L=>L.x+Ne)),G=Math.max(...N.map(L=>L.y+ye)),C=ps({x:A,y:I,width:B-A,height:G-I},E.clientWidth,E.clientHeight,_s,1,.08);m.setViewport(C,{duration:u})},[m]);c.useEffect(()=>{if(!n)return;const u=Date.now(),E=new Set(n.nodes.map(C=>C.id));for(const[C,L]of oe.current){const S=Y.get(C);(!S||S.x===L.x&&S.y===L.y||u-L.at>As)&&oe.current.delete(C)}for(const C of J.current.keys())E.has(C)||J.current.delete(C);const N=n.nodes.map(C=>{const L=oe.current.get(C.id);if(L)return{id:C.id,x:L.x,y:L.y};if(C.x!=null&&C.y!=null)return J.current.delete(C.id),{id:C.id,x:C.x,y:C.y};const S=J.current.get(C.id);return{id:C.id,x:(S==null?void 0:S.x)??null,y:(S==null?void 0:S.y)??null}}),A=ws(N,n.edges,qe);A.forEach((C,L)=>J.current.set(L,C));const I=new Map;for(const C of N)I.set(C.id,C.x!=null&&C.y!=null?{x:C.x,y:C.y}:A.get(C.id));U.current=I;const B=[];for(const C of n.nodes)O.current.has(C.id)||(O.current.set(C.id,Q.current?u:0),B.push(C.id));const G=C=>ze(C,V);r(C=>{const L=new Map(C.map(S=>[S.id,S]));return n.nodes.map(S=>{const W={queueName:S.queueName,connectionId:S.connectionId,connectionName:ie?S.connectionName:null,connectionColor:ie?G(S.connectionId):null,counts:S.counts,isPaused:S.isPaused,missing:S.missing,fresh:u-(O.current.get(S.id)??0)<nt},X=L.get(S.id);return X?{...X,data:W,deletable:p}:{id:S.id,type:"queue",position:I.get(S.id),data:W,width:Ne,height:ye,deletable:p}})}),F(C=>{const L=new Set(C.filter(S=>S.selected).map(S=>S.id));return n.edges.map(S=>{const W=Y.get(S.from),X=Y.get(S.to),ee=W&&X&&W.connectionId!==X.connectionId?{from:{name:W.connectionName,color:G(W.connectionId)},to:{name:X.connectionName,color:G(X.connectionId)}}:null;return{...yt(S,ee),selected:L.has(S.id)}})}),requestAnimationFrame(Ce),Q.current?B.length&&Date.now()-K.current>$s&&re.current.size===0&&requestAnimationFrame(()=>fe(st)):(Q.current=!0,n.nodes.length&&requestAnimationFrame(()=>fe(0)))},[n,Y,ie,V,p,r,F,Ce,fe]),c.useEffect(()=>{if(!x.some(E=>E.data.fresh))return;const u=window.setTimeout(()=>r(E=>E.map(N=>N.data.fresh?{...N,data:{...N.data,fresh:!1}}:N)),nt);return()=>window.clearTimeout(u)},[x,r]),c.useEffect(()=>()=>{k.current&&cancelAnimationFrame(k.current),window.clearTimeout(xe.current)},[]);const Ge=c.useCallback(()=>{if(!p||!n)return;const u=new Map(le.current);le.current.clear();for(const E of n.nodes)if(E.x==null&&!u.has(E.id)){const N=U.current.get(E.id);N&&u.set(E.id,N)}u.size!==0&&D.mutate({mapId:t,input:{positions:[...u].map(([E,N])=>({nodeId:E,x:Math.round(N.x),y:Math.round(N.y)}))}},{onError:E=>R.error(`Could not save the layout: ${H(E)}`)})},[p,n,t,D]),St=c.useCallback((u,E,N)=>{const A=p?Date.now():Number.POSITIVE_INFINITY;for(const I of N)re.current.delete(I.id),oe.current.set(I.id,{...I.position,at:A}),U.current.set(I.id,{...I.position}),le.current.set(I.id,{...I.position});p&&(window.clearTimeout(xe.current),xe.current=window.setTimeout(Ge,Rs))},[p,Ge]),It=c.useCallback(()=>{if(!n)return;const u=we(n.nodes.map(N=>({id:N.id})),n.edges,qe),E=p?Date.now():Number.POSITIVE_INFINITY;J.current.clear(),u.forEach((N,A)=>{U.current.set(A,{x:N.x,y:N.y}),oe.current.set(A,{x:N.x,y:N.y,at:E})}),Ce(),window.setTimeout(()=>fe(st),60),p&&(le.current.clear(),D.mutate({mapId:t,input:{positions:[...u.values()].map(N=>({nodeId:N.id,x:Math.round(N.x),y:Math.round(N.y)}))}},{onError:N=>R.error(`Could not save the layout: ${H(N)}`)}))},[n,p,Ce,fe,D,t]),zt=c.useCallback(u=>{if(!p||!u.source||!u.target||u.source===u.target)return;const E=I=>{const B=Y.get(I);return B?{connectionId:B.connectionId,queueName:B.queueName}:ys(I)},N=E(u.source),A=E(u.target);!N||!A||w.mutate({mapId:t,input:{from:N,to:A}},{onError:I=>R.error(H(I))})},[p,t,Y,w]),qt=c.useCallback(async({nodes:u,edges:E})=>{var A;if(!p)return!1;const N=new Set(u.map(I=>I.id));for(const I of u)b.mutate({mapId:t,nodeId:I.id},{onError:B=>R.error(H(B))});for(const I of E)I.selected&&((A=I.data)==null?void 0:A.source)==="manual"&&!N.has(I.source)&&!N.has(I.target)&&j.mutate({mapId:t,edgeId:I.id},{onError:B=>R.error(H(B))});return ae(null),!1},[p,t,b,j]),Xe=c.useCallback((u,E)=>{var A;const N=(A=v.current)==null?void 0:A.getBoundingClientRect();N&&(ae({id:u,x:E.clientX-N.left,y:E.clientY-N.top}),F(I=>I.map(B=>({...B,selected:B.id===u}))))},[F]);if(i.isError&&!n){const u=Xt(i.error)&&i.error.status===404;return e.jsx("div",{className:"flex h-full items-center justify-center",children:e.jsx(ve,{icon:e.jsx(he,{}),title:u?"This map no longer exists":"Could not load this map",description:u?"Someone deleted it, or the queues behind a detected map stopped linking.":H(i.error)})})}const Ye=x.filter(u=>u.selected),Me=Ye.length===1?Y.get(Ye[0].id)??null:null,ce=_?(n==null?void 0:n.edges.find(u=>u.id===_.id))??null:null,Ft=new Set((n==null?void 0:n.nodes.map(u=>u.id))??[]);return e.jsxs("div",{className:"flex h-full min-h-0 flex-col",children:[n?e.jsx(Vs,{map:n,maps:s,canEdit:p,canOperate:l,onSelectMap:d,onDeleted:a,live:!i.isError,actions:e.jsxs(e.Fragment,{children:[e.jsx(P,{size:"sm",variant:"ghost",leftIcon:e.jsx(jt,{}),onClick:It,disabled:n.nodes.length<2,title:p?"Lay the map out left to right and save it for everyone":"Lay the map out left to right (only for you)",children:"Re-layout"}),f&&l&&e.jsx(P,{size:"sm",variant:"primary",leftIcon:e.jsx(Yt,{}),loading:h.isPending,onClick:()=>h.mutate({id:n.id,name:n.name},{onSuccess:u=>(R.success(`Copied to "${u.name}": draw on it freely`),d(u.id)),onError:u=>R.error(H(u))}),children:"Copy to edit"}),p&&e.jsxs("div",{className:"relative",children:[e.jsx(P,{size:"sm",variant:"primary",leftIcon:e.jsx(ue,{}),onClick:()=>T(u=>!u),"aria-expanded":g,children:"Add queue"}),g&&e.jsx(Ds,{onNodeIds:Ft,connectionColor:u=>ze(u,V),onClose:()=>T(!1),onAdd:u=>z.mutate({mapId:t,input:u},{onError:E=>R.error(H(E))})})]})]})}):e.jsx("div",{className:"flex h-12 items-center border-b border-border bg-surface px-4",children:e.jsx(Fe,{label:"Loading map…"})}),e.jsxs("div",{ref:v,className:"relative min-h-0 flex-1",children:[e.jsx(We.Provider,{value:Xe,children:e.jsxs(ht,{nodes:x,edges:q,nodeTypes:Nt,edgeTypes:kt,onNodesChange:y,onEdgesChange:M,onNodeDragStart:(u,E,N)=>N.forEach(A=>re.current.add(A.id)),onNodeDragStop:St,onMoveStart:u=>{u&&(K.current=Date.now())},onEdgeClick:(u,E)=>Xe(E.id,u),onPaneClick:()=>ae(null),onConnect:zt,isValidConnection:u=>u.source!==u.target,onBeforeDelete:qt,deleteKeyCode:p?["Backspace","Delete"]:null,nodesConnectable:p,connectionRadius:28,minZoom:.2,maxZoom:1.75,proOptions:{hideAttribution:!0},colorMode:"system",className:Z("fm-canvas bg-bg",p&&"fm-editable"),children:[e.jsx(pt,{variant:ft.Dots,gap:20,size:1}),e.jsx(gt,{showInteractive:!1,position:"bottom-right"}),x.length>8&&e.jsx(bt,{pannable:!0,zoomable:!0,position:"bottom-left",nodeColor:()=>"var(--surface-3)",maskColor:"color-mix(in srgb, var(--bg) 70%, transparent)",style:{width:160,height:100}})]})}),n&&n.nodes.length>0&&e.jsx("div",{className:"pointer-events-none absolute top-3 left-3 flex max-w-[calc(100%-1.5rem)] flex-col gap-1.5",children:e.jsxs("div",{className:"pointer-events-auto w-fit rounded-md border border-border bg-surface/95 px-2.5 py-1.5 shadow-sm backdrop-blur-sm",children:[e.jsx(Ct,{}),ie&&e.jsxs("div",{className:"mt-1.5 flex flex-wrap items-center gap-1 border-t border-border pt-1.5",children:[e.jsx("span",{className:"mr-0.5 text-[10px] tracking-wider text-fg-subtle uppercase",children:"Connections"}),pe.map(u=>e.jsx(Qe,{name:`${u.name} · ${u.count}`,color:u.color},u.connectionId))]})]})}),n&&n.nodes.length===0&&e.jsx("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:e.jsx("div",{className:"pointer-events-auto",children:e.jsx(ve,{icon:e.jsx(Zt,{}),title:"This map is empty",description:"Add a queue, or ask your AI client (MCP) to draw this flow.",action:p&&e.jsx(P,{size:"sm",variant:"primary",leftIcon:e.jsx(ue,{}),onClick:()=>T(!0),children:"Add queue"})})})}),p&&n&&n.nodes.length>=2&&n.edges.length===0&&e.jsx("div",{className:"pointer-events-none absolute bottom-4 left-1/2 -translate-x-1/2 rounded-full border border-border bg-surface/95 px-3 py-1.5 text-[11px] text-fg-muted shadow-sm",children:"Drag from a queue's right edge to another queue to draw an arrow."}),Me&&e.jsx(Qs,{node:Me,multi:ie,color:ze(Me.connectionId,V),canEdit:p,removing:b.isPending,onRemove:()=>b.mutate({mapId:t,nodeId:Me.id},{onError:u=>R.error(H(u))})}),_&&ce&&e.jsx(Ws,{edge:ce,from:Y.get(ce.from),to:Y.get(ce.to),x:_.x,y:_.y,canEdit:p,saving:$.isPending,deleting:j.isPending,onClose:()=>ae(null),onSave:u=>$.mutate({mapId:t,edgeId:ce.id,input:{label:u}},{onSuccess:()=>ae(null),onError:E=>R.error(H(E))}),onDelete:()=>j.mutate({mapId:t,edgeId:ce.id},{onSuccess:()=>ae(null),onError:u=>R.error(H(u))})},ce.id)]})]})}function Vs({map:t,maps:s,canEdit:l,canOperate:d,live:a,actions:i,onSelectMap:n,onDeleted:o}){var F;const m=it(),v=Ut(),[f,p]=c.useState(!1),[z,b]=c.useState(t.name),[w,$]=c.useState(!1),[j,D]=c.useState(!1);c.useEffect(()=>{f||b(t.name)},[t.name,f]);const h=t.kind==="detected",x=h?[]:js(s,t.id),r=s.filter(M=>M.parentId===t.id),y=t.parentId?(F=s.find(M=>M.id===t.parentId))==null?void 0:F.name:null,q=()=>{const M=z.trim();if(!M||M===t.name){p(!1),b(t.name);return}m.mutate({id:t.id,input:{name:M}},{onSuccess:()=>p(!1),onError:g=>R.error(H(g))})};return e.jsxs("div",{className:"flex flex-wrap items-center gap-x-3 gap-y-2 border-b border-border bg-surface px-4 py-2",children:[e.jsxs("div",{className:"flex min-w-0 flex-1 items-center gap-2",children:[h?e.jsx(vt,{className:"size-4 shrink-0 text-teal","aria-hidden":!0}):e.jsx(he,{className:"size-4 shrink-0 text-pro","aria-hidden":!0}),x.map(M=>e.jsxs("span",{className:"flex min-w-0 items-center gap-1 text-[13px] text-fg-subtle",children:[e.jsx("button",{type:"button",className:"max-w-32 truncate hover:text-fg",onClick:()=>n(M.id),children:M.name}),e.jsx(at,{className:"size-3.5 shrink-0","aria-hidden":!0})]},M.id)),f?e.jsxs("form",{className:"flex items-center gap-1.5",onSubmit:M=>{M.preventDefault(),q()},children:[e.jsx(De,{autoFocus:!0,value:z,onChange:M=>b(M.target.value),maxLength:80,className:"!h-7 w-64","aria-label":"Map name",onKeyDown:M=>M.key==="Escape"&&(p(!1),b(t.name))}),e.jsx(P,{size:"icon-sm",type:"submit",variant:"primary","aria-label":"Save name",loading:m.isPending,children:e.jsx(rt,{})}),e.jsx(P,{size:"icon-sm",variant:"ghost","aria-label":"Cancel",onClick:()=>(p(!1),b(t.name)),children:e.jsx(Be,{})})]}):e.jsxs("h1",{className:"group flex min-w-0 items-center gap-1 text-sm font-semibold",children:[e.jsx("span",{className:"truncate",title:t.description??t.name,children:t.name}),l&&e.jsx(P,{size:"icon-xs",variant:"ghost",className:"opacity-60 group-hover:opacity-100","aria-label":"Rename map",title:"Rename",onClick:()=>p(!0),children:e.jsx(Kt,{})})]}),h&&e.jsx(be,{content:"From BullMQ FlowProducer: computed from the parent links of sampled jobs. Read-only; copy it to edit.",side:"bottom",children:e.jsx(me,{variant:"teal",size:"xs",children:"detected"})}),!h&&!d&&e.jsx(me,{variant:"outline",size:"xs",children:"read-only"}),e.jsxs("span",{className:"hidden shrink-0 text-xs text-fg-muted sm:inline",children:[se(t.nodes.length)," ",t.nodes.length===1?"queue":"queues"," · ",se(t.edges.length)," ",t.edges.length===1?"arrow":"arrows"]}),a&&e.jsx(be,{content:"Live: counts and changes made elsewhere (other users, MCP clients) show up within seconds.",side:"bottom",children:e.jsxs("span",{className:"flex shrink-0 items-center gap-1.5 text-[11px] text-fg-subtle",children:[e.jsx("span",{className:"status-dot pulse size-1.5 bg-success text-success","aria-hidden":!0}),"Live"]})})]}),e.jsxs("div",{className:"flex items-center gap-1.5",children:[i,l&&e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"mx-0.5 h-5 w-px bg-border","aria-hidden":!0}),e.jsx(P,{size:"icon-sm",variant:"ghost",title:"Move to…","aria-label":"Move map",onClick:()=>$(!0),children:e.jsx(fs,{})}),e.jsx(P,{size:"icon-sm",variant:"ghost",className:"hover:text-danger",title:"Delete map","aria-label":"Delete map",onClick:()=>D(!0),children:e.jsx(Le,{})})]})]}),t.description&&e.jsx("p",{className:"w-full truncate pl-6 text-xs text-fg-muted",children:t.description}),l&&e.jsx(Ps,{open:w,map:t,maps:s,onClose:()=>$(!1)}),e.jsx(Jt,{open:j,onClose:()=>D(!1),title:`Delete "${t.name}"`,description:r.length>0?`Its ${r.length===1?"child map moves":`${r.length} child maps move`} up to ${y?`"${y}"`:"the top level"}. The queues themselves are untouched.`:"Only the drawing goes away. The queues themselves are untouched.",confirmText:"Delete map",danger:!0,loading:v.isPending,onConfirm:()=>v.mutate(t.id,{onSuccess:()=>(D(!1),R.success("Map deleted"),o(t.parentId)),onError:M=>R.error(H(M))})})]})}function Qs({node:t,multi:s,color:l,canEdit:d,removing:a,onRemove:i}){return e.jsxs("div",{className:"fm-pop absolute top-3 right-3 z-10 w-72 rounded-lg border border-border bg-surface p-3 text-xs shadow-[var(--shadow)]",children:[e.jsxs("div",{className:"mb-1 flex items-center gap-2",children:[e.jsx("span",{className:"min-w-0 flex-1 truncate text-sm font-semibold",title:t.queueName,children:t.queueName}),t.isPaused&&e.jsx(me,{variant:"warning",size:"xs",children:"paused"})]}),e.jsx("div",{className:"mb-2",children:e.jsx(Qe,{name:t.connectionName,color:s?l:null})}),t.missing?e.jsx("p",{className:"text-fg-muted",children:"Not found on this connection: renamed, not created yet, or the connection is down."}):e.jsx("dl",{className:"grid grid-cols-2 gap-x-3 gap-y-1 text-fg-muted",children:Object.entries(t.counts).map(([n,o])=>e.jsxs("div",{className:"flex justify-between border-b border-border/60 py-0.5",children:[e.jsx("dt",{children:n}),e.jsx("dd",{className:"num text-fg",children:se(o)})]},n))}),e.jsxs("div",{className:"mt-3 flex items-center gap-2",children:[!t.missing&&e.jsxs(Re,{to:te.queue(t.connectionId,t.queueName),className:"inline-flex items-center gap-1 text-accent hover:underline",children:["Open queue ",e.jsx(Te,{className:"size-3"})]}),d&&e.jsx(P,{size:"xs",variant:"ghost",className:"ml-auto hover:text-danger",leftIcon:e.jsx(Le,{}),loading:a,onClick:i,title:"Remove from map (Delete)",children:"Remove from map"})]})]})}function Ws({edge:t,from:s,to:l,x:d,y:a,canEdit:i,saving:n,deleting:o,onClose:m,onSave:v,onDelete:f}){const[p,z]=c.useState(t.label??""),b=t.source==="manual",w=b&&i,$=s&&l&&s.connectionId!==l.connectionId;return e.jsxs("div",{className:"fm-pop absolute z-20 w-72 rounded-lg border border-border bg-surface p-3 text-xs shadow-[var(--shadow)]",style:{left:`min(${d+8}px, calc(100% - 18.5rem))`,top:`min(${a+8}px, calc(100% - 11rem))`},onKeyDown:j=>j.key==="Escape"&&m(),children:[e.jsxs("div",{className:"mb-2 flex items-start gap-2",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex min-w-0 items-center gap-1 font-mono text-[12px] font-semibold",children:[e.jsx("span",{className:"truncate",children:(s==null?void 0:s.queueName)??t.from}),e.jsx(He,{className:"size-3 shrink-0 text-fg-subtle","aria-hidden":!0}),e.jsx("span",{className:"truncate",children:(l==null?void 0:l.queueName)??t.to})]}),$&&e.jsxs("p",{className:"mt-0.5 text-[11px] text-fg-muted",children:["Crosses connections: ",s.connectionName," → ",l.connectionName]})]}),e.jsx(P,{size:"icon-xs",variant:"ghost","aria-label":"Close",onClick:m,children:e.jsx(Be,{})})]}),b?w?e.jsxs("form",{onSubmit:j=>{j.preventDefault(),v(p.trim()||null)},children:[e.jsx(De,{autoFocus:!0,label:"Label",value:p,onChange:j=>z(j.target.value),maxLength:120,placeholder:"e.g. on success",className:"!h-7"}),e.jsxs("div",{className:"mt-2.5 flex items-center gap-2",children:[e.jsx(P,{size:"xs",variant:"ghost",className:"hover:text-danger",leftIcon:e.jsx(Le,{}),loading:o,onClick:f,children:"Delete arrow"}),e.jsx(P,{size:"xs",variant:"primary",type:"submit",className:"ml-auto",loading:n,disabled:(t.label??"")===p.trim(),children:"Save"})]})]}):e.jsx("p",{className:"text-fg-muted",children:t.label?`Label: ${t.label}`:"Drawn by hand, no label."}):e.jsxs("div",{className:"space-y-1 text-fg-muted",children:[e.jsxs("p",{children:[e.jsx(me,{variant:"teal",size:"xs",children:"detected"})," ","from BullMQ FlowProducer."]}),e.jsxs("p",{children:[se(t.evidence)," sampled jobs in the child queue point to a parent in the other one. It disappears on its own once none do."]})]})]})}function Gs({connectionId:t}){var U,J,oe,re,le,xe;const s=lt(),{isOperator:l}=ct(),a=(U=Oe().data)==null?void 0:U.find(k=>k.id===t),[i,n]=c.useState(200),o=es(t,i),m=ts(),v=ss(),[f,p,z]=mt([]),[b,w,$]=xt([]),j=c.useRef(new Map),[D,h]=c.useState(null),[x,r]=c.useState(!1);c.useEffect(()=>{const k=o.data;if(!k)return;const O=we(k.nodes.map(Q=>({id:Q.id})),k.edges.map(Ie),qe);p(Q=>{const K=new Map(Q.map(V=>[V.id,V]));return k.nodes.map(V=>{const pe={queueName:V.queueName,connectionId:t,connectionName:null,connectionColor:null,counts:V.counts,isPaused:V.isPaused,missing:!1},ie=K.get(V.id);if(ie)return{...ie,data:pe};const Y=j.current.get(V.id)??O.get(V.id)??{x:0,y:0};return{id:V.id,type:"queue",position:{x:Y.x,y:Y.y},data:pe,width:Ne,height:ye,deletable:!1}})}),w(k.edges.map(Q=>({...yt(Ie(Q)),deletable:!1})))},[o.data,t,p,w]);const y=c.useCallback(()=>{j.current.clear();const k=o.data;if(!k)return;const O=we(k.nodes.map(Q=>({id:Q.id})),k.edges.map(Ie),qe);p(Q=>Q.map(K=>({...K,position:O.get(K.id)??K.position})))},[o.data,p]),q=((J=o.data)==null?void 0:J.edges.filter(k=>k.source==="detected").length)??0,F=((oe=o.data)==null?void 0:oe.edges.filter(k=>k.source==="manual").length)??0,M=D?(re=o.data)==null?void 0:re.edges.find(k=>k.id===D):void 0,g=M?Ie(M):null,T=(le=f.find(k=>k.selected))==null?void 0:le.id,_=T?((xe=o.data)==null?void 0:xe.nodes.find(k=>k.id===T))??null:null,ae=c.useCallback(k=>h(k),[]);return e.jsxs("div",{className:"flex h-full min-h-0 flex-col",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2 border-b border-border bg-surface px-4 py-2",children:[e.jsx(ge,{className:"size-4 text-fg-subtle","aria-hidden":!0}),e.jsxs("h1",{className:"shrink-0 text-sm font-semibold",children:["All queues ",e.jsxs("span",{className:"font-normal text-fg-muted",children:["· ",(a==null?void 0:a.name)??t]})]}),e.jsx(je,{"aria-label":"Sample size",className:"!h-7 !w-auto text-xs",value:String(i),onChange:k=>n(Number(k.target.value)),options:[50,200,500,1e3].map(k=>({value:String(k),label:`sample ${k} jobs/queue`}))}),e.jsx("span",{className:"hidden min-w-0 truncate text-xs text-fg-muted xl:inline",children:o.data?e.jsxs(e.Fragment,{children:[se(o.data.nodes.length)," queues · ",q," detected · ",F," manual · sampled ",se(o.data.sampledJobs)," jobs"]}):o.isLoading?e.jsx(Fe,{label:"Detecting flows…"}):null}),e.jsxs("div",{className:"ml-auto flex items-center gap-1.5",children:[e.jsx(P,{size:"sm",variant:"ghost",leftIcon:e.jsx(ns,{}),onClick:()=>o.refetch(),loading:o.isFetching,children:"Refresh"}),e.jsx(P,{size:"sm",variant:"ghost",leftIcon:e.jsx(jt,{}),onClick:y,children:"Re-layout"}),l&&e.jsx(P,{size:"sm",variant:"primary",leftIcon:e.jsx(ue,{}),onClick:()=>r(!0),disabled:!o.data||o.data.nodes.length<2,children:"Add edge"})]})]}),e.jsxs("div",{className:"relative min-h-0 flex-1",children:[o.isError&&!o.data&&e.jsx("div",{className:"absolute inset-0 z-10 flex items-center justify-center",children:e.jsx(ve,{title:"Could not load flows",description:H(o.error)})}),o.data&&o.data.nodes.length===0&&e.jsx("div",{className:"absolute inset-0 z-10 flex items-center justify-center",children:e.jsx(ve,{icon:e.jsx(he,{}),title:"No queues on this connection",description:"Flows are built from the queues discovered on the connection."})}),e.jsx(We.Provider,{value:ae,children:e.jsxs(ht,{nodes:f,edges:b,nodeTypes:Nt,edgeTypes:kt,onNodesChange:z,onEdgesChange:$,onNodeDragStop:(k,O)=>j.current.set(O.id,O.position),onEdgeClick:(k,O)=>h(O.id),onNodeClick:()=>h(null),onPaneClick:()=>h(null),onNodeDoubleClick:(k,O)=>s(te.queue(t,O.data.queueName)),fitView:!0,fitViewOptions:{padding:.2,maxZoom:1},minZoom:.2,maxZoom:1.75,nodesConnectable:!1,deleteKeyCode:null,proOptions:{hideAttribution:!0},colorMode:"system",className:"fm-canvas bg-bg",children:[e.jsx(pt,{variant:ft.Dots,gap:20,size:1}),e.jsx(gt,{showInteractive:!1,position:"bottom-right"}),e.jsx(bt,{pannable:!0,zoomable:!0,position:"bottom-left",nodeColor:()=>"var(--surface-3)",maskColor:"color-mix(in srgb, var(--bg) 70%, transparent)",style:{width:160,height:100}})]})}),e.jsxs("div",{className:"pointer-events-none absolute top-3 left-3 flex flex-col gap-2",children:[e.jsx("div",{className:"pointer-events-auto w-fit rounded-md border border-border bg-surface/95 px-2.5 py-1.5 shadow-sm",children:e.jsx(Ct,{})}),e.jsxs("div",{className:"pointer-events-auto flex max-w-sm items-start gap-2 rounded-md border border-border bg-surface/95 px-3 py-2 text-[11px] text-fg-muted shadow-sm",children:[e.jsx(as,{className:"mt-px size-3.5 shrink-0 text-info","aria-hidden":!0}),e.jsxs("span",{children:["Detected edges come from BullMQ flows: children carry a ",e.jsx("span",{className:"font-mono",children:"parent"})," reference, and the arrow points from the parent to each child queue it waits for. A worker that simply calls ",e.jsx("span",{className:"font-mono",children:"otherQueue.add()"})," leaves no trace in Redis; draw those by hand, or build a flow map. Double-click a queue to open it."]})]})]}),(g||_)&&e.jsxs("div",{className:"fm-pop absolute top-3 right-3 w-72 rounded-lg border border-border bg-surface p-3 text-xs shadow-[var(--shadow)]",children:[_&&!g&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-2 flex items-center justify-between",children:[e.jsx("span",{className:"text-sm font-semibold",children:_.queueName}),_.isPaused&&e.jsx(me,{variant:"warning",size:"xs",children:"paused"})]}),e.jsx("dl",{className:"grid grid-cols-2 gap-x-3 gap-y-1 text-fg-muted",children:Object.entries(_.counts).map(([k,O])=>e.jsxs("div",{className:"flex justify-between border-b border-border/60 py-0.5",children:[e.jsx("dt",{children:k}),e.jsx("dd",{className:"num text-fg",children:se(O)})]},k))}),e.jsxs(Re,{to:te.queue(t,_.queueName),className:"mt-3 inline-flex items-center gap-1 text-accent hover:underline",children:["Open queue ",e.jsx(Te,{className:"size-3"})]})]}),g&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-2 flex items-center gap-2",children:[e.jsxs("span",{className:"flex min-w-0 flex-1 items-center gap-1 font-mono text-[12px] font-semibold",children:[e.jsx("span",{className:"truncate",children:g.from}),e.jsx(He,{className:"size-3 shrink-0 text-fg-subtle","aria-hidden":!0}),e.jsx("span",{className:"truncate",children:g.to})]}),e.jsx(P,{size:"icon-xs",variant:"ghost","aria-label":"Close",onClick:()=>h(null),children:e.jsx(Be,{})})]}),e.jsxs("div",{className:"space-y-1 text-fg-muted",children:[e.jsxs("p",{children:["Source:"," ",e.jsx(me,{variant:g.source==="manual"?"accent":"teal",size:"xs",children:g.source})]}),g.source==="detected"&&e.jsxs("p",{children:["Evidence: ",se(g.evidence)," sampled jobs referenced this parent."]}),g.label&&e.jsxs("p",{children:["Label: ",g.label]})]}),g.source==="manual"&&l&&e.jsx(P,{size:"sm",variant:"ghost",className:"mt-3 hover:text-danger",leftIcon:e.jsx(Le,{}),loading:v.isPending,onClick:()=>v.mutate(g.id,{onSuccess:()=>(h(null),R.success("Edge removed")),onError:k=>R.error(H(k))}),children:"Delete manual edge"}),g.source==="detected"&&e.jsx("p",{className:"mt-2 text-fg-subtle",children:"Detected edges disappear on their own once no sampled job references the parent."})]})]})]}),o.data&&e.jsx(Xs,{open:x,onClose:()=>r(!1),queues:o.data.nodes.map(k=>k.queueName),saving:m.isPending,onSave:(k,O,Q)=>m.mutate({connectionId:t,from:k,to:O,label:Q||null},{onSuccess:()=>(r(!1),R.success("Edge added")),onError:K=>R.error(H(K))})})]})}function Ie(t){return t.source==="detected"?{...t,from:t.to,to:t.from}:t}function Xs({open:t,onClose:s,queues:l,onSave:d,saving:a}){const i=c.useMemo(()=>[...l].sort((b,w)=>b.localeCompare(w)),[l]),[n,o]=c.useState(i[0]??""),[m,v]=c.useState(i[1]??""),[f,p]=c.useState(""),z=n===m;return e.jsx(_e,{open:t,onClose:s,size:"sm",title:"Add manual edge",description:"Document a producer → consumer relationship that Redis cannot observe.",footer:e.jsxs(e.Fragment,{children:[e.jsx(P,{variant:"ghost",size:"sm",onClick:s,children:"Cancel"}),e.jsx(P,{variant:"primary",size:"sm",disabled:z||!n||!m,loading:a,onClick:()=>d(n,m,f.trim()),children:"Add edge"})]}),children:e.jsxs("div",{className:"space-y-3",children:[e.jsx(je,{label:"From (producer)",value:n,onChange:b=>o(b.target.value),options:i.map(b=>({value:b,label:b}))}),e.jsx(je,{label:"To (consumer)",value:m,onChange:b=>v(b.target.value),options:i.map(b=>({value:b,label:b})),error:z?"Pick two different queues":void 0}),e.jsx(De,{label:"Label (optional)",value:f,onChange:b=>p(b.target.value),maxLength:120,placeholder:"e.g. enqueues on success"})]})})}function Us(){const{has:t}=dt();return t("flows")?e.jsx(Et,{}):e.jsx(ut,{feature:"flows"})}function Ks(){const{has:t}=dt();return t("flows")?e.jsx(Et,{}):e.jsx(ut,{feature:"flows"})}function Et(){var b;const{connectionId:t}=os(),[s]=rs(),l=lt(),{isOperator:d}=ct(),a=Oe(),i=is(),[n,o]=c.useState(null),m=c.useMemo(()=>{var w;return((w=i.data)==null?void 0:w.maps)??[]},[i.data]),v=t?null:s.get("map"),f=t?{kind:"connection",id:t}:v?{kind:"map",id:v}:null;c.useEffect(()=>{if(f||!i.data)return;const w=Ve(m)[0];w&&l(te.flowMap(w.map.id),{replace:!0})},[f,i.data,m,l]);const p=c.useCallback(w=>l(w.kind==="map"?te.flowMap(w.id):te.flows(w.id)),[l]),z=c.useCallback(w=>o({parentId:w}),[]);return e.jsxs("div",{className:"flex h-full min-h-0",children:[e.jsx(Cs,{maps:m,loading:i.isLoading,detectedComplete:((b=i.data)==null?void 0:b.detectedComplete)??!0,connections:a.data??[],selection:f,canCreate:d,onSelect:p,onNewMap:z}),e.jsxs("div",{className:"relative min-w-0 flex-1",children:[(f==null?void 0:f.kind)==="connection"?e.jsx(Gs,{connectionId:f.id},f.id):(f==null?void 0:f.kind)==="map"?e.jsx(Bs,{mapId:f.id,maps:m,canOperate:d,onSelectMap:w=>p({kind:"map",id:w}),onDeleted:w=>l(w?te.flowMap(w):te.flows(),{replace:!0})},f.id):i.data&&e.jsx("div",{className:"flex h-full items-center justify-center",children:e.jsx(ve,{icon:e.jsx(he,{}),title:"Draw how work moves through your queues",description:e.jsxs(e.Fragment,{children:["A flow map is a named diagram of the queues one process goes through, across any of your connections, with live counts on every queue. Draw it here, or ask your AI client (MCP) to draw it for you.",m.some(w=>w.kind==="detected")&&" The detected maps on the left come straight from BullMQ FlowProducer."]}),action:d&&e.jsx(P,{variant:"primary",leftIcon:e.jsx(ue,{}),onClick:()=>z(null),children:"New map"})})}),!f&&i.data&&e.jsxs("p",{className:"pointer-events-none absolute bottom-6 left-1/2 flex -translate-x-1/2 items-center gap-1.5 text-[11px] text-fg-subtle",children:[e.jsx(ls,{className:"size-3.5","aria-hidden":!0}),"MCP tools: create_flow_map, add_flow_queue, add_flow_edge"]})]}),e.jsx(Ls,{open:!!n,parentId:(n==null?void 0:n.parentId)??null,maps:m,onClose:()=>o(null),onCreated:w=>p({kind:"map",id:w.id})})]})}export{Us as FlowsIndexPage,Ks as FlowsPage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.fm-canvas{--fm-edge-manual: var(--accent);--fm-edge-detected: var(--teal)}.fm-node{animation:fm-node-in .42s cubic-bezier(.22,1,.36,1) both;transition:border-color .15s,box-shadow .15s,opacity .15s}@keyframes fm-node-in{0%{opacity:0;transform:scale(.86)}60%{opacity:1}to{opacity:1;transform:none}}.fm-node.fm-fresh{animation:fm-node-in .42s cubic-bezier(.22,1,.36,1) both,fm-fresh 1.6s ease-out .2s}@keyframes fm-fresh{0%{box-shadow:0 0 color-mix(in srgb,var(--accent) 55%,transparent)}to{box-shadow:0 0 0 14px transparent}}.fm-canvas .react-flow__handle.fm-handle{width:10px;height:10px;border:2px solid var(--surface);background:var(--border-strong);transition:background .12s,transform .12s}.fm-canvas.fm-editable .react-flow__node:hover .react-flow__handle.fm-handle,.fm-canvas .react-flow__handle.fm-handle.connectingfrom,.fm-canvas .react-flow__handle.fm-handle.connectingto{background:var(--accent);transform:translate(var(--fm-hx, 0),-50%) scale(1.25)}.fm-canvas .react-flow__handle-left.fm-handle{--fm-hx: -50%}.fm-canvas .react-flow__handle-right.fm-handle{--fm-hx: 50%}.fm-edge-draw{stroke-dasharray:1;stroke-dashoffset:1;animation:fm-draw .64s cubic-bezier(.65,0,.35,1) forwards}@keyframes fm-draw{to{stroke-dashoffset:0}}.fm-edge-flow{stroke-dasharray:6 5;animation:fm-flow .9s linear infinite}@keyframes fm-flow{to{stroke-dashoffset:-11}}.fm-edge-label{animation:fm-fade-in .3s ease-out .45s both}@keyframes fm-fade-in{0%{opacity:0;transform:scale(.9)}to{opacity:1}}.fm-canvas .react-flow__connection-path{stroke:var(--fm-edge-manual);stroke-width:1.75;stroke-dasharray:5 4}.fm-tree-fresh{animation:fm-tree-fresh 2.4s ease-out}@keyframes fm-tree-fresh{0%,40%{background:color-mix(in srgb,var(--accent) 18%,transparent)}to{background:transparent}}.fm-pop{animation:dialog-in .12s ease-out}@media(prefers-reduced-motion:reduce){.fm-node,.fm-node.fm-fresh,.fm-edge-label,.fm-edge-flow,.fm-tree-fresh{animation:none}.fm-edge-draw{animation:none;stroke-dashoffset:0}}
|
|
1
|
+
.fm-canvas{--fm-edge-manual: var(--accent);--fm-edge-detected: var(--teal)}.fm-node{animation:fm-node-in .42s cubic-bezier(.22,1,.36,1) both;transition:border-color .15s,box-shadow .15s,opacity .15s}@keyframes fm-node-in{0%{opacity:0;transform:scale(.86)}60%{opacity:1}to{opacity:1;transform:none}}.fm-node.fm-fresh{animation:fm-node-in .42s cubic-bezier(.22,1,.36,1) both,fm-fresh 1.6s ease-out .2s}@keyframes fm-fresh{0%{box-shadow:0 0 color-mix(in srgb,var(--accent) 55%,transparent)}to{box-shadow:0 0 0 14px transparent}}.fm-canvas .react-flow__handle.fm-handle{width:10px;height:10px;border:2px solid var(--surface);background:var(--border-strong);transition:background .12s,transform .12s}.fm-canvas.fm-editable .react-flow__node:hover .react-flow__handle.fm-handle,.fm-canvas .react-flow__handle.fm-handle.connectingfrom,.fm-canvas .react-flow__handle.fm-handle.connectingto{background:var(--accent);transform:translate(var(--fm-hx, 0),-50%) scale(1.25)}.fm-canvas .react-flow__handle-left.fm-handle{--fm-hx: -50%}.fm-canvas .react-flow__handle-right.fm-handle{--fm-hx: 50%}.fm-edge-draw{stroke-dasharray:1;stroke-dashoffset:1;animation:fm-draw .64s cubic-bezier(.65,0,.35,1) forwards}@keyframes fm-draw{to{stroke-dashoffset:0}}.fm-edge-flow{stroke-dasharray:6 5;animation:fm-flow .9s linear infinite}@keyframes fm-flow{to{stroke-dashoffset:-11}}.fm-arrowhead{animation:fm-fade-in .2s ease-out both}.fm-edge-label{animation:fm-fade-in .3s ease-out .45s both}@keyframes fm-fade-in{0%{opacity:0;transform:scale(.9)}to{opacity:1}}.fm-canvas .react-flow__connection-path{stroke:var(--fm-edge-manual);stroke-width:1.75;stroke-dasharray:5 4}.fm-tree-fresh{animation:fm-tree-fresh 2.4s ease-out}@keyframes fm-tree-fresh{0%,40%{background:color-mix(in srgb,var(--accent) 18%,transparent)}to{background:transparent}}.fm-pop{animation:dialog-in .12s ease-out}@media(prefers-reduced-motion:reduce){.fm-node,.fm-node.fm-fresh,.fm-edge-label,.fm-arrowhead,.fm-edge-flow,.fm-tree-fresh{animation:none}.fm-edge-draw{animation:none;stroke-dashoffset:0}}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{c as L,a7 as T,a8 as B,ah as $,j as e,ai as g,aj as b,d as z,O as f,L as x,i as u,v as F,ak as w,e as p,ad as O,r as h,al as j,b as N,h as P,am as K,an as S,ao as y}from"./index-
|
|
1
|
+
import{c as L,a7 as T,a8 as B,ah as $,j as e,ai as g,aj as b,d as z,O as f,L as x,i as u,v as F,ak as w,e as p,ad as O,r as h,al as j,b as N,h as P,am as K,an as S,ao as y}from"./index-CIHznD1e.js";import{b as A,c as H,i as q,e as I,f as G,C as J,M as R,l as V,h as D,H as k,P as v}from"./style-B5JqANGk.js";/**
|
|
2
2
|
* @license lucide-react v0.469.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
5
5
|
* See the LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/const W=L("ArrowLeft",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]]),C=230,_=92;function ee(){const{connectionId:a,queue:r,jobId:s}=T(),[
|
|
6
|
+
*/const W=L("ArrowLeft",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]]),C=230,_=92;function ee(){const{connectionId:a,queue:r,jobId:s}=T(),[c]=B(),d=Number(c.get("maxNodes"))||200,o=$(a,r,s,d);if(o.isLoading)return e.jsxs(g,{children:[e.jsx(b,{title:"Flow tree"}),e.jsx(z,{label:"Walking the flow…"})]});if(o.isError||!o.data)return e.jsxs(g,{children:[e.jsx(b,{title:"Flow tree"}),e.jsx(f,{icon:e.jsx(w,{}),title:"Could not load the flow",description:o.isError?F(o.error):"This job no longer exists.",action:e.jsxs(x,{to:u.queue(a,r),className:"text-accent hover:underline",children:["Back to ",r]})})]});const n=o.data,t=n.nodes.length<=1;return e.jsxs(g,{children:[e.jsx(b,{title:"Flow tree",description:`${p(n.nodes.length)} job${n.nodes.length===1?"":"s"}${n.climbedLevels>0?` · root is ${n.climbedLevels} level${n.climbedLevels===1?"":"s"} up`:""}`,actions:e.jsxs(x,{to:u.job(a,r,s),className:"inline-flex items-center gap-1.5 text-[13px] text-accent hover:underline",children:[e.jsx(W,{className:"size-3.5","aria-hidden":!0}),"Back to job"]})}),n.truncated&&e.jsxs("div",{className:"mb-3 flex items-start gap-2 rounded-md border border-warning/40 bg-warning/10 px-3 py-2 text-xs text-fg-muted",children:[e.jsx(O,{className:"mt-0.5 size-3.5 shrink-0 text-warning","aria-hidden":!0}),e.jsxs("span",{children:["This flow is larger than ",p(d)," jobs, so only part of it is drawn. Child counts on each card come straight from Redis and are exact even where the tree is cut."]})]}),t?e.jsx(f,{icon:e.jsx(w,{}),title:"This job is not part of a flow",description:"It has no parent and no children. Flows are created with FlowProducer, which links a parent job to the children it waits on.",action:e.jsx(x,{to:u.job(a,r,s),className:"text-accent hover:underline",children:"Back to job"})}):e.jsx(Z,{data:n,connectionId:a})]})}function Z({data:a,connectionId:r}){const{nodes:s,edges:c}=h.useMemo(()=>Q(a,r),[a,r]),[d,o,n]=A(s),[t,l,m]=H(c);h.useEffect(()=>{o(s),l(c)},[s,c,o,l]);const i=h.useMemo(()=>({job:U}),[]),E=h.useCallback(M=>j(M.data.node.state).fg,[]);return e.jsx("div",{className:"card h-[calc(100vh-220px)] min-h-[420px] overflow-hidden",children:e.jsxs(q,{nodes:d,edges:t,onNodesChange:n,onEdgesChange:m,nodeTypes:i,fitView:!0,minZoom:.1,proOptions:{hideAttribution:!0},nodesConnectable:!1,edgesFocusable:!1,children:[e.jsx(I,{variant:G.Dots,gap:16,size:1}),e.jsx(J,{showInteractive:!1}),e.jsx(R,{pannable:!0,zoomable:!0,position:"bottom-right",nodeColor:E,maskColor:"color-mix(in srgb, var(--bg) 70%, transparent)",style:{width:160,height:100,background:"var(--surface)"}})]})})}function Q(a,r){const s=new Map(a.nodes.map(t=>[t.key,t])),c=a.nodes.filter(t=>t.parentKey&&s.has(t.parentKey)).map(t=>({from:t.parentKey,to:t.key})),d=V(a.nodes.map(t=>({id:t.key})),c,{nodeWidth:C,nodeHeight:_,columnGap:100,rowGap:24}),o=a.nodes.map(t=>{const l=t.parentKey?s.get(t.parentKey):void 0,m=!!l&&l.state==="waiting-children"&&t.finishedOn===null&&!t.missing,i=d.get(t.key);return{id:t.key,type:"job",position:{x:(i==null?void 0:i.x)??0,y:(i==null?void 0:i.y)??0},data:{node:t,connectionId:r,isFocus:t.key===a.focusKey,isBlocker:m}}}),n=c.map(t=>{const l=s.get(t.to),m=s.get(t.from),i=!!l&&l.finishedOn===null&&(m==null?void 0:m.state)==="waiting-children";return{id:`${t.from}->${t.to}`,source:t.from,target:t.to,animated:i,style:i?{stroke:j("waiting-children").fg}:void 0,markerEnd:{type:D.ArrowClosed,width:14,height:14}}});return{nodes:o,edges:n}}const U=h.memo(function({data:r}){const{node:s,connectionId:c,isFocus:d,isBlocker:o}=r,n=j(s.state),t=s.dependencies;return e.jsxs("div",{className:N("rounded-lg border bg-surface px-3 py-2 text-fg shadow-sm",d?"border-accent ring-2 ring-accent/30":"border-border-strong",o&&!d&&"border-warning",s.missing&&"border-dashed opacity-70"),style:{width:C},children:[e.jsx(k,{type:"target",position:v.Left,className:"!bg-border-strong"}),e.jsx(k,{type:"source",position:v.Right,className:"!bg-border-strong"}),e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx("span",{className:N("size-2 shrink-0 rounded-full",n.dotClass),"aria-hidden":!0}),e.jsx("span",{className:"truncate text-[11px] text-fg-subtle",title:s.queueName,children:s.queueName}),o&&e.jsx(P,{variant:"outline",size:"xs",className:"ml-auto border-warning/50 text-warning",children:"blocking"})]}),s.missing?e.jsxs("p",{className:"mt-1 truncate font-mono text-[13px] text-fg-subtle",title:"This child was removed or cleaned",children:[s.id," · gone"]}):e.jsxs(x,{to:u.job(c,s.queueName,s.id),className:"mt-1 block truncate font-mono text-[13px] text-accent hover:underline",title:`${s.name||"job"} #${s.id}`,children:[s.name||"job"," #",s.id]}),e.jsxs("div",{className:"mt-1 flex items-center gap-1.5",children:[e.jsxs("span",{className:"state-chip",style:S(s.state),children:[s.state==="active"&&e.jsx(K,{className:"size-2.5 animate-spin","aria-hidden":!0}),n.label]}),t&&e.jsxs("span",{className:"text-[11px] text-fg-subtle",title:"Children of this job",children:[e.jsx("span",{className:"text-success",children:p(t.processed)}),"/",p(t.processed+t.unprocessed)]}),s.attemptsMade>1&&e.jsxs("span",{className:"text-[11px] text-fg-subtle",title:"Attempts made",children:["×",s.attemptsMade]}),e.jsx("span",{className:"ml-auto shrink-0 text-[11px] text-fg-subtle",children:s.finishedOn?y(s.finishedOn):s.timestamp?y(s.timestamp):""})]}),s.childrenTruncated&&e.jsx("p",{className:"mt-1 text-[10px] text-warning",children:"more children not shown"})]})});export{ee as JobTreePage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a8 as P,ab as S,$ as N,j as e,ac as o,ad as M,d as w,ae as B,af as A,ag as q,r as p,v as j,B as m,aa as E,b as $}from"./index-
|
|
1
|
+
import{a8 as P,ab as S,$ as N,j as e,ac as o,ad as M,d as w,ae as B,af as A,ag as q,r as p,v as j,B as m,aa as E,b as $}from"./index-CIHznD1e.js";function R(){const[n]=P(),t=S(),{user:s,loading:a}=N(),r=n.get("error"),i=n.get("request");return r||!i?e.jsx(o,{title:"Could not connect to Bullpane",children:e.jsxs("p",{className:"flex gap-2 text-sm text-fg",children:[e.jsx(M,{className:"mt-0.5 size-4 shrink-0 text-danger"}),r??"This page needs a sign-in request from an MCP client. Start the connection again from Claude."]})}):a?e.jsx("div",{className:"flex min-h-screen items-center justify-center",children:e.jsx(w,{})}):s?e.jsx(D,{request:i}):e.jsx(B,{to:"/login",replace:!0,state:{from:t}})}function D({request:n}){const{user:t}=N(),s=A(n),a=q(),[r,i]=p.useState(null),[f,g]=p.useState(null);if(s.isLoading)return e.jsx("div",{className:"flex min-h-screen items-center justify-center",children:e.jsx(w,{})});if(s.isError||!s.data)return e.jsx(o,{title:"Could not connect to Bullpane",children:e.jsx("p",{className:"text-sm text-fg",children:j(s.error,"This sign-in request is no longer valid. Start the connection again from Claude.")})});const{clientName:c,redirectHost:C,allowed:l,requested:y,maxAccess:k}=s.data,d=r??(l==="write"&&y==="write"?"write":"read"),h=l==="write"?null:k!=="write"?"an admin limited MCP to read only":`your role is ${t==null?void 0:t.role}`,x=v=>{g(null),a.mutate({request:n,approve:v,access:d},{onSuccess:({redirectTo:u})=>window.location.assign(u),onError:u=>g(j(u))})};return l==="off"?e.jsxs(o,{title:"MCP is turned off",children:[e.jsxs("p",{className:"text-sm text-fg-muted",children:["An admin has to allow MCP clients in ",e.jsx("span",{className:"text-fg",children:"Settings → MCP"})," before ",c," can connect."]}),e.jsxs(m,{className:"mt-5 w-full",variant:"secondary",loading:a.isPending,onClick:()=>x(!1),children:["Back to ",c]})]}):e.jsxs(o,{title:`Connect ${c} to Bullpane`,description:e.jsxs(e.Fragment,{children:["It will act as ",e.jsx("span",{className:"text-fg",children:t==null?void 0:t.email})," (",t==null?void 0:t.role,") and send you back to ",C,"."]}),children:[e.jsxs("div",{className:"flex flex-col gap-2",role:"radiogroup","aria-label":"Access",children:[e.jsx(b,{selected:d==="read",onSelect:()=>i("read"),title:"Read only",detail:"See connections, queues, jobs, logs and schedulers."}),e.jsx(b,{selected:d==="write",disabled:l!=="write",onSelect:()=>i("write"),title:"Read & write",detail:h?`Not available: ${h}.`:"Also retry, promote, remove and add jobs, pause and resume queues."})]}),e.jsxs("p",{className:"mt-3 flex gap-1.5 text-[11px] text-fg-subtle",children:[e.jsx(E,{className:"size-3.5 shrink-0"}),"Every change is in the audit log. Drain, clean and obliterate always need you in the dashboard. Disconnect any time in Settings → MCP."]}),f&&e.jsx("p",{className:"mt-3 text-xs text-danger",children:f}),e.jsxs("div",{className:"mt-5 flex gap-2",children:[e.jsx(m,{className:"flex-1",variant:"ghost",disabled:a.isPending,onClick:()=>x(!1),children:"Cancel"}),e.jsx(m,{className:"flex-1",variant:"primary",loading:a.isPending,onClick:()=>x(!0),children:"Allow"})]})]})}function b({selected:n,disabled:t,onSelect:s,title:a,detail:r}){return e.jsxs("button",{type:"button",role:"radio","aria-checked":n,disabled:t,onClick:s,className:$("rounded-md border px-3 py-2.5 text-left transition-colors disabled:cursor-not-allowed disabled:opacity-60",n?"border-accent bg-accent/10":"border-border bg-surface-2 hover:border-border-strong"),children:[e.jsx("span",{className:"block text-sm font-medium text-fg",children:a}),e.jsx("span",{className:"mt-0.5 block text-xs text-fg-muted",children:r})]})}export{R as McpConsentPage};
|