agentix-cli 0.14.0 → 0.15.0
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/README.md +64 -34
- package/dist/agent-LRSSVFDR.js +2 -0
- package/dist/board-dashboard-DAPHL6W2.js +4508 -0
- package/dist/board-dashboard-DAPHL6W2.js.map +1 -0
- package/dist/chunk-3XULOWYB.js +199 -0
- package/dist/chunk-3XULOWYB.js.map +1 -0
- package/dist/{chunk-ZRYBSI5G.js → chunk-7MBTMCKY.js} +2 -2
- package/dist/{chunk-XVYMZ4M7.js → chunk-AU37OREL.js} +5 -5
- package/dist/chunk-B4Q4X7D5.js +6 -0
- package/dist/chunk-B4Q4X7D5.js.map +1 -0
- package/dist/chunk-CXY2RYNI.js +11 -0
- package/dist/chunk-CXY2RYNI.js.map +1 -0
- package/dist/chunk-FIOB3MEX.js +2 -0
- package/dist/chunk-HLRVDCNH.js +5 -0
- package/dist/{chunk-NJR5WF5E.js.map → chunk-HLRVDCNH.js.map} +1 -1
- package/dist/chunk-IEVEMKAI.js +168 -0
- package/dist/chunk-IEVEMKAI.js.map +1 -0
- package/dist/{chunk-O72LPMBR.js → chunk-RB7265LO.js} +2 -2
- package/dist/chunk-RGUYBH77.js +7 -0
- package/dist/chunk-RGUYBH77.js.map +1 -0
- package/dist/chunk-WVNSWBCU.js +138 -0
- package/dist/chunk-WVNSWBCU.js.map +1 -0
- package/dist/cli.js +34 -24
- package/dist/cli.js.map +1 -1
- package/dist/{compaction-7I3E5QUO.js → compaction-JBZZ7I7Y.js} +4 -4
- package/dist/config-DCJYECL4.js +2 -0
- package/dist/config-mutator-6EJHSP3Y.js +2 -0
- package/dist/debug-3MEUO2P2.js +2 -0
- package/dist/{heal-OTGT5HHJ.js → heal-WKY5I4KB.js} +2 -2
- package/dist/index.d.ts +224 -1
- package/dist/index.js +11 -11
- package/dist/index.js.map +1 -1
- package/dist/loader-PTCXLJEX.js +2 -0
- package/dist/memory-extract-AD7KMRPC.js +2 -0
- package/dist/{providers-G3WZ4RVJ.js → providers-VUIWO3ZK.js} +2 -2
- package/dist/registry-5ND7GLFJ.js +2 -0
- package/dist/scheduler-FCXOXCN5.js +2 -0
- package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
- package/dist/{usage-dashboard-M7AFIGMJ.js → usage-dashboard-QX3CF6KK.js} +8 -8
- package/dist/usage-dashboard-QX3CF6KK.js.map +1 -0
- package/dist/whatsapp-state-6D6FHHD2.js +2 -0
- package/dist/whatsapp-state-6D6FHHD2.js.map +1 -0
- package/dist/workspace-setup-ZWTT42XT.js +2 -0
- package/dist/workspace-setup-ZWTT42XT.js.map +1 -0
- package/package.json +2 -1
- package/dist/agent-DW3NYIIL.js +0 -2
- package/dist/board-dashboard-6TKE6NMV.js +0 -1435
- package/dist/board-dashboard-6TKE6NMV.js.map +0 -1
- package/dist/chunk-4YCH6IZV.js +0 -2
- package/dist/chunk-DYOBFJLX.js +0 -11
- package/dist/chunk-DYOBFJLX.js.map +0 -1
- package/dist/chunk-ITZEVXZ4.js +0 -2
- package/dist/chunk-ITZEVXZ4.js.map +0 -1
- package/dist/chunk-M33CB2H4.js +0 -6
- package/dist/chunk-M33CB2H4.js.map +0 -1
- package/dist/chunk-NJR5WF5E.js +0 -5
- package/dist/chunk-RB73FZXJ.js +0 -133
- package/dist/chunk-RB73FZXJ.js.map +0 -1
- package/dist/chunk-UPT2UMF6.js +0 -159
- package/dist/chunk-UPT2UMF6.js.map +0 -1
- package/dist/config-7QJ2QJIC.js +0 -2
- package/dist/config-mutator-GAEVGHDD.js +0 -2
- package/dist/debug-N3B5NVJU.js +0 -2
- package/dist/loader-YQE6XJBT.js +0 -2
- package/dist/memory-extract-RGPUPMKU.js +0 -2
- package/dist/registry-YVAG4EO5.js +0 -2
- package/dist/usage-dashboard-M7AFIGMJ.js.map +0 -1
- package/dist/workspace-setup-QQTCJY4B.js +0 -2
- /package/dist/{agent-DW3NYIIL.js.map → agent-LRSSVFDR.js.map} +0 -0
- /package/dist/{chunk-ZRYBSI5G.js.map → chunk-7MBTMCKY.js.map} +0 -0
- /package/dist/{chunk-XVYMZ4M7.js.map → chunk-AU37OREL.js.map} +0 -0
- /package/dist/{chunk-4YCH6IZV.js.map → chunk-FIOB3MEX.js.map} +0 -0
- /package/dist/{chunk-O72LPMBR.js.map → chunk-RB7265LO.js.map} +0 -0
- /package/dist/{compaction-7I3E5QUO.js.map → compaction-JBZZ7I7Y.js.map} +0 -0
- /package/dist/{config-7QJ2QJIC.js.map → config-DCJYECL4.js.map} +0 -0
- /package/dist/{config-mutator-GAEVGHDD.js.map → config-mutator-6EJHSP3Y.js.map} +0 -0
- /package/dist/{debug-N3B5NVJU.js.map → debug-3MEUO2P2.js.map} +0 -0
- /package/dist/{heal-OTGT5HHJ.js.map → heal-WKY5I4KB.js.map} +0 -0
- /package/dist/{loader-YQE6XJBT.js.map → loader-PTCXLJEX.js.map} +0 -0
- /package/dist/{memory-extract-RGPUPMKU.js.map → memory-extract-AD7KMRPC.js.map} +0 -0
- /package/dist/{providers-G3WZ4RVJ.js.map → providers-VUIWO3ZK.js.map} +0 -0
- /package/dist/{registry-YVAG4EO5.js.map → registry-5ND7GLFJ.js.map} +0 -0
- /package/dist/{workspace-setup-QQTCJY4B.js.map → scheduler-FCXOXCN5.js.map} +0 -0
- /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
|
@@ -1,1435 +0,0 @@
|
|
|
1
|
-
import{a as I}from"./chunk-6U2XOQHU.js";import{c as A,d as T}from"./chunk-ITZEVXZ4.js";import"./chunk-4YCH6IZV.js";import{createServer as O}from"http";import{appendFileSync as N,existsSync as R,mkdirSync as B}from"fs";import{resolve as _}from"path";var M='/*! Sortable 1.15.2 - MIT | git://github.com/SortableJS/Sortable.git */\n!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Sortable=e()}(this,function(){"use strict";function e(e,t){var n,o=Object.keys(e);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(e),t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),o.push.apply(o,n)),o}function I(o){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?e(Object(i),!0).forEach(function(t){var e,n;e=o,t=i[n=t],n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t}):Object.getOwnPropertyDescriptors?Object.defineProperties(o,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach(function(t){Object.defineProperty(o,t,Object.getOwnPropertyDescriptor(i,t))})}return o}function o(t){return(o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function a(){return(a=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n,o=arguments[e];for(n in o)Object.prototype.hasOwnProperty.call(o,n)&&(t[n]=o[n])}return t}).apply(this,arguments)}function i(t,e){if(null==t)return{};var n,o=function(t,e){if(null==t)return{};for(var n,o={},i=Object.keys(t),r=0;r<i.length;r++)n=i[r],0<=e.indexOf(n)||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols)for(var i=Object.getOwnPropertySymbols(t),r=0;r<i.length;r++)n=i[r],0<=e.indexOf(n)||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n]);return o}function r(t){return function(t){if(Array.isArray(t))return l(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,e){if(t){if("string"==typeof t)return l(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return"Map"===(n="Object"===n&&t.constructor?t.constructor.name:n)||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?l(t,e):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function l(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,o=new Array(e);n<e;n++)o[n]=t[n];return o}function t(t){if("undefined"!=typeof window&&window.navigator)return!!navigator.userAgent.match(t)}var y=t(/(?:Trident.*rv[ :]?11\\.|msie|iemobile|Windows Phone)/i),w=t(/Edge/i),s=t(/firefox/i),u=t(/safari/i)&&!t(/chrome/i)&&!t(/android/i),n=t(/iP(ad|od|hone)/i),c=t(/chrome/i)&&t(/android/i),d={capture:!1,passive:!1};function h(t,e,n){t.addEventListener(e,n,!y&&d)}function f(t,e,n){t.removeEventListener(e,n,!y&&d)}function p(t,e){if(e&&(">"===e[0]&&(e=e.substring(1)),t))try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return}}function P(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(">"!==e[0]||t.parentNode===n)&&p(t,e)||o&&t===n)return t}while(t!==n&&(t=(i=t).host&&i!==document&&i.host.nodeType?i.host:i.parentNode))}var i;return null}var g,m=/\\s+/g;function k(t,e,n){var o;t&&e&&(t.classList?t.classList[n?"add":"remove"](e):(o=(" "+t.className+" ").replace(m," ").replace(" "+e+" "," "),t.className=(o+(n?" "+e:"")).replace(m," ")))}function R(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,""):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];o[e=!(e in o||-1!==e.indexOf("webkit"))?"-webkit-"+e:e]=n+("string"==typeof n?"":"px")}}function v(t,e){var n="";if("string"==typeof t)n=t;else do{var o=R(t,"transform")}while(o&&"none"!==o&&(n=o+" "+n),!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function b(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i<r;i++)n(o[i],i);return o}return[]}function O(){var t=document.scrollingElement;return t||document.documentElement}function X(t,e,n,o,i){if(t.getBoundingClientRect||t===window){var r,a,l,s,c,u,d=t!==window&&t.parentNode&&t!==O()?(a=(r=t.getBoundingClientRect()).top,l=r.left,s=r.bottom,c=r.right,u=r.height,r.width):(l=a=0,s=window.innerHeight,c=window.innerWidth,u=window.innerHeight,window.innerWidth);if((e||n)&&t!==window&&(i=i||t.parentNode,!y))do{if(i&&i.getBoundingClientRect&&("none"!==R(i,"transform")||n&&"static"!==R(i,"position"))){var h=i.getBoundingClientRect();a-=h.top+parseInt(R(i,"border-top-width")),l-=h.left+parseInt(R(i,"border-left-width")),s=a+r.height,c=l+r.width;break}}while(i=i.parentNode);return o&&t!==window&&(o=(e=v(i||t))&&e.a,t=e&&e.d,e&&(s=(a/=t)+(u/=t),c=(l/=o)+(d/=o))),{top:a,left:l,bottom:s,right:c,width:d,height:u}}}function Y(t,e,n){for(var o=M(t,!0),i=X(t)[e];o;){var r=X(o)[n];if(!("top"===n||"left"===n?r<=i:i<=r))return o;if(o===O())break;o=M(o,!1)}return!1}function B(t,e,n,o){for(var i=0,r=0,a=t.children;r<a.length;){if("none"!==a[r].style.display&&a[r]!==Ft.ghost&&(o||a[r]!==Ft.dragged)&&P(a[r],n.draggable,t,!1)){if(i===e)return a[r];i++}r++}return null}function F(t,e){for(var n=t.lastElementChild;n&&(n===Ft.ghost||"none"===R(n,"display")||e&&!p(n,e));)n=n.previousElementSibling;return n||null}function j(t,e){var n=0;if(!t||!t.parentNode)return-1;for(;t=t.previousElementSibling;)"TEMPLATE"===t.nodeName.toUpperCase()||t===Ft.clone||e&&!p(t,e)||n++;return n}function E(t){var e=0,n=0,o=O();if(t)do{var i=v(t),r=i.a,i=i.d}while(e+=t.scrollLeft*r,n+=t.scrollTop*i,t!==o&&(t=t.parentNode));return[e,n]}function M(t,e){if(!t||!t.getBoundingClientRect)return O();var n=t,o=!1;do{if(n.clientWidth<n.scrollWidth||n.clientHeight<n.scrollHeight){var i=R(n);if(n.clientWidth<n.scrollWidth&&("auto"==i.overflowX||"scroll"==i.overflowX)||n.clientHeight<n.scrollHeight&&("auto"==i.overflowY||"scroll"==i.overflowY)){if(!n.getBoundingClientRect||n===document.body)return O();if(o||e)return n;o=!0}}}while(n=n.parentNode);return O()}function D(t,e){return Math.round(t.top)===Math.round(e.top)&&Math.round(t.left)===Math.round(e.left)&&Math.round(t.height)===Math.round(e.height)&&Math.round(t.width)===Math.round(e.width)}function S(e,n){return function(){var t;g||(1===(t=arguments).length?e.call(this,t[0]):e.apply(this,t),g=setTimeout(function(){g=void 0},n))}}function H(t,e,n){t.scrollLeft+=e,t.scrollTop+=n}function _(t){var e=window.Polymer,n=window.jQuery||window.Zepto;return e&&e.dom?e.dom(t).cloneNode(!0):n?n(t).clone(!0)[0]:t.cloneNode(!0)}function C(t,e){R(t,"position","absolute"),R(t,"top",e.top),R(t,"left",e.left),R(t,"width",e.width),R(t,"height",e.height)}function T(t){R(t,"position",""),R(t,"top",""),R(t,"left",""),R(t,"width",""),R(t,"height","")}function L(n,o,i){var r={};return Array.from(n.children).forEach(function(t){var e;P(t,o.draggable,n,!1)&&!t.animated&&t!==i&&(e=X(t),r.left=Math.min(null!==(t=r.left)&&void 0!==t?t:1/0,e.left),r.top=Math.min(null!==(t=r.top)&&void 0!==t?t:1/0,e.top),r.right=Math.max(null!==(t=r.right)&&void 0!==t?t:-1/0,e.right),r.bottom=Math.max(null!==(t=r.bottom)&&void 0!==t?t:-1/0,e.bottom))}),r.width=r.right-r.left,r.height=r.bottom-r.top,r.x=r.left,r.y=r.top,r}var K="Sortable"+(new Date).getTime();function x(){var e,o=[];return{captureAnimationState:function(){o=[],this.options.animation&&[].slice.call(this.el.children).forEach(function(t){var e,n;"none"!==R(t,"display")&&t!==Ft.ghost&&(o.push({target:t,rect:X(t)}),e=I({},o[o.length-1].rect),!t.thisAnimationDuration||(n=v(t,!0))&&(e.top-=n.f,e.left-=n.e),t.fromRect=e)})},addAnimationState:function(t){o.push(t)},removeAnimationState:function(t){o.splice(function(t,e){for(var n in t)if(t.hasOwnProperty(n))for(var o in e)if(e.hasOwnProperty(o)&&e[o]===t[n][o])return Number(n);return-1}(o,{target:t}),1)},animateAll:function(t){var c=this;if(!this.options.animation)return clearTimeout(e),void("function"==typeof t&&t());var u=!1,d=0;o.forEach(function(t){var e=0,n=t.target,o=n.fromRect,i=X(n),r=n.prevFromRect,a=n.prevToRect,l=t.rect,s=v(n,!0);s&&(i.top-=s.f,i.left-=s.e),n.toRect=i,n.thisAnimationDuration&&D(r,i)&&!D(o,i)&&(l.top-i.top)/(l.left-i.left)==(o.top-i.top)/(o.left-i.left)&&(t=l,s=r,r=a,a=c.options,e=Math.sqrt(Math.pow(s.top-t.top,2)+Math.pow(s.left-t.left,2))/Math.sqrt(Math.pow(s.top-r.top,2)+Math.pow(s.left-r.left,2))*a.animation),D(i,o)||(n.prevFromRect=o,n.prevToRect=i,e=e||c.options.animation,c.animate(n,l,i,e)),e&&(u=!0,d=Math.max(d,e),clearTimeout(n.animationResetTimer),n.animationResetTimer=setTimeout(function(){n.animationTime=0,n.prevFromRect=null,n.fromRect=null,n.prevToRect=null,n.thisAnimationDuration=null},e),n.thisAnimationDuration=e)}),clearTimeout(e),u?e=setTimeout(function(){"function"==typeof t&&t()},d):"function"==typeof t&&t(),o=[]},animate:function(t,e,n,o){var i,r;o&&(R(t,"transition",""),R(t,"transform",""),i=(r=v(this.el))&&r.a,r=r&&r.d,i=(e.left-n.left)/(i||1),r=(e.top-n.top)/(r||1),t.animatingX=!!i,t.animatingY=!!r,R(t,"transform","translate3d("+i+"px,"+r+"px,0)"),this.forRepaintDummy=t.offsetWidth,R(t,"transition","transform "+o+"ms"+(this.options.easing?" "+this.options.easing:"")),R(t,"transform","translate3d(0,0,0)"),"number"==typeof t.animated&&clearTimeout(t.animated),t.animated=setTimeout(function(){R(t,"transition",""),R(t,"transform",""),t.animated=!1,t.animatingX=!1,t.animatingY=!1},o))}}}var A=[],N={initializeByDefault:!0},W={mount:function(e){for(var t in N)!N.hasOwnProperty(t)||t in e||(e[t]=N[t]);A.forEach(function(t){if(t.pluginName===e.pluginName)throw"Sortable: Cannot mount plugin ".concat(e.pluginName," more than once")}),A.push(e)},pluginEvent:function(e,n,o){var t=this;this.eventCanceled=!1,o.cancel=function(){t.eventCanceled=!0};var i=e+"Global";A.forEach(function(t){n[t.pluginName]&&(n[t.pluginName][i]&&n[t.pluginName][i](I({sortable:n},o)),n.options[t.pluginName]&&n[t.pluginName][e]&&n[t.pluginName][e](I({sortable:n},o)))})},initializePlugins:function(n,o,i,t){for(var e in A.forEach(function(t){var e=t.pluginName;(n.options[e]||t.initializeByDefault)&&((t=new t(n,o,n.options)).sortable=n,t.options=n.options,n[e]=t,a(i,t.defaults))}),n.options){var r;n.options.hasOwnProperty(e)&&(void 0!==(r=this.modifyOption(n,e,n.options[e]))&&(n.options[e]=r))}},getEventProperties:function(e,n){var o={};return A.forEach(function(t){"function"==typeof t.eventProperties&&a(o,t.eventProperties.call(n[t.pluginName],e))}),o},modifyOption:function(e,n,o){var i;return A.forEach(function(t){e[t.pluginName]&&t.optionListeners&&"function"==typeof t.optionListeners[n]&&(i=t.optionListeners[n].call(e[t.pluginName],o))}),i}};function z(t){var e=t.sortable,n=t.rootEl,o=t.name,i=t.targetEl,r=t.cloneEl,a=t.toEl,l=t.fromEl,s=t.oldIndex,c=t.newIndex,u=t.oldDraggableIndex,d=t.newDraggableIndex,h=t.originalEvent,f=t.putSortable,p=t.extraEventProperties;if(e=e||n&&n[K]){var g,m=e.options,t="on"+o.charAt(0).toUpperCase()+o.substr(1);!window.CustomEvent||y||w?(g=document.createEvent("Event")).initEvent(o,!0,!0):g=new CustomEvent(o,{bubbles:!0,cancelable:!0}),g.to=a||n,g.from=l||n,g.item=i||n,g.clone=r,g.oldIndex=s,g.newIndex=c,g.oldDraggableIndex=u,g.newDraggableIndex=d,g.originalEvent=h,g.pullMode=f?f.lastPutMode:void 0;var v,b=I(I({},p),W.getEventProperties(o,e));for(v in b)g[v]=b[v];n&&n.dispatchEvent(g),m[t]&&m[t].call(e,g)}}function G(t,e){var n=(o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{}).evt,o=i(o,U);W.pluginEvent.bind(Ft)(t,e,I({dragEl:V,parentEl:Z,ghostEl:$,rootEl:Q,nextEl:J,lastDownEl:tt,cloneEl:et,cloneHidden:nt,dragStarted:gt,putSortable:st,activeSortable:Ft.active,originalEvent:n,oldIndex:ot,oldDraggableIndex:rt,newIndex:it,newDraggableIndex:at,hideGhostForTarget:Rt,unhideGhostForTarget:Xt,cloneNowHidden:function(){nt=!0},cloneNowShown:function(){nt=!1},dispatchSortableEvent:function(t){q({sortable:e,name:t,originalEvent:n})}},o))}var U=["evt"];function q(t){z(I({putSortable:st,cloneEl:et,targetEl:V,rootEl:Q,oldIndex:ot,oldDraggableIndex:rt,newIndex:it,newDraggableIndex:at},t))}var V,Z,$,Q,J,tt,et,nt,ot,it,rt,at,lt,st,ct,ut,dt,ht,ft,pt,gt,mt,vt,bt,yt,wt=!1,Et=!1,Dt=[],St=!1,_t=!1,Ct=[],Tt=!1,xt=[],Ot="undefined"!=typeof document,Mt=n,At=w||y?"cssFloat":"float",Nt=Ot&&!c&&!n&&"draggable"in document.createElement("div"),It=function(){if(Ot){if(y)return!1;var t=document.createElement("x");return t.style.cssText="pointer-events:auto","auto"===t.style.pointerEvents}}(),Pt=function(t,e){var n=R(t),o=parseInt(n.width)-parseInt(n.paddingLeft)-parseInt(n.paddingRight)-parseInt(n.borderLeftWidth)-parseInt(n.borderRightWidth),i=B(t,0,e),r=B(t,1,e),a=i&&R(i),l=r&&R(r),s=a&&parseInt(a.marginLeft)+parseInt(a.marginRight)+X(i).width,t=l&&parseInt(l.marginLeft)+parseInt(l.marginRight)+X(r).width;if("flex"===n.display)return"column"===n.flexDirection||"column-reverse"===n.flexDirection?"vertical":"horizontal";if("grid"===n.display)return n.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(i&&a.float&&"none"!==a.float){e="left"===a.float?"left":"right";return!r||"both"!==l.clear&&l.clear!==e?"horizontal":"vertical"}return i&&("block"===a.display||"flex"===a.display||"table"===a.display||"grid"===a.display||o<=s&&"none"===n[At]||r&&"none"===n[At]&&o<s+t)?"vertical":"horizontal"},kt=function(t){function l(r,a){return function(t,e,n,o){var i=t.options.group.name&&e.options.group.name&&t.options.group.name===e.options.group.name;if(null==r&&(a||i))return!0;if(null==r||!1===r)return!1;if(a&&"clone"===r)return r;if("function"==typeof r)return l(r(t,e,n,o),a)(t,e,n,o);e=(a?t:e).options.group.name;return!0===r||"string"==typeof r&&r===e||r.join&&-1<r.indexOf(e)}}var e={},n=t.group;n&&"object"==o(n)||(n={name:n}),e.name=n.name,e.checkPull=l(n.pull,!0),e.checkPut=l(n.put),e.revertClone=n.revertClone,t.group=e},Rt=function(){!It&&$&&R($,"display","none")},Xt=function(){!It&&$&&R($,"display","")};Ot&&!c&&document.addEventListener("click",function(t){if(Et)return t.preventDefault(),t.stopPropagation&&t.stopPropagation(),t.stopImmediatePropagation&&t.stopImmediatePropagation(),Et=!1},!0);function Yt(t){if(V){t=t.touches?t.touches[0]:t;var e=(i=t.clientX,r=t.clientY,Dt.some(function(t){var e=t[K].options.emptyInsertThreshold;if(e&&!F(t)){var n=X(t),o=i>=n.left-e&&i<=n.right+e,e=r>=n.top-e&&r<=n.bottom+e;return o&&e?a=t:void 0}}),a);if(e){var n,o={};for(n in t)t.hasOwnProperty(n)&&(o[n]=t[n]);o.target=o.rootEl=e,o.preventDefault=void 0,o.stopPropagation=void 0,e[K]._onDragOver(o)}}var i,r,a}function Bt(t){V&&V.parentNode[K]._isOutsideThisEl(t.target)}function Ft(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be an HTMLElement, not ".concat({}.toString.call(t));this.el=t,this.options=e=a({},e),t[K]=this;var n,o,i={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Pt(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==Ft.supportPointer&&"PointerEvent"in window&&!u,emptyInsertThreshold:5};for(n in W.initializePlugins(this,t,i),i)n in e||(e[n]=i[n]);for(o in kt(e),this)"_"===o.charAt(0)&&"function"==typeof this[o]&&(this[o]=this[o].bind(this));this.nativeDraggable=!e.forceFallback&&Nt,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?h(t,"pointerdown",this._onTapStart):(h(t,"mousedown",this._onTapStart),h(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(h(t,"dragover",this),h(t,"dragenter",this)),Dt.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),a(this,x())}function jt(t,e,n,o,i,r,a,l){var s,c,u=t[K],d=u.options.onMove;return!window.CustomEvent||y||w?(s=document.createEvent("Event")).initEvent("move",!0,!0):s=new CustomEvent("move",{bubbles:!0,cancelable:!0}),s.to=e,s.from=t,s.dragged=n,s.draggedRect=o,s.related=i||e,s.relatedRect=r||X(e),s.willInsertAfter=l,s.originalEvent=a,t.dispatchEvent(s),c=d?d.call(u,s,a):c}function Ht(t){t.draggable=!1}function Lt(){Tt=!1}function Kt(t){return setTimeout(t,0)}function Wt(t){return clearTimeout(t)}Ft.prototype={constructor:Ft,_isOutsideThisEl:function(t){this.el.contains(t)||t===this.el||(mt=null)},_getDirection:function(t,e){return"function"==typeof this.options.direction?this.options.direction.call(this,t,e,V):this.options.direction},_onTapStart:function(e){if(e.cancelable){var n=this,o=this.el,t=this.options,i=t.preventOnFilter,r=e.type,a=e.touches&&e.touches[0]||e.pointerType&&"touch"===e.pointerType&&e,l=(a||e).target,s=e.target.shadowRoot&&(e.path&&e.path[0]||e.composedPath&&e.composedPath()[0])||l,c=t.filter;if(!function(t){xt.length=0;var e=t.getElementsByTagName("input"),n=e.length;for(;n--;){var o=e[n];o.checked&&xt.push(o)}}(o),!V&&!(/mousedown|pointerdown/.test(r)&&0!==e.button||t.disabled)&&!s.isContentEditable&&(this.nativeDraggable||!u||!l||"SELECT"!==l.tagName.toUpperCase())&&!((l=P(l,t.draggable,o,!1))&&l.animated||tt===l)){if(ot=j(l),rt=j(l,t.draggable),"function"==typeof c){if(c.call(this,e,l,this))return q({sortable:n,rootEl:s,name:"filter",targetEl:l,toEl:o,fromEl:o}),G("filter",n,{evt:e}),void(i&&e.cancelable&&e.preventDefault())}else if(c=c&&c.split(",").some(function(t){if(t=P(s,t.trim(),o,!1))return q({sortable:n,rootEl:t,name:"filter",targetEl:l,fromEl:o,toEl:o}),G("filter",n,{evt:e}),!0}))return void(i&&e.cancelable&&e.preventDefault());t.handle&&!P(s,t.handle,o,!1)||this._prepareDragStart(e,a,l)}}},_prepareDragStart:function(t,e,n){var o,i=this,r=i.el,a=i.options,l=r.ownerDocument;n&&!V&&n.parentNode===r&&(o=X(n),Q=r,Z=(V=n).parentNode,J=V.nextSibling,tt=n,lt=a.group,ct={target:Ft.dragged=V,clientX:(e||t).clientX,clientY:(e||t).clientY},ft=ct.clientX-o.left,pt=ct.clientY-o.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,V.style["will-change"]="all",o=function(){G("delayEnded",i,{evt:t}),Ft.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!s&&i.nativeDraggable&&(V.draggable=!0),i._triggerDragStart(t,e),q({sortable:i,name:"choose",originalEvent:t}),k(V,a.chosenClass,!0))},a.ignore.split(",").forEach(function(t){b(V,t.trim(),Ht)}),h(l,"dragover",Yt),h(l,"mousemove",Yt),h(l,"touchmove",Yt),h(l,"mouseup",i._onDrop),h(l,"touchend",i._onDrop),h(l,"touchcancel",i._onDrop),s&&this.nativeDraggable&&(this.options.touchStartThreshold=4,V.draggable=!0),G("delayStart",this,{evt:t}),!a.delay||a.delayOnTouchOnly&&!e||this.nativeDraggable&&(w||y)?o():Ft.eventCanceled?this._onDrop():(h(l,"mouseup",i._disableDelayedDrag),h(l,"touchend",i._disableDelayedDrag),h(l,"touchcancel",i._disableDelayedDrag),h(l,"mousemove",i._delayedDragTouchMoveHandler),h(l,"touchmove",i._delayedDragTouchMoveHandler),a.supportPointer&&h(l,"pointermove",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(o,a.delay)))},_delayedDragTouchMoveHandler:function(t){t=t.touches?t.touches[0]:t;Math.max(Math.abs(t.clientX-this._lastX),Math.abs(t.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){V&&Ht(V),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._disableDelayedDrag),f(t,"touchend",this._disableDelayedDrag),f(t,"touchcancel",this._disableDelayedDrag),f(t,"mousemove",this._delayedDragTouchMoveHandler),f(t,"touchmove",this._delayedDragTouchMoveHandler),f(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||"touch"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?h(document,"pointermove",this._onTouchMove):h(document,e?"touchmove":"mousemove",this._onTouchMove):(h(V,"dragend",this),h(Q,"dragstart",this._onDragStart));try{document.selection?Kt(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(t){}},_dragStarted:function(t,e){var n;wt=!1,Q&&V?(G("dragStarted",this,{evt:e}),this.nativeDraggable&&h(document,"dragover",Bt),n=this.options,t||k(V,n.dragClass,!1),k(V,n.ghostClass,!0),Ft.active=this,t&&this._appendGhost(),q({sortable:this,name:"start",originalEvent:e})):this._nulling()},_emulateDragOver:function(){if(ut){this._lastX=ut.clientX,this._lastY=ut.clientY,Rt();for(var t=document.elementFromPoint(ut.clientX,ut.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(ut.clientX,ut.clientY))!==e;)e=t;if(V.parentNode[K]._isOutsideThisEl(t),e)do{if(e[K])if(e[K]._onDragOver({clientX:ut.clientX,clientY:ut.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}while(e=(t=e).parentNode);Xt()}},_onTouchMove:function(t){if(ct){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=$&&v($,!0),a=$&&r&&r.a,l=$&&r&&r.d,e=Mt&&yt&&E(yt),a=(i.clientX-ct.clientX+o.x)/(a||1)+(e?e[0]-Ct[0]:0)/(a||1),l=(i.clientY-ct.clientY+o.y)/(l||1)+(e?e[1]-Ct[1]:0)/(l||1);if(!Ft.active&&!wt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))<n)return;this._onDragStart(t,!0)}$&&(r?(r.e+=a-(dt||0),r.f+=l-(ht||0)):r={a:1,b:0,c:0,d:1,e:a,f:l},r="matrix(".concat(r.a,",").concat(r.b,",").concat(r.c,",").concat(r.d,",").concat(r.e,",").concat(r.f,")"),R($,"webkitTransform",r),R($,"mozTransform",r),R($,"msTransform",r),R($,"transform",r),dt=a,ht=l,ut=i),t.cancelable&&t.preventDefault()}},_appendGhost:function(){if(!$){var t=this.options.fallbackOnBody?document.body:Q,e=X(V,!0,Mt,!0,t),n=this.options;if(Mt){for(yt=t;"static"===R(yt,"position")&&"none"===R(yt,"transform")&&yt!==document;)yt=yt.parentNode;yt!==document.body&&yt!==document.documentElement?(yt===document&&(yt=O()),e.top+=yt.scrollTop,e.left+=yt.scrollLeft):yt=O(),Ct=E(yt)}k($=V.cloneNode(!0),n.ghostClass,!1),k($,n.fallbackClass,!0),k($,n.dragClass,!0),R($,"transition",""),R($,"transform",""),R($,"box-sizing","border-box"),R($,"margin",0),R($,"top",e.top),R($,"left",e.left),R($,"width",e.width),R($,"height",e.height),R($,"opacity","0.8"),R($,"position",Mt?"absolute":"fixed"),R($,"zIndex","100000"),R($,"pointerEvents","none"),Ft.ghost=$,t.appendChild($),R($,"transform-origin",ft/parseInt($.style.width)*100+"% "+pt/parseInt($.style.height)*100+"%")}},_onDragStart:function(t,e){var n=this,o=t.dataTransfer,i=n.options;G("dragStart",this,{evt:t}),Ft.eventCanceled?this._onDrop():(G("setupClone",this),Ft.eventCanceled||((et=_(V)).removeAttribute("id"),et.draggable=!1,et.style["will-change"]="",this._hideClone(),k(et,this.options.chosenClass,!1),Ft.clone=et),n.cloneId=Kt(function(){G("clone",n),Ft.eventCanceled||(n.options.removeCloneOnHide||Q.insertBefore(et,V),n._hideClone(),q({sortable:n,name:"clone"}))}),e||k(V,i.dragClass,!0),e?(Et=!0,n._loopId=setInterval(n._emulateDragOver,50)):(f(document,"mouseup",n._onDrop),f(document,"touchend",n._onDrop),f(document,"touchcancel",n._onDrop),o&&(o.effectAllowed="move",i.setData&&i.setData.call(n,o,V)),h(document,"drop",n),R(V,"transform","translateZ(0)")),wt=!0,n._dragStartId=Kt(n._dragStarted.bind(n,e,t)),h(document,"selectstart",n),gt=!0,u&&R(document.body,"user-select","none"))},_onDragOver:function(n){var o,i,r,t,e,a=this.el,l=n.target,s=this.options,c=s.group,u=Ft.active,d=lt===c,h=s.sort,f=st||u,p=this,g=!1;if(!Tt){if(void 0!==n.preventDefault&&n.cancelable&&n.preventDefault(),l=P(l,s.draggable,a,!0),O("dragOver"),Ft.eventCanceled)return g;if(V.contains(n.target)||l.animated&&l.animatingX&&l.animatingY||p._ignoreWhileAnimating===l)return A(!1);if(Et=!1,u&&!s.disabled&&(d?h||(i=Z!==Q):st===this||(this.lastPutMode=lt.checkPull(this,u,V,n))&&c.checkPut(this,u,V,n))){if(r="vertical"===this._getDirection(n,l),o=X(V),O("dragOverValid"),Ft.eventCanceled)return g;if(i)return Z=Q,M(),this._hideClone(),O("revert"),Ft.eventCanceled||(J?Q.insertBefore(V,J):Q.appendChild(V)),A(!0);var m=F(a,s.draggable);if(m&&(S=n,c=r,x=X(F((D=this).el,D.options.draggable)),D=L(D.el,D.options,$),!(c?S.clientX>D.right+10||S.clientY>x.bottom&&S.clientX>x.left:S.clientY>D.bottom+10||S.clientX>x.right&&S.clientY>x.top)||m.animated)){if(m&&(t=n,e=r,C=X(B((_=this).el,0,_.options,!0)),_=L(_.el,_.options,$),e?t.clientX<_.left-10||t.clientY<C.top&&t.clientX<C.right:t.clientY<_.top-10||t.clientY<C.bottom&&t.clientX<C.left)){var v=B(a,0,s,!0);if(v===V)return A(!1);if(E=X(l=v),!1!==jt(Q,a,V,o,l,E,n,!1))return M(),a.insertBefore(V,v),Z=a,N(),A(!0)}else if(l.parentNode===a){var b,y,w,E=X(l),D=V.parentNode!==a,S=(S=V.animated&&V.toRect||o,x=l.animated&&l.toRect||E,_=(e=r)?S.left:S.top,t=e?S.right:S.bottom,C=e?S.width:S.height,v=e?x.left:x.top,S=e?x.right:x.bottom,x=e?x.width:x.height,!(_===v||t===S||_+C/2===v+x/2)),_=r?"top":"left",C=Y(l,"top","top")||Y(V,"top","top"),v=C?C.scrollTop:void 0;if(mt!==l&&(y=E[_],St=!1,_t=!S&&s.invertSwap||D),0!==(b=function(t,e,n,o,i,r,a,l){var s=o?t.clientY:t.clientX,c=o?n.height:n.width,t=o?n.top:n.left,o=o?n.bottom:n.right,n=!1;if(!a)if(l&&bt<c*i){if(St=!St&&(1===vt?t+c*r/2<s:s<o-c*r/2)?!0:St)n=!0;else if(1===vt?s<t+bt:o-bt<s)return-vt}else if(t+c*(1-i)/2<s&&s<o-c*(1-i)/2)return function(t){return j(V)<j(t)?1:-1}(e);if((n=n||a)&&(s<t+c*r/2||o-c*r/2<s))return t+c/2<s?1:-1;return 0}(n,l,E,r,S?1:s.swapThreshold,null==s.invertedSwapThreshold?s.swapThreshold:s.invertedSwapThreshold,_t,mt===l)))for(var T=j(V);(w=Z.children[T-=b])&&("none"===R(w,"display")||w===$););if(0===b||w===l)return A(!1);vt=b;var x=(mt=l).nextElementSibling,D=!1,S=jt(Q,a,V,o,l,E,n,D=1===b);if(!1!==S)return 1!==S&&-1!==S||(D=1===S),Tt=!0,setTimeout(Lt,30),M(),D&&!x?a.appendChild(V):l.parentNode.insertBefore(V,D?x:l),C&&H(C,0,v-C.scrollTop),Z=V.parentNode,void 0===y||_t||(bt=Math.abs(y-X(l)[_])),N(),A(!0)}}else{if(m===V)return A(!1);if((l=m&&a===n.target?m:l)&&(E=X(l)),!1!==jt(Q,a,V,o,l,E,n,!!l))return M(),m&&m.nextSibling?a.insertBefore(V,m.nextSibling):a.appendChild(V),Z=a,N(),A(!0)}if(a.contains(V))return A(!1)}return!1}function O(t,e){G(t,p,I({evt:n,isOwner:d,axis:r?"vertical":"horizontal",revert:i,dragRect:o,targetRect:E,canSort:h,fromSortable:f,target:l,completed:A,onMove:function(t,e){return jt(Q,a,V,o,t,X(t),n,e)},changed:N},e))}function M(){O("dragOverAnimationCapture"),p.captureAnimationState(),p!==f&&f.captureAnimationState()}function A(t){return O("dragOverCompleted",{insertion:t}),t&&(d?u._hideClone():u._showClone(p),p!==f&&(k(V,(st||u).options.ghostClass,!1),k(V,s.ghostClass,!0)),st!==p&&p!==Ft.active?st=p:p===Ft.active&&st&&(st=null),f===p&&(p._ignoreWhileAnimating=l),p.animateAll(function(){O("dragOverAnimationComplete"),p._ignoreWhileAnimating=null}),p!==f&&(f.animateAll(),f._ignoreWhileAnimating=null)),(l===V&&!V.animated||l===a&&!l.animated)&&(mt=null),s.dragoverBubble||n.rootEl||l===document||(V.parentNode[K]._isOutsideThisEl(n.target),t||Yt(n)),!s.dragoverBubble&&n.stopPropagation&&n.stopPropagation(),g=!0}function N(){it=j(V),at=j(V,s.draggable),q({sortable:p,name:"change",toEl:a,newIndex:it,newDraggableIndex:at,originalEvent:n})}},_ignoreWhileAnimating:null,_offMoveEvents:function(){f(document,"mousemove",this._onTouchMove),f(document,"touchmove",this._onTouchMove),f(document,"pointermove",this._onTouchMove),f(document,"dragover",Yt),f(document,"mousemove",Yt),f(document,"touchmove",Yt)},_offUpEvents:function(){var t=this.el.ownerDocument;f(t,"mouseup",this._onDrop),f(t,"touchend",this._onDrop),f(t,"pointerup",this._onDrop),f(t,"touchcancel",this._onDrop),f(document,"selectstart",this)},_onDrop:function(t){var e=this.el,n=this.options;it=j(V),at=j(V,n.draggable),G("drop",this,{evt:t}),Z=V&&V.parentNode,it=j(V),at=j(V,n.draggable),Ft.eventCanceled||(St=_t=wt=!1,clearInterval(this._loopId),clearTimeout(this._dragStartTimer),Wt(this.cloneId),Wt(this._dragStartId),this.nativeDraggable&&(f(document,"drop",this),f(e,"dragstart",this._onDragStart)),this._offMoveEvents(),this._offUpEvents(),u&&R(document.body,"user-select",""),R(V,"transform",""),t&&(gt&&(t.cancelable&&t.preventDefault(),n.dropBubble||t.stopPropagation()),$&&$.parentNode&&$.parentNode.removeChild($),(Q===Z||st&&"clone"!==st.lastPutMode)&&et&&et.parentNode&&et.parentNode.removeChild(et),V&&(this.nativeDraggable&&f(V,"dragend",this),Ht(V),V.style["will-change"]="",gt&&!wt&&k(V,(st||this).options.ghostClass,!1),k(V,this.options.chosenClass,!1),q({sortable:this,name:"unchoose",toEl:Z,newIndex:null,newDraggableIndex:null,originalEvent:t}),Q!==Z?(0<=it&&(q({rootEl:Z,name:"add",toEl:Z,fromEl:Q,originalEvent:t}),q({sortable:this,name:"remove",toEl:Z,originalEvent:t}),q({rootEl:Z,name:"sort",toEl:Z,fromEl:Q,originalEvent:t}),q({sortable:this,name:"sort",toEl:Z,originalEvent:t})),st&&st.save()):it!==ot&&0<=it&&(q({sortable:this,name:"update",toEl:Z,originalEvent:t}),q({sortable:this,name:"sort",toEl:Z,originalEvent:t})),Ft.active&&(null!=it&&-1!==it||(it=ot,at=rt),q({sortable:this,name:"end",toEl:Z,originalEvent:t}),this.save())))),this._nulling()},_nulling:function(){G("nulling",this),Q=V=Z=$=J=et=tt=nt=ct=ut=gt=it=at=ot=rt=mt=vt=st=lt=Ft.dragged=Ft.ghost=Ft.clone=Ft.active=null,xt.forEach(function(t){t.checked=!0}),xt.length=dt=ht=0},handleEvent:function(t){switch(t.type){case"drop":case"dragend":this._onDrop(t);break;case"dragenter":case"dragover":V&&(this._onDragOver(t),function(t){t.dataTransfer&&(t.dataTransfer.dropEffect="move");t.cancelable&&t.preventDefault()}(t));break;case"selectstart":t.preventDefault()}},toArray:function(){for(var t,e=[],n=this.el.children,o=0,i=n.length,r=this.options;o<i;o++)P(t=n[o],r.draggable,this.el,!1)&&e.push(t.getAttribute(r.dataIdAttr)||function(t){var e=t.tagName+t.className+t.src+t.href+t.textContent,n=e.length,o=0;for(;n--;)o+=e.charCodeAt(n);return o.toString(36)}(t));return e},sort:function(t,e){var n={},o=this.el;this.toArray().forEach(function(t,e){e=o.children[e];P(e,this.options.draggable,o,!1)&&(n[t]=e)},this),e&&this.captureAnimationState(),t.forEach(function(t){n[t]&&(o.removeChild(n[t]),o.appendChild(n[t]))}),e&&this.animateAll()},save:function(){var t=this.options.store;t&&t.set&&t.set(this)},closest:function(t,e){return P(t,e||this.options.draggable,this.el,!1)},option:function(t,e){var n=this.options;if(void 0===e)return n[t];var o=W.modifyOption(this,t,e);n[t]=void 0!==o?o:e,"group"===t&&kt(n)},destroy:function(){G("destroy",this);var t=this.el;t[K]=null,f(t,"mousedown",this._onTapStart),f(t,"touchstart",this._onTapStart),f(t,"pointerdown",this._onTapStart),this.nativeDraggable&&(f(t,"dragover",this),f(t,"dragenter",this)),Array.prototype.forEach.call(t.querySelectorAll("[draggable]"),function(t){t.removeAttribute("draggable")}),this._onDrop(),this._disableDelayedDragEvents(),Dt.splice(Dt.indexOf(this.el),1),this.el=t=null},_hideClone:function(){nt||(G("hideClone",this),Ft.eventCanceled||(R(et,"display","none"),this.options.removeCloneOnHide&&et.parentNode&&et.parentNode.removeChild(et),nt=!0))},_showClone:function(t){"clone"===t.lastPutMode?nt&&(G("showClone",this),Ft.eventCanceled||(V.parentNode!=Q||this.options.group.revertClone?J?Q.insertBefore(et,J):Q.appendChild(et):Q.insertBefore(et,V),this.options.group.revertClone&&this.animate(V,et),R(et,"display",""),nt=!1)):this._hideClone()}},Ot&&h(document,"touchmove",function(t){(Ft.active||wt)&&t.cancelable&&t.preventDefault()}),Ft.utils={on:h,off:f,css:R,find:b,is:function(t,e){return!!P(t,e,t,!1)},extend:function(t,e){if(t&&e)for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t},throttle:S,closest:P,toggleClass:k,clone:_,index:j,nextTick:Kt,cancelNextTick:Wt,detectDirection:Pt,getChild:B},Ft.get=function(t){return t[K]},Ft.mount=function(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];(e=e[0].constructor===Array?e[0]:e).forEach(function(t){if(!t.prototype||!t.prototype.constructor)throw"Sortable: Mounted plugin must be a constructor function, not ".concat({}.toString.call(t));t.utils&&(Ft.utils=I(I({},Ft.utils),t.utils)),W.mount(t)})},Ft.create=function(t,e){return new Ft(t,e)};var zt,Gt,Ut,qt,Vt,Zt,$t=[],Qt=!(Ft.version="1.15.2");function Jt(){$t.forEach(function(t){clearInterval(t.pid)}),$t=[]}function te(){clearInterval(Zt)}var ee,ne=S(function(n,t,e,o){if(t.scroll){var i,r=(n.touches?n.touches[0]:n).clientX,a=(n.touches?n.touches[0]:n).clientY,l=t.scrollSensitivity,s=t.scrollSpeed,c=O(),u=!1;Gt!==e&&(Gt=e,Jt(),zt=t.scroll,i=t.scrollFn,!0===zt&&(zt=M(e,!0)));var d=0,h=zt;do{var f=h,p=X(f),g=p.top,m=p.bottom,v=p.left,b=p.right,y=p.width,w=p.height,E=void 0,D=void 0,S=f.scrollWidth,_=f.scrollHeight,C=R(f),T=f.scrollLeft,p=f.scrollTop,D=f===c?(E=y<S&&("auto"===C.overflowX||"scroll"===C.overflowX||"visible"===C.overflowX),w<_&&("auto"===C.overflowY||"scroll"===C.overflowY||"visible"===C.overflowY)):(E=y<S&&("auto"===C.overflowX||"scroll"===C.overflowX),w<_&&("auto"===C.overflowY||"scroll"===C.overflowY)),T=E&&(Math.abs(b-r)<=l&&T+y<S)-(Math.abs(v-r)<=l&&!!T),p=D&&(Math.abs(m-a)<=l&&p+w<_)-(Math.abs(g-a)<=l&&!!p);if(!$t[d])for(var x=0;x<=d;x++)$t[x]||($t[x]={});$t[d].vx==T&&$t[d].vy==p&&$t[d].el===f||($t[d].el=f,$t[d].vx=T,$t[d].vy=p,clearInterval($t[d].pid),0==T&&0==p||(u=!0,$t[d].pid=setInterval(function(){o&&0===this.layer&&Ft.active._onTouchMove(Vt);var t=$t[this.layer].vy?$t[this.layer].vy*s:0,e=$t[this.layer].vx?$t[this.layer].vx*s:0;"function"==typeof i&&"continue"!==i.call(Ft.dragged.parentNode[K],e,t,n,Vt,$t[this.layer].el)||H($t[this.layer].el,e,t)}.bind({layer:d}),24))),d++}while(t.bubbleScroll&&h!==c&&(h=M(h,!1)));Qt=u}},30),c=function(t){var e=t.originalEvent,n=t.putSortable,o=t.dragEl,i=t.activeSortable,r=t.dispatchSortableEvent,a=t.hideGhostForTarget,t=t.unhideGhostForTarget;e&&(i=n||i,a(),e=e.changedTouches&&e.changedTouches.length?e.changedTouches[0]:e,e=document.elementFromPoint(e.clientX,e.clientY),t(),i&&!i.el.contains(e)&&(r("spill"),this.onSpill({dragEl:o,putSortable:n})))};function oe(){}function ie(){}oe.prototype={startIndex:null,dragStart:function(t){t=t.oldDraggableIndex;this.startIndex=t},onSpill:function(t){var e=t.dragEl,n=t.putSortable;this.sortable.captureAnimationState(),n&&n.captureAnimationState();t=B(this.sortable.el,this.startIndex,this.options);t?this.sortable.el.insertBefore(e,t):this.sortable.el.appendChild(e),this.sortable.animateAll(),n&&n.animateAll()},drop:c},a(oe,{pluginName:"revertOnSpill"}),ie.prototype={onSpill:function(t){var e=t.dragEl,t=t.putSortable||this.sortable;t.captureAnimationState(),e.parentNode&&e.parentNode.removeChild(e),t.animateAll()},drop:c},a(ie,{pluginName:"removeOnSpill"});var re,ae,le,se,ce,ue=[],de=[],he=!1,fe=!1,pe=!1;function ge(n,o){de.forEach(function(t,e){e=o.children[t.sortableIndex+(n?Number(e):0)];e?o.insertBefore(t,e):o.appendChild(t)})}function me(){ue.forEach(function(t){t!==le&&t.parentNode&&t.parentNode.removeChild(t)})}return Ft.mount(new function(){function t(){for(var t in this.defaults={scroll:!0,forceAutoScrollFallback:!1,scrollSensitivity:30,scrollSpeed:10,bubbleScroll:!0},this)"_"===t.charAt(0)&&"function"==typeof this[t]&&(this[t]=this[t].bind(this))}return t.prototype={dragStarted:function(t){t=t.originalEvent;this.sortable.nativeDraggable?h(document,"dragover",this._handleAutoScroll):this.options.supportPointer?h(document,"pointermove",this._handleFallbackAutoScroll):t.touches?h(document,"touchmove",this._handleFallbackAutoScroll):h(document,"mousemove",this._handleFallbackAutoScroll)},dragOverCompleted:function(t){t=t.originalEvent;this.options.dragOverBubble||t.rootEl||this._handleAutoScroll(t)},drop:function(){this.sortable.nativeDraggable?f(document,"dragover",this._handleAutoScroll):(f(document,"pointermove",this._handleFallbackAutoScroll),f(document,"touchmove",this._handleFallbackAutoScroll),f(document,"mousemove",this._handleFallbackAutoScroll)),te(),Jt(),clearTimeout(g),g=void 0},nulling:function(){Vt=Gt=zt=Qt=Zt=Ut=qt=null,$t.length=0},_handleFallbackAutoScroll:function(t){this._handleAutoScroll(t,!0)},_handleAutoScroll:function(e,n){var o,i=this,r=(e.touches?e.touches[0]:e).clientX,a=(e.touches?e.touches[0]:e).clientY,t=document.elementFromPoint(r,a);Vt=e,n||this.options.forceAutoScrollFallback||w||y||u?(ne(e,this.options,t,n),o=M(t,!0),!Qt||Zt&&r===Ut&&a===qt||(Zt&&te(),Zt=setInterval(function(){var t=M(document.elementFromPoint(r,a),!0);t!==o&&(o=t,Jt()),ne(e,i.options,t,n)},10),Ut=r,qt=a)):this.options.bubbleScroll&&M(t,!0)!==O()?ne(e,this.options,M(t,!1),!1):Jt()}},a(t,{pluginName:"scroll",initializeByDefault:!0})}),Ft.mount(ie,oe),Ft.mount(new function(){function t(){this.defaults={swapClass:"sortable-swap-highlight"}}return t.prototype={dragStart:function(t){t=t.dragEl;ee=t},dragOverValid:function(t){var e=t.completed,n=t.target,o=t.onMove,i=t.activeSortable,r=t.changed,a=t.cancel;i.options.swap&&(t=this.sortable.el,i=this.options,n&&n!==t&&(t=ee,ee=!1!==o(n)?(k(n,i.swapClass,!0),n):null,t&&t!==ee&&k(t,i.swapClass,!1)),r(),e(!0),a())},drop:function(t){var e,n,o=t.activeSortable,i=t.putSortable,r=t.dragEl,a=i||this.sortable,l=this.options;ee&&k(ee,l.swapClass,!1),ee&&(l.swap||i&&i.options.swap)&&r!==ee&&(a.captureAnimationState(),a!==o&&o.captureAnimationState(),n=ee,t=(e=r).parentNode,l=n.parentNode,t&&l&&!t.isEqualNode(n)&&!l.isEqualNode(e)&&(i=j(e),r=j(n),t.isEqualNode(l)&&i<r&&r++,t.insertBefore(n,t.children[i]),l.insertBefore(e,l.children[r])),a.animateAll(),a!==o&&o.animateAll())},nulling:function(){ee=null}},a(t,{pluginName:"swap",eventProperties:function(){return{swapItem:ee}}})}),Ft.mount(new function(){function t(o){for(var t in this)"_"===t.charAt(0)&&"function"==typeof this[t]&&(this[t]=this[t].bind(this));o.options.avoidImplicitDeselect||(o.options.supportPointer?h(document,"pointerup",this._deselectMultiDrag):(h(document,"mouseup",this._deselectMultiDrag),h(document,"touchend",this._deselectMultiDrag))),h(document,"keydown",this._checkKeyDown),h(document,"keyup",this._checkKeyUp),this.defaults={selectedClass:"sortable-selected",multiDragKey:null,avoidImplicitDeselect:!1,setData:function(t,e){var n="";ue.length&&ae===o?ue.forEach(function(t,e){n+=(e?", ":"")+t.textContent}):n=e.textContent,t.setData("Text",n)}}}return t.prototype={multiDragKeyDown:!1,isMultiDrag:!1,delayStartGlobal:function(t){t=t.dragEl;le=t},delayEnded:function(){this.isMultiDrag=~ue.indexOf(le)},setupClone:function(t){var e=t.sortable,t=t.cancel;if(this.isMultiDrag){for(var n=0;n<ue.length;n++)de.push(_(ue[n])),de[n].sortableIndex=ue[n].sortableIndex,de[n].draggable=!1,de[n].style["will-change"]="",k(de[n],this.options.selectedClass,!1),ue[n]===le&&k(de[n],this.options.chosenClass,!1);e._hideClone(),t()}},clone:function(t){var e=t.sortable,n=t.rootEl,o=t.dispatchSortableEvent,t=t.cancel;this.isMultiDrag&&(this.options.removeCloneOnHide||ue.length&&ae===e&&(ge(!0,n),o("clone"),t()))},showClone:function(t){var e=t.cloneNowShown,n=t.rootEl,t=t.cancel;this.isMultiDrag&&(ge(!1,n),de.forEach(function(t){R(t,"display","")}),e(),ce=!1,t())},hideClone:function(t){var e=this,n=(t.sortable,t.cloneNowHidden),t=t.cancel;this.isMultiDrag&&(de.forEach(function(t){R(t,"display","none"),e.options.removeCloneOnHide&&t.parentNode&&t.parentNode.removeChild(t)}),n(),ce=!0,t())},dragStartGlobal:function(t){t.sortable;!this.isMultiDrag&&ae&&ae.multiDrag._deselectMultiDrag(),ue.forEach(function(t){t.sortableIndex=j(t)}),ue=ue.sort(function(t,e){return t.sortableIndex-e.sortableIndex}),pe=!0},dragStarted:function(t){var e,n=this,t=t.sortable;this.isMultiDrag&&(this.options.sort&&(t.captureAnimationState(),this.options.animation&&(ue.forEach(function(t){t!==le&&R(t,"position","absolute")}),e=X(le,!1,!0,!0),ue.forEach(function(t){t!==le&&C(t,e)}),he=fe=!0)),t.animateAll(function(){he=fe=!1,n.options.animation&&ue.forEach(function(t){T(t)}),n.options.sort&&me()}))},dragOver:function(t){var e=t.target,n=t.completed,t=t.cancel;fe&&~ue.indexOf(e)&&(n(!1),t())},revert:function(t){var n,o,e=t.fromSortable,i=t.rootEl,r=t.sortable,a=t.dragRect;1<ue.length&&(ue.forEach(function(t){r.addAnimationState({target:t,rect:fe?X(t):a}),T(t),t.fromRect=a,e.removeAnimationState(t)}),fe=!1,n=!this.options.removeCloneOnHide,o=i,ue.forEach(function(t,e){e=o.children[t.sortableIndex+(n?Number(e):0)];e?o.insertBefore(t,e):o.appendChild(t)}))},dragOverCompleted:function(t){var e,n=t.sortable,o=t.isOwner,i=t.insertion,r=t.activeSortable,a=t.parentEl,l=t.putSortable,t=this.options;i&&(o&&r._hideClone(),he=!1,t.animation&&1<ue.length&&(fe||!o&&!r.options.sort&&!l)&&(e=X(le,!1,!0,!0),ue.forEach(function(t){t!==le&&(C(t,e),a.appendChild(t))}),fe=!0),o||(fe||me(),1<ue.length?(o=ce,r._showClone(n),r.options.animation&&!ce&&o&&de.forEach(function(t){r.addAnimationState({target:t,rect:se}),t.fromRect=se,t.thisAnimationDuration=null})):r._showClone(n)))},dragOverAnimationCapture:function(t){var e=t.dragRect,n=t.isOwner,t=t.activeSortable;ue.forEach(function(t){t.thisAnimationDuration=null}),t.options.animation&&!n&&t.multiDrag.isMultiDrag&&(se=a({},e),e=v(le,!0),se.top-=e.f,se.left-=e.e)},dragOverAnimationComplete:function(){fe&&(fe=!1,me())},drop:function(t){var e=t.originalEvent,n=t.rootEl,o=t.parentEl,i=t.sortable,r=t.dispatchSortableEvent,a=t.oldIndex,l=t.putSortable,s=l||this.sortable;if(e){var c,u,d,h=this.options,f=o.children;if(!pe)if(h.multiDragKey&&!this.multiDragKeyDown&&this._deselectMultiDrag(),k(le,h.selectedClass,!~ue.indexOf(le)),~ue.indexOf(le))ue.splice(ue.indexOf(le),1),re=null,z({sortable:i,rootEl:n,name:"deselect",targetEl:le,originalEvent:e});else{if(ue.push(le),z({sortable:i,rootEl:n,name:"select",targetEl:le,originalEvent:e}),e.shiftKey&&re&&i.el.contains(re)){var p=j(re),t=j(le);if(~p&&~t&&p!==t)for(var g,m=p<t?(g=p,t):(g=t,p+1);g<m;g++)~ue.indexOf(f[g])||(k(f[g],h.selectedClass,!0),ue.push(f[g]),z({sortable:i,rootEl:n,name:"select",targetEl:f[g],originalEvent:e}))}else re=le;ae=s}pe&&this.isMultiDrag&&(fe=!1,(o[K].options.sort||o!==n)&&1<ue.length&&(c=X(le),u=j(le,":not(."+this.options.selectedClass+")"),!he&&h.animation&&(le.thisAnimationDuration=null),s.captureAnimationState(),he||(h.animation&&(le.fromRect=c,ue.forEach(function(t){var e;t.thisAnimationDuration=null,t!==le&&(e=fe?X(t):c,t.fromRect=e,s.addAnimationState({target:t,rect:e}))})),me(),ue.forEach(function(t){f[u]?o.insertBefore(t,f[u]):o.appendChild(t),u++}),a===j(le)&&(d=!1,ue.forEach(function(t){t.sortableIndex!==j(t)&&(d=!0)}),d&&(r("update"),r("sort")))),ue.forEach(function(t){T(t)}),s.animateAll()),ae=s),(n===o||l&&"clone"!==l.lastPutMode)&&de.forEach(function(t){t.parentNode&&t.parentNode.removeChild(t)})}},nullingGlobal:function(){this.isMultiDrag=pe=!1,de.length=0},destroyGlobal:function(){this._deselectMultiDrag(),f(document,"pointerup",this._deselectMultiDrag),f(document,"mouseup",this._deselectMultiDrag),f(document,"touchend",this._deselectMultiDrag),f(document,"keydown",this._checkKeyDown),f(document,"keyup",this._checkKeyUp)},_deselectMultiDrag:function(t){if(!(void 0!==pe&&pe||ae!==this.sortable||t&&P(t.target,this.options.draggable,this.sortable.el,!1)||t&&0!==t.button))for(;ue.length;){var e=ue[0];k(e,this.options.selectedClass,!1),ue.shift(),z({sortable:this.sortable,rootEl:this.sortable.el,name:"deselect",targetEl:e,originalEvent:t})}},_checkKeyDown:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!0)},_checkKeyUp:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!1)}},a(t,{pluginName:"multiDrag",utils:{select:function(t){var e=t.parentNode[K];e&&e.options.multiDrag&&!~ue.indexOf(t)&&(ae&&ae!==e&&(ae.multiDrag._deselectMultiDrag(),ae=e),k(t,e.options.selectedClass,!0),ue.push(t))},deselect:function(t){var e=t.parentNode[K],n=ue.indexOf(t);e&&e.options.multiDrag&&~n&&(k(t,e.options.selectedClass,!1),ue.splice(n,1))}},eventProperties:function(){var n=this,o=[],i=[];return ue.forEach(function(t){var e;o.push({multiDragElement:t,index:t.sortableIndex}),e=fe&&t!==le?-1:fe?j(t,":not(."+n.options.selectedClass+")"):j(t),i.push({multiDragElement:t,index:e})}),{items:r(ue),clones:[].concat(de),oldIndicies:o,newIndicies:i}},optionListeners:{multiDragKey:function(t){return"ctrl"===(t=t.toLowerCase())?t="Control":1<t.length&&(t=t.charAt(0).toUpperCase()+t.substr(1)),t}}})}),Ft});';function st(n){let t=n.dashboard,o=n.boards;if(o.length===0){console.log(" No boards configured in agentx.json (boards[]). Nothing to serve.");return}let i=t.port,e=t.bind,r=t.token,m=new Map;for(let u of o)try{m.set(u.id,H(u,n))}catch(s){console.error(` Board "${u.id}" failed to initialize: ${s.message}`)}O(async(u,s)=>{try{await P(u,s,{boards:o,sources:m,token:r,config:n})}catch(y){c(s,500,{error:y.message||"internal error"})}}).listen(i,e,()=>{console.log(`
|
|
2
|
-
Kanban dashboard: http://${e==="0.0.0.0"?"localhost":e}:${i}
|
|
3
|
-
`),console.log(` Boards: ${o.map(s=>s.id).join(", ")}`),e==="127.0.0.1"&&!r?console.log(" (bound to localhost; no auth token \u2014 safe default)"):e!=="127.0.0.1"&&!r&&console.log(" \u26A0 bound to a non-loopback address without a token \u2014 writes are UNAUTHENTICATED")})}async function P(n,t,o){let i=new URL(n.url||"/","http://localhost"),e=i.pathname,r=(n.method||"GET").toUpperCase();if(t.setHeader("Access-Control-Allow-Origin","*"),t.setHeader("Access-Control-Allow-Methods","GET, POST, PATCH, OPTIONS"),t.setHeader("Access-Control-Allow-Headers","Content-Type, Authorization, X-Requested-With"),r==="OPTIONS"){t.writeHead(204),t.end();return}if(r==="GET"&&e==="/"){t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(K());return}if(r==="GET"&&e==="/live"){t.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),t.end(Y());return}if(r==="GET"&&e==="/sortable.min.js"){t.writeHead(200,{"Content-Type":"application/javascript; charset=utf-8","Cache-Control":"public, max-age=86400"}),t.end(M);return}if(r==="GET"&&e==="/api/live"){try{let a=await L(o.config);c(t,200,a)}catch(a){c(t,502,{error:a.message})}return}if(r==="GET"&&e==="/api/live/stream"){X(n,t,o.config);return}if(r==="GET"&&e==="/api/agents"){try{let a={};o.config.dashboard.token&&(a.Authorization=`Bearer ${o.config.dashboard.token}`);let l=await fetch(o.config.dashboard.daemonUrl.replace(/\/+$/,"")+"/agents",{headers:a});if(!l.ok)throw new Error(`HTTP ${l.status}`);let v=await l.json();c(t,200,v)}catch(a){c(t,502,{error:a.message||"agents fetch failed"})}return}if(r==="POST"&&e==="/api/draft"){let a=await w(n);try{let l=await W(o.config,a);c(t,200,l)}catch(l){c(t,502,{error:l.message||"draft failed"})}return}if(e.startsWith("/api/")&&o.token){let a=n.headers.authorization||"";if((a.startsWith("Bearer ")?a.slice(7):"")!==o.token){c(t,401,{error:"unauthorized"});return}}if((r==="POST"||r==="PATCH"||r==="DELETE")&&e.startsWith("/api/")&&n.headers["x-requested-with"]!=="agentx-board"){c(t,400,{error:"missing X-Requested-With: agentx-board"});return}if(r==="GET"&&e==="/api/boards"){c(t,200,o.boards.map(a=>({id:a.id,name:a.name,source:a.source,primaryToolLabel:a.primaryToolLabel,labels:a.labels,columns:a.columns,timeRangeDays:a.timeRangeDays})));return}let f=e.match(/^\/api\/boards\/([a-z0-9_-]+)\/members$/);if(f&&r==="GET"){let a=o.sources.get(f[1]);if(!a){c(t,404,{error:"unknown board"});return}try{let l=typeof a.listMembers=="function"?await a.listMembers():[];c(t,200,l)}catch(l){c(t,502,{error:l.message})}return}let u=e.match(/^\/api\/boards\/([a-z0-9_-]+)\/milestones$/);if(u&&r==="GET"){let a=o.sources.get(u[1]);if(!a){c(t,404,{error:"unknown board"});return}try{let l=typeof a.listMilestones=="function"?await a.listMilestones():[];c(t,200,l)}catch(l){c(t,502,{error:l.message})}return}let s=e.match(/^\/api\/boards\/([a-z0-9_-]+)\/items\/(.+?)\/move$/),y=e.match(/^\/api\/boards\/([a-z0-9_-]+)\/items(?:\/(.+))?$/);if(s&&r==="PATCH"){let[,a,l]=s,v=o.boards.find(d=>d.id===a);if(!v){c(t,404,{error:`unknown board: ${a}`});return}let h=o.sources.get(a);if(!h){c(t,503,{error:`board "${a}" source unavailable`});return}let p=await w(n),g=v.columns.find(d=>d.id===p.to),k=v.columns.find(d=>d.id===p.from);if(!g){c(t,400,{error:`unknown 'to' column: ${p.to}`});return}try{await z(h,l,k,g),E({actor:S(n),action:"move",boardId:a,itemId:l,payload:{from:p.from,to:p.to}}),c(t,200,{ok:!0})}catch(d){c(t,502,{error:d.message||"transition failed"})}return}if(y){let[,a,l]=y,v=o.boards.find(p=>p.id===a);if(!v){c(t,404,{error:`unknown board: ${a}`});return}let h=o.sources.get(a);if(!h){c(t,503,{error:`board "${a}" source unavailable`});return}if(r==="GET"&&!l){let{columns:p,totals:g}=await $(v,h,i.searchParams);c(t,200,{columns:p,totals:g});return}if(r==="GET"&&l){try{let p=typeof h.getItem=="function"?await h.getItem(l):null;if(!p){c(t,404,{error:"not found"});return}c(t,200,p)}catch(p){c(t,502,{error:p.message})}return}if(r==="PATCH"&&l){let p=await w(n);try{let g=typeof h.updateItem=="function"?await h.updateItem(l,p):null;if(!g){c(t,501,{error:"update unsupported"});return}E({actor:S(n),action:"update",boardId:a,itemId:l,payload:p}),c(t,200,g)}catch(g){c(t,502,{error:g.message})}return}if(r==="POST"&&!l){let p=await w(n);if(!p.title){c(t,400,{error:"title required"});return}if(!h.capabilities.create||!h.create){c(t,400,{error:`source ${h.type} does not support create`});return}try{let g=await h.create({title:p.title,description:p.description,labels:p.labels,assigneeUsernames:p.assigneeUsernames,milestoneTitle:p.milestoneTitle,projectHint:p.projectHint});E({actor:S(n),action:"create",boardId:a,itemId:g.id,payload:{title:p.title}}),c(t,201,g)}catch(g){c(t,502,{error:g.message})}return}}c(t,404,{error:"not found",path:e})}async function $(n,t,o){if(!t.capabilities.listAll||!t.listAll){let d={};for(let b of n.columns)d[b.id]=[];return{columns:d,totals:Object.fromEntries(n.columns.map(b=>[b.id,0]))}}let i=[];n.primaryToolLabel&&i.push(n.primaryToolLabel);let e=o.get("label")||void 0;e&&i.push(e);let r=o.get("q")||void 0,m=o.get("milestone")||void 0,f=o.get("assignee")||void 0,u=parseInt(o.get("days")||String(n.timeRangeDays),10),s=parseInt(o.get("closedDays")||String(n.closedWindowDays??30),10),y=n.columns.some(d=>d.kind!=="closed"),a=n.columns.some(d=>d.kind==="closed"),l=y?await t.listAll({sinceDays:u,labels:i.length?i:void 0,search:r,milestone:m,state:"opened"}):[],v=a?await t.listAll({sinceDays:s,labels:i.length?i:void 0,search:r,milestone:m,state:"closed"}):[],h={};for(let d of n.columns)h[d.id]=[];let p=n.columns.find(d=>d.kind==="closed");for(let d of v)p&&h[p.id].push({...d,stage:p.id});let g=n.columns.filter(d=>d.kind!=="closed");for(let d of l){let b=A(d,g);if(!b)continue;let x=h[b];x&&x.push({...d,stage:b})}if(f)for(let d of Object.keys(h))h[d]=h[d].filter(b=>b.assignee===f);for(let d of Object.keys(h))h[d].sort((b,x)=>{let D=(b.priority??99)-(x.priority??99);return D!==0?D:(x.updatedAt||"").localeCompare(b.updatedAt||"")});let k=Object.fromEntries(Object.entries(h).map(([d,b])=>[d,b.length]));return{columns:h,totals:k}}async function z(n,t,o,i){let e=T(o,i),r=n;e.closeIssue&&typeof r.setState=="function"?await r.setState(t,"close"):e.reopen&&typeof r.setState=="function"&&await r.setState(t,"reopen"),(e.addLabels?.length||e.removeLabels?.length)&&typeof r.transitionMany=="function"?await r.transitionMany(t,e.addLabels||[],e.removeLabels||[]):e.addLabels?.length&&n.transition&&await n.transition(t,e.addLabels[0],e.removeLabels?.[0])}function H(n,t){if(n.source.type==="gitlab"){let o=t.channels.gitlab;if(!o?.token)throw new Error("channels.gitlab.token is required for gitlab boards");let i={};for(let e of o.agentMappings)e.gitlabUsernames.length&&(i[e.agentId]=e.gitlabUsernames);return new I(o.host,o.token,n.source.projects,i,(...e)=>console.log("[board]",...e))}throw new Error(`unsupported board source: ${n.source.type}`)}function E(n){try{let t=_(process.cwd(),".agentx");R(t)||B(t,{recursive:!0});let o=JSON.stringify({ts:new Date().toISOString(),...n})+`
|
|
4
|
-
`;N(_(t,"board-audit.jsonl"),o)}catch{}}function S(n){let t=n.headers["x-forwarded-for"];return(Array.isArray(t)?t[0]:t?.split(",")[0])||n.socket.remoteAddress||"unknown"}async function j(n,t,o){let i={};t&&(i.Authorization=`Bearer ${t}`);let e={id:n,name:n,url:n,reachable:!1,agents:[]};try{let[r,m,f]=await Promise.all([fetch(n+"/health",{headers:i,signal:o}).catch(()=>null),fetch(n+"/agents",{headers:i,signal:o}).catch(()=>null),fetch(n+"/mesh",{headers:i,signal:o}).catch(()=>null)]);if(!m||!m.ok)return e.error=m?`HTTP ${m.status}`:"unreachable",e;let u=await m.json();if(e.agents=u.map(s=>({id:s.id,name:s.name,tier:s.tier,model:s.model,active:s.active||0,total:s.total||0,errors:s.errors||0,lastActive:s.lastActive,runningTasks:Array.isArray(s.runningTasks)?s.runningTasks:[]})),r&&r.ok){let s=await r.json();e.uptimeSec=s.uptime,e.name=s.node?.name||s.node?.id||n,e.id=s.node?.id||n}e.reachable=!0,e._peers=Array.isArray(f&&await F(f))?e._peers:void 0}catch(r){e.error=r.message||String(r)}return e}var C=new WeakMap;async function F(n){if(C.has(n))return C.get(n);try{let t=n.ok?await n.json():null;return C.set(n,t),t}catch{return null}}async function V(n,t,o){try{let i={};t&&(i.Authorization=`Bearer ${t}`);let e=await fetch(n+"/mesh",{headers:i,signal:o});return e.ok?(await e.json()).filter(m=>m&&m.peerUrl).map(m=>({url:String(m.peerUrl).replace(/\/+$/,""),name:m.peer||m.peerUrl})):[]}catch{return[]}}async function L(n){let t=n.dashboard,o=t.daemonUrl.replace(/\/+$/,""),i=t.token,e=new Map;e.set(o,{name:"primary",url:o,token:i});for(let f of t.daemons){let u=f.url.replace(/\/+$/,"");e.has(u)||e.set(u,{name:f.name,url:u,token:f.token})}let r=new AbortController,m=setTimeout(()=>r.abort(),3e3);try{let f=await V(o,i,r.signal);for(let s of f)e.has(s.url)||e.set(s.url,s);let u=await Promise.all([...e.values()].map(s=>j(s.url,s.token,r.signal)));return{ts:new Date().toISOString(),nodes:u}}finally{clearTimeout(m)}}function X(n,t,o){t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive","X-Accel-Buffering":"no"});let i=!1,e=(f,u)=>{i||t.write(`event: ${f}
|
|
5
|
-
data: ${JSON.stringify(u)}
|
|
6
|
-
|
|
7
|
-
`)},r=async()=>{try{let f=await L(o);e("snapshot",f)}catch(f){e("error",{message:f.message})}};r();let m=setInterval(r,2e3);n.on("close",()=>{i=!0,clearInterval(m),t.end()})}var U=`
|
|
8
|
-
You are helping draft a new GitLab issue. Follow these conventions strictly.
|
|
9
|
-
|
|
10
|
-
Title pattern:
|
|
11
|
-
<Kind>: <page|component> \u2013 <short symptom>
|
|
12
|
-
Kinds: Bug, UX, Copy, Perf, A11y, Security, UI, Incident (defects)
|
|
13
|
-
Feat, Enh, Deprecate (features)
|
|
14
|
-
Epic, Story, Spike, Req (planning)
|
|
15
|
-
Data, Env, Test, Refactor, Chore, Task, Mgmt (technical)
|
|
16
|
-
|
|
17
|
-
Title guidance:
|
|
18
|
-
- Be specific about the location ("Contact page", not "frontend").
|
|
19
|
-
- Use lowercase for the symptom.
|
|
20
|
-
- Keep the symptom 4-7 words max, present tense.
|
|
21
|
-
- Do NOT include issue IDs.
|
|
22
|
-
|
|
23
|
-
Description structure (Markdown):
|
|
24
|
-
For bugs: Problem / Steps to Reproduce / Expected / Actual / Environment / Impact.
|
|
25
|
-
For features/stories: User Story (As a \u2026 I want \u2026 So that \u2026) + Acceptance Criteria bullets.
|
|
26
|
-
Otherwise: concise problem statement + any relevant context.
|
|
27
|
-
|
|
28
|
-
Labels:
|
|
29
|
-
- Pick ONE Role label from: Dev, Design, QA, Ops, PM, mgmt.
|
|
30
|
-
- Pick ONE Kind label matching the title prefix (bug, task, ux, ui, perf, security, copy, enhancement, \u2026).
|
|
31
|
-
- Pick ONE Priority: critical, high, medium, low.
|
|
32
|
-
- Pick ONE Difficulty: diff-XS, diff-S, diff-M, diff-L, diff-XL.
|
|
33
|
-
- Optional Area/feature labels if clearly applicable from the known label list.
|
|
34
|
-
|
|
35
|
-
Return ONLY a single JSON object on one line \u2014 no prose, no code fences, no explanation.
|
|
36
|
-
Shape:
|
|
37
|
-
{ "title": string, "description": string, "labels": string[], "assigneeUsername": string | null, "kind": string }
|
|
38
|
-
If you don't have enough information to pick a label confidently, omit it rather than guessing.
|
|
39
|
-
`;async function W(n,t){let o=(t?.rough||"").toString().trim();if(!o)throw new Error("rough description required");let i=t?.agent||n.dashboard.draftAgent;if(!i)throw new Error("no draft agent configured (set dashboard.draftAgent or pass agent in body)");let e=t?.context||{},r=["Rough description from the user:",'"""',o,'"""',"",`Board: ${e.boardName||"(unspecified)"}`,e.primaryLabel?`Primary board label (include in labels): ${e.primaryLabel}`:"",e.columnLabel?`Target column label (include in labels): ${e.columnLabel}`:"",e.project?`GitLab project: ${e.project}`:"",e.knownLabels?.length?`Known labels in this project (prefer picking from these when they fit):
|
|
40
|
-
- ${e.knownLabels.slice(0,60).join(`
|
|
41
|
-
- `)}`:"",e.members?.length?`Possible assignees (GitLab usernames): ${e.members.slice(0,20).join(", ")}`:"","","Produce the JSON draft now."].filter(Boolean).join(`
|
|
42
|
-
`),m=n.dashboard.daemonUrl.replace(/\/+$/,"")+"/task",f={"Content-Type":"application/json"};n.dashboard.token&&(f.Authorization=`Bearer ${n.dashboard.token}`);let u=await fetch(m,{method:"POST",headers:f,body:JSON.stringify({agent:i,message:r,context:{channel:"a2a",sender:"board-dashboard",systemPrompt:U}})});if(!u.ok)throw new Error(`daemon /task HTTP ${u.status}: ${await u.text().catch(()=>"")}`);let s=await u.json();if(s.error)throw new Error(String(s.error));let y=(s.content||"").toString(),a=G(y);if(!a||typeof a.title!="string")throw new Error("agent did not return a valid JSON draft. Raw: "+y.slice(0,400));let l=Array.isArray(a.labels)?a.labels.filter(v=>typeof v=="string"):[];return e.primaryLabel&&!l.includes(e.primaryLabel)&&l.unshift(e.primaryLabel),e.columnLabel&&!l.includes(e.columnLabel)&&l.unshift(e.columnLabel),{title:a.title.trim(),description:(a.description||"").toString(),labels:l,assigneeUsername:typeof a.assigneeUsername=="string"?a.assigneeUsername:void 0,kind:typeof a.kind=="string"?a.kind:void 0,raw:y}}function G(n){if(!n)return null;try{return JSON.parse(n)}catch{}let t=n.match(/```(?:json)?\s*([\s\S]*?)```/);if(t)try{return JSON.parse(t[1])}catch{}let o=n.indexOf("{");if(o===-1)return null;let i=0;for(let e=o;e<n.length;e++){let r=n[e];if(r==="{")i++;else if(r==="}"&&(i--,i===0))try{return JSON.parse(n.slice(o,e+1))}catch{return null}}return null}function c(n,t,o){n.writeHead(t,{"Content-Type":"application/json; charset=utf-8"}),n.end(JSON.stringify(o))}async function w(n){return new Promise((t,o)=>{let i=[];n.on("data",e=>i.push(e)),n.on("end",()=>{try{let e=Buffer.concat(i).toString("utf-8");t(e?JSON.parse(e):{})}catch(e){o(e)}}),n.on("error",o)})}function K(){return`<!DOCTYPE html>
|
|
43
|
-
<html lang="en">
|
|
44
|
-
<head>
|
|
45
|
-
<meta charset="utf-8">
|
|
46
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
47
|
-
<title>AgentX Boards</title>
|
|
48
|
-
<style>${Q}</style>
|
|
49
|
-
</head>
|
|
50
|
-
<body>
|
|
51
|
-
<header>
|
|
52
|
-
<div class="brand">AgentX</div>
|
|
53
|
-
<select id="boardPicker" title="Switch board"></select>
|
|
54
|
-
<div class="search">
|
|
55
|
-
<span class="icon">\u2315</span>
|
|
56
|
-
<input id="filterSearch" type="search" placeholder="Search or filter\u2026" autocomplete="off" />
|
|
57
|
-
</div>
|
|
58
|
-
<select id="filterAssignee" title="Assignee"><option value="">All assignees</option></select>
|
|
59
|
-
<select id="filterMilestone" title="Milestone"><option value="">Any milestone</option></select>
|
|
60
|
-
<div id="filterLabels" class="chips"></div>
|
|
61
|
-
<div class="spacer"></div>
|
|
62
|
-
<a href="/live" class="link" title="Live mesh view (L)">\u25CE Live</a>
|
|
63
|
-
<button id="activityBtn" title="Agent activity (a)">\u25C9 Activity</button>
|
|
64
|
-
<button id="newIssueBtn" title="New issue">+ New</button>
|
|
65
|
-
<button id="refreshBtn" title="Refresh (r)">\u21BB</button>
|
|
66
|
-
<div id="conn" class="conn ok" title="ready">\u25CF</div>
|
|
67
|
-
</header>
|
|
68
|
-
<aside id="activity-panel" class="activity-panel hidden" aria-hidden="true">
|
|
69
|
-
<header>
|
|
70
|
-
<h2>Agent Activity</h2>
|
|
71
|
-
<span id="activity-source" class="source"></span>
|
|
72
|
-
<button id="activity-close" class="x" title="Close">\u2715</button>
|
|
73
|
-
</header>
|
|
74
|
-
<div id="activity-body">Loading\u2026</div>
|
|
75
|
-
<footer id="activity-events" class="events"></footer>
|
|
76
|
-
</aside>
|
|
77
|
-
<main id="columns"></main>
|
|
78
|
-
<div id="toast"></div>
|
|
79
|
-
<div id="modal" class="modal" aria-hidden="true">
|
|
80
|
-
<div class="modal-backdrop"></div>
|
|
81
|
-
<div class="modal-panel" role="dialog" aria-modal="true">
|
|
82
|
-
<header class="modal-h">
|
|
83
|
-
<div class="modal-title-row">
|
|
84
|
-
<span id="m-iid" class="iid"></span>
|
|
85
|
-
<input id="m-title" type="text" placeholder="Issue title" />
|
|
86
|
-
</div>
|
|
87
|
-
<button id="m-close" class="x" title="Close (esc)">\u2715</button>
|
|
88
|
-
</header>
|
|
89
|
-
<div class="modal-body">
|
|
90
|
-
<div class="m-main">
|
|
91
|
-
<div id="m-draft" class="draft-box" hidden>
|
|
92
|
-
<label class="fld">
|
|
93
|
-
<span>\u2728 Describe the issue (the agent will draft a proper title + description)</span>
|
|
94
|
-
<textarea id="m-rough" rows="3" placeholder="e.g. when I upload a CSV over 500 rows in Receiving Records the import silently fails"></textarea>
|
|
95
|
-
</label>
|
|
96
|
-
<div class="draft-row">
|
|
97
|
-
<select id="m-draft-agent" title="Drafting agent"><option value="">Agent\u2026</option></select>
|
|
98
|
-
<button id="m-draft-go" class="btn-primary" type="button">\u2728 Draft</button>
|
|
99
|
-
<span id="m-draft-hint" class="hint"></span>
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
<label class="fld">
|
|
103
|
-
<span>Description</span>
|
|
104
|
-
<textarea id="m-desc" rows="8" placeholder="Markdown supported"></textarea>
|
|
105
|
-
</label>
|
|
106
|
-
<div class="fld">
|
|
107
|
-
<span>Labels</span>
|
|
108
|
-
<div id="m-labels" class="chip-picker"></div>
|
|
109
|
-
</div>
|
|
110
|
-
</div>
|
|
111
|
-
<aside class="m-side">
|
|
112
|
-
<div class="fld">
|
|
113
|
-
<span>State</span>
|
|
114
|
-
<div id="m-state" class="state-toggle">
|
|
115
|
-
<button data-state="opened">Open</button>
|
|
116
|
-
<button data-state="closed">Closed</button>
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
<div class="fld">
|
|
120
|
-
<span>Assignees</span>
|
|
121
|
-
<div id="m-assignees" class="chip-picker"></div>
|
|
122
|
-
</div>
|
|
123
|
-
<div class="fld">
|
|
124
|
-
<span>Milestone</span>
|
|
125
|
-
<select id="m-milestone"><option value="">None</option></select>
|
|
126
|
-
</div>
|
|
127
|
-
<div class="fld muted">
|
|
128
|
-
<span>Author</span>
|
|
129
|
-
<div id="m-author"></div>
|
|
130
|
-
</div>
|
|
131
|
-
<div class="fld muted">
|
|
132
|
-
<span>Created</span>
|
|
133
|
-
<div id="m-created"></div>
|
|
134
|
-
</div>
|
|
135
|
-
<div class="fld muted">
|
|
136
|
-
<span>Updated</span>
|
|
137
|
-
<div id="m-updated"></div>
|
|
138
|
-
</div>
|
|
139
|
-
<a id="m-link" class="ext-link" href="#" target="_blank" rel="noopener">Open in GitLab \u2197</a>
|
|
140
|
-
</aside>
|
|
141
|
-
</div>
|
|
142
|
-
<footer class="modal-f">
|
|
143
|
-
<div id="m-hint" class="hint"></div>
|
|
144
|
-
<div class="spacer"></div>
|
|
145
|
-
<button id="m-cancel" class="btn-ghost">Cancel</button>
|
|
146
|
-
<button id="m-save" class="btn-primary">Save</button>
|
|
147
|
-
</footer>
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
<script src="/sortable.min.js"></script>
|
|
151
|
-
<script>${Z}</script>
|
|
152
|
-
</body>
|
|
153
|
-
</html>`}function Y(){return`<!DOCTYPE html>
|
|
154
|
-
<html lang="en">
|
|
155
|
-
<head>
|
|
156
|
-
<meta charset="utf-8">
|
|
157
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
158
|
-
<title>AgentX \u2014 Live</title>
|
|
159
|
-
<style>${q}</style>
|
|
160
|
-
</head>
|
|
161
|
-
<body>
|
|
162
|
-
<header>
|
|
163
|
-
<div class="brand">AgentX <span class="sub">\xB7 Live</span></div>
|
|
164
|
-
<div id="summary" class="summary"></div>
|
|
165
|
-
<div class="spacer"></div>
|
|
166
|
-
<a href="/" class="link">\u2190 Boards</a>
|
|
167
|
-
<span id="ts" class="ts" title="Last update"></span>
|
|
168
|
-
<span id="conn" class="conn ok" title="connected">\u25CF</span>
|
|
169
|
-
</header>
|
|
170
|
-
<main id="grid"></main>
|
|
171
|
-
<script>${J}</script>
|
|
172
|
-
</body>
|
|
173
|
-
</html>`}var q=`
|
|
174
|
-
:root {
|
|
175
|
-
--bg: #0b0d14; --card: #151823; --node: #1a1d29; --border: #2a2d3a;
|
|
176
|
-
--text: #e6e8ef; --muted: #8b8fa3; --accent: #6366f1;
|
|
177
|
-
--green: #22c55e; --yellow: #f59e0b; --red: #ef4444; --blue: #3b82f6; --gray: #6b7280;
|
|
178
|
-
}
|
|
179
|
-
* { box-sizing: border-box; }
|
|
180
|
-
html, body { margin: 0; min-height: 100%; background: var(--bg); color: var(--text);
|
|
181
|
-
font: 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
|
182
|
-
header { display: flex; align-items: center; gap: 14px; padding: 10px 18px;
|
|
183
|
-
background: #10131c; border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 10; }
|
|
184
|
-
header .brand { font-weight: 600; color: var(--accent); font-size: 15px; }
|
|
185
|
-
header .brand .sub { color: var(--muted); font-weight: 500; }
|
|
186
|
-
header .summary { display: flex; gap: 16px; color: var(--muted); font-size: 12px; }
|
|
187
|
-
header .summary b { color: var(--text); }
|
|
188
|
-
header .spacer { flex: 1; }
|
|
189
|
-
header .link { color: var(--muted); text-decoration: none; font-size: 13px; padding: 4px 10px; border: 1px solid var(--border); border-radius: 6px; }
|
|
190
|
-
header .link:hover { color: var(--accent); border-color: var(--accent); }
|
|
191
|
-
header .ts { color: var(--muted); font-size: 11px; font-family: ui-monospace, monospace; }
|
|
192
|
-
header .conn { font-size: 14px; }
|
|
193
|
-
header .conn.ok { color: var(--green); }
|
|
194
|
-
header .conn.warn { color: var(--yellow); }
|
|
195
|
-
header .conn.err { color: var(--red); }
|
|
196
|
-
|
|
197
|
-
main#grid { padding: 16px; display: flex; flex-direction: column; gap: 16px; }
|
|
198
|
-
|
|
199
|
-
.node { background: var(--node); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
|
|
200
|
-
.node > header { background: transparent; position: static; padding: 12px 16px; border-bottom: 1px solid var(--border); gap: 10px; }
|
|
201
|
-
.node .name { font-weight: 600; font-size: 14px; }
|
|
202
|
-
.node .url { color: var(--muted); font-family: ui-monospace, monospace; font-size: 11px; }
|
|
203
|
-
.node .tag { font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px;
|
|
204
|
-
padding: 2px 7px; border-radius: 10px; background: rgba(99,102,241,0.2); color: #c5c8d6; }
|
|
205
|
-
.node .tag.down { background: rgba(239,68,68,0.25); color: #fca5a5; }
|
|
206
|
-
.node .tag.up { background: rgba(34,197,94,0.2); color: #86efac; }
|
|
207
|
-
.node .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
|
208
|
-
gap: 10px; padding: 12px 14px; }
|
|
209
|
-
|
|
210
|
-
.agent { background: var(--card); border: 1px solid var(--border); border-radius: 8px;
|
|
211
|
-
padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; transition: border-color 0.2s; }
|
|
212
|
-
.agent.busy { border-color: var(--green); box-shadow: 0 0 0 1px rgba(34,197,94,0.3); }
|
|
213
|
-
.agent.errored { border-color: var(--red); }
|
|
214
|
-
.agent > .top { display: flex; align-items: center; gap: 8px; }
|
|
215
|
-
.agent .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--gray); flex-shrink: 0; }
|
|
216
|
-
.agent.busy .dot { background: var(--green); box-shadow: 0 0 6px var(--green); animation: pulse 1.2s ease-in-out infinite; }
|
|
217
|
-
.agent.errored .dot { background: var(--red); }
|
|
218
|
-
.agent .name { font-weight: 600; font-size: 13px; flex: 1; }
|
|
219
|
-
.agent .tier { font-size: 9px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); padding: 2px 6px; border: 1px solid var(--border); border-radius: 4px; }
|
|
220
|
-
.agent .meta { display: flex; align-items: center; gap: 6px; font-size: 10px; color: var(--muted); flex-wrap: wrap; }
|
|
221
|
-
.agent .model { font-family: ui-monospace, monospace; font-size: 10px; color: var(--accent);
|
|
222
|
-
background: rgba(99,102,241,0.12); padding: 1px 7px; border-radius: 3px; letter-spacing: 0.2px; }
|
|
223
|
-
.agent .last { font-size: 10px; color: var(--muted); }
|
|
224
|
-
.agent .last.muted { font-style: italic; opacity: 0.7; }
|
|
225
|
-
.agent .stats { display: flex; gap: 10px; font-size: 11px; color: var(--muted); }
|
|
226
|
-
.agent .stats b { color: var(--text); font-weight: 600; }
|
|
227
|
-
.agent .stats .err b { color: var(--red); }
|
|
228
|
-
.agent .tasks { display: flex; flex-direction: column; gap: 6px; padding-top: 6px; border-top: 1px dashed var(--border); }
|
|
229
|
-
.agent .task { display: flex; align-items: center; gap: 6px; font-size: 11px; }
|
|
230
|
-
.agent .task .channel { background: rgba(99,102,241,0.18); color: var(--accent);
|
|
231
|
-
font-size: 9px; text-transform: uppercase; padding: 1px 6px; border-radius: 3px; letter-spacing: 0.5px; }
|
|
232
|
-
.agent .task .elapsed { color: var(--muted); font-family: ui-monospace, monospace; font-size: 10px; }
|
|
233
|
-
.agent .task .preview { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
|
|
234
|
-
.agent .idle { color: var(--muted); font-size: 11px; font-style: italic; }
|
|
235
|
-
|
|
236
|
-
@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: 0.4 } }
|
|
237
|
-
`,J=`
|
|
238
|
-
'use strict';
|
|
239
|
-
|
|
240
|
-
const ui = { nodes: new Map(), summary: { nodes: 0, agents: 0, busy: 0, errors: 0 } };
|
|
241
|
-
|
|
242
|
-
function escapeHtml(s) {
|
|
243
|
-
return String(s).replace(/[&<>"']/g, c => ({ '&':'&', '<':'<', '>':'>', '"':'"', "'":''' }[c]));
|
|
244
|
-
}
|
|
245
|
-
function fmtElapsed(ms) {
|
|
246
|
-
if (ms < 1000) return ms + 'ms';
|
|
247
|
-
const s = Math.floor(ms / 1000); if (s < 60) return s + 's';
|
|
248
|
-
const m = Math.floor(s / 60); const rs = s % 60;
|
|
249
|
-
if (m < 60) return m + 'm ' + (rs < 10 ? '0' : '') + rs + 's';
|
|
250
|
-
const h = Math.floor(m / 60); const rm = m % 60;
|
|
251
|
-
return h + 'h ' + (rm < 10 ? '0' : '') + rm + 'm';
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function render(snapshot) {
|
|
255
|
-
const grid = document.getElementById('grid');
|
|
256
|
-
const nowTs = new Date(snapshot.ts).toLocaleTimeString();
|
|
257
|
-
document.getElementById('ts').textContent = nowTs;
|
|
258
|
-
const summary = { nodes: snapshot.nodes.length, reachable: 0, agents: 0, busy: 0, errors: 0 };
|
|
259
|
-
grid.innerHTML = '';
|
|
260
|
-
for (const node of snapshot.nodes) {
|
|
261
|
-
if (node.reachable) summary.reachable++;
|
|
262
|
-
summary.agents += node.agents.length;
|
|
263
|
-
for (const a of node.agents) {
|
|
264
|
-
const busy = (a.runningTasks && a.runningTasks.length > 0) || (a.active || 0) > 0;
|
|
265
|
-
if (busy) summary.busy++;
|
|
266
|
-
summary.errors += (a.errors || 0);
|
|
267
|
-
}
|
|
268
|
-
grid.appendChild(renderNode(node));
|
|
269
|
-
}
|
|
270
|
-
document.getElementById('summary').innerHTML =
|
|
271
|
-
'<span><b>' + summary.reachable + '/' + summary.nodes + '</b> nodes</span>' +
|
|
272
|
-
'<span><b>' + summary.agents + '</b> agents</span>' +
|
|
273
|
-
'<span><b>' + summary.busy + '</b> active now</span>' +
|
|
274
|
-
'<span><b>' + summary.errors + '</b> errors</span>';
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function renderNode(node) {
|
|
278
|
-
const sec = document.createElement('section'); sec.className = 'node';
|
|
279
|
-
const tag = node.reachable
|
|
280
|
-
? '<span class="tag up">online \xB7 ' + (node.uptimeSec ? Math.round(node.uptimeSec / 60) + 'm' : '\u2014') + '</span>'
|
|
281
|
-
: '<span class="tag down">offline \u2014 ' + escapeHtml(node.error || 'unreachable') + '</span>';
|
|
282
|
-
sec.innerHTML = '<header><div class="name">' + escapeHtml(node.name) + '</div>' +
|
|
283
|
-
'<div class="url">' + escapeHtml(node.url) + '</div>' +
|
|
284
|
-
tag + '</header><div class="grid"></div>';
|
|
285
|
-
const g = sec.querySelector('.grid');
|
|
286
|
-
if (!node.reachable || node.agents.length === 0) {
|
|
287
|
-
const empty = document.createElement('div'); empty.style.color = 'var(--muted)'; empty.style.padding = '8px 4px';
|
|
288
|
-
empty.textContent = node.reachable ? 'No agents on this node.' : 'Unreachable.';
|
|
289
|
-
g.appendChild(empty);
|
|
290
|
-
} else {
|
|
291
|
-
for (const a of node.agents) g.appendChild(renderAgent(a));
|
|
292
|
-
}
|
|
293
|
-
return sec;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function renderAgent(a) {
|
|
297
|
-
const card = document.createElement('div');
|
|
298
|
-
const busy = (a.runningTasks && a.runningTasks.length > 0) || (a.active || 0) > 0;
|
|
299
|
-
const errored = (a.errors || 0) > 0;
|
|
300
|
-
card.className = 'agent' + (busy ? ' busy' : '') + (errored ? ' errored' : '');
|
|
301
|
-
const tasks = (a.runningTasks || []).map(t => {
|
|
302
|
-
const elapsed = fmtElapsed(Date.now() - new Date(t.startedAt).getTime());
|
|
303
|
-
return '<div class="task">' +
|
|
304
|
-
'<span class="channel">' + escapeHtml(t.channel || '\u2014') + '</span>' +
|
|
305
|
-
'<span class="preview" title="' + escapeHtml(t.messagePreview || '') + '">' + escapeHtml(t.messagePreview || '(no preview)') + '</span>' +
|
|
306
|
-
'<span class="elapsed">' + elapsed + '</span>' +
|
|
307
|
-
'</div>';
|
|
308
|
-
}).join('');
|
|
309
|
-
const tasksBlock = tasks
|
|
310
|
-
? '<div class="tasks">' + tasks + '</div>'
|
|
311
|
-
: (busy ? '<div class="tasks"><div class="task"><span class="elapsed">running \xB7 no preview</span></div></div>'
|
|
312
|
-
: '<div class="idle">idle</div>');
|
|
313
|
-
const modelLabel = a.model ? '<span class="model" title="Model">' + escapeHtml(shortenModel(a.model)) + '</span>' : '';
|
|
314
|
-
const lastLabel = a.lastActive
|
|
315
|
-
? '<span class="last" title="' + escapeHtml(new Date(a.lastActive).toLocaleString()) + '">last ' + escapeHtml(fmtAgo(a.lastActive)) + '</span>'
|
|
316
|
-
: '<span class="last muted">never ran</span>';
|
|
317
|
-
card.innerHTML =
|
|
318
|
-
'<div class="top">' +
|
|
319
|
-
'<span class="dot"></span>' +
|
|
320
|
-
'<span class="name">' + escapeHtml(a.name || a.id) + '</span>' +
|
|
321
|
-
'<span class="tier">' + escapeHtml(a.tier || '') + '</span>' +
|
|
322
|
-
'</div>' +
|
|
323
|
-
'<div class="meta">' + modelLabel + lastLabel + '</div>' +
|
|
324
|
-
'<div class="stats">' +
|
|
325
|
-
'<span>Active <b>' + (a.active || 0) + '</b></span>' +
|
|
326
|
-
'<span>Total <b>' + (a.total || 0) + '</b></span>' +
|
|
327
|
-
'<span class="err">Err <b>' + (a.errors || 0) + '</b></span>' +
|
|
328
|
-
'</div>' + tasksBlock;
|
|
329
|
-
return card;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function shortenModel(m) {
|
|
333
|
-
if (!m) return '';
|
|
334
|
-
return String(m)
|
|
335
|
-
.replace(/^claude-/, '')
|
|
336
|
-
.replace(/-\\d{8}$/, '')
|
|
337
|
-
.replace(/\\[1m\\]$/, ' \xB7 1M');
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function fmtAgo(iso) {
|
|
341
|
-
const t = new Date(iso).getTime();
|
|
342
|
-
if (!t) return '\u2014';
|
|
343
|
-
const s = Math.floor((Date.now() - t) / 1000);
|
|
344
|
-
if (s < 60) return s + 's ago';
|
|
345
|
-
const m = Math.floor(s / 60); if (m < 60) return m + 'm ago';
|
|
346
|
-
const h = Math.floor(m / 60); if (h < 24) return h + 'h ago';
|
|
347
|
-
const d = Math.floor(h / 24); return d + 'd ago';
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
function connect() {
|
|
351
|
-
const conn = document.getElementById('conn');
|
|
352
|
-
let es;
|
|
353
|
-
const open = () => {
|
|
354
|
-
try { es = new EventSource('/api/live/stream'); } catch (e) { setTimeout(open, 2000); return; }
|
|
355
|
-
es.addEventListener('snapshot', (ev) => {
|
|
356
|
-
conn.className = 'conn ok'; conn.title = 'connected';
|
|
357
|
-
try { render(JSON.parse(ev.data)); } catch {}
|
|
358
|
-
});
|
|
359
|
-
es.addEventListener('error', () => {
|
|
360
|
-
conn.className = 'conn err'; conn.title = 'reconnecting\u2026';
|
|
361
|
-
es.close(); setTimeout(open, 2000);
|
|
362
|
-
});
|
|
363
|
-
};
|
|
364
|
-
open();
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Re-render every second to tick elapsed counters even when no new snapshot arrives.
|
|
368
|
-
let lastSnap = null;
|
|
369
|
-
fetch('/api/live').then(r => r.json()).then(s => { lastSnap = s; render(s); }).catch(() => {});
|
|
370
|
-
setInterval(() => { if (lastSnap) {
|
|
371
|
-
// Patch elapsed times without a fresh snapshot
|
|
372
|
-
document.querySelectorAll('.agent .task .elapsed').forEach((el) => { /* placeholder: server snapshot advances on stream */ });
|
|
373
|
-
} }, 1000);
|
|
374
|
-
connect();
|
|
375
|
-
`,Q=`
|
|
376
|
-
:root {
|
|
377
|
-
--bg: #0f1117; --card: #1a1d27; --col: #161922; --col-hdr: #1e2230;
|
|
378
|
-
--border: #2a2d3a; --border-soft: #22253220;
|
|
379
|
-
--text: #e6e8ef; --muted: #8b8fa3; --accent: #6366f1;
|
|
380
|
-
--green: #22c55e; --yellow: #f59e0b; --orange: #fb7a35;
|
|
381
|
-
--red: #ef4444; --blue: #3b82f6; --gray: #6b7280;
|
|
382
|
-
}
|
|
383
|
-
* { box-sizing: border-box; }
|
|
384
|
-
html, body { margin: 0; height: 100%; background: var(--bg); color: var(--text);
|
|
385
|
-
font: 13px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
|
386
|
-
header { display: flex; align-items: center; gap: 10px; padding: 10px 16px;
|
|
387
|
-
background: var(--card); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 10;
|
|
388
|
-
flex-wrap: wrap; }
|
|
389
|
-
header .brand { font-weight: 600; letter-spacing: 0.5px; color: var(--accent); font-size: 14px; }
|
|
390
|
-
header select, header input, header button { background: var(--col); color: var(--text);
|
|
391
|
-
border: 1px solid var(--border); padding: 6px 10px; border-radius: 6px; font: inherit; outline: none; }
|
|
392
|
-
header select:focus, header input:focus { border-color: var(--accent); }
|
|
393
|
-
header .search { position: relative; }
|
|
394
|
-
header .search .icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
|
|
395
|
-
color: var(--muted); pointer-events: none; font-size: 14px; }
|
|
396
|
-
header .search input { padding-left: 28px; width: 260px; }
|
|
397
|
-
header button { cursor: pointer; }
|
|
398
|
-
header button:hover { border-color: var(--accent); }
|
|
399
|
-
header .chips { display: flex; gap: 4px; flex-wrap: wrap; }
|
|
400
|
-
header .spacer { flex: 1; }
|
|
401
|
-
header .conn { font-size: 16px; line-height: 1; }
|
|
402
|
-
header .conn.ok { color: var(--green); }
|
|
403
|
-
header .conn.warn { color: var(--yellow); }
|
|
404
|
-
header .conn.err { color: var(--red); }
|
|
405
|
-
|
|
406
|
-
main#columns { display: flex; gap: 10px; padding: 12px; overflow-x: auto; align-items: stretch;
|
|
407
|
-
min-height: calc(100vh - 60px); }
|
|
408
|
-
.col { background: var(--col); border: 1px solid var(--border); border-radius: 8px;
|
|
409
|
-
min-width: 280px; max-width: 320px; flex: 1 0 280px; display: flex; flex-direction: column;
|
|
410
|
-
max-height: calc(100vh - 84px); }
|
|
411
|
-
.col .accent { height: 3px; border-top-left-radius: 7px; border-top-right-radius: 7px; background: var(--gray); }
|
|
412
|
-
.col header.col-h { margin: 0; padding: 10px 12px; background: var(--col-hdr);
|
|
413
|
-
border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px;
|
|
414
|
-
position: sticky; top: 0; z-index: 1; border-radius: 0; }
|
|
415
|
-
.col header.col-h h3 { margin: 0; font-size: 13px; font-weight: 600; color: var(--text); flex: 1; }
|
|
416
|
-
.col header.col-h .count { background: rgba(255,255,255,0.08); color: var(--muted);
|
|
417
|
-
font-size: 11px; padding: 2px 8px; border-radius: 10px; font-weight: 500; }
|
|
418
|
-
.stack { padding: 8px; display: flex; flex-direction: column; gap: 8px; overflow-y: auto;
|
|
419
|
-
flex: 1; min-height: 40px; }
|
|
420
|
-
|
|
421
|
-
.card { background: var(--card); border: 1px solid var(--border); border-radius: 6px;
|
|
422
|
-
padding: 10px 12px; cursor: grab; transition: border-color .15s, transform .15s;
|
|
423
|
-
display: flex; flex-direction: column; gap: 6px; }
|
|
424
|
-
.card:hover { border-color: var(--accent); }
|
|
425
|
-
.card.closed { opacity: 0.75; }
|
|
426
|
-
.card .title { font-weight: 500; font-size: 13px; line-height: 1.4; color: var(--text);
|
|
427
|
-
overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; word-break: break-word; }
|
|
428
|
-
.card .labels { display: flex; gap: 4px; flex-wrap: wrap; }
|
|
429
|
-
.card .label { font-size: 10px; padding: 2px 8px; border-radius: 10px; font-weight: 500;
|
|
430
|
-
background: rgba(99, 102, 241, 0.25); color: #e6e8ef; white-space: nowrap; line-height: 1.4; }
|
|
431
|
-
.card .foot { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted);
|
|
432
|
-
flex-wrap: wrap; }
|
|
433
|
-
.card .foot .iid { font-family: ui-monospace, monospace; }
|
|
434
|
-
.card .foot .milestone { background: rgba(99,102,241,0.15); color: var(--accent);
|
|
435
|
-
padding: 1px 6px; border-radius: 3px; font-size: 10px; }
|
|
436
|
-
.card .foot .est { background: rgba(255,255,255,0.08); padding: 1px 6px; border-radius: 3px; font-size: 10px; }
|
|
437
|
-
.card .foot .date { margin-left: auto; }
|
|
438
|
-
.card .foot a.link { color: var(--muted); text-decoration: none; }
|
|
439
|
-
.card .foot a.link:hover { color: var(--accent); }
|
|
440
|
-
|
|
441
|
-
.avatar { display: inline-flex; align-items: center; justify-content: center;
|
|
442
|
-
width: 22px; height: 22px; border-radius: 50%; font-size: 10px; font-weight: 600;
|
|
443
|
-
color: white; flex-shrink: 0; }
|
|
444
|
-
.avatar.unassigned { background: var(--gray); }
|
|
445
|
-
|
|
446
|
-
.sortable-ghost { opacity: 0.4; }
|
|
447
|
-
.sortable-drag { cursor: grabbing; }
|
|
448
|
-
.sortable-chosen { border-color: var(--accent); }
|
|
449
|
-
.empty { color: var(--muted); font-style: italic; text-align: center; padding: 16px; font-size: 12px; }
|
|
450
|
-
|
|
451
|
-
#toast { position: fixed; right: 16px; bottom: 16px; padding: 10px 14px;
|
|
452
|
-
background: var(--red); color: white; border-radius: 6px; font-size: 13px;
|
|
453
|
-
max-width: 360px; display: none; z-index: 200; box-shadow: 0 4px 12px rgba(0,0,0,0.4); }
|
|
454
|
-
#toast.show { display: block; }
|
|
455
|
-
|
|
456
|
-
/* Multi-assignee stack on cards */
|
|
457
|
-
.avatar-stack { display: inline-flex; }
|
|
458
|
-
.avatar-stack .avatar { margin-left: -6px; border: 2px solid var(--card); }
|
|
459
|
-
.avatar-stack .avatar:first-child { margin-left: 0; }
|
|
460
|
-
.avatar img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
|
|
461
|
-
|
|
462
|
-
/* Column add button */
|
|
463
|
-
.col header.col-h .add { background: transparent; color: var(--muted); border: 1px solid var(--border);
|
|
464
|
-
padding: 0 8px; line-height: 22px; border-radius: 4px; cursor: pointer; font-size: 14px; }
|
|
465
|
-
.col header.col-h .add:hover { color: var(--accent); border-color: var(--accent); }
|
|
466
|
-
|
|
467
|
-
/* Header buttons */
|
|
468
|
-
header #newIssueBtn { background: var(--accent); color: white; border-color: var(--accent); }
|
|
469
|
-
header #newIssueBtn:hover { filter: brightness(1.1); }
|
|
470
|
-
|
|
471
|
-
/* --- Modal --- */
|
|
472
|
-
.modal { position: fixed; inset: 0; z-index: 150; display: none; }
|
|
473
|
-
.modal.open { display: block; }
|
|
474
|
-
.modal-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,0.6); backdrop-filter: blur(2px); }
|
|
475
|
-
.modal-panel { position: relative; max-width: 940px; width: calc(100% - 40px); height: calc(100vh - 60px);
|
|
476
|
-
margin: 30px auto; background: var(--card); border: 1px solid var(--border); border-radius: 10px;
|
|
477
|
-
display: flex; flex-direction: column; box-shadow: 0 16px 48px rgba(0,0,0,0.55); overflow: hidden; }
|
|
478
|
-
.modal-h { display: flex; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--border); align-items: center; }
|
|
479
|
-
.modal-h .modal-title-row { flex: 1; display: flex; gap: 10px; align-items: center; }
|
|
480
|
-
.modal-h .iid { font-family: ui-monospace, monospace; color: var(--muted); font-size: 13px; }
|
|
481
|
-
.modal-h input[type="text"] { flex: 1; background: var(--col); border: 1px solid var(--border); color: var(--text);
|
|
482
|
-
padding: 8px 10px; border-radius: 6px; font-size: 15px; font-weight: 500; outline: none; }
|
|
483
|
-
.modal-h input[type="text"]:focus { border-color: var(--accent); }
|
|
484
|
-
.modal-h .x { background: transparent; color: var(--muted); border: none; font-size: 18px; cursor: pointer; padding: 6px 8px; }
|
|
485
|
-
.modal-h .x:hover { color: var(--text); }
|
|
486
|
-
|
|
487
|
-
.modal-body { flex: 1; display: grid; grid-template-columns: 1fr 300px; gap: 0;
|
|
488
|
-
overflow: hidden; min-height: 0; }
|
|
489
|
-
.modal-body .m-main { padding: 16px; overflow-y: auto; min-height: 0; }
|
|
490
|
-
.modal-body .m-side { padding: 16px; border-left: 1px solid var(--border); background: var(--col);
|
|
491
|
-
overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: 14px; }
|
|
492
|
-
|
|
493
|
-
.fld { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
|
|
494
|
-
.fld > span { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
495
|
-
.fld.muted > div { font-size: 12px; color: var(--muted); }
|
|
496
|
-
.fld textarea { background: var(--col); border: 1px solid var(--border); color: var(--text);
|
|
497
|
-
padding: 10px; border-radius: 6px; font: 13px/1.5 ui-monospace, "SF Mono", monospace; outline: none; resize: vertical; }
|
|
498
|
-
.fld textarea:focus { border-color: var(--accent); }
|
|
499
|
-
.fld select { background: var(--col); border: 1px solid var(--border); color: var(--text);
|
|
500
|
-
padding: 6px 10px; border-radius: 6px; font: inherit; outline: none; }
|
|
501
|
-
|
|
502
|
-
.chip-picker { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; padding: 6px;
|
|
503
|
-
background: var(--col); border: 1px solid var(--border); border-radius: 6px; min-height: 36px; }
|
|
504
|
-
.chip-picker .chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px;
|
|
505
|
-
border-radius: 10px; font-size: 11px; background: rgba(99,102,241,0.25); color: #e6e8ef; font-weight: 500; }
|
|
506
|
-
.chip-picker .chip .remove { cursor: pointer; color: rgba(255,255,255,0.7); margin-left: 2px; }
|
|
507
|
-
.chip-picker .chip .remove:hover { color: white; }
|
|
508
|
-
.chip-picker .add-chip { background: transparent; border: 1px dashed var(--border); color: var(--muted);
|
|
509
|
-
padding: 3px 10px; border-radius: 10px; cursor: pointer; font: inherit; font-size: 11px; }
|
|
510
|
-
.chip-picker .add-chip:hover { border-color: var(--accent); color: var(--accent); }
|
|
511
|
-
|
|
512
|
-
.chip-picker .dropdown { position: relative; }
|
|
513
|
-
.chip-picker .dropdown .menu { position: absolute; top: 100%; left: 0; margin-top: 4px;
|
|
514
|
-
background: var(--card); border: 1px solid var(--border); border-radius: 6px; min-width: 240px;
|
|
515
|
-
max-height: 280px; overflow-y: auto; box-shadow: 0 8px 20px rgba(0,0,0,0.4); z-index: 10; padding: 4px; }
|
|
516
|
-
.chip-picker .dropdown .menu input.search { width: 100%; background: var(--col); border: 1px solid var(--border);
|
|
517
|
-
color: var(--text); padding: 6px 10px; border-radius: 4px; font: inherit; margin-bottom: 4px; outline: none; }
|
|
518
|
-
.chip-picker .dropdown .menu .opt { padding: 6px 10px; cursor: pointer; border-radius: 4px;
|
|
519
|
-
font-size: 12px; display: flex; align-items: center; gap: 8px; }
|
|
520
|
-
.chip-picker .dropdown .menu .opt:hover { background: var(--col); }
|
|
521
|
-
.chip-picker .dropdown .menu .opt.selected { background: rgba(99,102,241,0.15); color: var(--accent); }
|
|
522
|
-
.chip-picker .dropdown .menu .empty { padding: 10px; color: var(--muted); font-size: 12px; text-align: center; }
|
|
523
|
-
|
|
524
|
-
.state-toggle { display: inline-flex; background: var(--col); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
|
|
525
|
-
.state-toggle button { background: transparent; border: none; color: var(--muted); padding: 6px 12px;
|
|
526
|
-
cursor: pointer; font: inherit; font-size: 12px; }
|
|
527
|
-
.state-toggle button.active[data-state="opened"] { background: var(--green); color: white; }
|
|
528
|
-
.state-toggle button.active[data-state="closed"] { background: var(--gray); color: white; }
|
|
529
|
-
|
|
530
|
-
.ext-link { color: var(--muted); text-decoration: none; font-size: 12px; margin-top: auto; padding-top: 10px;
|
|
531
|
-
border-top: 1px solid var(--border); }
|
|
532
|
-
.ext-link:hover { color: var(--accent); }
|
|
533
|
-
|
|
534
|
-
.modal-f { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--border); }
|
|
535
|
-
.modal-f .spacer { flex: 1; }
|
|
536
|
-
.modal-f .hint { color: var(--muted); font-size: 12px; }
|
|
537
|
-
.btn-primary { background: var(--accent); color: white; border: 1px solid var(--accent);
|
|
538
|
-
padding: 7px 16px; border-radius: 6px; cursor: pointer; font: inherit; font-weight: 500; }
|
|
539
|
-
.btn-primary:hover { filter: brightness(1.1); }
|
|
540
|
-
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
541
|
-
.btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border);
|
|
542
|
-
padding: 7px 14px; border-radius: 6px; cursor: pointer; font: inherit; }
|
|
543
|
-
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }
|
|
544
|
-
|
|
545
|
-
@media (max-width: 720px) {
|
|
546
|
-
.modal-body { grid-template-columns: 1fr; }
|
|
547
|
-
.modal-body .m-side { border-left: none; border-top: 1px solid var(--border); }
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
/* --- Activity panel --- */
|
|
551
|
-
.activity-panel { position: fixed; top: 0; right: 0; bottom: 0; width: 340px;
|
|
552
|
-
background: var(--card); border-left: 1px solid var(--border); z-index: 140;
|
|
553
|
-
display: flex; flex-direction: column; box-shadow: -8px 0 24px rgba(0,0,0,0.35);
|
|
554
|
-
transition: transform 0.2s ease; }
|
|
555
|
-
.activity-panel.hidden { transform: translateX(100%); pointer-events: none; }
|
|
556
|
-
.activity-panel > header { display: flex; align-items: center; gap: 8px; padding: 12px 14px;
|
|
557
|
-
border-bottom: 1px solid var(--border); background: var(--col-hdr); }
|
|
558
|
-
.activity-panel > header h2 { margin: 0; font-size: 13px; font-weight: 600; flex: 1; }
|
|
559
|
-
.activity-panel > header .source { font-size: 10px; color: var(--muted); font-family: ui-monospace, monospace; }
|
|
560
|
-
.activity-panel > header .x { background: transparent; border: none; color: var(--muted); font-size: 16px;
|
|
561
|
-
cursor: pointer; padding: 4px 8px; }
|
|
562
|
-
.activity-panel > header .x:hover { color: var(--text); }
|
|
563
|
-
#activity-body { flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 8px; }
|
|
564
|
-
.agent-card { background: var(--col); border: 1px solid var(--border); border-radius: 6px;
|
|
565
|
-
padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; }
|
|
566
|
-
.agent-card .top { display: flex; align-items: center; gap: 8px; }
|
|
567
|
-
.agent-card .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--gray); flex-shrink: 0; }
|
|
568
|
-
.agent-card .dot.active { background: var(--green); box-shadow: 0 0 6px var(--green); animation: pulse 1.2s ease-in-out infinite; }
|
|
569
|
-
.agent-card .dot.errored { background: var(--red); }
|
|
570
|
-
.agent-card .name { font-weight: 600; font-size: 13px; flex: 1; }
|
|
571
|
-
.agent-card .tier { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }
|
|
572
|
-
.agent-card .meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
|
|
573
|
-
.agent-card .model { font-family: ui-monospace, monospace; font-size: 10px; color: var(--accent);
|
|
574
|
-
background: rgba(99,102,241,0.12); padding: 1px 7px; border-radius: 3px; letter-spacing: 0.2px; }
|
|
575
|
-
.agent-card .last { font-size: 10px; color: var(--muted); }
|
|
576
|
-
.agent-card .last.muted { font-style: italic; opacity: 0.7; }
|
|
577
|
-
.agent-card .stats { display: flex; gap: 10px; font-size: 11px; color: var(--muted); }
|
|
578
|
-
.agent-card .stats b { color: var(--text); font-weight: 600; }
|
|
579
|
-
.agent-card .stats .errors b { color: var(--red); }
|
|
580
|
-
.agent-card .current { font-size: 11px; color: var(--muted); padding-top: 4px; border-top: 1px dashed var(--border);
|
|
581
|
-
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
582
|
-
.agent-card .current b { color: var(--text); font-weight: 500; }
|
|
583
|
-
@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: 0.4 } }
|
|
584
|
-
|
|
585
|
-
.activity-panel .events { padding: 10px 14px; max-height: 200px; overflow-y: auto;
|
|
586
|
-
border-top: 1px solid var(--border); background: #0a0c11; font: 11px/1.5 ui-monospace, monospace; }
|
|
587
|
-
.activity-panel .events .evt { color: var(--muted); margin-bottom: 4px; }
|
|
588
|
-
.activity-panel .events .evt .ts { color: #4a4e5a; }
|
|
589
|
-
.activity-panel .events .evt .who { color: var(--accent); }
|
|
590
|
-
.activity-panel .events .evt .what { color: var(--text); }
|
|
591
|
-
.activity-panel .events .evt.err .what { color: var(--red); }
|
|
592
|
-
|
|
593
|
-
/* Shift the main grid left when activity panel is open so cards aren't hidden */
|
|
594
|
-
body.panel-open main#columns { padding-right: 352px; }
|
|
595
|
-
body.panel-open header { padding-right: 356px; }
|
|
596
|
-
|
|
597
|
-
header #activityBtn.on { background: var(--accent); color: white; border-color: var(--accent); }
|
|
598
|
-
header a.link { color: var(--text); text-decoration: none; padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 12px; }
|
|
599
|
-
header a.link:hover { color: var(--accent); border-color: var(--accent); }
|
|
600
|
-
|
|
601
|
-
/* AI draft row inside the create modal */
|
|
602
|
-
.draft-box { background: var(--col); border: 1px dashed var(--accent); border-radius: 8px;
|
|
603
|
-
padding: 10px 12px; margin-bottom: 14px; }
|
|
604
|
-
.draft-box .fld { margin-bottom: 8px; }
|
|
605
|
-
.draft-box .draft-row { display: flex; gap: 8px; align-items: center; }
|
|
606
|
-
.draft-box .draft-row select { flex: 0 0 160px; }
|
|
607
|
-
.draft-box .draft-row .hint { color: var(--muted); font-size: 12px; flex: 1; }
|
|
608
|
-
.draft-box .draft-row .hint.err { color: var(--red); }
|
|
609
|
-
.draft-box .draft-row .hint.ok { color: var(--green); }
|
|
610
|
-
`,Z=`
|
|
611
|
-
'use strict';
|
|
612
|
-
const state = {
|
|
613
|
-
boards: [], current: null, items: {},
|
|
614
|
-
filter: { q: '', assignee: '', label: '', milestone: '' },
|
|
615
|
-
// Per-board caches \u2014 members + milestones + known labels.
|
|
616
|
-
meta: { members: [], milestones: [], labels: [] },
|
|
617
|
-
modal: { mode: null, id: null, original: null, draft: null }
|
|
618
|
-
};
|
|
619
|
-
|
|
620
|
-
function csrfHeaders(extra) {
|
|
621
|
-
return Object.assign({ 'Content-Type': 'application/json', 'X-Requested-With': 'agentx-board' }, extra || {});
|
|
622
|
-
}
|
|
623
|
-
function toast(msg, kind) {
|
|
624
|
-
const el = document.getElementById('toast');
|
|
625
|
-
el.textContent = msg;
|
|
626
|
-
el.style.background = kind === 'ok' ? 'var(--green)' : 'var(--red)';
|
|
627
|
-
el.classList.add('show');
|
|
628
|
-
clearTimeout(toast._t);
|
|
629
|
-
toast._t = setTimeout(() => el.classList.remove('show'), 4000);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
async function loadBoards() {
|
|
633
|
-
const r = await fetch('/api/boards');
|
|
634
|
-
if (!r.ok) throw new Error('failed to load boards');
|
|
635
|
-
state.boards = await r.json();
|
|
636
|
-
const picker = document.getElementById('boardPicker');
|
|
637
|
-
picker.innerHTML = '';
|
|
638
|
-
for (const b of state.boards) {
|
|
639
|
-
const opt = document.createElement('option');
|
|
640
|
-
opt.value = b.id; opt.textContent = b.name || b.id;
|
|
641
|
-
picker.appendChild(opt);
|
|
642
|
-
}
|
|
643
|
-
picker.onchange = () => switchBoard(picker.value);
|
|
644
|
-
if (state.boards.length) switchBoard(state.boards[0].id);
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
async function switchBoard(id) {
|
|
648
|
-
state.current = state.boards.find(b => b.id === id);
|
|
649
|
-
state.meta = { members: [], milestones: [], labels: [] };
|
|
650
|
-
renderColumns();
|
|
651
|
-
populateFilters();
|
|
652
|
-
await loadItems();
|
|
653
|
-
// Fetch members + milestones in the background for pickers.
|
|
654
|
-
loadMeta();
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
async function loadMeta() {
|
|
658
|
-
if (!state.current) return;
|
|
659
|
-
try {
|
|
660
|
-
const [mem, ms] = await Promise.all([
|
|
661
|
-
fetch(\`/api/boards/\${state.current.id}/members\`).then(r => r.ok ? r.json() : []),
|
|
662
|
-
fetch(\`/api/boards/\${state.current.id}/milestones\`).then(r => r.ok ? r.json() : []),
|
|
663
|
-
]);
|
|
664
|
-
state.meta.members = mem;
|
|
665
|
-
state.meta.milestones = ms;
|
|
666
|
-
state.meta.labels = collectKnownLabels();
|
|
667
|
-
} catch (e) { /* non-fatal */ }
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
function collectKnownLabels() {
|
|
671
|
-
const set = new Set();
|
|
672
|
-
for (const list of Object.values(state.items)) for (const i of list) {
|
|
673
|
-
for (const l of (i.labels || [])) set.add(l);
|
|
674
|
-
}
|
|
675
|
-
return [...set].sort();
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
function populateFilters() {
|
|
679
|
-
if (!state.current) return;
|
|
680
|
-
const labelsEl = document.getElementById('filterLabels');
|
|
681
|
-
labelsEl.innerHTML = '';
|
|
682
|
-
for (const lbl of state.current.labels || []) {
|
|
683
|
-
const chip = document.createElement('span');
|
|
684
|
-
chip.className = 'label';
|
|
685
|
-
chip.style.cursor = 'pointer';
|
|
686
|
-
chip.style.background = lbl.color || labelColor(lbl.name);
|
|
687
|
-
chip.style.color = pickTextColor(lbl.color);
|
|
688
|
-
chip.textContent = lbl.name;
|
|
689
|
-
chip.onclick = () => {
|
|
690
|
-
state.filter.label = state.filter.label === lbl.name ? '' : lbl.name;
|
|
691
|
-
renderChipStates(); loadItems();
|
|
692
|
-
};
|
|
693
|
-
labelsEl.appendChild(chip);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
function renderChipStates() {
|
|
697
|
-
document.querySelectorAll('#filterLabels .label').forEach(chip => {
|
|
698
|
-
chip.style.opacity = (state.filter.label && chip.textContent !== state.filter.label) ? '0.35' : '1';
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
function renderColumns() {
|
|
703
|
-
const main = document.getElementById('columns');
|
|
704
|
-
main.innerHTML = '';
|
|
705
|
-
if (!state.current) return;
|
|
706
|
-
for (const col of state.current.columns) {
|
|
707
|
-
const section = document.createElement('section');
|
|
708
|
-
section.className = 'col';
|
|
709
|
-
section.dataset.colId = col.id;
|
|
710
|
-
const canAdd = col.kind !== 'closed';
|
|
711
|
-
section.innerHTML = \`
|
|
712
|
-
<div class="accent" style="background:\${col.accent || 'var(--gray)'}"></div>
|
|
713
|
-
<header class="col-h">
|
|
714
|
-
<h3>\${escapeHtml(col.title)}</h3>
|
|
715
|
-
<span class="count">0</span>
|
|
716
|
-
\${canAdd ? \`<button class="add" data-col-id="\${col.id}" title="New issue in this column">+</button>\` : ''}
|
|
717
|
-
</header>
|
|
718
|
-
<div class="stack" data-col-id="\${col.id}"></div>
|
|
719
|
-
\`;
|
|
720
|
-
main.appendChild(section);
|
|
721
|
-
}
|
|
722
|
-
document.querySelectorAll('.col .add').forEach(btn => {
|
|
723
|
-
btn.onclick = () => openCreateModal(btn.dataset.colId);
|
|
724
|
-
});
|
|
725
|
-
document.querySelectorAll('.stack').forEach(el => {
|
|
726
|
-
Sortable.create(el, {
|
|
727
|
-
group: 'board', animation: 150,
|
|
728
|
-
onEnd: async (ev) => {
|
|
729
|
-
const from = ev.from.dataset.colId;
|
|
730
|
-
const to = ev.to.dataset.colId;
|
|
731
|
-
if (from === to) return;
|
|
732
|
-
const itemId = ev.item.dataset.id;
|
|
733
|
-
try {
|
|
734
|
-
const r = await fetch(\`/api/boards/\${state.current.id}/items/\${encodeURIComponent(itemId)}/move\`, {
|
|
735
|
-
method: 'PATCH', headers: csrfHeaders(), body: JSON.stringify({ from, to })
|
|
736
|
-
});
|
|
737
|
-
if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || r.statusText);
|
|
738
|
-
toast('Moved \u2713', 'ok');
|
|
739
|
-
updateCounts();
|
|
740
|
-
} catch (e) {
|
|
741
|
-
ev.from.insertBefore(ev.item, ev.from.children[ev.oldIndex] || null);
|
|
742
|
-
updateCounts();
|
|
743
|
-
toast('Move failed: ' + (e.message || 'unknown'));
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
});
|
|
747
|
-
});
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
function updateCounts() {
|
|
751
|
-
document.querySelectorAll('.col').forEach(col => {
|
|
752
|
-
const n = col.querySelector('.stack').children.length;
|
|
753
|
-
const empty = col.querySelector('.stack .empty');
|
|
754
|
-
const real = empty ? 0 : n;
|
|
755
|
-
col.querySelector('.count').textContent = real;
|
|
756
|
-
});
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
async function loadItems() {
|
|
760
|
-
const conn = document.getElementById('conn');
|
|
761
|
-
conn.className = 'conn warn'; conn.title = 'loading\u2026';
|
|
762
|
-
if (!state.current) return;
|
|
763
|
-
const params = new URLSearchParams();
|
|
764
|
-
if (state.filter.q) params.set('q', state.filter.q);
|
|
765
|
-
if (state.filter.assignee) params.set('assignee', state.filter.assignee);
|
|
766
|
-
if (state.filter.label) params.set('label', state.filter.label);
|
|
767
|
-
if (state.filter.milestone) params.set('milestone', state.filter.milestone);
|
|
768
|
-
try {
|
|
769
|
-
const r = await fetch(\`/api/boards/\${state.current.id}/items?\${params.toString()}\`);
|
|
770
|
-
if (!r.ok) throw new Error(await r.text());
|
|
771
|
-
const data = await r.json();
|
|
772
|
-
state.items = data.columns;
|
|
773
|
-
paintItems();
|
|
774
|
-
populateAssigneeFilter();
|
|
775
|
-
populateMilestoneFilter();
|
|
776
|
-
conn.className = 'conn ok'; conn.title = 'ok';
|
|
777
|
-
} catch (e) {
|
|
778
|
-
conn.className = 'conn err'; conn.title = 'load failed';
|
|
779
|
-
toast('Load failed: ' + (e.message || 'unknown'));
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
function populateAssigneeFilter() {
|
|
784
|
-
const set = new Set();
|
|
785
|
-
for (const list of Object.values(state.items)) for (const i of list) if (i.assignee) set.add(i.assignee);
|
|
786
|
-
const sel = document.getElementById('filterAssignee');
|
|
787
|
-
const current = sel.value;
|
|
788
|
-
sel.innerHTML = '<option value="">All assignees</option>';
|
|
789
|
-
for (const a of [...set].sort()) {
|
|
790
|
-
const o = document.createElement('option'); o.value = a; o.textContent = a;
|
|
791
|
-
sel.appendChild(o);
|
|
792
|
-
}
|
|
793
|
-
sel.value = current;
|
|
794
|
-
}
|
|
795
|
-
function populateMilestoneFilter() {
|
|
796
|
-
const set = new Set();
|
|
797
|
-
for (const list of Object.values(state.items)) for (const i of list) if (i.milestone) set.add(i.milestone);
|
|
798
|
-
const sel = document.getElementById('filterMilestone');
|
|
799
|
-
const current = sel.value;
|
|
800
|
-
sel.innerHTML = '<option value="">Any milestone</option>';
|
|
801
|
-
for (const m of [...set].sort()) {
|
|
802
|
-
const o = document.createElement('option'); o.value = m; o.textContent = m;
|
|
803
|
-
sel.appendChild(o);
|
|
804
|
-
}
|
|
805
|
-
sel.value = current;
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
function paintItems() {
|
|
809
|
-
if (!state.current) return;
|
|
810
|
-
for (const col of state.current.columns) {
|
|
811
|
-
const stack = document.querySelector(\`.stack[data-col-id="\${col.id}"]\`);
|
|
812
|
-
if (!stack) continue;
|
|
813
|
-
stack.innerHTML = '';
|
|
814
|
-
const list = state.items[col.id] || [];
|
|
815
|
-
if (list.length === 0) {
|
|
816
|
-
const empty = document.createElement('div'); empty.className = 'empty'; empty.textContent = 'Empty';
|
|
817
|
-
stack.appendChild(empty);
|
|
818
|
-
} else {
|
|
819
|
-
for (const item of list) stack.appendChild(renderCard(item, col));
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
updateCounts();
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
function renderCard(item, col) {
|
|
826
|
-
const card = document.createElement('div');
|
|
827
|
-
card.className = 'card' + (item.state === 'closed' ? ' closed' : '');
|
|
828
|
-
card.dataset.id = item.id;
|
|
829
|
-
const iid = item.id.split(':').slice(-1)[0];
|
|
830
|
-
|
|
831
|
-
// Filter out Status::* scoped labels from display (represented by the column).
|
|
832
|
-
const details = (item.labelDetails || (item.labels || []).map(n => ({ name: n })))
|
|
833
|
-
.filter(l => !/^Status::/i.test(l.name));
|
|
834
|
-
const labelChips = details.slice(0, 5).map(l => {
|
|
835
|
-
const bg = l.color || labelColor(l.name);
|
|
836
|
-
const fg = l.text_color || pickTextColor(bg);
|
|
837
|
-
return \`<span class="label" title="\${escapeHtml(l.name)}" style="background:\${bg};color:\${fg}">\${escapeHtml(l.name)}</span>\`;
|
|
838
|
-
}).join('');
|
|
839
|
-
|
|
840
|
-
const assignees = (item.assignees && item.assignees.length)
|
|
841
|
-
? item.assignees
|
|
842
|
-
: (item.assignee ? [{ username: item.assignee }] : []);
|
|
843
|
-
const avatarStack = assignees.length
|
|
844
|
-
? \`<span class="avatar-stack">\${assignees.slice(0, 3).map(renderAvatar).join('')}</span>\`
|
|
845
|
-
: \`<span class="avatar unassigned" title="unassigned">?</span>\`;
|
|
846
|
-
|
|
847
|
-
const milestone = item.milestone ? \`<span class="milestone" title="Milestone">\u{1F3C1} \${escapeHtml(item.milestone)}</span>\` : '';
|
|
848
|
-
const est = item.estimatedSeconds ? \`<span class="est" title="Estimate">\u23F1 \${fmtDuration(item.estimatedSeconds)}</span>\` : '';
|
|
849
|
-
const date = item.updatedAt ? \`<span class="date" title="Updated \${escapeHtml(item.updatedAt)}">\${fmtDate(item.updatedAt)}</span>\` : '';
|
|
850
|
-
const link = item.url ? \`<a class="link" href="\${escapeHtml(item.url)}" target="_blank" rel="noopener" title="Open in GitLab" onclick="event.stopPropagation()">\u2197</a>\` : '';
|
|
851
|
-
|
|
852
|
-
card.innerHTML = \`
|
|
853
|
-
<div class="title">\${escapeHtml(item.title || '(untitled)')}</div>
|
|
854
|
-
\${labelChips ? \`<div class="labels">\${labelChips}</div>\` : ''}
|
|
855
|
-
<div class="foot">
|
|
856
|
-
\${avatarStack}
|
|
857
|
-
<span class="iid">#\${escapeHtml(iid)}</span>
|
|
858
|
-
\${milestone}
|
|
859
|
-
\${est}
|
|
860
|
-
\${date}
|
|
861
|
-
\${link}
|
|
862
|
-
</div>
|
|
863
|
-
\`;
|
|
864
|
-
card.addEventListener('click', (e) => {
|
|
865
|
-
if (e.target.closest('a,button')) return;
|
|
866
|
-
openDetailModal(item.id);
|
|
867
|
-
});
|
|
868
|
-
return card;
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
function renderAvatar(a) {
|
|
872
|
-
const name = a.name || a.username || '?';
|
|
873
|
-
const img = a.avatarUrl ? \`<img src="\${escapeHtml(a.avatarUrl)}" alt="">\` : '';
|
|
874
|
-
const bg = labelColor(a.username || name);
|
|
875
|
-
return \`<span class="avatar" title="\${escapeHtml(name)}" style="background:\${bg}">\${img || escapeHtml(initials(name))}</span>\`;
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
function escapeHtml(s) {
|
|
879
|
-
return String(s).replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
880
|
-
}
|
|
881
|
-
function initials(s) {
|
|
882
|
-
const parts = String(s).replace(/[_\\-\\.]+/g, ' ').trim().split(/\\s+/);
|
|
883
|
-
if (!parts.length) return '?';
|
|
884
|
-
if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
|
|
885
|
-
return (parts[0][0] + parts[1][0]).toUpperCase();
|
|
886
|
-
}
|
|
887
|
-
function labelColor(s) {
|
|
888
|
-
let h = 0;
|
|
889
|
-
for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
|
|
890
|
-
return 'hsl(' + (h % 360) + ', 55%, 45%)';
|
|
891
|
-
}
|
|
892
|
-
function pickTextColor(bg) {
|
|
893
|
-
if (!bg) return '#fff';
|
|
894
|
-
if (bg.startsWith('#') && bg.length >= 7) {
|
|
895
|
-
const r = parseInt(bg.slice(1,3),16), g = parseInt(bg.slice(3,5),16), b = parseInt(bg.slice(5,7),16);
|
|
896
|
-
return (r*299 + g*587 + b*114) / 1000 >= 160 ? '#111' : '#fff';
|
|
897
|
-
}
|
|
898
|
-
return '#fff';
|
|
899
|
-
}
|
|
900
|
-
function fmtDuration(sec) {
|
|
901
|
-
if (!sec) return '';
|
|
902
|
-
const m = Math.round(sec / 60);
|
|
903
|
-
if (m < 60) return m + 'm';
|
|
904
|
-
const h = Math.floor(m / 60); const rem = m % 60;
|
|
905
|
-
return rem ? \`\${h}h\${rem}m\` : h + 'h';
|
|
906
|
-
}
|
|
907
|
-
function fmtDate(iso) {
|
|
908
|
-
const d = new Date(iso); if (Number.isNaN(d.getTime())) return '';
|
|
909
|
-
return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
910
|
-
}
|
|
911
|
-
function debounce(fn, ms) { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); }; }
|
|
912
|
-
|
|
913
|
-
document.getElementById('filterSearch').oninput = debounce(e => { state.filter.q = e.target.value; loadItems(); }, 250);
|
|
914
|
-
document.getElementById('filterAssignee').onchange = e => { state.filter.assignee = e.target.value; loadItems(); };
|
|
915
|
-
document.getElementById('filterMilestone').onchange = e => { state.filter.milestone = e.target.value; loadItems(); };
|
|
916
|
-
document.getElementById('refreshBtn').onclick = () => loadItems();
|
|
917
|
-
document.getElementById('newIssueBtn').onclick = () => openCreateModal(null);
|
|
918
|
-
document.getElementById('activityBtn').onclick = () => toggleActivity();
|
|
919
|
-
document.getElementById('activity-close').onclick = () => toggleActivity(false);
|
|
920
|
-
window.addEventListener('keydown', (e) => {
|
|
921
|
-
if (e.key === 'Escape' && document.getElementById('modal').classList.contains('open')) { closeModal(); return; }
|
|
922
|
-
if (e.target.matches('input,select,textarea')) return;
|
|
923
|
-
if (e.key === 'r') loadItems();
|
|
924
|
-
if (e.key === 'a') toggleActivity();
|
|
925
|
-
});
|
|
926
|
-
|
|
927
|
-
// --- Modal (detail view / create) ---
|
|
928
|
-
|
|
929
|
-
async function openDetailModal(itemId) {
|
|
930
|
-
openModal('edit', itemId);
|
|
931
|
-
const hint = document.getElementById('m-hint');
|
|
932
|
-
hint.textContent = 'Loading\u2026';
|
|
933
|
-
try {
|
|
934
|
-
const r = await fetch('/api/boards/' + state.current.id + '/items/' + encodeURIComponent(itemId));
|
|
935
|
-
if (!r.ok) throw new Error((await r.json().catch(()=>({}))).error || r.statusText);
|
|
936
|
-
const detail = await r.json();
|
|
937
|
-
state.modal.original = detail;
|
|
938
|
-
state.modal.draft = cloneForDraft(detail);
|
|
939
|
-
paintModal(state.modal.draft);
|
|
940
|
-
hint.textContent = '';
|
|
941
|
-
} catch (e) {
|
|
942
|
-
hint.textContent = 'Load failed: ' + e.message;
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
function openCreateModal(columnId) {
|
|
947
|
-
state.modal.targetColumnId = columnId || null;
|
|
948
|
-
const blank = {
|
|
949
|
-
id: null, title: '', description: '',
|
|
950
|
-
labels: [], labelDetails: [], assignees: [], milestone: '', state: 'opened',
|
|
951
|
-
};
|
|
952
|
-
// Pre-apply the target column's scoped label so the new issue lands in that column.
|
|
953
|
-
if (columnId && state.current) {
|
|
954
|
-
const col = state.current.columns.find(c => c.id === columnId);
|
|
955
|
-
if (col && col.kind === 'scoped-label' && col.scopedLabel) {
|
|
956
|
-
blank.labels.push(col.scopedLabel);
|
|
957
|
-
blank.labelDetails.push({ name: col.scopedLabel, color: col.accent });
|
|
958
|
-
} else if (col && col.kind === 'label' && col.mapsToLabel) {
|
|
959
|
-
blank.labels.push(col.mapsToLabel);
|
|
960
|
-
blank.labelDetails.push({ name: col.mapsToLabel, color: col.accent });
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
// Carry the board's primary label (the team filter) so created issues show up here.
|
|
964
|
-
if (state.current && state.current.primaryToolLabel && !blank.labels.includes(state.current.primaryToolLabel)) {
|
|
965
|
-
blank.labels.push(state.current.primaryToolLabel);
|
|
966
|
-
blank.labelDetails.push({ name: state.current.primaryToolLabel });
|
|
967
|
-
}
|
|
968
|
-
openModal('create', null);
|
|
969
|
-
state.modal.original = null;
|
|
970
|
-
state.modal.draft = blank;
|
|
971
|
-
paintModal(blank);
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
function openModal(mode, id) {
|
|
975
|
-
state.modal.mode = mode;
|
|
976
|
-
state.modal.id = id;
|
|
977
|
-
const m = document.getElementById('modal');
|
|
978
|
-
m.classList.add('open');
|
|
979
|
-
m.setAttribute('aria-hidden', 'false');
|
|
980
|
-
document.body.style.overflow = 'hidden';
|
|
981
|
-
document.getElementById('m-save').textContent = mode === 'create' ? 'Create' : 'Save';
|
|
982
|
-
document.getElementById('m-iid').textContent = id ? ('#' + id.split(':').slice(-1)[0]) : 'NEW';
|
|
983
|
-
// Draft box only in create mode.
|
|
984
|
-
const draftBox = document.getElementById('m-draft');
|
|
985
|
-
if (draftBox) {
|
|
986
|
-
draftBox.hidden = mode !== 'create';
|
|
987
|
-
if (mode === 'create') {
|
|
988
|
-
document.getElementById('m-rough').value = '';
|
|
989
|
-
document.getElementById('m-draft-hint').textContent = '';
|
|
990
|
-
document.getElementById('m-draft-hint').className = 'hint';
|
|
991
|
-
populateDraftAgents();
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
// Pre-focus: rough for create, description for edit.
|
|
995
|
-
setTimeout(() => {
|
|
996
|
-
const el = document.getElementById(mode === 'create' ? 'm-rough' : 'm-desc');
|
|
997
|
-
if (el) el.focus();
|
|
998
|
-
}, 50);
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
async function populateDraftAgents() {
|
|
1002
|
-
const sel = document.getElementById('m-draft-agent');
|
|
1003
|
-
if (!sel) return;
|
|
1004
|
-
if (sel.options.length > 1) {
|
|
1005
|
-
// Already populated \u2014 just sync selection.
|
|
1006
|
-
const saved = localStorage.getItem('agentx.draftAgent');
|
|
1007
|
-
if (saved) sel.value = saved;
|
|
1008
|
-
return;
|
|
1009
|
-
}
|
|
1010
|
-
try {
|
|
1011
|
-
const r = await fetch('/api/agents');
|
|
1012
|
-
if (!r.ok) return;
|
|
1013
|
-
const list = await r.json();
|
|
1014
|
-
sel.innerHTML = '<option value="">Agent\u2026</option>';
|
|
1015
|
-
for (const a of list) {
|
|
1016
|
-
const o = document.createElement('option');
|
|
1017
|
-
o.value = a.id; o.textContent = a.name + ' (' + a.tier + ')';
|
|
1018
|
-
sel.appendChild(o);
|
|
1019
|
-
}
|
|
1020
|
-
const saved = localStorage.getItem('agentx.draftAgent');
|
|
1021
|
-
if (saved && list.some(a => a.id === saved)) sel.value = saved;
|
|
1022
|
-
sel.onchange = () => localStorage.setItem('agentx.draftAgent', sel.value);
|
|
1023
|
-
} catch {}
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
async function draftWithAgent() {
|
|
1027
|
-
const rough = document.getElementById('m-rough').value.trim();
|
|
1028
|
-
const agentId = document.getElementById('m-draft-agent').value;
|
|
1029
|
-
const hint = document.getElementById('m-draft-hint');
|
|
1030
|
-
const goBtn = document.getElementById('m-draft-go');
|
|
1031
|
-
if (!rough) { hint.textContent = 'Describe the issue first.'; hint.className = 'hint err'; return; }
|
|
1032
|
-
if (!agentId) { hint.textContent = 'Pick a drafting agent.'; hint.className = 'hint err'; return; }
|
|
1033
|
-
goBtn.disabled = true;
|
|
1034
|
-
hint.textContent = 'Drafting with ' + agentId + '\u2026'; hint.className = 'hint';
|
|
1035
|
-
|
|
1036
|
-
// Build board context for convention-aware prompting.
|
|
1037
|
-
const board = state.current;
|
|
1038
|
-
const col = state.modal.targetColumnId ? board.columns.find(c => c.id === state.modal.targetColumnId) : null;
|
|
1039
|
-
const columnLabel = col && (col.scopedLabel || col.mapsToLabel);
|
|
1040
|
-
const ctx = {
|
|
1041
|
-
boardName: board.name,
|
|
1042
|
-
primaryLabel: board.primaryToolLabel,
|
|
1043
|
-
project: board.source && board.source.projects && board.source.projects[0],
|
|
1044
|
-
knownLabels: state.meta.labels || [],
|
|
1045
|
-
members: (state.meta.members || []).map(m => m.username),
|
|
1046
|
-
columnLabel: columnLabel,
|
|
1047
|
-
};
|
|
1048
|
-
|
|
1049
|
-
try {
|
|
1050
|
-
const r = await fetch('/api/draft', {
|
|
1051
|
-
method: 'POST', headers: csrfHeaders(),
|
|
1052
|
-
body: JSON.stringify({ rough, agent: agentId, context: ctx }),
|
|
1053
|
-
});
|
|
1054
|
-
if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || r.statusText);
|
|
1055
|
-
const drafted = await r.json();
|
|
1056
|
-
// Populate the existing form fields.
|
|
1057
|
-
document.getElementById('m-title').value = drafted.title || '';
|
|
1058
|
-
document.getElementById('m-desc').value = drafted.description || '';
|
|
1059
|
-
state.modal.draft.title = drafted.title || '';
|
|
1060
|
-
state.modal.draft.description = drafted.description || '';
|
|
1061
|
-
state.modal.draft.labels = Array.isArray(drafted.labels) ? drafted.labels.slice() : state.modal.draft.labels;
|
|
1062
|
-
if (drafted.assigneeUsername) {
|
|
1063
|
-
const m = state.meta.members.find(x => x.username === drafted.assigneeUsername);
|
|
1064
|
-
if (!state.modal.draft.assignees.some(a => a.username === drafted.assigneeUsername)) {
|
|
1065
|
-
state.modal.draft.assignees.push({
|
|
1066
|
-
username: drafted.assigneeUsername, name: m && m.name, avatarUrl: m && m.avatarUrl,
|
|
1067
|
-
});
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
paintModal(state.modal.draft);
|
|
1071
|
-
hint.textContent = 'Draft ready \u2014 review and edit before creating.';
|
|
1072
|
-
hint.className = 'hint ok';
|
|
1073
|
-
} catch (e) {
|
|
1074
|
-
hint.textContent = 'Draft failed: ' + e.message;
|
|
1075
|
-
hint.className = 'hint err';
|
|
1076
|
-
} finally {
|
|
1077
|
-
goBtn.disabled = false;
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
function closeModal() {
|
|
1081
|
-
const m = document.getElementById('modal');
|
|
1082
|
-
m.classList.remove('open');
|
|
1083
|
-
m.setAttribute('aria-hidden', 'true');
|
|
1084
|
-
document.body.style.overflow = '';
|
|
1085
|
-
state.modal = { mode: null, id: null, original: null, draft: null };
|
|
1086
|
-
}
|
|
1087
|
-
function cloneForDraft(d) {
|
|
1088
|
-
return {
|
|
1089
|
-
id: d.id, title: d.title || '', description: d.description || '',
|
|
1090
|
-
labels: (d.labels || []).slice(), labelDetails: (d.labelDetails || []).slice(),
|
|
1091
|
-
assignees: (d.assignees || []).slice(),
|
|
1092
|
-
milestone: d.milestone || '',
|
|
1093
|
-
state: d.state || 'opened',
|
|
1094
|
-
stage: d.stage, url: d.url, createdAt: d.createdAt, updatedAt: d.updatedAt, author: d.author,
|
|
1095
|
-
};
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
function paintModal(d) {
|
|
1099
|
-
document.getElementById('m-title').value = d.title || '';
|
|
1100
|
-
document.getElementById('m-desc').value = d.description || '';
|
|
1101
|
-
|
|
1102
|
-
document.querySelectorAll('#m-state button').forEach(b => {
|
|
1103
|
-
b.classList.toggle('active', b.dataset.state === (d.state || 'opened'));
|
|
1104
|
-
b.onclick = () => { state.modal.draft.state = b.dataset.state; paintModal(state.modal.draft); };
|
|
1105
|
-
});
|
|
1106
|
-
|
|
1107
|
-
renderChipPicker('m-labels', d.labels || [], state.meta.labels, {
|
|
1108
|
-
chipStyle: (name) => {
|
|
1109
|
-
const match = (d.labelDetails || []).find(l => l.name === name);
|
|
1110
|
-
const bg = match && match.color ? match.color : labelColor(name);
|
|
1111
|
-
return { background: bg, color: pickTextColor(bg) };
|
|
1112
|
-
},
|
|
1113
|
-
onAdd: (name) => { if (!state.modal.draft.labels.includes(name)) { state.modal.draft.labels.push(name); paintModal(state.modal.draft); } },
|
|
1114
|
-
onRemove: (name) => { state.modal.draft.labels = state.modal.draft.labels.filter(l => l !== name); paintModal(state.modal.draft); },
|
|
1115
|
-
placeholder: 'Add label\u2026',
|
|
1116
|
-
allowCustom: true,
|
|
1117
|
-
});
|
|
1118
|
-
|
|
1119
|
-
const aNames = (d.assignees || []).map(a => a.username);
|
|
1120
|
-
renderChipPicker('m-assignees', aNames, state.meta.members.map(m => m.username), {
|
|
1121
|
-
chipLabel: (u) => {
|
|
1122
|
-
const m = state.meta.members.find(x => x.username === u) || (d.assignees || []).find(x => x.username === u);
|
|
1123
|
-
return (m && m.name) || u;
|
|
1124
|
-
},
|
|
1125
|
-
onAdd: (u) => {
|
|
1126
|
-
if (state.modal.draft.assignees.some(a => a.username === u)) return;
|
|
1127
|
-
const m = state.meta.members.find(x => x.username === u);
|
|
1128
|
-
state.modal.draft.assignees.push({ username: u, name: m && m.name, avatarUrl: m && m.avatarUrl });
|
|
1129
|
-
paintModal(state.modal.draft);
|
|
1130
|
-
},
|
|
1131
|
-
onRemove: (u) => { state.modal.draft.assignees = state.modal.draft.assignees.filter(a => a.username !== u); paintModal(state.modal.draft); },
|
|
1132
|
-
placeholder: 'Assign\u2026',
|
|
1133
|
-
});
|
|
1134
|
-
|
|
1135
|
-
const ms = document.getElementById('m-milestone');
|
|
1136
|
-
ms.innerHTML = '<option value="">None</option>';
|
|
1137
|
-
for (const m of state.meta.milestones) {
|
|
1138
|
-
const o = document.createElement('option');
|
|
1139
|
-
o.value = m.title; o.textContent = m.title + (m.state === 'closed' ? ' (closed)' : '');
|
|
1140
|
-
if (m.title === d.milestone) o.selected = true;
|
|
1141
|
-
ms.appendChild(o);
|
|
1142
|
-
}
|
|
1143
|
-
if (d.milestone && !state.meta.milestones.some(m => m.title === d.milestone)) {
|
|
1144
|
-
const o = document.createElement('option'); o.value = d.milestone; o.textContent = d.milestone; o.selected = true;
|
|
1145
|
-
ms.appendChild(o);
|
|
1146
|
-
}
|
|
1147
|
-
ms.onchange = () => { state.modal.draft.milestone = ms.value; };
|
|
1148
|
-
|
|
1149
|
-
document.getElementById('m-author').textContent = (d.author && (d.author.name || d.author.username)) || '\u2014';
|
|
1150
|
-
document.getElementById('m-created').textContent = d.createdAt ? fmtDate(d.createdAt) : '\u2014';
|
|
1151
|
-
document.getElementById('m-updated').textContent = d.updatedAt ? fmtDate(d.updatedAt) : '\u2014';
|
|
1152
|
-
const link = document.getElementById('m-link');
|
|
1153
|
-
if (d.url) { link.href = d.url; link.style.display = ''; } else { link.style.display = 'none'; }
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
function renderChipPicker(elId, selected, options, opts) {
|
|
1157
|
-
const el = document.getElementById(elId);
|
|
1158
|
-
el.innerHTML = '';
|
|
1159
|
-
for (const name of selected) {
|
|
1160
|
-
const chip = document.createElement('span');
|
|
1161
|
-
chip.className = 'chip';
|
|
1162
|
-
chip.textContent = opts.chipLabel ? opts.chipLabel(name) : name;
|
|
1163
|
-
if (opts.chipStyle) {
|
|
1164
|
-
const st = opts.chipStyle(name);
|
|
1165
|
-
if (st.background) chip.style.background = st.background;
|
|
1166
|
-
if (st.color) chip.style.color = st.color;
|
|
1167
|
-
}
|
|
1168
|
-
const x = document.createElement('span');
|
|
1169
|
-
x.className = 'remove'; x.textContent = '\u2715'; x.title = 'Remove';
|
|
1170
|
-
x.onclick = (e) => { e.stopPropagation(); opts.onRemove(name); };
|
|
1171
|
-
chip.appendChild(x);
|
|
1172
|
-
el.appendChild(chip);
|
|
1173
|
-
}
|
|
1174
|
-
const dd = document.createElement('span');
|
|
1175
|
-
dd.className = 'dropdown';
|
|
1176
|
-
const btn = document.createElement('button');
|
|
1177
|
-
btn.className = 'add-chip'; btn.textContent = '+ ' + (opts.placeholder || 'Add');
|
|
1178
|
-
btn.type = 'button';
|
|
1179
|
-
btn.onclick = (e) => {
|
|
1180
|
-
e.stopPropagation();
|
|
1181
|
-
let menu = dd.querySelector('.menu');
|
|
1182
|
-
if (menu) { menu.remove(); return; }
|
|
1183
|
-
menu = document.createElement('div');
|
|
1184
|
-
menu.className = 'menu';
|
|
1185
|
-
const search = document.createElement('input');
|
|
1186
|
-
search.className = 'search'; search.type = 'search'; search.placeholder = opts.placeholder || 'Search\u2026';
|
|
1187
|
-
menu.appendChild(search);
|
|
1188
|
-
const list = document.createElement('div');
|
|
1189
|
-
menu.appendChild(list);
|
|
1190
|
-
const paintList = (q) => {
|
|
1191
|
-
list.innerHTML = '';
|
|
1192
|
-
const ql = (q || '').toLowerCase();
|
|
1193
|
-
const filtered = options.filter(o => o.toLowerCase().includes(ql) && !selected.includes(o));
|
|
1194
|
-
if (filtered.length === 0 && !(opts.allowCustom && q)) {
|
|
1195
|
-
const empty = document.createElement('div'); empty.className = 'empty'; empty.textContent = 'No matches';
|
|
1196
|
-
list.appendChild(empty);
|
|
1197
|
-
}
|
|
1198
|
-
for (const o of filtered.slice(0, 50)) {
|
|
1199
|
-
const opt = document.createElement('div');
|
|
1200
|
-
opt.className = 'opt'; opt.textContent = opts.chipLabel ? opts.chipLabel(o) : o;
|
|
1201
|
-
opt.onclick = () => { opts.onAdd(o); menu.remove(); };
|
|
1202
|
-
list.appendChild(opt);
|
|
1203
|
-
}
|
|
1204
|
-
if (opts.allowCustom && q && !options.includes(q) && !selected.includes(q)) {
|
|
1205
|
-
const opt = document.createElement('div');
|
|
1206
|
-
opt.className = 'opt'; opt.textContent = 'Create "' + q + '"';
|
|
1207
|
-
opt.onclick = () => { opts.onAdd(q); menu.remove(); };
|
|
1208
|
-
list.appendChild(opt);
|
|
1209
|
-
}
|
|
1210
|
-
};
|
|
1211
|
-
search.oninput = (ev) => paintList(ev.target.value);
|
|
1212
|
-
paintList('');
|
|
1213
|
-
dd.appendChild(menu);
|
|
1214
|
-
setTimeout(() => search.focus(), 0);
|
|
1215
|
-
const away = (ev) => {
|
|
1216
|
-
if (!dd.contains(ev.target)) { menu.remove(); document.removeEventListener('click', away); }
|
|
1217
|
-
};
|
|
1218
|
-
setTimeout(() => document.addEventListener('click', away), 0);
|
|
1219
|
-
};
|
|
1220
|
-
dd.appendChild(btn);
|
|
1221
|
-
el.appendChild(dd);
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
async function saveModal() {
|
|
1225
|
-
const saveBtn = document.getElementById('m-save');
|
|
1226
|
-
const hint = document.getElementById('m-hint');
|
|
1227
|
-
saveBtn.disabled = true;
|
|
1228
|
-
hint.textContent = 'Saving\u2026';
|
|
1229
|
-
const d = state.modal.draft;
|
|
1230
|
-
d.title = document.getElementById('m-title').value.trim();
|
|
1231
|
-
d.description = document.getElementById('m-desc').value;
|
|
1232
|
-
if (!d.title) { hint.textContent = 'Title required'; saveBtn.disabled = false; return; }
|
|
1233
|
-
|
|
1234
|
-
try {
|
|
1235
|
-
if (state.modal.mode === 'create') {
|
|
1236
|
-
const r = await fetch('/api/boards/' + state.current.id + '/items', {
|
|
1237
|
-
method: 'POST', headers: csrfHeaders(),
|
|
1238
|
-
body: JSON.stringify({
|
|
1239
|
-
title: d.title, description: d.description, labels: d.labels,
|
|
1240
|
-
assigneeUsernames: (d.assignees || []).map(a => a.username),
|
|
1241
|
-
milestoneTitle: d.milestone || undefined,
|
|
1242
|
-
}),
|
|
1243
|
-
});
|
|
1244
|
-
if (!r.ok) throw new Error((await r.json().catch(()=>({}))).error || r.statusText);
|
|
1245
|
-
toast('Created \u2713', 'ok');
|
|
1246
|
-
} else {
|
|
1247
|
-
const orig = state.modal.original || {};
|
|
1248
|
-
const patch = {};
|
|
1249
|
-
if (d.title !== (orig.title || '')) patch.title = d.title;
|
|
1250
|
-
if (d.description !== (orig.description || '')) patch.description = d.description;
|
|
1251
|
-
const origLabels = (orig.labels || []).slice().sort().join('|');
|
|
1252
|
-
const newLabels = (d.labels || []).slice().sort().join('|');
|
|
1253
|
-
if (origLabels !== newLabels) patch.labels = d.labels;
|
|
1254
|
-
const origA = (orig.assignees || []).map(a => a.username).sort().join('|');
|
|
1255
|
-
const newA = (d.assignees || []).map(a => a.username).sort().join('|');
|
|
1256
|
-
if (origA !== newA) patch.assigneeUsernames = (d.assignees || []).map(a => a.username);
|
|
1257
|
-
if (d.milestone !== (orig.milestone || '')) patch.milestoneTitle = d.milestone || null;
|
|
1258
|
-
|
|
1259
|
-
if (Object.keys(patch).length > 0) {
|
|
1260
|
-
const r = await fetch('/api/boards/' + state.current.id + '/items/' + encodeURIComponent(state.modal.id), {
|
|
1261
|
-
method: 'PATCH', headers: csrfHeaders(), body: JSON.stringify(patch),
|
|
1262
|
-
});
|
|
1263
|
-
if (!r.ok) throw new Error((await r.json().catch(()=>({}))).error || r.statusText);
|
|
1264
|
-
}
|
|
1265
|
-
// State change \u2192 move to the appropriate column (reuses transition logic on server).
|
|
1266
|
-
if (d.state !== (orig.state || 'opened')) {
|
|
1267
|
-
const fromCol = state.current.columns.find(c => c.id === orig.stage);
|
|
1268
|
-
const toCol = d.state === 'closed'
|
|
1269
|
-
? state.current.columns.find(c => c.kind === 'closed')
|
|
1270
|
-
: state.current.columns.find(c => c.kind === 'open-backlog');
|
|
1271
|
-
if (toCol) {
|
|
1272
|
-
await fetch('/api/boards/' + state.current.id + '/items/' + encodeURIComponent(state.modal.id) + '/move', {
|
|
1273
|
-
method: 'PATCH', headers: csrfHeaders(),
|
|
1274
|
-
body: JSON.stringify({ from: fromCol && fromCol.id, to: toCol.id }),
|
|
1275
|
-
});
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
toast('Saved \u2713', 'ok');
|
|
1279
|
-
}
|
|
1280
|
-
closeModal();
|
|
1281
|
-
await loadItems();
|
|
1282
|
-
state.meta.labels = collectKnownLabels();
|
|
1283
|
-
} catch (e) {
|
|
1284
|
-
hint.textContent = 'Save failed: ' + e.message;
|
|
1285
|
-
} finally {
|
|
1286
|
-
saveBtn.disabled = false;
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
document.getElementById('m-close').onclick = closeModal;
|
|
1291
|
-
document.getElementById('m-cancel').onclick = closeModal;
|
|
1292
|
-
document.getElementById('m-save').onclick = saveModal;
|
|
1293
|
-
document.querySelector('#modal .modal-backdrop').onclick = closeModal;
|
|
1294
|
-
document.getElementById('m-title').addEventListener('keydown', (e) => {
|
|
1295
|
-
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) saveModal();
|
|
1296
|
-
});
|
|
1297
|
-
const draftGo = document.getElementById('m-draft-go');
|
|
1298
|
-
if (draftGo) draftGo.onclick = draftWithAgent;
|
|
1299
|
-
const roughEl = document.getElementById('m-rough');
|
|
1300
|
-
if (roughEl) roughEl.addEventListener('keydown', (e) => {
|
|
1301
|
-
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) draftWithAgent();
|
|
1302
|
-
});
|
|
1303
|
-
|
|
1304
|
-
// --- Agent activity panel (live polling of the daemon API) ---
|
|
1305
|
-
|
|
1306
|
-
const activity = {
|
|
1307
|
-
open: false, timer: null, daemonUrl: null, lastEventAt: 0, eventsRing: [],
|
|
1308
|
-
};
|
|
1309
|
-
|
|
1310
|
-
function resolveDaemonUrl() {
|
|
1311
|
-
// Allow ?daemon=http://host:port override; fall back to localhost:18800 (MacBook) then 19900 (clawd).
|
|
1312
|
-
const q = new URLSearchParams(window.location.search).get('daemon');
|
|
1313
|
-
if (q) return q.replace(/\\/+$/, '');
|
|
1314
|
-
return localStorage.getItem('agentx.daemon') || 'http://localhost:18800';
|
|
1315
|
-
}
|
|
1316
|
-
|
|
1317
|
-
function toggleActivity(force) {
|
|
1318
|
-
activity.open = typeof force === 'boolean' ? force : !activity.open;
|
|
1319
|
-
const panel = document.getElementById('activity-panel');
|
|
1320
|
-
const btn = document.getElementById('activityBtn');
|
|
1321
|
-
panel.classList.toggle('hidden', !activity.open);
|
|
1322
|
-
panel.setAttribute('aria-hidden', activity.open ? 'false' : 'true');
|
|
1323
|
-
btn.classList.toggle('on', activity.open);
|
|
1324
|
-
document.body.classList.toggle('panel-open', activity.open);
|
|
1325
|
-
if (activity.open) startActivity(); else stopActivity();
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
async function startActivity() {
|
|
1329
|
-
activity.daemonUrl = resolveDaemonUrl();
|
|
1330
|
-
document.getElementById('activity-source').textContent = activity.daemonUrl.replace(/^https?:\\/\\//, '');
|
|
1331
|
-
await tickActivity();
|
|
1332
|
-
activity.timer = setInterval(tickActivity, 3000);
|
|
1333
|
-
}
|
|
1334
|
-
function stopActivity() {
|
|
1335
|
-
if (activity.timer) { clearInterval(activity.timer); activity.timer = null; }
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
async function tickActivity() {
|
|
1339
|
-
const body = document.getElementById('activity-body');
|
|
1340
|
-
try {
|
|
1341
|
-
// Fetch /agents (always present) + /business/status (optional \u2014 404 tolerated)
|
|
1342
|
-
const [agentsRes, bizRes] = await Promise.all([
|
|
1343
|
-
fetch(activity.daemonUrl + '/agents').catch(() => null),
|
|
1344
|
-
fetch(activity.daemonUrl + '/business/status').catch(() => null),
|
|
1345
|
-
]);
|
|
1346
|
-
if (!agentsRes || !agentsRes.ok) throw new Error('daemon unreachable at ' + activity.daemonUrl);
|
|
1347
|
-
const agents = await agentsRes.json();
|
|
1348
|
-
const biz = bizRes && bizRes.ok ? await bizRes.json() : null;
|
|
1349
|
-
|
|
1350
|
-
// Merge business data (currentItem, onClock, lastReport) per-agent when available.
|
|
1351
|
-
const bizByAgent = {};
|
|
1352
|
-
if (biz && Array.isArray(biz.employees)) for (const e of biz.employees) bizByAgent[e.agentId || e.id] = e;
|
|
1353
|
-
|
|
1354
|
-
body.innerHTML = '';
|
|
1355
|
-
for (const a of agents) {
|
|
1356
|
-
const b = bizByAgent[a.id];
|
|
1357
|
-
body.appendChild(renderAgentCard(a, b));
|
|
1358
|
-
}
|
|
1359
|
-
if (agents.length === 0) {
|
|
1360
|
-
body.innerHTML = '<div class="empty">No agents on this daemon.</div>';
|
|
1361
|
-
}
|
|
1362
|
-
// Events ring
|
|
1363
|
-
if (biz && biz.recentEvents) appendEvents(biz.recentEvents);
|
|
1364
|
-
} catch (e) {
|
|
1365
|
-
body.innerHTML = '<div class="empty" style="color:var(--red)">' + escapeHtml(e.message) + '</div>';
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
function renderAgentCard(a, biz) {
|
|
1370
|
-
const card = document.createElement('div');
|
|
1371
|
-
card.className = 'agent-card';
|
|
1372
|
-
const active = (a.active || 0) > 0 || (biz && biz.onClock && biz.busy);
|
|
1373
|
-
const dotClass = (a.errors || 0) > 0 ? 'errored' : (active ? 'active' : '');
|
|
1374
|
-
const current = biz && biz.currentItem
|
|
1375
|
-
? '<div class="current">Working on <b>' + escapeHtml(biz.currentItem.title || biz.currentItem.id) + '</b></div>'
|
|
1376
|
-
: (active ? '<div class="current"><b>' + a.active + ' active task' + (a.active === 1 ? '' : 's') + '</b></div>' : '');
|
|
1377
|
-
const modelLabel = a.model ? '<span class="model" title="Model">' + escapeHtml(shortenModelName(a.model)) + '</span>' : '';
|
|
1378
|
-
const lastLabel = a.lastActive
|
|
1379
|
-
? '<span class="last" title="' + escapeHtml(new Date(a.lastActive).toLocaleString()) + '">last ' + escapeHtml(fmtAgoBoard(a.lastActive)) + '</span>'
|
|
1380
|
-
: '<span class="last muted">never ran</span>';
|
|
1381
|
-
card.innerHTML =
|
|
1382
|
-
'<div class="top">' +
|
|
1383
|
-
'<span class="dot ' + dotClass + '"></span>' +
|
|
1384
|
-
'<span class="name">' + escapeHtml(a.name || a.id) + '</span>' +
|
|
1385
|
-
'<span class="tier">' + escapeHtml(a.tier || '') + '</span>' +
|
|
1386
|
-
'</div>' +
|
|
1387
|
-
'<div class="meta">' + modelLabel + lastLabel + '</div>' +
|
|
1388
|
-
'<div class="stats">' +
|
|
1389
|
-
'<span>Active <b>' + (a.active || 0) + '</b></span>' +
|
|
1390
|
-
'<span>Total <b>' + (a.total || 0) + '</b></span>' +
|
|
1391
|
-
'<span class="errors">Errors <b>' + (a.errors || 0) + '</b></span>' +
|
|
1392
|
-
'</div>' + current;
|
|
1393
|
-
return card;
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
function shortenModelName(m) {
|
|
1397
|
-
if (!m) return '';
|
|
1398
|
-
return String(m)
|
|
1399
|
-
.replace(/^claude-/, '')
|
|
1400
|
-
.replace(/-\\d{8}$/, '')
|
|
1401
|
-
.replace(/\\[1m\\]$/, ' \xB7 1M');
|
|
1402
|
-
}
|
|
1403
|
-
|
|
1404
|
-
function fmtAgoBoard(iso) {
|
|
1405
|
-
const t = new Date(iso).getTime();
|
|
1406
|
-
if (!t) return '\u2014';
|
|
1407
|
-
const s = Math.floor((Date.now() - t) / 1000);
|
|
1408
|
-
if (s < 60) return s + 's ago';
|
|
1409
|
-
const m = Math.floor(s / 60); if (m < 60) return m + 'm ago';
|
|
1410
|
-
const h = Math.floor(m / 60); if (h < 24) return h + 'h ago';
|
|
1411
|
-
const d = Math.floor(h / 24); return d + 'd ago';
|
|
1412
|
-
}
|
|
1413
|
-
|
|
1414
|
-
function appendEvents(events) {
|
|
1415
|
-
const holder = document.getElementById('activity-events');
|
|
1416
|
-
const existingKeys = new Set([...holder.querySelectorAll('.evt')].map(e => e.dataset.key));
|
|
1417
|
-
for (const ev of events.slice(-50)) {
|
|
1418
|
-
const key = (ev.ts || '') + '|' + (ev.type || '') + '|' + (ev.agentId || '');
|
|
1419
|
-
if (existingKeys.has(key)) continue;
|
|
1420
|
-
const div = document.createElement('div');
|
|
1421
|
-
div.className = 'evt' + (ev.type && ev.type.includes('error') ? ' err' : '');
|
|
1422
|
-
div.dataset.key = key;
|
|
1423
|
-
div.innerHTML = '<span class="ts">' + (ev.ts ? new Date(ev.ts).toLocaleTimeString() : '') + '</span> ' +
|
|
1424
|
-
'<span class="who">' + escapeHtml(ev.agentId || '\u2014') + '</span> ' +
|
|
1425
|
-
'<span class="what">' + escapeHtml(ev.summary || ev.type || '') + '</span>';
|
|
1426
|
-
holder.appendChild(div);
|
|
1427
|
-
}
|
|
1428
|
-
// Keep last 50 entries
|
|
1429
|
-
while (holder.children.length > 50) holder.removeChild(holder.firstChild);
|
|
1430
|
-
holder.scrollTop = holder.scrollHeight;
|
|
1431
|
-
}
|
|
1432
|
-
|
|
1433
|
-
loadBoards().catch(e => toast('Init failed: ' + e.message));
|
|
1434
|
-
`;export{st as startBoardDashboard};
|
|
1435
|
-
//# sourceMappingURL=board-dashboard-6TKE6NMV.js.map
|