@starlove/orbit 1.2.0 → 1.3.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/Dockerfile +2 -2
- package/README.md +16 -17
- package/SKILL-ORBIT.md +1 -1
- package/dist/full/index.html +225 -29
- package/dist/full/js/app.b8296a78.js +742 -0
- package/dist/full/js/app.b8296a78.js.map +7 -0
- package/dist/full/styles.e2284569.css +1 -0
- package/dist/full/version.json +2 -2
- package/docs/MCP_SETUP.md +6 -5
- package/docs/setup/orbit-init-wizard.md +3 -3
- package/package.json +3 -3
- package/public/index.html +223 -27
- package/public/js/app.js +4 -1
- package/public/js/board-menu.js +21 -8
- package/public/js/help-menu.js +31 -0
- package/public/js/main.js +4 -0
- package/public/js/settings.js +95 -3
- package/public/js/ticket-detail.js +240 -93
- package/public/js/topbar-notes.js +155 -0
- package/public/styles.css +220 -487
- package/src/cli/orbit.js +48 -10
- package/src/core/agents-md.js +15 -0
- package/src/core/board.js +6 -3
- package/src/core/boards.js +54 -11
- package/src/core/db.js +13 -0
- package/src/core/migrate.js +1 -1
- package/src/core/paths.js +1 -1
- package/src/core/registry.js +4 -3
- package/src/server.js +24 -2
- package/dist/full/js/app.ccc442f7.js +0 -706
- package/dist/full/js/app.ccc442f7.js.map +0 -7
- package/dist/full/styles.baccc8e3.css +0 -1
|
@@ -1,706 +0,0 @@
|
|
|
1
|
-
var Nn=Object.defineProperty;var C=(e,t)=>()=>(e&&(t=e(e=0)),t);var On=(e,t)=>{for(var a in t)Nn(e,a,{get:t[a],enumerable:!0})};var y,le,V,T,U,k,ye,Te,Nt,ve,st,de,Ta,Ot,ne=C(()=>{y=e=>document.querySelector(e),le=y("#boardMenuBtn"),V=y("#boardFlyout"),T=y("#kanban"),U=y("#drawer"),k=y("#drawerInner"),ye=y("#drawerBackdrop"),Te=y("#createFlyout"),Nt=y("#createFlyoutInner"),ve=y("#createFlyoutBackdrop"),st=y("#searchInput"),de=y("#searchResults"),Ta=y("#themeToggle"),Ot=y("#themeIcon")});function _e(e){try{return e??globalThis.localStorage??null}catch{return null}}function jt(e){return typeof window<"u"&&typeof window.matchMedia=="function"?window.matchMedia(e):{matches:!1}}function ct(e=_e()){let a=_e(e)?.getItem?.(_a);return a==="true"?!0:a==="false"?!1:null}function Ht(e=jt){try{return!!e("(prefers-reduced-motion: reduce)")?.matches}catch{return!1}}function Ce({storage:e=_e(),mediaMatcher:t=jt}={}){return ct(e)??Ht(t)}function Ca(e,t=_e()){let a=_e(t),n=e?"true":"false";return a?.setItem?.(_a,n),e}function ze({doc:e=globalThis.document,storage:t=_e(),mediaMatcher:a=jt}={}){let n=e?.documentElement,r=ct(t);return n&&(r===!0?n.setAttribute("data-motion","reduce"):r===!1?n.setAttribute("data-motion","full"):n.removeAttribute("data-motion")),Ce({storage:t,mediaMatcher:a})}var _a,qt=C(()=>{_a="mab_reduce_motion"});function Ie(e){try{return e??globalThis.localStorage??null}catch{return null}}function Me(e){try{return e??globalThis.document??null}catch{return null}}function Ut(e){try{return e??globalThis.window??null}catch{return null}}function jn(e){return zt.has(e)?e:"light"}function Ma(e=Ie()){let a=Ie(e)?.getItem?.(Ia);return zt.has(a)?a:null}function ue({doc:e=Me(),storage:t=Ie()}={}){let a=Me(e)?.documentElement?.getAttribute?.("data-theme");return zt.has(a)?a:Ma(t)??"light"}function Pa({doc:e=Me(),storage:t=Ie()}={}){let a=Ma(t);return a&&Me(e)?.documentElement?.setAttribute?.("data-theme",a),ue({doc:e,storage:t})}function Gt(e,{doc:t=Me(),storage:a=Ie(),eventTarget:n=Ut(),notify:r=!0}={}){let o=jn(e);return Me(t)?.documentElement?.setAttribute?.("data-theme",o),Ie(a)?.setItem?.(Ia,o),r&&Hn(o,n),o}function Aa(e={}){let t=ue(e)==="dark"?"light":"dark";return Gt(t,e)}function Hn(e,t=Ut()){let a=Ut(t);if(typeof a?.dispatchEvent=="function"){if(typeof CustomEvent=="function"){a.dispatchEvent(new CustomEvent(lt,{detail:{theme:e}}));return}a.dispatchEvent({type:lt,detail:{theme:e}})}}var Ia,lt,zt,Jt=C(()=>{Ia="mab_theme",lt="orbit:theme-change",zt=new Set(["light","dark"])});function Ge(e){return!e||!dt.has(e)?"lanes":e}var dt,ut=C(()=>{dt=new Set(["lanes","appearance","ai","notes","journal","repository","archive"])});function Da(){return Ge(c.settingsTab||localStorage.getItem("mab_settings_tab"))}function Pe(){if(!c.data)return;let e=c.data.boards||[],t=c.boardId?e.find(a=>a.id===c.boardId):null;if(!t&&c.boardSlug&&(t=e.find(a=>a.slug===c.boardSlug)||e.find(a=>a.id===c.boardSlug)),!t){let a=c.data.active_board_id;t=e.find(n=>n.id===a)||e[0]||null}c.boardId=t?.id||"",c.boardSlug=t?.slug||""}function Y(){return(c.data?.boards||[]).find(e=>e.id===c.boardId)}function Fa(){return c.boardId?(c.data?.labels||[]).filter(e=>e.board_id===c.boardId):[]}function Je(){return c.data.states.filter(e=>e.board_id===c.boardId)}function Na(e){return e?.role==="ai_ready"||e?.name==="AI Ready"}function Ae(){let e=Je();return Y()?.ai_enabled!==0?e:e.filter(t=>!Na(t))}function Oa(e=""){let t=Je();return Y()?.ai_enabled!==0?t:t.filter(a=>!Na(a)||a.id===e)}function W(){return c.data.tickets.filter(e=>e.board_id===c.boardId)}function Kn(e){return e?{id:e.id,board_id:e.board_id,board_slug:e.board_slug,number:e.number,title:e.title,type:e.type,state_name:e.state_name,state_role:e.state_role,priority:e.priority}:null}function Yn(e){if(!c.data?.tickets)return;let t=c.data.tickets.filter(r=>r.board_id===e&&!r.archived_at),a=new Map(t.map(r=>[r.id,r])),n=new Map;for(let r of t)r.parent_ticket_id&&n.set(r.parent_ticket_id,(n.get(r.parent_ticket_id)||0)+1);for(let r of t)r.parent_ticket=r.parent_ticket_id?Kn(a.get(r.parent_ticket_id)):null,r.child_count=n.get(r.id)||0}function ja(e){if(!c.data?.tickets||!e?.id)return;let t=c.data.tickets.findIndex(r=>r.id===e.id),a=t>=0?c.data.tickets[t]:{},n={...a,...e,labels:e.labels||a.labels||[],comment_count:e.comment_count??a.comment_count??0,child_count:e.child_count??a.child_count??0,parent_ticket:e.parent_ticket||a.parent_ticket||null};t>=0?c.data.tickets.splice(t,1,n):c.data.tickets.push(n),c.data.tickets.sort((r,o)=>String(o.updated_at||"").localeCompare(String(r.updated_at||""))||String(r.id).localeCompare(String(o.id))),Yn(n.board_id||c.boardId)}var c,z=C(()=>{ut();c={data:null,boardId:"",boardSlug:"",selectedTicketId:"",expandedCardIds:new Set,detailMode:"empty",settingsTab:Ge(localStorage.getItem("mab_settings_tab")),showPriority:localStorage.getItem("mab_show_priority")!=="false",eventSource:null}});function l(e){return String(e??"").replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function Xn(e){if(!e)return e;let t=e;for(let[a,n]of Wn)t=t.split(a).join(n);return t}function ie(e){return e?Xn(String(e)).replace(/\r\n?/g,`
|
|
2
|
-
`):""}function we(e){return l(ie(e))}function Qn(e){let t=String(e||"").trim();if(!t)return"";let a=t.replace(/[\u0000-\u001f\u007f\s]+/g,"").toLowerCase();if(/^(?:javascript|data|vbscript):/.test(a))return"";let n=a.match(/^([a-z][a-z0-9+.-]*):/);return n&&!Vn.has(`${n[1]}:`)?"":t}function Yt(e){let t=l(e);return t=t.replace(/(\*\*|__)(?=\S)([\s\S]*?\S)\1/g,"<strong>$2</strong>"),t=t.replace(/(\*|_)(?=\S)([^*_]*?\S)\1/g,"<em>$2</em>"),t}function Wt(e){let t=/\[([^\]\n]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g,a="",n=0,r;for(;r=t.exec(e);){a+=Yt(e.slice(n,r.index));let o=Yt(r[1]),i=Qn(r[2]);a+=i?`<a href="${l(i)}" target="_blank" rel="noopener noreferrer">${o}</a>`:o,n=r.index+r[0].length}return a+=Yt(e.slice(n)),a}function pt(e){let t=String(e??""),a="",n=0;for(;n<t.length;){let r=t.indexOf("`",n);if(r===-1){a+=Wt(t.slice(n));break}let o=t.indexOf("`",r+1);if(o===-1){a+=Wt(t.slice(n));break}a+=Wt(t.slice(n,r)),a+=`<code>${l(t.slice(r+1,o))}</code>`,n=o+1}return a}function Zn(e){return`<p>${pt(e.join(`
|
|
3
|
-
`)).replaceAll(`
|
|
4
|
-
`,"<br>")}</p>`}function er(e){let t=e.match(/^\[( |x|X)\]\s+(.*)$/);if(!t)return null;let a=t[1].toLowerCase()==="x",n=pt(t[2].trim()).replaceAll(`
|
|
5
|
-
`,"<br>");return{checked:a,html:`<input type="checkbox" disabled${a?" checked":""}> ${n}`}}function Xt(e){let t=ie(e);if(!t.trim())return"";let a=[],n=t.split(`
|
|
6
|
-
`),r=[],o=null,i=null,s=()=>{r.length&&(a.push(Zn(r)),r=[])},u=()=>{if(!o)return;let p=o.taskList?' class="task-list"':"";a.push(`<${o.type}${p}>${o.items.map(f=>`<li${f.taskClass?` class="${f.taskClass}"`:""}>${f.html}</li>`).join("")}</${o.type}>`),o=null},d=()=>{i&&(a.push(`<blockquote>${Xt(i.join(`
|
|
7
|
-
`))}</blockquote>`),i=null)},m=()=>{s(),u(),d()};for(let p=0;p<n.length;p+=1){let f=n[p];if(/^\s*```/.test(f)){m();let A=[];for(p+=1;p<n.length&&!/^\s*```\s*$/.test(n[p]);)A.push(n[p]),p+=1;a.push(`<pre><code>${l(A.join(`
|
|
8
|
-
`))}</code></pre>`);continue}if(/^\s*(?:-\s*){3,}\s*$/.test(f)||/^\s*(?:\*\s*){3,}\s*$/.test(f)||/^\s*(?:_\s*){3,}\s*$/.test(f)){m(),a.push("<hr>");continue}let h=f.match(/^\s{0,3}(#{1,6})\s+(.+?)\s*#*\s*$/);if(h){m();let A=h[1].length;a.push(`<h${A}>${pt(h[2])}</h${A}>`);continue}let w=f.match(/^\s{0,3}>\s?(.*)$/);if(w){s(),u(),i||(i=[]),i.push(w[1]);continue}if(i&&!f.trim()){d();continue}if(d(),!f.trim()){s(),u();continue}let $=f.match(/^\s*\d+[.)]\s+(.+)$/),M=f.match(/^\s*[-*+]\s+(.+)$/);if($||M){s();let A=$?"ol":"ul",te=($||M)[1].trim(),B=null;M&&(B=er(te)),(!o||o.type!==A||!!o.taskList!=!!B)&&(u(),o={type:A,items:[],taskList:!!B}),B?o.items.push({html:B.html,taskClass:B.checked?"task-item task-item-done":"task-item"}):o.items.push({html:pt(te).replaceAll(`
|
|
9
|
-
`,"<br>")});continue}u(),r.push(f)}return m(),a.join("")}function Ke(e){return e?new Intl.DateTimeFormat(void 0,{month:"short",day:"numeric",hour:"numeric",minute:"2-digit"}).format(new Date(e)):""}function Ha(e){return e?new Intl.DateTimeFormat(void 0,{year:"numeric",month:"short",day:"numeric",hour:"numeric",minute:"2-digit",second:"2-digit"}).format(new Date(e)):""}function Q(e){return`#${e.number}`}function Ye(e){let t=e?.state_role||e?.role||e?.state_name||e?.name||"state";return String(t).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,"")||"state"}function N(e){return e==="epic"||e==="feature"||e==="bug"||e==="task"?e:"task"}function Be(e){return{epic:"Epic",feature:"Feature",task:"Task",bug:"Bug"}[N(e)]||"Task"}function qa(e){return{epic:"Epic",feature:"Feature",task:"Task",bug:"Bug"}[N(e)]||"Task"}function Re(e){return{0:"MAYBE",1:"LOW",2:"MED",3:"HIGH",4:"URGENT"}[Number(e)]||"MED"}function De(e){let t=["maybe","low","med","high","urgent"],a=Number(e);return t[Number.isFinite(a)?Math.max(0,Math.min(4,a)):2]}function Ua(e=[]){return e.length?`
|
|
10
|
-
<div class="labels">
|
|
11
|
-
${e.map(t=>`<span class="label" style="--label-color: ${l(t.color)}">${l(t.name)}</span>`).join("")}
|
|
12
|
-
</div>
|
|
13
|
-
`:""}function za(e){let t=String(e||"").trim();if(!t)return"Not set";let a=t.match(/github\.com\/([^/\s]+)\/([^/\s]+?)(?:\.git)?$/i);if(a)return`${a[1]}/${a[2]}`;let n=t.match(/github\.com[:/]([^/\s]+)\/([^/\s]+?)(?:\.git)?$/i);return n?`${n[1]}/${n[2]}`:t}var Wn,Vn,re=C(()=>{Wn=[["\u0393\xC7\xF4","\u2013"],["\u0393\xC7\xF6","\u2014"],["\u0393\xC7\xA3",'"'],["\u0393\xC7\xA5",'"'],["\u0393\xC7\xFF","'"],["\u0393\xC7\xD6","'"],["\u0393\xC7\xAA","\u2026"],["\u0393\xC7\xF3","\u2022"],["\u0393\xC7\u2591","\u2030"],["\u0393\xC7\u2563","\u2039"],["\u0393\xC7\u2551","\u203A"],["\u0393\xF6\xC7","\u2500"],["\u0393\xF6\xE9","\u2502"],["\u0393\xF6\xEE","\u250C"],["\u0393\xF6\xC9","\u2510"],["\u0393\xF6\xF6","\u2514"],["\u0393\xF6\xFF","\u2518"],["\u0393\xF6\xA3","\u251C"],["\u0393\xF6\xF1","\u2524"],["\u0393\xF6\xBC","\u252C"],["\u0393\xF6\u2524","\u2534"],["\u0393\xF6\u255D","\u253C"],["\u0393\xF2\xC9","\u2550"],["\u0393\xF2\xE6","\u2551"],["\u0393\xF2\xC6","\u2552"],["\u0393\xF2\xF6","\u2554"],["\u0393\xF2\xA1","\u256D"],["\u0393\xF2\xAB","\u256E"],["\u0393\xF2\xBB","\u256F"],["\u0393\xF2\u2591","\u2570"],["\u0393\xFB\xE6","\u2591"],["\u0393\xFB\xC6","\u2592"],["\u0393\xFB\xF4","\u2593"],["\u0393\xFB\xC9","\u2590"],["\u0393\xFB\xEE","\u258C"],["\u0393\xFB\xC4",">"],["\u0393\xFB\xE1","\u25A0"],["\u0393\xFB\u255D","\u25BC"],["\u0393\xF9\xC5","\u25CF"],["\u0393\xF9\xEF","\u25CB"],["\u0393\xDC\xF2","\u2695"],["\u0393\xDC\xE1","\u26A0"],["\u0393\xDC\xED","\u26A1"],["\u0393\xDC\xF4","\u2693"],["\u0393\xE5\xC9","\u2190"],["\u0393\xE5\xE6","\u2191"],["\u0393\xE5\xC6","\u2192"],["\u0393\xE5\xF4","\u2193"],["\u0393\xE5\xF6","\u2194"],["\u252C\xE1"," "],["\u252C\u2310","\xA9"],["\u252C\xAB","\xAE"],["\u252C\u2591","\xB0"],["\u252C\xBA","\xA7"],["\u252C\u2556","\xB7"],["\u201C",'"'],["\u201D",'"'],["\u2018","'"],["\u2019","'"]];Vn=new Set(["http:","https:","mailto:","tel:"])});function ar(){return ke&&ke.isConnected||(ke=document.createElement("div"),ke.className="toast-container",document.body.append(ke)),ke}function b(e,t="info",a=4e3){let n=ar(),r=document.createElement("div");r.className=`toast toast-${t}`,r.setAttribute("role","status");let o=tr[t];r.innerHTML=`<span class="toast-icon">${o}</span><span class="toast-msg">${l(e)}</span><button class="toast-close" aria-label="Dismiss">×</button>`,r.querySelector(".toast-close").addEventListener("click",()=>Vt(r)),n.prepend(r),requestAnimationFrame(()=>requestAnimationFrame(()=>r.classList.add("toast-enter")));let i=setTimeout(()=>Vt(r),a);r.addEventListener("mouseenter",()=>clearTimeout(i)),r.addEventListener("mouseleave",()=>{let s=setTimeout(()=>Vt(r),1500);r.addEventListener("mouseenter",()=>clearTimeout(s),{once:!0})})}function Vt(e){e.classList.contains("toast-exit")||(e.classList.add("toast-exit"),e.addEventListener("animationend",()=>e.remove(),{once:!0}))}function Ga(e,t){let a=new Blob([JSON.stringify(t,null,2)],{type:"application/json"}),n=URL.createObjectURL(a),r=document.createElement("a");r.href=n,r.download=e,document.body.append(r),r.click(),r.remove(),URL.revokeObjectURL(n)}function mt(e,t){let a=0;return(...n)=>{clearTimeout(a),a=setTimeout(()=>e(...n),t)}}var tr,ke,pe=C(()=>{re();tr={info:'<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" clip-rule="evenodd"/></svg>',success:'<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clip-rule="evenodd"/></svg>',warning:'<svg viewBox="0 0 24 24" fill="currentColor"><path fill-rule="evenodd" d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" clip-rule="evenodd"/></svg>',error:'<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd"/></svg>'},ke=null;b.info=(e,t)=>b(e,"info",t);b.success=(e,t)=>b(e,"success",t);b.warning=(e,t)=>b(e,"warning",t);b.error=(e,t)=>b(e,"error",t)});async function Ja({method:e="GET",path:t,body:a,headers:n}){let r={"Content-Type":"application/json",...n||{}},o=await fetch(t,{method:e,headers:r,body:a!==void 0?JSON.stringify(a):void 0}),i=await o.text(),s=i?JSON.parse(i):{};if(!o.ok){let u=new Error(s.error||o.statusText);throw u.payload=s,u.status=o.status,u}return s}var Ka=C(()=>{});function ft(){return`/api/bootstrap${c.boardId?`?board_id=${encodeURIComponent(c.boardId)}`:c.boardSlug?`?board=${encodeURIComponent(c.boardSlug)}`:""}`}function S(e){if(!c.boardId||e.includes("board_id="))return e;let t=e.includes("?")?"&":"?";return`${e}${t}board_id=${encodeURIComponent(c.boardId)}`}async function v(e,t={}){try{return await Ja({method:t.method||"GET",path:e,body:t.body,headers:t.headers})}catch(a){throw b.error(a.payload?.error||a.message||"Request failed"),a}}var Z=C(()=>{z();pe();Ka()});function Zt(){Qt+=1,U.hidden=!1,ye.hidden=!1,requestAnimationFrame(()=>{U.classList.add("is-open"),ye.classList.add("is-visible")})}function ht(){let e=++Qt,t=U.classList.contains("is-open");U.classList.remove("is-open"),ye.classList.remove("is-visible");let a=()=>{if(Qt!==e)return;U.hidden=!0,ye.hidden=!0,k.innerHTML="";let o=document.getElementById("drawerTitleBlock"),i=document.getElementById("drawerTabs");o&&(o.innerHTML=""),i&&(i.innerHTML="",i.hidden=!0),U.classList.remove("is-wide")};if(!t){a();return}let n=!1,r=()=>{n||(n=!0,a())};U.addEventListener("transitionend",r,{once:!0}),setTimeout(r,360)}function bt({eyebrow:e,title:t,titleAttrs:a,subtitleHtml:n,tabs:r,activeTab:o,onTabSelect:i,body:s,mode:u}){U.classList.toggle("is-settings",u==="settings"),U.classList.toggle("is-wide",u==="ticket"||u==="settings");let d=document.getElementById("drawerTitleBlock"),m=document.getElementById("drawerTabs"),f=` class="drawer-title drawer-title-text${a?.class?` ${a.class}`:""}"`;for(let[$,M]of Object.entries(a||{}))$!=="class"&&(f+=` ${$}="${l(String(M))}"`);let h=[];e&&h.push(`<span class="drawer-eyebrow">${l(e)}</span>`),h.push(`<h2${f}>${l(t??"")}</h2>`),n&&h.push(`<div class="drawer-subtitle">${n}</div>`),d.innerHTML=h.join(""),r&&r.length?(m.hidden=!1,m.innerHTML=r.map($=>`<button type="button" role="tab" class="drawer-tab ${$.id===o?"is-active":""}" data-drawer-tab="${l($.id)}" aria-selected="${$.id===o}">${l($.label)}</button>`).join(""),typeof i=="function"&&m.querySelectorAll(".drawer-tab").forEach($=>{$.addEventListener("click",()=>i($.dataset.drawerTab))})):(m.hidden=!0,m.innerHTML="");let w=document.getElementById("drawerArchiveBtn");w&&(w.hidden=c.detailMode!=="ticket"),k.innerHTML=s??""}var Qt,gt=C(()=>{ne();z();re();Qt=0});function sr(e,t=Ae()){return e?[{id:"move",label:"Move to",children:t.map(a=>({id:`move:${a.id}`,label:a.name,action:"move",value:a.id,checked:a.id===e.state_id,disabled:a.id===e.state_id}))},{id:"priority",label:"Priority",children:rr.map(a=>({id:`priority:${a}`,label:Re(a),action:"priority",value:a,checked:Number(e.priority)===a,disabled:Number(e.priority)===a}))},{id:"type",label:"Type",children:nr.map(a=>({id:`type:${a}`,label:Be(a),action:"type",value:a,checked:N(e.type)===a,disabled:N(e.type)===a}))},{id:"open",label:"Open",action:"open"},{id:"archive",label:"Archive",action:"archive",danger:!0}]:[]}function cr({x:e,y:t,menuWidth:a,menuHeight:n,viewportWidth:r,viewportHeight:o,margin:i=ta}){let s=Math.max(i,r-a-i),u=Math.max(i,o-n-i),d=e+a+i>r?e-a:e,m=t+n+i>o?t-n:t;return{left:Math.min(Math.max(i,d),s),top:Math.min(Math.max(i,m),u)}}function lr({triggerRect:e,submenuWidth:t,submenuHeight:a,viewportWidth:n,viewportHeight:r,margin:o=ta,gap:i=or}){let s=Math.max(o,n-t-o),u=e.right+i,d=e.left-t-i,m=u+t+o<=n,p=d>=o,f=m||!p?u:d,h=Math.max(0,r-o*2),w=Math.min(a,h),$=Math.max(o,r-w-o),M=e.top-i;return{left:Math.min(Math.max(o,f),s),top:Math.min(Math.max(o,M),$),maxHeight:h}}function kt({apiClient:e=v,navigator:t=null,confirmer:a=globalThis.confirm}={}){async function n(r,o){await e(S(`/api/tickets/${encodeURIComponent(r.id)}`),{method:"PATCH",body:o})}return{async open(r){return t?t({boardId:c.boardId,view:"ticket",ticketId:r.id}):null},async move(r,o){await n(r,{state_id:o})},async priority(r,o){await n(r,{priority:o})},async type(r,o){await n(r,{type:o})},async archive(r){return typeof a=="function"&&!a(`Archive "${r.title||"card"}"? You can restore it from Settings \u2192 Card Archive.`)?!1:(await e(S(`/api/tickets/${encodeURIComponent(r.id)}/archive`),{method:"POST"}),b.error(`Archived: ${r.title||"card"}`),!0)}}}function na(e){aa=!!e}function ee({restoreFocus:e=!1}={}){se(),We&&(We.abort(),We=null);for(let t of Ve)t.isConnected&&t.remove();Ve.clear(),G&&(G.querySelectorAll(".card-action-menu-item.has-submenu").forEach(t=>{let a=$e.get(t);a?.isConnected&&a.remove(),$e.delete(t)}),G.remove(),G=null),e&&yt?.isConnected&&yt.focus({preventScroll:!0}),yt=null}function dr({ticket:e,anchor:t,x:a,y:n,handlers:r=kt(),onBeforeOpen:o=null}){if(ee(),!e||aa)return null;typeof o=="function"&&o(),yt=t||document.activeElement;let i=document.createElement("div");i.className="card-action-menu",i.setAttribute("role","menu"),i.setAttribute("aria-label",`Actions for #${e.number||""} ${e.title||"card"}`.trim()),i.dataset.ticketId=e.id,i.innerHTML=Xa(sr(e)),document.body.append(i),G=i,i.querySelectorAll(".card-action-menu-item.has-submenu").forEach(p=>{let f=p.querySelector(":scope > .card-action-submenu");f&&(f.remove(),document.body.append(f),$e.set(p,f),ea.set(f,p),mr(f,e,r))});let s=i.getBoundingClientRect(),u=cr({x:a,y:n,menuWidth:s.width,menuHeight:s.height,viewportWidth:window.innerWidth,viewportHeight:window.innerHeight});i.style.left=`${u.left}px`,i.style.top=`${u.top}px`,pr(i,e,r),oa(i)?.focus({preventScroll:!0}),We=new AbortController;let m=We.signal;return requestAnimationFrame(()=>{document.addEventListener("pointerdown",fr,{signal:m,capture:!0})}),document.addEventListener("keydown",hr,{signal:m,capture:!0}),document.addEventListener("scroll",br,{signal:m,capture:!0,passive:!0}),window.addEventListener("resize",()=>ee(),{signal:m,passive:!0}),window.addEventListener("blur",()=>ee(),{signal:m}),i}function Wa(e,t,{handlers:a=kt(),onBeforeOpen:n=null}={}){e.addEventListener("contextmenu",r=>{let o=r.target instanceof Element?r.target.closest(".card"):null;if(!o)return;let i=t(o.dataset.ticketId);if(i){if(r.preventDefault(),r.stopPropagation(),aa){ee();return}dr({ticket:i,anchor:o,x:r.clientX,y:r.clientY,handlers:a,onBeforeOpen:n})}})}function Xa(e){return`<ul class="card-action-menu-list" role="none">${e.map(ur).join("")}</ul>`}function ur(e){let t=!!e.children?.length,a=["card-action-menu-item"];t&&a.push("has-submenu"),e.danger&&a.push("is-danger");let n=e.checked?'<span class="card-action-check" aria-hidden="true">\u2713</span>':'<span class="card-action-check" aria-hidden="true"></span>',r=t?'<span class="card-action-arrow" aria-hidden="true">\u203A</span>':"";return`
|
|
14
|
-
<li class="${a.join(" ")}" role="none">
|
|
15
|
-
<button type="button"
|
|
16
|
-
class="card-action-button"
|
|
17
|
-
role="menuitem${e.checked?"checkbox":""}"
|
|
18
|
-
data-action="${l(e.action||"")}" data-value="${l(e.value??"")}" data-menu-id="${l(e.id)}"
|
|
19
|
-
${e.disabled?"disabled":""}
|
|
20
|
-
${e.checked?'aria-checked="true"':""}
|
|
21
|
-
${t?'aria-haspopup="menu" aria-expanded="false"':""}>
|
|
22
|
-
${t?"":n}
|
|
23
|
-
<span class="card-action-label">${l(e.label)}</span>
|
|
24
|
-
${r}
|
|
25
|
-
</button>
|
|
26
|
-
${t?`<div class="card-action-submenu" role="menu">${Xa(e.children)}</div>`:""}
|
|
27
|
-
</li>
|
|
28
|
-
`}function pr(e,t,a){e.addEventListener("pointerover",n=>{let r=n.target instanceof Element?n.target.closest(".card-action-menu-item"):null;!r||!e.contains(r)||(r.classList.contains("has-submenu")?(se(),vt(r)):gr(e))},!0),e.addEventListener("focusin",n=>{let r=n.target instanceof Element?n.target.closest(".card-action-menu-item"):null;!r||!e.contains(r)||(r.classList.contains("has-submenu")?(se(),vt(r)):(se(),en(e)))}),e.addEventListener("click",n=>Va(n,e,t,a)),e.addEventListener("keydown",n=>Qa(n,e))}function mr(e,t,a){e.addEventListener("pointerenter",se),e.addEventListener("pointerover",n=>{n.target instanceof Element&&e.contains(n.target)&&se()},!0),e.addEventListener("click",n=>{G&&Va(n,G,t,a,e)}),e.addEventListener("keydown",n=>{G&&Qa(n,G)})}function Va(e,t,a,n,r=t){let o=e.target instanceof Element?e.target.closest("button.card-action-button"):null;if(!o||!r.contains(o)||o.disabled)return;e.preventDefault(),e.stopPropagation();let i=o.closest(".card-action-menu-item");if(i?.classList.contains("has-submenu")){se(),vt(i),oa($e.get(i))?.focus({preventScroll:!0});return}let s=o.dataset.action,u=o.dataset.value;if(ee(),s==="open")return n.open(a);if(s==="move")return n.move(a,u);if(s==="priority")return n.priority(a,Number(u));if(s==="type")return n.type(a,u);if(s==="archive")return n.archive(a)}function Qa(e,t){let a=e.target instanceof Element?e.target.closest("button.card-action-button"):null;if(!a)return;let n=a.closest(".card-action-submenu");if(!t.contains(a)&&!n)return;let o=a.closest(".card-action-menu-list"),i=a.closest(".card-action-menu-item");if(e.key==="Escape"){if(e.preventDefault(),n){let s=ea.get(n),u=s?.querySelector(":scope > .card-action-button");wt(s),u?.focus({preventScroll:!0})}else ee({restoreFocus:!0});return}if(e.key==="ArrowDown"||e.key==="ArrowUp"){e.preventDefault(),yr(o,a,e.key==="ArrowDown"?1:-1);return}if(e.key==="Home"||e.key==="End"){e.preventDefault();let s=ra(o);s[e.key==="Home"?0:s.length-1]?.focus({preventScroll:!0});return}if(e.key==="ArrowRight"&&i?.classList.contains("has-submenu")){e.preventDefault(),se(),vt(i),oa($e.get(i))?.focus({preventScroll:!0});return}if(e.key==="ArrowLeft"&&n){let s=ea.get(n),u=s?.querySelector(":scope > .card-action-button");u&&(e.preventDefault(),wt(s),u.focus({preventScroll:!0}))}}function fr(e){G&&(Za(e)||ee())}function hr(e){e.key==="Escape"&&G&&(e.preventDefault(),ee({restoreFocus:!0}))}function br(e){G&&(Za(e)||ee())}function Za(e){let t=e.target;if(!(t instanceof Node))return!1;if(G?.contains(t))return!0;for(let a of Ve)if(a.contains(t))return!0;return!1}function vt(e){let t=e.parentElement?.children||[];for(let n of t)n!==e&&n.classList?.contains("has-submenu")&&n.classList.contains("is-open")&&wt(n);let a=$e.get(e);if(a){if(e.classList.contains("is-open")){Ya(e,a);return}e.classList.add("is-open"),e.querySelector(":scope > .card-action-button")?.setAttribute("aria-expanded","true"),a.classList.add("is-open"),Ve.add(a),Ya(e,a)}}function wt(e){if(!e)return;e.classList.remove("is-open"),e.querySelector(":scope > .card-action-button")?.setAttribute("aria-expanded","false");let t=$e.get(e);t&&(t.classList.remove("is-open"),t.style.removeProperty("left"),t.style.removeProperty("top"),t.style.removeProperty("max-height"),Ve.delete(t))}function en(e){e.querySelectorAll(".card-action-menu-item.has-submenu.is-open").forEach(wt)}function gr(e){se(),Xe=setTimeout(()=>{Xe=null,G===e&&en(e)},ir)}function se(){Xe!=null&&(clearTimeout(Xe),Xe=null)}function Ya(e,t){let a=e.querySelector(":scope > .card-action-button");if(!t||!a)return;let n=Math.max(0,window.innerHeight-ta*2);t.style.maxHeight=`${n}px`,t.style.left="0px",t.style.top="0px";let r=t.getBoundingClientRect(),o=lr({triggerRect:a.getBoundingClientRect(),submenuWidth:r.width,submenuHeight:r.height,viewportWidth:window.innerWidth,viewportHeight:window.innerHeight});t.style.left=`${o.left}px`,t.style.top=`${o.top}px`,t.style.maxHeight=`${o.maxHeight}px`}function ra(e){if(!e)return[];let t=e.classList?.contains("card-action-menu-list")?e:e.querySelector(":scope > .card-action-menu-list");return t?[...t.querySelectorAll(":scope > .card-action-menu-item > .card-action-button")].filter(a=>!a.disabled):[]}function oa(e){return ra(e)[0]||null}function yr(e,t,a){let n=ra(e);if(!n.length)return;let r=n.indexOf(t),o=r===-1?0:(r+a+n.length)%n.length;n[o].focus({preventScroll:!0})}var nr,rr,ta,or,ir,G,aa,We,yt,$e,ea,Ve,Xe,tn=C(()=>{Z();z();re();pe();nr=["epic","feature","task","bug"],rr=[0,1,2,3,4],ta=8,or=6,ir=180,G=null,aa=!1,We=null,yt=null,$e=new WeakMap,ea=new WeakMap,Ve=new Set,Xe=null});function vr(e){return e?typeof e=="string"?{readAt:e,count:0}:{readAt:e.readAt||null,count:Number(e.count)||0}:{readAt:null,count:0}}function Qe(e){if(!e)return new Map;let t=sa.get(e);if(t)return t;t=new Map;try{let a=localStorage.getItem(an(e));if(a){let n=JSON.parse(a);for(let[r,o]of Object.entries(n))t.set(r,vr(o))}}catch{}return sa.set(e,t),t}function $t(e){let t=sa.get(e);if(t)try{let a={};for(let[n,r]of t)a[n]=r;localStorage.setItem(an(e),JSON.stringify(a))}catch{}}function nn(e,t){let a=e.get(t);return a||(a={readAt:null,count:0},e.set(t,a)),a}function xt(e){let t=e?.board_id||c.boardId;if(!t||!e?.id)return 0;let n=Qe(t).get(e.id);return n?n.count:localStorage.getItem(ia(t))==="1"?1:0}function St(e,t){let a=c.boardId;if(!a||!e)return;let n=Qe(a),r=nn(n,e);r.count===0&&r.readAt&&t&&r.readAt>=t||(r.count=0,t&&(r.readAt=t),$t(a))}function rn(e){let t=c.boardId;if(!t||!e)return;let a=Qe(t),n=nn(a,e);n.count=(n.count||0)+1,$t(t)}function on(e){let t=c.boardId;if(!t||!e)return;let a=Qe(t);a.has(e)&&(a.delete(e),$t(t))}function sn(e,t){if(!e||localStorage.getItem(ia(e))==="1")return;let a=Qe(e);for(let n of t||[])!a.has(n.id)&&n.updated_at&&a.set(n.id,{readAt:n.updated_at,count:0});$t(e);try{localStorage.setItem(ia(e),"1")}catch{}}var an,ia,sa,Ze=C(()=>{z();an=e=>`mab_card_read:${e}`,ia=e=>`mab_card_read_seeded:${e}`,sa=new Map});function Fe(){let e=Ae(),t=W();la=new Map(t.map(a=>[a.id,a])),T.innerHTML=e.map(a=>$r(a,t)).join("")}function wr(e){if(!e)return;let t=e.dataset.ticketId,a=!c.expandedCardIds.has(t);if(a){if(c.expandedCardIds.add(t),!e.querySelector(".card-expandable")){let r=la.get(t),o=e.querySelector(".card-meta");r&&o&&o.insertAdjacentHTML("afterend",dn(r))}}else c.expandedCardIds.delete(t);e.classList.toggle("is-expanded",a);let n=e.querySelector("button.card-expand-trigger");n&&n.setAttribute("aria-expanded",a?"true":"false")}function cn(){ca=ca||kt({navigator:P});let e=null,t=a=>{e!==a&&(e&&e.classList.remove("drag-over"),e=a,e&&e.classList.add("drag-over"))};T.addEventListener("dragstart",a=>{let n=a.target instanceof Element?a.target.closest(".card"):null;n&&(ee(),na(!0),a.dataTransfer.setData("text/plain",n.dataset.ticketId),a.dataTransfer.effectAllowed="move")}),T.addEventListener("dragover",a=>{let n=a.target instanceof Element?a.target.closest(".column"):null;n&&(a.preventDefault(),t(n))}),T.addEventListener("dragend",()=>{na(!1),t(null),Lt=!0,setTimeout(()=>{Lt=!1},0)}),T.addEventListener("drop",async a=>{let n=a.target instanceof Element?a.target.closest(".column"):null;if(!n)return;a.preventDefault(),t(null);let r=a.dataTransfer.getData("text/plain");r&&(F(),await v(S(`/api/tickets/${r}`),{method:"PATCH",body:{state_id:n.dataset.stateId}}),await _())}),T.addEventListener("click",a=>{let n=a.target instanceof Element?a.target:a.target?.parentElement;if(!n)return;let r=n.closest("[data-add-to-state]");if(r){a.stopPropagation(),Tt(r.dataset.addToState||"");return}let o=n.closest("button.card-expand-trigger");if(o){a.stopPropagation(),a.preventDefault(),wr(o.closest(".card"));return}if(n.closest("a")){a.stopPropagation();return}let i=n.closest(".epic-mini-header[data-ticket-id]");if(i){F(),P({boardId:c.boardId,view:"ticket",ticketId:i.dataset.ticketId});return}let s=n.closest(".card");if(s){if(Lt){Lt=!1,a.preventDefault(),a.stopPropagation();return}F(),P({boardId:c.boardId,view:"ticket",ticketId:s.dataset.ticketId})}}),Wa(T,a=>la.get(a),{handlers:ca,onBeforeOpen:F})}function ln(){T.querySelectorAll(".card.selected").forEach(e=>e.classList.remove("selected")),c.selectedTicketId&&T.querySelectorAll(`.card[data-ticket-id="${CSS.escape(c.selectedTicketId)}"]`).forEach(e=>e.classList.add("selected"))}function kr(e){let t=e.toLowerCase(),a={icon:'<svg class="empty-state-icon" viewBox="0 0 24 24"><path d="M18 6L6 18M6 6l12 12" opacity="0.5" /></svg>',title:"Nothing here",subtitle:"Tasks will appear when ready"};return t==="ai ready"?a={icon:`<svg class="empty-state-icon ai-sparkle" viewBox="0 0 24 24">
|
|
29
|
-
<path d="M12 3L14.5 9L21 11.5L14.5 14L12 20L9.5 14L3 11.5L9.5 9L12 3Z" fill="currentColor" opacity="0.65" />
|
|
30
|
-
<circle cx="12" cy="11.5" r="1.8" fill="#fff" />
|
|
31
|
-
</svg>`,title:"Ready for agents",subtitle:"Drop unblocked cards here \u2014 the claim-next tool picks them up"}:t.includes("progress")?a={icon:`<svg class="empty-state-icon orbit" viewBox="0 0 100 100">
|
|
32
|
-
<circle cx="50" cy="50" r="10" fill="currentColor" opacity="0.9" />
|
|
33
|
-
<circle class="ring" cx="50" cy="50" r="30" stroke="currentColor" stroke-dasharray="4 6" />
|
|
34
|
-
<circle class="satellite" cx="80" cy="50" r="4" fill="currentColor" />
|
|
35
|
-
</svg>`,title:"Work in progress",subtitle:"Active work will appear here"}:t.includes("review")?a={icon:`<svg class="empty-state-icon star" viewBox="0 0 24 24">
|
|
36
|
-
<path d="M12 2L14.5 9.5L22 12L14.5 14.5L12 22L9.5 14.5L2 12L9.5 9.5L12 2Z" fill="currentColor" />
|
|
37
|
-
</svg>`,title:"Under review",subtitle:"Quality gate before merge"}:t.includes("todo")||t.includes("backlog")?a={icon:`<svg class="empty-state-icon ai-sparkle" viewBox="0 0 24 24">
|
|
38
|
-
<path d="M12 3L14.5 9L21 11.5L14.5 14L12 20L9.5 14L3 11.5L9.5 9L12 3Z" fill="currentColor" opacity="0.5" />
|
|
39
|
-
<path d="M19 8L20 10L21 8L20 6L19 8ZM19 16L20 18L21 16L20 14L19 16ZM5 8L6 10L7 8L6 6L5 8Z" fill="currentColor" />
|
|
40
|
-
</svg>`,title:"Ready for launch",subtitle:"Create a card manually or task an AI to generate tasks"}:t.includes("done")||t.includes("finished")?a={icon:`<svg class="empty-state-icon" viewBox="0 0 24 24">
|
|
41
|
-
<circle cx="12" cy="12" r="10" />
|
|
42
|
-
<path d="M8 12L11 15L16 9" />
|
|
43
|
-
</svg>`,title:"Done",subtitle:"Move completed tickets here"}:t.includes("cancel")&&(a={icon:`<svg class="empty-state-icon" viewBox="0 0 24 24">
|
|
44
|
-
<circle cx="12" cy="12" r="10" opacity="0.2" />
|
|
45
|
-
<path d="M15 9L9 15M9 9l6 6" />
|
|
46
|
-
</svg>`,title:"Not moving forward",subtitle:"Parked or replaced"}),`
|
|
47
|
-
<div class="column-empty-state">
|
|
48
|
-
<div class="empty-state-visual">
|
|
49
|
-
<div class="empty-state-glow"></div>
|
|
50
|
-
${a.icon}
|
|
51
|
-
</div>
|
|
52
|
-
<div class="empty-state-title">${l(a.title)}</div>
|
|
53
|
-
<div class="empty-state-subtitle">${l(a.subtitle)}</div>
|
|
54
|
-
</div>
|
|
55
|
-
`}function $r(e,t){let a=t.filter(s=>s.state_id===e.id),n=a.length===0,r=e.name==="AI Ready",o=["column","lane"];r&&o.push("column--ai-ready");let i=r?' data-accent="ai-ready"':"";return`
|
|
56
|
-
<section class="${o.join(" ")}" data-flat${i} data-state-id="${e.id}" data-state-name="${l(e.name)}">
|
|
57
|
-
<div class="column-head lane-head">
|
|
58
|
-
<h2 class="lane-title"${r?' title="Agent staging \u2014 cards here are picked up by the claim-next tool when unblocked"':""}>
|
|
59
|
-
${r?'<span class="agent-dot" aria-hidden="true"></span>':""}${l(e.name)}
|
|
60
|
-
</h2>
|
|
61
|
-
<div class="column-head-actions lane-actions">
|
|
62
|
-
<button type="button" class="column-add-btn" data-variant="plus" data-add-to-state="${l(e.id)}" title="New card">+</button>
|
|
63
|
-
<span class="count">${a.length}</span>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
${n?kr(e.name):xr(a,t)}
|
|
67
|
-
<button type="button" class="add-card-phantom" data-add-to-state="${l(e.id)}">+ Add a card</button>
|
|
68
|
-
</section>
|
|
69
|
-
`}function xr(e,t){let a=new Map(t.filter(s=>N(s.type)==="epic").map(s=>[s.id,s])),n=new Set(e.filter(s=>N(s.type)==="epic").map(s=>s.id)),r=new Map;for(let s of e)if(s.parent_ticket_id&&a.has(s.parent_ticket_id)){let u=r.get(s.parent_ticket_id)||[];u.push(s),r.set(s.parent_ticket_id,u)}let o=new Set,i=[];for(let s of e)if(!o.has(s.id)){if(N(s.type)==="epic"){let u=r.get(s.id)||[];if(u.length){let d=u.map(m=>et(m,{asEpicChild:!0})).join("");i.push(`${et(s)}<div class="epic-children">${d}</div>`),u.forEach(m=>o.add(m.id))}else i.push(et(s));o.add(s.id);continue}if(!(s.parent_ticket_id&&n.has(s.parent_ticket_id))){if(s.parent_ticket_id&&a.has(s.parent_ticket_id)&&!n.has(s.parent_ticket_id)){let u=a.get(s.parent_ticket_id),d=r.get(s.parent_ticket_id)||[],m=d.map(p=>et(p,{asEpicChild:!0})).join("");i.push(`${Sr(u)}<div class="epic-children">${m}</div>`),d.forEach(p=>o.add(p.id));continue}i.push(et(s)),o.add(s.id)}}return i.join("")}function Sr(e){return`
|
|
70
|
-
<div class="epic-mini-header" data-ticket-id="${l(e.id)}" title="Open epic">
|
|
71
|
-
<div class="card-type-id type-pill-epic">
|
|
72
|
-
${Et("epic")}
|
|
73
|
-
<span class="card-type-label">EPIC</span>
|
|
74
|
-
</div>
|
|
75
|
-
<span class="ticket-number">${l(Q(e))}</span>
|
|
76
|
-
<span class="epic-mini-title">${l(e.title)}</span>
|
|
77
|
-
</div>
|
|
78
|
-
`}function da(e){let t=De(e),a=Re(e);return`<div class="card-priority-id priority-pill" data-variant="${l(t)}"><span class="card-priority-label">${l(a)}</span></div>`}function Et(e){let t=N(e),a="",n="currentColor";switch(t){case"epic":n="#c084fc",a='<path d="M12 2L14.5 9.5L22 12L14.5 14.5L12 22L9.5 14.5L2 12L9.5 9.5L12 2Z" fill="currentColor" />';break;case"feature":n="#4ade80",a='<path d="M19 21l-7-5-7 5V5a2 2 0 012-2h10a2 2 0 012 2v16z" fill="currentColor" />';break;case"bug":n="#f87171",a='<circle cx="12" cy="12" r="10" /><path d="M12 8v4M12 16h.01" />';break;default:n="#60a5fa",a='<rect x="3" y="3" width="18" height="18" rx="2" /><path d="M9 12l2 2 4-4" />'}return`
|
|
79
|
-
<svg class="card-type-icon" viewBox="0 0 24 24" style="color: ${n}">
|
|
80
|
-
${a}
|
|
81
|
-
</svg>
|
|
82
|
-
`}function et(e,{asEpicChild:t=!1}={}){let a=N(e.type),n=De(e.priority),r=Be(e.type).toUpperCase(),o=c.expandedCardIds.has(e.id),s=!!String(e.description||"").trim()||e.labels&&e.labels.length||e.parent_ticket||e.child_count,u=`
|
|
83
|
-
<span class="ticket-number">${l(Q(e))}</span>
|
|
84
|
-
<div class="card-type-id type-pill-${l(a)}">
|
|
85
|
-
${Et(e.type)}
|
|
86
|
-
<span class="card-type-label">${l(r)}</span>
|
|
87
|
-
</div>
|
|
88
|
-
${c.showPriority?da(e.priority):""}
|
|
89
|
-
${s?'<svg class="card-expand-chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6" /></svg>':""}
|
|
90
|
-
`,d=xt(e),m=d===0?"":d===1?'<span class="card-unread-dot" role="status" aria-label="1 unread update"></span>':`<span class="card-unread-dot has-count" role="status" aria-label="${d} unread updates">${d>99?"99+":d}</span>`;return`
|
|
91
|
-
<article class="card type-${l(a)} priority-${l(n)}${o?" is-expanded":""}${t?" is-epic-child":""} ${e.id===c.selectedTicketId?"selected":""}" data-variant="${l(a)}" draggable="true" data-ticket-id="${e.id}">
|
|
92
|
-
${m}
|
|
93
|
-
<h3>${l(e.title)}</h3>
|
|
94
|
-
|
|
95
|
-
<div class="card-meta">
|
|
96
|
-
${s?`<button type="button" class="card-expand-trigger" data-variant="card-accordion" data-card-toggle aria-expanded="${o?"true":"false"}">${u}</button>`:`<div class="card-expand-trigger card-expand-trigger--static">${u}</div>`}
|
|
97
|
-
</div>
|
|
98
|
-
|
|
99
|
-
${s&&o?dn(e):""}
|
|
100
|
-
</article>
|
|
101
|
-
`}function dn(e){return`
|
|
102
|
-
<div class="card-expandable">
|
|
103
|
-
${!!String(e.description||"").trim()?`<div class="card-description markdown-body">${Xt(e.description)}</div>`:""}
|
|
104
|
-
${Ua(e.labels)}
|
|
105
|
-
${e.parent_ticket?`<div class="parent-line">-> ${l(Q(e.parent_ticket))}</div>`:""}
|
|
106
|
-
${e.child_count?`<div class="child-count">${e.child_count} feature${e.child_count===1?"":"s"} inside</div>`:""}
|
|
107
|
-
</div>
|
|
108
|
-
`}function un(){let a=()=>Math.max(T.clientWidth,1),n=!1,r=!1,o=0,i=0,s=0,u=p=>p instanceof Element?!!(p.closest(".card")||p.closest("a, button, input, select, textarea, [draggable='true']")):!1,d=(p,f)=>f===WheelEvent.DOM_DELTA_LINE?p*16:f===WheelEvent.DOM_DELTA_PAGE?p*a():p;T.addEventListener("wheel",p=>{if(p.defaultPrevented||p.ctrlKey)return;let f=p.deltaX||(p.shiftKey?p.deltaY:0);!f||!(Math.abs(f)>=Math.abs(p.deltaY)||p.shiftKey)||T.scrollWidth<=T.clientWidth||(p.preventDefault(),T.scrollLeft+=d(f,p.deltaMode))},{passive:!1}),T.addEventListener("pointerdown",p=>{p.button===0&&(u(p.target)||(n=!0,r=!1,s=p.pointerId,o=p.clientX,i=T.scrollLeft,T.classList.add("is-grabbing")))}),T.addEventListener("pointermove",p=>{if(!n||p.pointerId!==s)return;let f=p.clientX-o;if(!r&&Math.abs(f)>4){r=!0;try{T.setPointerCapture(s)}catch{}}r&&(p.preventDefault(),T.scrollLeft=i-f)});let m=p=>{if(!(!n||p&&p.pointerId!==s)){n=!1,T.classList.remove("is-grabbing");try{T.releasePointerCapture(s)}catch{}}};T.addEventListener("pointerup",m),T.addEventListener("pointercancel",m),T.addEventListener("pointerleave",m),T.addEventListener("click",p=>{r&&(p.stopPropagation(),p.preventDefault(),r=!1)},!0)}var la,ca,Lt,tt=C(()=>{ne();z();re();Z();fe();Ne();me();tn();Ze();la=new Map,ca=null,Lt=!1});var pn,xe,H,Oe=C(()=>{pn="full";pn="full";xe=pn,H={sse:xe==="full",ai:xe==="full",multiBoard:xe==="full",tokenAuth:xe==="full",attachments:xe==="full"}});async function he(){let e=await v(`/api/boards/${c.boardId}/context?include_struck=true`),t=e.board,a=Da(),n=[{id:"lanes",label:"Lanes"},{id:"appearance",label:"Appearance"},{id:"archive",label:"Card Archive"},{id:"notes",label:"Notes"},{id:"journal",label:"Journal"},...H.ai?[{id:"ai",label:"AI"}]:[],{id:"repository",label:"Repository"}],r=a==="ai"&&!H.ai?"lanes":a;bt({eyebrow:"SETTINGS",mode:"settings",title:t.name,tabs:n,activeTab:r,onTabSelect:async o=>{P({boardId:c.boardId,view:"settings",tab:o})},body:Lr(e,r)}),Ur(e,r)}function Lr(e,t){switch(t){case"notes":return Or(e);case"lanes":return fn();case"appearance":return jr();case"ai":return H.ai?Er(e):fn();case"journal":return Hr(e);case"archive":return Rr();case"repository":default:return Dr(e)}}function Er(e){let t=e.board,a=t.ai_enabled!==0,n=a?'<div class="ai-section-divider" aria-hidden="true"></div>':"",r=a?Tr(t):"",o=a?_r(e):"";return`
|
|
109
|
-
<div class="section">
|
|
110
|
-
<label class="orbit-check" for="aiEnabledToggle">
|
|
111
|
-
<input
|
|
112
|
-
type="checkbox"
|
|
113
|
-
id="aiEnabledToggle"
|
|
114
|
-
${a?"checked":""}
|
|
115
|
-
/>
|
|
116
|
-
<span class="orbit-check-box" aria-hidden="true">
|
|
117
|
-
<svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
|
|
118
|
-
</span>
|
|
119
|
-
<span class="orbit-check-label">Enable AI</span>
|
|
120
|
-
</label>
|
|
121
|
-
<p class="description">Provisions the <strong>AI Ready</strong>, <strong>In Progress</strong>, and <strong>Review</strong> lanes if missing, surfaces agent context fields, and exposes the MCP setup snippet. Disabling AI hides the AI Ready column but keeps its cards in place.</p>
|
|
122
|
-
</div>
|
|
123
|
-
${n}
|
|
124
|
-
${r}
|
|
125
|
-
${n}
|
|
126
|
-
${o}
|
|
127
|
-
`}function Tr(e){let t=`This project is about\u2026
|
|
128
|
-
|
|
129
|
-
How any AI agent should use this board and work in this repo.`,a=e.agent_instructions||"",n=!!a.trim(),r=n?"":"is-placeholder",o=n?we(a):l(t);return`
|
|
130
|
-
<div class="section ai-subsection ai-context-setup">
|
|
131
|
-
<div class="ai-context-title-row">
|
|
132
|
-
<span class="ai-context-inline-mark" aria-hidden="true">Context</span>
|
|
133
|
-
<h3>Agent Instructions</h3>
|
|
134
|
-
</div>
|
|
135
|
-
<p class="description">This project-level context is provided to agents when they work on tickets.</p>
|
|
136
|
-
<div class="inline-md-field">
|
|
137
|
-
<div
|
|
138
|
-
class="inline-md-field-body preserved-text-body editable-field settings-agent-instructions-body ${r}"
|
|
139
|
-
data-edit-field="agent_instructions"
|
|
140
|
-
tabindex="0"
|
|
141
|
-
title="Click to edit"
|
|
142
|
-
role="button"
|
|
143
|
-
aria-label="Edit Agent Instructions"
|
|
144
|
-
>${o}</div>
|
|
145
|
-
</div>
|
|
146
|
-
</div>
|
|
147
|
-
`}function _r(e){let t=_t(),a="claude-code",n=Ir(e,t),r=ua(a,n),o=pa(t,n),i=It[a];return`
|
|
148
|
-
<details class="section ai-subsection mcp-setup" data-mcp-setup>
|
|
149
|
-
<summary>
|
|
150
|
-
<div class="mcp-title-row">
|
|
151
|
-
<span class="mcp-inline-mark" aria-hidden="true">MCP</span>
|
|
152
|
-
<h3>Connect Your Agent to MCP</h3>
|
|
153
|
-
</div>
|
|
154
|
-
</summary>
|
|
155
|
-
<div>
|
|
156
|
-
<p class="description">A tiny service running on your computer alongside your AI agent, providing it with tools and context to collaborate with you more effectively. Tell your AI to use the snippet below, it knows what MCP is.</p>
|
|
157
|
-
<div class="mcp-flow">
|
|
158
|
-
<label>
|
|
159
|
-
<span>Operating system</span>
|
|
160
|
-
<select id="mcpOsSelect" class="select-chevron-field">
|
|
161
|
-
${Object.entries(Ct).map(([s,u])=>`<option value="${s}" ${s===t?"selected":""}>${l(u.label)}</option>`).join("")}
|
|
162
|
-
</select>
|
|
163
|
-
</label>
|
|
164
|
-
<label>
|
|
165
|
-
<span>Agent app</span>
|
|
166
|
-
<select id="mcpClientSelect" class="select-chevron-field">
|
|
167
|
-
${Object.entries(It).map(([s,u])=>`<option value="${s}" ${s===a?"selected":""}>${l(u.label)}</option>`).join("")}
|
|
168
|
-
</select>
|
|
169
|
-
</label>
|
|
170
|
-
</div>
|
|
171
|
-
<div class="mcp-path-grid">
|
|
172
|
-
<label>
|
|
173
|
-
<span>Orbit MCP server file</span>
|
|
174
|
-
<input id="mcpServerPathInput" value="${l(n.serverPath)}" />
|
|
175
|
-
</label>
|
|
176
|
-
</div>
|
|
177
|
-
</div>
|
|
178
|
-
|
|
179
|
-
<div class="mcp-options-stack">
|
|
180
|
-
<div class="mcp-option-card">
|
|
181
|
-
<div class="mcp-option-card-head">
|
|
182
|
-
<div>
|
|
183
|
-
<strong data-mcp-output-title>${l(i.label)} setup</strong>
|
|
184
|
-
<p class="description" data-mcp-helper>${l(i.helper)}</p>
|
|
185
|
-
</div>
|
|
186
|
-
<button type="button" data-variant="secondary" data-copy-mcp="config">Copy setup</button>
|
|
187
|
-
</div>
|
|
188
|
-
<div class="mcp-terminal">
|
|
189
|
-
<div class="mcp-terminal-chrome" aria-hidden="true"><span></span><span></span><span></span></div>
|
|
190
|
-
<pre class="mcp-snippet"><code data-mcp-config>${l(r)}</code></pre>
|
|
191
|
-
</div>
|
|
192
|
-
</div>
|
|
193
|
-
|
|
194
|
-
<div class="mcp-or-divider" aria-hidden="true"><span>OR</span></div>
|
|
195
|
-
|
|
196
|
-
<div class="mcp-option-card">
|
|
197
|
-
<div class="mcp-option-card-head">
|
|
198
|
-
<div>
|
|
199
|
-
<strong>Manual run command</strong>
|
|
200
|
-
<p class="description">Use this only when an app asks you to start the MCP server yourself.</p>
|
|
201
|
-
</div>
|
|
202
|
-
<button type="button" data-variant="secondary" data-copy-mcp="command">Copy command</button>
|
|
203
|
-
</div>
|
|
204
|
-
<div class="mcp-terminal">
|
|
205
|
-
<div class="mcp-terminal-chrome" aria-hidden="true"><span></span><span></span><span></span></div>
|
|
206
|
-
<pre class="mcp-snippet mcp-snippet--compact"><code data-mcp-command>${l(o)}</code></pre>
|
|
207
|
-
</div>
|
|
208
|
-
</div>
|
|
209
|
-
</div>
|
|
210
|
-
</details>
|
|
211
|
-
`}function _t(){let e=String(navigator.userAgentData?.platform||navigator.platform||navigator.userAgent||"").toLowerCase();return e.includes("win")?"windows":e.includes("mac")?"macos":"linux"}function Cr(e){return{serverPath:(Ct[e]||Ct.linux).serverPath}}function Ir(e,t){let a=Cr(t);return{serverPath:(e.deployment||{}).mcp_server_path||a.serverPath}}function mn(){return{serverPath:y("#mcpServerPathInput")?.value||""}}function ua(e,t){if(e==="claude-code")return Ar(t);if(e==="codex")return Pr(t);if(e==="opencode")return Mr(t);let a={mcpServers:{"starscape-orbit":{command:"node",args:[t.serverPath]}}};return JSON.stringify(a,null,2)}function Mr(e){return JSON.stringify({$schema:"https://opencode.ai/config.json",mcp:{"starscape-orbit":{type:"local",command:["node",e.serverPath],enabled:!0}}},null,2)}function Pr(e){return`[mcp_servers.starscape-orbit]
|
|
212
|
-
command = "node"
|
|
213
|
-
args = ["${Br(e.serverPath)}"]
|
|
214
|
-
enabled = true`}function Ar(e){return["claude mcp add starscape-orbit","--","node",bn(e.serverPath)].join(" ")}function pa(e,t){return`node ${bn(t.serverPath)}`}function bn(e){return`"${String(e||"").replace(/"/g,'\\"')}"`}function Br(e){return String(e||"").replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function Rr(){return`
|
|
215
|
-
<p class="description">Archived cards are hidden from the kanban, search, and agent claim-next. Restore to put a card back on the board, or delete to permanently remove it.</p>
|
|
216
|
-
<div class="section">
|
|
217
|
-
<div id="archiveList" class="archive-list">Loading\u2026</div>
|
|
218
|
-
</div>
|
|
219
|
-
`}function Dr(e){let t=e.board;return O.boardId!==t.id&&(O.boardId=t.id,O.step="idle",O.exported=!1),`
|
|
220
|
-
${H.multiBoard?`
|
|
221
|
-
<div class="detail-head project-manual-head">
|
|
222
|
-
<div class="meta-grid">
|
|
223
|
-
<div class="meta meta--repo-path">
|
|
224
|
-
<span>Repo</span>
|
|
225
|
-
<strong>${l(za(t.repo_url))}</strong>
|
|
226
|
-
<span>Path</span>
|
|
227
|
-
<strong>${l(t.system_path||e.deployment?.system_path||"Not available")}</strong>
|
|
228
|
-
</div>
|
|
229
|
-
</div>
|
|
230
|
-
</div>`:`
|
|
231
|
-
<p class="description">This board lives in your browser only. Export a snapshot if you want to move it to a real Orbit install, or import one to seed this preview.</p>`}
|
|
232
|
-
|
|
233
|
-
${H.multiBoard?Fr(t):""}
|
|
234
|
-
|
|
235
|
-
<div class="section">
|
|
236
|
-
<h3>Board Snapshot</h3>
|
|
237
|
-
<p class="description">Import accepts Orbit snapshots or Trello board JSON exports. The default import creates a separate board and leaves this board untouched.</p>
|
|
238
|
-
${H.attachments?`
|
|
239
|
-
<label class="orbit-check">
|
|
240
|
-
<input type="checkbox" id="exportProjectImages" />
|
|
241
|
-
<span class="orbit-check-box" aria-hidden="true">
|
|
242
|
-
<svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
|
|
243
|
-
</span>
|
|
244
|
-
<span>Include attached images</span>
|
|
245
|
-
</label>
|
|
246
|
-
<p class="description">Image exports are embedded in the .orbit.json snapshot and may be much larger.</p>
|
|
247
|
-
`:""}
|
|
248
|
-
<div class="deployment-actions">
|
|
249
|
-
<button type="button" data-variant="secondary" id="exportProject">Export</button>
|
|
250
|
-
<input type="file" id="importSnapshotAsNewFile" class="import-file-input" accept=".orbit.json,.json,application/json" />
|
|
251
|
-
<button type="button" data-variant="secondary" id="importSnapshotAsNewButton">Import as New Board</button>
|
|
252
|
-
</div>
|
|
253
|
-
</div>
|
|
254
|
-
|
|
255
|
-
<div class="section repository-danger-zone">
|
|
256
|
-
<h3>Replace Current Board</h3>
|
|
257
|
-
<p class="description">Advanced: replace this board from an Orbit snapshot or Trello export after making the normal pre-import backup. Type <strong>${l(t.slug||"")}</strong> to enable replacement.</p>
|
|
258
|
-
<label class="repository-confirm-field">
|
|
259
|
-
<span>Board slug</span>
|
|
260
|
-
<input id="replaceImportConfirmInput" autocomplete="off" spellcheck="false" placeholder="${l(t.slug||"")}" />
|
|
261
|
-
</label>
|
|
262
|
-
<div class="deployment-actions">
|
|
263
|
-
<input type="file" id="replaceCurrentBoardImportFile" class="import-file-input" accept=".orbit.json,.json,application/json" disabled />
|
|
264
|
-
<button type="button" data-variant="secondary" id="replaceCurrentBoardImportButton" disabled>Replace Current Board</button>
|
|
265
|
-
</div>
|
|
266
|
-
</div>
|
|
267
|
-
|
|
268
|
-
${H.multiBoard?Nr(t):""}
|
|
269
|
-
`}function Fr(e){return`
|
|
270
|
-
<div class="section board-rename-section">
|
|
271
|
-
<h3>Board Name</h3>
|
|
272
|
-
<p class="description">Rename the board display name only. The canonical URL slug stays <strong>${l(e.slug||"")}</strong>, so existing board links keep working.</p>
|
|
273
|
-
<form id="boardRenameForm" class="field-form">
|
|
274
|
-
<label>
|
|
275
|
-
<span>Display name</span>
|
|
276
|
-
<input name="name" value="${l(e.name||"")}" maxlength="120" required />
|
|
277
|
-
</label>
|
|
278
|
-
<button type="submit" data-variant="secondary">Rename Board</button>
|
|
279
|
-
</form>
|
|
280
|
-
</div>
|
|
281
|
-
`}function Nr(e){let t=l(e.slug||"");if(O.step==="choice")return`
|
|
282
|
-
<div class="section repository-danger-zone">
|
|
283
|
-
<h3>Delete Board</h3>
|
|
284
|
-
<p class="description">Delete this board from Orbit and remove its repo-local Orbit files. Export a snapshot first if you may need this board again.</p>
|
|
285
|
-
<div class="repository-delete-actions">
|
|
286
|
-
<button type="button" data-variant="secondary" id="deleteBoardExportFirst">Export first</button>
|
|
287
|
-
<button type="button" data-variant="ghost" class="danger-button" id="deleteBoardSkipExport">Delete without exporting</button>
|
|
288
|
-
<button type="button" data-variant="ghost" id="deleteBoardCancel">Cancel</button>
|
|
289
|
-
</div>
|
|
290
|
-
</div>
|
|
291
|
-
`;if(O.step==="confirm"){let a=O.exported?"Snapshot exported. Type the board slug to confirm permanent deletion.":"Type the board slug to confirm permanent deletion.";return`
|
|
292
|
-
<div class="section repository-danger-zone is-confirming">
|
|
293
|
-
<h3>Delete Board</h3>
|
|
294
|
-
<p class="description">${l(a)}</p>
|
|
295
|
-
<label class="repository-confirm-field">
|
|
296
|
-
<span>Board slug</span>
|
|
297
|
-
<strong>${t}</strong>
|
|
298
|
-
<input id="deleteBoardConfirmInput" autocomplete="off" spellcheck="false" placeholder="${t}" autofocus />
|
|
299
|
-
</label>
|
|
300
|
-
<div class="repository-delete-actions">
|
|
301
|
-
<button type="button" data-variant="primary" class="danger-button" id="deleteBoardConfirm" disabled>Delete board permanently</button>
|
|
302
|
-
<button type="button" data-variant="ghost" id="deleteBoardCancel">Cancel</button>
|
|
303
|
-
</div>
|
|
304
|
-
</div>
|
|
305
|
-
`}return`
|
|
306
|
-
<div class="section repository-danger-zone">
|
|
307
|
-
<h3>Delete Board</h3>
|
|
308
|
-
<p class="description">Remove this board from Orbit and delete its repo-local Orbit files. This cannot be undone without an exported snapshot.</p>
|
|
309
|
-
<button type="button" data-variant="ghost" class="danger-button" id="deleteBoardStart">Delete Board</button>
|
|
310
|
-
</div>
|
|
311
|
-
`}function Or(e){let t=e.board,a="Remember to mention SKILL-ORBIT.md to my AI agents if they get confused",n=t.project_notes||"",r=!!n.trim(),o=r?"":"is-placeholder",i=r?we(n):l(a);return`
|
|
312
|
-
<div class="section ai-fields">
|
|
313
|
-
<div class="inline-md-field">
|
|
314
|
-
<span class="inline-md-field-label">Notes For You</span>
|
|
315
|
-
<div
|
|
316
|
-
class="inline-md-field-body preserved-text-body editable-field settings-notes-body ${o}"
|
|
317
|
-
data-edit-field="project_notes"
|
|
318
|
-
tabindex="0"
|
|
319
|
-
title="Click to edit"
|
|
320
|
-
role="button"
|
|
321
|
-
aria-label="Edit Notes For You"
|
|
322
|
-
>${i}</div>
|
|
323
|
-
</div>
|
|
324
|
-
</div>
|
|
325
|
-
`}function fn(){return`
|
|
326
|
-
<div class="section">
|
|
327
|
-
<label class="orbit-check" for="showPriorityToggle">
|
|
328
|
-
<input
|
|
329
|
-
type="checkbox"
|
|
330
|
-
id="showPriorityToggle"
|
|
331
|
-
${c.showPriority?"checked":""}
|
|
332
|
-
/>
|
|
333
|
-
<span class="orbit-check-box" aria-hidden="true">
|
|
334
|
-
<svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
|
|
335
|
-
</span>
|
|
336
|
-
<span class="orbit-check-label">Show priority label on cards</span>
|
|
337
|
-
</label>
|
|
338
|
-
<p class="description lanes-description">Rename, reorder, add, or remove lanes.</p>
|
|
339
|
-
${Yr()}
|
|
340
|
-
<form id="laneCreateForm" class="lane-create-form">
|
|
341
|
-
<input name="name" placeholder="New lane name..." required />
|
|
342
|
-
<button type="submit" data-variant="primary">Add Lane</button>
|
|
343
|
-
</form>
|
|
344
|
-
</div>
|
|
345
|
-
`}function jr(){let e=ue(),t=Ce(),a=ct(),n=Ht(),r=a===null?`Using browser default (${n?"reduced motion":"motion allowed"}).`:"Using your saved preference.";return`
|
|
346
|
-
<div class="section appearance-theme-section">
|
|
347
|
-
<fieldset class="appearance-theme-field">
|
|
348
|
-
<legend>Theme</legend>
|
|
349
|
-
<div class="appearance-theme-options">
|
|
350
|
-
${hn("light","Light",e)}
|
|
351
|
-
${hn("dark","Dark",e)}
|
|
352
|
-
</div>
|
|
353
|
-
<p class="description">Applies the light/dark theme used by the top rail toggle.</p>
|
|
354
|
-
</fieldset>
|
|
355
|
-
</div>
|
|
356
|
-
<div class="section">
|
|
357
|
-
<label class="orbit-check" for="reduceMotionToggle">
|
|
358
|
-
<input
|
|
359
|
-
type="checkbox"
|
|
360
|
-
id="reduceMotionToggle"
|
|
361
|
-
${t?"checked":""}
|
|
362
|
-
/>
|
|
363
|
-
<span class="orbit-check-box" aria-hidden="true">
|
|
364
|
-
<svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
|
|
365
|
-
</span>
|
|
366
|
-
<span class="orbit-check-label">Reduce theme motion</span>
|
|
367
|
-
</label>
|
|
368
|
-
<p class="description">Turns off the animated starfield, meteors, and theme animations. ${l(r)}</p>
|
|
369
|
-
</div>
|
|
370
|
-
`}function hn(e,t,a){return`
|
|
371
|
-
<label class="appearance-theme-option">
|
|
372
|
-
<input type="radio" name="themePreference" value="${e}" ${a===e?"checked":""} />
|
|
373
|
-
<span class="appearance-theme-option-mark" aria-hidden="true">
|
|
374
|
-
<svg
|
|
375
|
-
class="appearance-theme-option-icon"
|
|
376
|
-
width="16"
|
|
377
|
-
height="16"
|
|
378
|
-
viewBox="0 0 24 24"
|
|
379
|
-
fill="none"
|
|
380
|
-
stroke="currentColor"
|
|
381
|
-
stroke-width="2"
|
|
382
|
-
stroke-linecap="round"
|
|
383
|
-
stroke-linejoin="round"
|
|
384
|
-
>${e==="dark"?'<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" />':'<circle cx="12" cy="12" r="5" /><path d="M12 1v2m0 18v2M4.2 4.2l1.4 1.4m12.8 12.8l1.4 1.4M1 12h2m18 0h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4" />'}</svg>
|
|
385
|
-
</span>
|
|
386
|
-
<span class="appearance-theme-option-label">${t}</span>
|
|
387
|
-
</label>
|
|
388
|
-
`}function Hr(e){return`
|
|
389
|
-
${e.board?.ai_enabled!==0?'<p class="description">Agent-facing project memory: decisions to follow and lessons to apply. Struck entries stay visible here but are excluded from agent context.</p>':""}
|
|
390
|
-
<div class="section">
|
|
391
|
-
${Gr(e.entries)}
|
|
392
|
-
<form id="projectEntryForm" class="comment-form project-entry-form">
|
|
393
|
-
<select name="type" class="select-chevron-field" aria-label="Entry type">
|
|
394
|
-
<option value="decision">Decision</option>
|
|
395
|
-
<option value="lesson">Lesson</option>
|
|
396
|
-
</select>
|
|
397
|
-
<input name="title" placeholder="Entry title..." required />
|
|
398
|
-
<textarea name="body" placeholder="Decision rationale or lesson: do X instead of Y when working with Z." required></textarea>
|
|
399
|
-
<button type="submit" data-variant="primary">Add Entry</button>
|
|
400
|
-
</form>
|
|
401
|
-
</div>
|
|
402
|
-
`}function qr(e){if(String(e||"").trimStart().startsWith("<"))throw new Error("That file looks like an HTML page instead of JSON. In Trello, use Export JSON/download the board JSON, not Save Page As.");try{return JSON.parse(e)}catch(a){throw new Error(a?.message||"invalid JSON")}}function Ur(e,t){let a=e.board;if(t==="repository"){let n=async(p=!!y("#exportProjectImages")?.checked)=>{let f=p?"?include_attachments=true":"",h=await v(`/api/boards/${encodeURIComponent(a.id)}/export${f}`),w=p?`${a.slug}.with-images.orbit.json`:`${a.slug}.orbit.json`;Ga(w,h)};y("#exportProject")?.addEventListener("click",async()=>{await n()}),y("#boardRenameForm")?.addEventListener("submit",async p=>{p.preventDefault();let f=new FormData(p.currentTarget),h=String(f.get("name")||"").trim();!h||h===a.name||(await v(`/api/boards/${encodeURIComponent(a.id)}`,{method:"PATCH",body:{name:h}}),await _(),c.detailMode="settings",b.success(`Renamed board to ${h}. Canonical slug unchanged: ${a.slug}`))});let r=async(p,f)=>{let h=await p.text(),w=qr(h);return v("/api/admin/import",{method:"POST",body:f(w)})},o=y("#importSnapshotAsNewFile");y("#importSnapshotAsNewButton")?.addEventListener("click",()=>{o?.click()}),o?.addEventListener("change",async p=>{let f=p.currentTarget,h=f.files?.[0];if(h)try{let w=await r(h,A=>({snapshot:A,create_new:!0,board_id:a.id}));c.boardId=w.imported_board_id,c.boardSlug=w.imported_board_slug,c.detailMode="empty",await _(),await P({boardId:w.imported_board_id,view:"board"});let $=w.imported_counts||{},M=Number.isFinite(Number($.tickets))?` (${$.tickets} cards)`:"";b.success(w.source_format==="trello-board-export"?`Trello board imported as new board${M}`:"Board imported as new board")}catch(w){b.error(`Import failed: ${w?.message||"invalid JSON"}`)}finally{f.value=""}});let i=y("#replaceImportConfirmInput"),s=y("#replaceCurrentBoardImportFile"),u=y("#replaceCurrentBoardImportButton");i?.addEventListener("input",()=>{let p=i.value.trim()!==a.slug;s&&(s.disabled=p),u&&(u.disabled=p)}),u?.addEventListener("click",()=>{u.disabled||s?.disabled||s?.click()}),s?.addEventListener("change",async p=>{let f=p.currentTarget,h=f.files?.[0];if(h){if(i.value.trim()!==a.slug){b.error("Type the board slug before replacing this board."),f.value="";return}try{let w=await r(h,A=>({snapshot:A,replace_existing:!0,board_id:a.id}));await _(),c.detailMode="settings";let $=w.imported_counts||{},M=Number.isFinite(Number($.tickets))?` (${$.tickets} cards)`:"";b.success(w.source_format==="trello-board-export"?`Trello board replaced from Trello import${M}`:"Board replaced from snapshot")}catch(w){b.error(`Import failed: ${w?.message||"invalid JSON"}`)}finally{f.value=""}}}),y("#deleteBoardStart")?.addEventListener("click",async()=>{O.step="choice",O.exported=!1,await he()}),y("#deleteBoardExportFirst")?.addEventListener("click",async()=>{await n(),O.step="confirm",O.exported=!0,await he()}),y("#deleteBoardSkipExport")?.addEventListener("click",async()=>{O.step="confirm",O.exported=!1,await he()}),y("#deleteBoardCancel")?.addEventListener("click",async()=>{O.step="idle",O.exported=!1,await he()});let d=y("#deleteBoardConfirmInput"),m=y("#deleteBoardConfirm");d?.addEventListener("input",()=>{m&&(m.disabled=d.value.trim()!==a.slug)}),m?.addEventListener("click",async()=>{await v(`/api/boards/${encodeURIComponent(a.id)}`,{method:"DELETE",body:{confirm_slug:a.slug,delete_files:!0}}),O.step="idle",O.exported=!1,await _(),b.success(`Deleted board: ${a.name}`),P({boardId:c.boardId,view:"board"},{replace:!0})})}if(t==="notes"){let n=k.querySelector('[data-edit-field="project_notes"]');if(n){let r=()=>je(n,{fieldName:"project_notes",multiline:!0,initialValue:a.project_notes||"",ariaLabel:"Edit Notes For You",rerender:async()=>{c.detailMode="settings",await he()},commit:async o=>{await v(`/api/boards/${a.id}`,{method:"PATCH",body:{project_notes:ie(o)}}),await _(),c.detailMode="settings",b.success("Saved")}});n.addEventListener("click",o=>{o.preventDefault(),r()}),n.addEventListener("keydown",o=>{(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),r())})}}if(t==="lanes"&&(k.querySelectorAll("[data-lane-action]").forEach(n=>{n.addEventListener("click",async()=>{await Wr(n)})}),k.querySelectorAll(".lane-row input, .lane-row select").forEach(n=>{n.addEventListener("change",async()=>{await yn(n.closest(".lane-row"))})}),y("#laneCreateForm")?.addEventListener("submit",async n=>{n.preventDefault();let r=new FormData(n.currentTarget);await v(`/api/boards/${a.id}/states`,{method:"POST",body:Object.fromEntries(r.entries())}),await _(),c.detailMode="settings",b.success("Lane added")}),y("#showPriorityToggle")?.addEventListener("change",n=>{c.showPriority=!!n.currentTarget.checked,localStorage.setItem("mab_show_priority",c.showPriority?"true":"false"),Fe()})),t==="appearance"&&(k.querySelectorAll("input[name='themePreference']").forEach(n=>{n.addEventListener("change",r=>{if(!r.currentTarget.checked)return;let o=Gt(r.currentTarget.value);b.success(o==="dark"?"Dark theme enabled":"Light theme enabled")})}),y("#reduceMotionToggle")?.addEventListener("change",n=>{let r=!!n.currentTarget.checked;Ca(r),ze(),window.dispatchEvent(new CustomEvent("orbit:motion-preference-change",{detail:{reduce:r}})),b.success(r?"Theme motion reduced":"Theme motion enabled")})),t==="ai"){let n=()=>{let o=y("#mcpOsSelect")?.value||_t(),i=y("#mcpClientSelect")?.value||"claude-code",s=It[i]||It.generic,u=mn(),d=ua(i,u),m=pa(o,u),p=k.querySelector("[data-mcp-os-note]");p&&(p.textContent=`Detected: ${Ct[_t()]?.label||"Linux"}`);let f=k.querySelector("[data-mcp-client-format]");f&&(f.textContent=s.format);let h=k.querySelector("[data-mcp-output-title]");h&&(h.textContent=`${s.label} setup`);let w=k.querySelector("[data-mcp-helper]");w&&(w.textContent=s.helper);let $=k.querySelector("[data-mcp-config]");$&&($.textContent=d);let M=k.querySelector("[data-mcp-command]");M&&(M.textContent=m)};y("#mcpOsSelect")?.addEventListener("change",()=>n()),y("#mcpClientSelect")?.addEventListener("change",()=>n()),["#mcpServerPathInput"].forEach(o=>{y(o)?.addEventListener("input",()=>n())}),k.querySelectorAll("[data-copy-mcp]").forEach(o=>{o.addEventListener("click",async()=>{let i=y("#mcpOsSelect")?.value||_t(),s=y("#mcpClientSelect")?.value||"claude-code",u=mn(),d=o.dataset.copyMcp==="command"?pa(i,u):ua(s,u);await zr(d),b.success(o.dataset.copyMcp==="command"?"MCP command copied":"MCP config copied")})});let r=k.querySelector('[data-edit-field="agent_instructions"]');if(r){let o=()=>je(r,{fieldName:"agent_instructions",multiline:!0,initialValue:a.agent_instructions||"",ariaLabel:"Edit Agent Instructions",rerender:async()=>{c.detailMode="settings",await he()},commit:async i=>{await v(`/api/boards/${a.id}`,{method:"PATCH",body:{agent_instructions:ie(i)}}),await _(),c.detailMode="settings",b.success("Saved")}});r.addEventListener("click",i=>{i.preventDefault(),o()}),r.addEventListener("keydown",i=>{(i.key==="Enter"||i.key===" ")&&(i.preventDefault(),o())})}y("#aiEnabledToggle")?.addEventListener("change",async o=>{let i=!!o.currentTarget.checked;await v(`/api/boards/${a.id}`,{method:"PATCH",body:{ai_enabled:i}}),await _(),c.detailMode="settings",b.success(i?"AI features enabled \u2014 agent lanes provisioned":"AI features disabled \u2014 AI Ready hidden")})}if(t==="archive"){let n=y("#archiveList");if(n){let r=async()=>{try{let i=(await v(`/api/boards/${encodeURIComponent(a.id)}/archive`)).tickets||[];if(i.length===0){n.innerHTML='<p class="description">No archived cards yet. Click the trash icon in the card detail header to archive a card.</p>';return}n.innerHTML=`<div class="detail-card-grid">${i.map(s=>at(s,{archive:{id:s.id},disableOpen:!0})).join("")}</div>`}catch(o){n.innerHTML=`<p class="description">Could not load archive: ${l(o?.message||"unknown error")}</p>`}};r(),n.addEventListener("click",async o=>{let i=o.target.closest("[data-archive-restore]"),s=o.target.closest("[data-archive-delete]");if(!i&&!s)return;o.stopPropagation();let d=(i||s).closest("article")?.querySelector("h3")?.textContent||"card";if(i){let m=i.dataset.archiveRestore;await v(S(`/api/tickets/${encodeURIComponent(m)}/restore`),{method:"POST"}),b.success(`Restored: ${d}`),await _(),await r();return}if(s){let m=s.dataset.archiveDelete;if(!confirm(`Permanently delete "${d}"? This cannot be undone.`))return;await v(S(`/api/tickets/${encodeURIComponent(m)}`),{method:"DELETE"}),b.success(`Deleted: ${d}`),await r()}})}}t==="journal"&&(k.querySelectorAll("[data-entry-action='toggle-struck']").forEach(n=>{n.addEventListener("click",async()=>{await v(S(`/api/board-entries/${n.dataset.entryId}`),{method:"PATCH",body:{struck:n.dataset.struck!=="true"}}),await _(),c.detailMode="settings",b.info(n.dataset.struck==="true"?"Project entry restored":"Project entry struck")})}),y("#projectEntryForm")?.addEventListener("submit",async n=>{n.preventDefault();let r=new FormData(n.currentTarget);await v(`/api/boards/${a.id}/entries`,{method:"POST",body:Object.fromEntries(r.entries())}),await _(),c.detailMode="settings",b.success("Project entry added")}))}async function zr(e){if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(e);return}let t=document.createElement("textarea");t.value=e,t.setAttribute("readonly",""),t.style.position="fixed",t.style.left="-9999px",document.body.append(t),t.select(),document.execCommand("copy"),t.remove()}function Gr(e=[]){return e.length?e.map(t=>{let a=!!t.struck_at;return`
|
|
403
|
-
<div class="comment project-entry ${l(t.type)} ${a?"is-struck":""}">
|
|
404
|
-
<div class="comment-meta">
|
|
405
|
-
<div>
|
|
406
|
-
<strong>${l(t.type.toUpperCase())}: ${l(t.title)}</strong>
|
|
407
|
-
${a?'<span class="project-entry-status">Excluded from agent context</span>':""}
|
|
408
|
-
</div>
|
|
409
|
-
<div class="project-entry-actions">
|
|
410
|
-
<span>${l(t.created_by)} - ${Ke(t.created_at)}</span>
|
|
411
|
-
<button
|
|
412
|
-
type="button"
|
|
413
|
-
data-variant="ghost"
|
|
414
|
-
class="project-entry-strike"
|
|
415
|
-
data-entry-action="toggle-struck"
|
|
416
|
-
data-entry-id="${l(t.id)}"
|
|
417
|
-
data-struck="${a?"true":"false"}"
|
|
418
|
-
aria-pressed="${a?"true":"false"}"
|
|
419
|
-
>${a?"Restore":"Strike"}</button>
|
|
420
|
-
</div>
|
|
421
|
-
</div>
|
|
422
|
-
<div class="comment-body">${l(t.body)}</div>
|
|
423
|
-
</div>
|
|
424
|
-
`}).join(""):'<p class="description">No project entries yet.</p>'}function Jr(e,t,a){return!!(t.get(e.id)||e.is_default||gn.has(e.role)||a&&e.role==="ai_ready")}function Kr(e,t,a){let n=t.get(e.id)||0;return n>0?`Cannot delete \u2014 ${n} card${n===1?"":"s"} in this lane`:e.is_default?"Cannot delete \u2014 default lane for new cards (move it first)":gn.has(e.role)?"Cannot delete \u2014 core kanban lane":a&&e.role==="ai_ready"?"Cannot delete \u2014 required by agent flow while AI is enabled":"Delete lane"}function Yr(){let e=Je(),t=Y()?.ai_enabled!==0,a=new Map;for(let n of W())a.set(n.state_id,(a.get(n.state_id)||0)+1);return`
|
|
425
|
-
<div class="lane-list">
|
|
426
|
-
${e.map((n,r)=>{let o=n.role==="ai_ready",i=o?"AI Ready lane name is locked \u2014 disable AI to hide this column":"";return`
|
|
427
|
-
<div class="lane-row${o?" lane-row--locked":""}" data-lane-id="${n.id}">
|
|
428
|
-
<button type="button" data-variant="ghost" class="settings-lane-icon-btn" data-lane-action="up" ${r===0?"disabled":""} title="Move lane left" aria-label="Move lane left">\u2191</button>
|
|
429
|
-
<button type="button" data-variant="ghost" class="settings-lane-icon-btn" data-lane-action="down" ${r===e.length-1?"disabled":""} title="Move lane right" aria-label="Move lane right">\u2193</button>
|
|
430
|
-
<input name="name" value="${l(n.name)}" aria-label="Lane name" ${o?`readonly title="${l(i)}"`:""} />
|
|
431
|
-
<button type="button" data-variant="${n.is_default?"secondary":"ghost"}" class="settings-lane-new-toggle ${n.is_default?"is-on":""}" data-lane-action="toggle-default" aria-pressed="${n.is_default?"true":"false"}" title="${n.is_default?"Default lane for new cards \u2014 click another lane to move it":"Make this the default lane for new cards"}" aria-label="Default lane for new cards">
|
|
432
|
-
<span class="lane-new-sun" aria-hidden="true"></span>
|
|
433
|
-
<span class="lane-new-label">New</span>
|
|
434
|
-
</button>
|
|
435
|
-
<button type="button" data-variant="ghost" class="settings-lane-delete-btn danger-button" data-lane-action="delete" ${Jr(n,a,t)?"disabled":""} title="${Kr(n,a,t)}" aria-label="Delete lane">\xD7</button>
|
|
436
|
-
</div>
|
|
437
|
-
`}).join("")}
|
|
438
|
-
</div>
|
|
439
|
-
`}async function Wr(e){let t=e.closest(".lane-row"),a=t.dataset.laneId,n=e.dataset.laneAction;if(n==="delete"){await v(S(`/api/states/${a}`),{method:"DELETE"}),await _(),c.detailMode="settings",b.success("Lane deleted");return}if(n==="toggle-default"){if(e.getAttribute("aria-pressed")==="true")return;k.querySelectorAll('[data-lane-action="toggle-default"]').forEach(u=>{u.setAttribute("aria-pressed","false"),u.setAttribute("data-variant","ghost"),u.classList.remove("is-on")}),e.setAttribute("aria-pressed","true"),e.setAttribute("data-variant","secondary"),e.classList.add("is-on"),await yn(t,{silent:!0}),b.success("Default lane updated");return}let r=Je().map(s=>s.id),o=r.indexOf(a),i=n==="up"?o-1:o+1;i<0||i>=r.length||([r[o],r[i]]=[r[i],r[o]],await v(`/api/boards/${c.boardId}/states`,{method:"PATCH",body:{state_ids:r}}),await _(),c.detailMode="settings",b.success("Lanes reordered"))}async function yn(e,{silent:t=!1}={}){let a=e.querySelector('input[name="name"]').value,r=e.querySelector('[data-lane-action="toggle-default"]')?.getAttribute("aria-pressed")==="true";await v(S(`/api/states/${e.dataset.laneId}`),{method:"PATCH",body:{name:a,is_default:r}}),await _(),c.detailMode="settings",t||b.success("Lane updated")}var O,Ct,It,gn,vn=C(()=>{z();ne();re();Z();pe();gt();fe();tt();ma();me();Oe();qt();Jt();O={boardId:"",step:"idle",exported:!1};Ct={windows:{label:"Windows",shell:"PowerShell",serverPath:"C:\\Path\\To\\StarscapeOrbit\\src\\mcp-server.js"},macos:{label:"macOS",shell:"Terminal",serverPath:"/Users/you/apps/starscape-orbit/src/mcp-server.js"},linux:{label:"Linux",shell:"Terminal",serverPath:"/home/you/apps/starscape-orbit/src/mcp-server.js"}},It={"claude-code":{label:"Claude Code",format:"CLI command",helper:"Run this command once. Claude Code records the local Orbit tool server for the project."},codex:{label:"Codex",format:"config.toml",helper:"Paste this TOML into Codex config. Project-local config may require the project to be trusted."},cursor:{label:"Cursor",format:"MCP JSON",helper:"Paste this JSON into Cursor MCP settings. Cursor starts Orbit when an agent needs the tools."},opencode:{label:"OpenCode",format:"opencode.json",helper:"Paste this into OpenCode config under the local MCP server list."},openclaw:{label:"OpenClaw",format:"MCP JSON",helper:"Paste this JSON into OpenClaw's MCP server list. OpenClaw will spawn Orbit on demand."},generic:{label:"Other MCP app",format:"MCP JSON",helper:"Most MCP clients accept this JSON shape for local stdio servers."}};gn=new Set(["in_progress","review"])});function Se(e){return String(e||"").trim()}function ha(e){let t=Se(e);return t?t.replace(/^@+/,"").replace(/[-_]+/g," ").replace(/\s+/g," ").trim().replace(/\b\w/g,a=>a.toUpperCase()):""}function kn({actor:e,actorId:t}={}){let a=Se(e),n=Se(t),r=a.toLowerCase(),o=n.toLowerCase();return a&&!fa.has(r)&&!wn.has(r)?ha(a):n&&!fa.has(o)&&!wn.has(o)?ha(n):""}function ba({actor:e,actorType:t,actorId:a}={}){let n=Se(t).toLowerCase(),r=Se(e),o=r.toLowerCase();if(n==="agent"){let i=kn({actor:r,actorId:a});return i?`Agent \xB7 ${i}`:"Agent"}return!r||fa.has(o)?"You":ha(r)}function $n(e={}){let t=Se(e.kind).toLowerCase(),a=Se(e.author);if(Xr.has(t)||t&&t!=="human_comment"){let r=kn({actor:a});return r?`Agent \xB7 ${r}`:"Agent"}return ba({actor:a,actorType:"human"})}var fa,wn,Xr,xn=C(()=>{fa=new Set(["local","local-owner","owner"]),wn=new Set(["agent","local-agent","orbit-agent"]),Xr=new Set(["agent_note","checkpoint","completion"])});async function ce(e,{renderBoardAfter:t=!1}={}){let[a,n,r]=await Promise.all([v(S(`/api/tickets/${e}/context?depth=1`)),v(S(`/api/tickets/${e}/history`)),v(S(`/api/tickets/${e}/comments`))]);a.ticket?.id===e&&(ja(a.ticket),t&&Fe(),!(c.detailMode!=="ticket"||c.selectedTicketId!==e)&&await Le({context:a,statusHistory:n,comments:r.comments||[]}))}async function be(e,t){await v(S(`/api/tickets/${e}`),{method:"PATCH",body:t}),await ce(e,{renderBoardAfter:!0})}function Vr(e,t,a){if(!e||!t||typeof t.clientX!="number"||typeof t.clientY!="number")return null;let n=null;if(document.caretPositionFromPoint){let r=document.caretPositionFromPoint(t.clientX,t.clientY);r?.offsetNode&&(r.offsetNode===e||e.contains(r.offsetNode))&&(n=document.createRange(),n.setStart(e,0),n.setEnd(r.offsetNode,r.offset))}else if(document.caretRangeFromPoint){let r=document.caretRangeFromPoint(t.clientX,t.clientY);r?.startContainer&&(r.startContainer===e||e.contains(r.startContainer))&&(n=document.createRange(),n.setStart(e,0),n.setEnd(r.startContainer,r.startOffset))}return n?Math.max(0,Math.min(a,n.toString().length)):null}function Qr(e){let t=e?.closest?.(".drawer-inner");return t?{scrollContainer:t,nodeRect:e.getBoundingClientRect(),scrollTop:t.scrollTop}:null}function Zr(e,t){if(!e?.scrollContainer||!t?.isConnected)return;let a=t.getBoundingClientRect(),n=Math.max(0,e.scrollContainer.scrollTop+(a.top-e.nodeRect.top));Number.isFinite(n)?e.scrollContainer.scrollTop=n:e.scrollContainer.scrollTop=e.scrollTop}function eo(e){try{e.focus({preventScroll:!0})}catch{e.focus()}}function je(e,t){if(!e||e.dataset.editing==="true")return;let{fieldName:a,multiline:n=!0,initialValue:r="",ariaLabel:o,emptyMessage:i,sourceEvent:s,rerender:u,commit:d}=t,m=n?Qr(e):null,p=String(r),f=n?Vr(e,s,p.length):null;e.dataset.editing="true";let h=document.createElement(n?"textarea":"input");if(h.className=n?"inline-desc-editor":"inline-title-editor",h.value=p,a&&h.setAttribute("data-editing-field",a),o&&h.setAttribute("aria-label",o),!n)h.type="text",h.maxLength=500,h.autocomplete="off",h.spellcheck=!0;else{h.rows=Math.max(3,p.split(`
|
|
440
|
-
`).length+1);let x=e.getBoundingClientRect().height;Number.isFinite(x)&&x>0&&(h.style.minHeight=`${Math.ceil(x)}px`)}if(e.replaceWith(h),eo(h),!n)h.select();else{let x=f??0;h.setSelectionRange(x,x),Zr(m,h)}let w=!1,$=()=>{document.removeEventListener("pointerdown",te,!0)},M=()=>{h.isConnected&&h.replaceWith(e),delete e.dataset.editing},A=()=>{w=!0,$(),M()};function te(x){w||x.target===h||h.contains(x.target)||h.blur()}document.addEventListener("pointerdown",te,!0);let B=async()=>{if(w)return;let x=n?h.value:h.value.trim();if(A(),String(x)===String(r)){u&&await u();return}if(i&&!x.trim()){b.warning(i),u&&await u();return}d&&await d(x)},L=async()=>{w||(A(),u&&await u())};h.addEventListener("blur",B),h.addEventListener("keydown",x=>{if(x.key==="Escape"){x.preventDefault(),L();return}x.key==="Enter"&&(n?(x.metaKey||x.ctrlKey)&&(x.preventDefault(),B()):(x.preventDefault(),B()))})}function to(e){let t=e.id,a=()=>je(n,{fieldName:"title",multiline:!1,initialValue:e.title||"",ariaLabel:"Edit ticket title",emptyMessage:"Title cannot be empty",rerender:()=>Le(),commit:d=>be(t,{title:d})}),n=U.querySelector('[data-edit-field="title"]');n&&(n.addEventListener("click",a),n.addEventListener("keydown",d=>{(d.key==="Enter"||d.key===" ")&&(d.preventDefault(),a())}));let r=k.querySelector('[data-edit-field="description"]'),o=d=>je(r,{fieldName:"description",multiline:!0,initialValue:e.description||"",ariaLabel:"Edit ticket description",sourceEvent:d,rerender:()=>Le(),commit:m=>be(t,{description:ie(m)})});r&&(r.addEventListener("click",d=>{d.preventDefault(),o(d)}),r.addEventListener("keydown",d=>{(d.key==="Enter"||d.key===" ")&&(d.preventDefault(),o())}));let i=[{key:"ai_plan",label:"AI plan"},{key:"implementation_summary",label:"Implementation summary"},{key:"implementation_updates",label:"Implementation updates / lessons"}];for(let{key:d,label:m}of i){let p=k.querySelector(`[data-edit-field="${d}"]`);if(!p)continue;let f=h=>je(p,{fieldName:d,multiline:!0,initialValue:e[d]||"",ariaLabel:`Edit ${m}`,sourceEvent:h,rerender:()=>Le(),commit:w=>be(t,{[d]:ie(w)})});p.addEventListener("click",h=>{h.preventDefault(),f(h)}),p.addEventListener("keydown",h=>{(h.key==="Enter"||h.key===" ")&&(h.preventDefault(),f())})}U.querySelectorAll(".meta-select[data-meta-field]").forEach(d=>{d.addEventListener("change",async()=>{let m=d.dataset.metaField;if(m==="type"){await be(t,{type:d.value});return}if(m==="state_id"){await be(t,{state_id:d.value});return}if(m==="parent_ticket_id"){await be(t,{parent_ticket_id:d.value||null});return}m==="priority"&&await be(t,{priority:Number(d.value)})})});let s=async d=>{await be(t,{labels:d})};k.querySelectorAll("[data-remove-label]").forEach(d=>{d.addEventListener("click",async()=>{let m=d.getAttribute("data-remove-label"),p=(e.labels||[]).map(f=>f.name).filter(f=>f!==m);await s(p)})});let u=async()=>{let d=k.querySelector("[data-label-input]");if(!d)return;let m=d.value.trim();if(!m)return;let p=(e.labels||[]).map(f=>f.name);if(p.includes(m)){d.value="";return}p.push(m),d.value="",await s(p)};k.querySelector("[data-label-add]")?.addEventListener("click",()=>{u()}),k.querySelector("[data-label-input]")?.addEventListener("keydown",d=>{d.key==="Enter"&&(d.preventDefault(),u())})}async function Le(e={}){if(c.detailMode==="settings"){await he(),Zt();return}if(!c.selectedTicketId){ht();return}let t=c.selectedTicketId,a=g=>Object.prototype.hasOwnProperty.call(e,g),[n,r,o,i]=await Promise.all([a("context")?Promise.resolve(e.context):v(S(`/api/tickets/${t}/context?depth=1`)),a("statusHistory")?Promise.resolve(e.statusHistory):v(S(`/api/tickets/${t}/history`)),a("comments")?Promise.resolve(e.comments):v(S(`/api/tickets/${t}/comments`)).then(g=>g.comments||[]),a("attachmentList")?Promise.resolve(e.attachmentList):H.attachments?v(S(`/api/tickets/${t}/attachments`)).catch(()=>({attachments:[]})):Promise.resolve({attachments:[]})]);if(c.detailMode!=="ticket"||c.selectedTicketId!==t||n.ticket?.id!==t)return;let s=n.ticket;St(s.id,s.updated_at),document.querySelector(`.card[data-ticket-id="${CSS.escape(s.id)}"] .card-unread-dot`)?.remove();let u=Oa(s.state_id),d=["epic","feature","task","bug"].map(g=>`<option value="${l(g)}" ${N(s.type)===g?"selected":""}>${l(qa(g))}</option>`).join(""),m=u.map(g=>`<option value="${l(g.id)}" ${g.id===s.state_id?"selected":""}>${l(g.name)}</option>`).join(""),p=[0,1,2,3,4].map(g=>`<option value="${g}" ${Number(s.priority)===g?"selected":""}>${l(Re(g))}</option>`).join(""),f=(s.labels||[]).map(g=>`
|
|
441
|
-
<span class="label-pill-removable" style="--label-color: ${l(g.color)}">
|
|
442
|
-
${l(g.name)}
|
|
443
|
-
<button type="button" class="label-pill-remove" data-remove-label="${l(g.name)}" title="Remove label">\xD7</button>
|
|
444
|
-
</span>`).join(""),w=Fa().map(g=>g.name).filter(g=>!s.labels?.some(I=>I.name===g)).map(g=>`<option value="${l(g)}"></option>`).join(""),$=N(s.type),M=De(s.priority),A=u.find(g=>g.id===s.state_id),te=Ye(A),B=s.description?we(s.description):l("No description yet."),L=s.description?"description preserved-text-body editable-field":"description editable-field is-placeholder",x=`
|
|
445
|
-
<span class="ticket-number">${l(Q(s))}</span>
|
|
446
|
-
<div class="detail-meta-badge-row" aria-label="Ticket metadata controls">
|
|
447
|
-
<select class="detail-meta-badge detail-state-badge meta-select" data-variant="${l(te)}" data-meta-field="state_id" aria-label="Ticket state">${m}</select>
|
|
448
|
-
<select class="detail-meta-badge detail-type-badge type-pill-${l($)} meta-select" data-meta-field="type" aria-label="Ticket type">${d}</select>
|
|
449
|
-
<select class="detail-meta-badge detail-priority-badge priority-pill meta-select" data-variant="${l(M)}" data-meta-field="priority" aria-label="Ticket priority">${p}</select>
|
|
450
|
-
</div>
|
|
451
|
-
`;bt({mode:"ticket",title:s.title,titleAttrs:{class:"editable-field","data-edit-field":"title",role:"button",tabindex:"0",title:"Click or press Enter to edit ticket title","aria-label":"Edit ticket title"},subtitleHtml:x,body:`
|
|
452
|
-
<div class="detail-head">
|
|
453
|
-
<p class="detail-updated" title="Last saved change">Updated ${l(Ha(s.updated_at))}</p>
|
|
454
|
-
<div class="${L}" data-edit-field="description" tabindex="0" title="Click to edit">${B}</div>
|
|
455
|
-
<dl class="ticket-meta" id="ticketMetaGrid" data-ticket-id="${l(s.id)}">
|
|
456
|
-
<div class="ticket-meta-row ticket-meta-row--stack">
|
|
457
|
-
<dt>Labels</dt>
|
|
458
|
-
<dd>
|
|
459
|
-
<div class="label-pills-row" data-other-labels>${f}</div>
|
|
460
|
-
<div class="label-add-row">
|
|
461
|
-
<input type="text" class="label-add-input meta-inline-input" data-label-input list="board-label-suggestions" placeholder="Add label\u2026" aria-label="Add label" />
|
|
462
|
-
<datalist id="board-label-suggestions">${w}</datalist>
|
|
463
|
-
<button type="button" class="ghost meta-add-btn" data-label-add>Add</button>
|
|
464
|
-
</div>
|
|
465
|
-
</dd>
|
|
466
|
-
</div>
|
|
467
|
-
</dl>
|
|
468
|
-
</div>
|
|
469
|
-
|
|
470
|
-
${po(s,n)}
|
|
471
|
-
|
|
472
|
-
${H.attachments?no(s,i.attachments||[]):""}
|
|
473
|
-
|
|
474
|
-
${mo(s,n)}
|
|
475
|
-
|
|
476
|
-
<div class="section">
|
|
477
|
-
<h3>Related</h3>
|
|
478
|
-
${uo(n.relations,s)}
|
|
479
|
-
</div>
|
|
480
|
-
|
|
481
|
-
${Y()?.ai_enabled!==0?`
|
|
482
|
-
<details class="section ai-fields" data-ai-plan-toggle data-ticket-id="${l(s.id)}"${ao(s.id)?" open":""}>
|
|
483
|
-
<summary><h3>AI Plan / Implementation Record</h3></summary>
|
|
484
|
-
<div class="ai-fields-grid">
|
|
485
|
-
${ya({fieldName:"ai_plan",label:"AI-Written Plan",value:s.ai_plan,placeholder:"Paste or let an AI write the plan..."})}
|
|
486
|
-
${ya({fieldName:"implementation_summary",label:"Implementation Summary",value:s.implementation_summary,placeholder:"What changed, what shipped, what remains..."})}
|
|
487
|
-
${ya({fieldName:"implementation_updates",label:"Implementation Updates / Lessons",value:s.implementation_updates,placeholder:"Progress notes, mistakes to avoid, discoveries..."})}
|
|
488
|
-
</div>
|
|
489
|
-
</details>`:""}
|
|
490
|
-
|
|
491
|
-
<div class="section">
|
|
492
|
-
<h3>Status History</h3>
|
|
493
|
-
${co(r)}
|
|
494
|
-
</div>
|
|
495
|
-
|
|
496
|
-
<div class="section">
|
|
497
|
-
<h3>Comments</h3>
|
|
498
|
-
${o.map(lo).join("")||'<p class="description">No comments yet.</p>'}
|
|
499
|
-
<form class="comment-form" id="detailCommentForm">
|
|
500
|
-
<textarea name="body" placeholder="Add a comment, decision, or instruction..." required></textarea>
|
|
501
|
-
<button type="submit">Comment</button>
|
|
502
|
-
</form>
|
|
503
|
-
</div>
|
|
504
|
-
`}),to(s),H.attachments&&io(s),y("#detailCommentForm").addEventListener("submit",async g=>{g.preventDefault();let I=ie(new FormData(g.currentTarget).get("body")).trim();I&&(await v(S(`/api/tickets/${s.id}/comments`),{method:"POST",body:{body:I}}),await ce(s.id))}),k.querySelectorAll("[data-open-ticket]").forEach(g=>{g.addEventListener("click",async I=>{I.target.closest("[data-detach-child],[data-detach-parent],[data-detach-relation]")||P({boardId:c.boardId,view:"ticket",ticketId:g.dataset.openTicket})})}),k.querySelectorAll("[data-detach-child]").forEach(g=>{g.addEventListener("click",async I=>{I.stopPropagation();let D=g.dataset.detachChild;try{await v(S(`/api/tickets/${D}`),{method:"PATCH",body:{parent_ticket_id:null}})}catch(q){b.error(q.message||"Failed to remove child");return}await ce(s.id,{renderBoardAfter:!0}),b.success("Removed from epic")})}),k.querySelectorAll("[data-detach-parent]").forEach(g=>{g.addEventListener("click",async I=>{I.stopPropagation();try{await v(S(`/api/tickets/${s.id}`),{method:"PATCH",body:{parent_ticket_id:null}})}catch(D){b.error(D.message||"Failed to remove parent");return}await ce(s.id,{renderBoardAfter:!0}),b.success("Parent epic removed")})}),k.querySelectorAll("[data-detach-relation]").forEach(g=>{g.addEventListener("click",async I=>{I.stopPropagation();let D=g.dataset.detachRelation;try{await v(S(`/api/relations/${D}`),{method:"DELETE"})}catch(q){b.error(q.message||"Failed to remove relation");return}await ce(s.id),b.success("Relation removed")})});let R=k.querySelector("[data-ai-plan-toggle]");R&&R.addEventListener("toggle",()=>{let g=R.dataset.ticketId;R.open?localStorage.setItem(`mab_ai_plan_open:${g}`,"1"):localStorage.removeItem(`mab_ai_plan_open:${g}`)});let j=k.querySelector("#parentEpicForm");if(j){let g=j.querySelector("[name=target]"),I=j.querySelector("[data-parent-epic-clear]"),D=()=>{j.classList.toggle("has-value",g.value.length>0)};g.addEventListener("input",D),D(),I.addEventListener("click",()=>{g.value="",D(),g.focus()}),j.addEventListener("submit",async q=>{q.preventDefault();let X=String(new FormData(q.currentTarget).get("target")||"").trim();if(!X)return;let J=Sn(X,s.id);if(!J){b.warning("Pick an epic from the suggestions");return}let ae=W().find(K=>K.id===J);if(!ae||N(ae.type)!=="epic"){b.warning("Parent must be an epic");return}try{await v(S(`/api/tickets/${s.id}`),{method:"PATCH",body:{parent_ticket_id:J}})}catch(K){b.error(K.message||"Failed to set parent epic");return}await ce(s.id,{renderBoardAfter:!0}),b.success("Parent epic set")})}let E=k.querySelector("#relatedAddForm");if(E){let g=E.querySelector("[name=target]"),I=E.querySelector("[data-related-clear]"),D=()=>{E.classList.toggle("has-value",g.value.length>0)};g.addEventListener("input",D),D(),I.addEventListener("click",()=>{g.value="",D(),g.focus()}),E.addEventListener("submit",async q=>{q.preventDefault();let X=new FormData(q.currentTarget),J=String(X.get("target")||"").trim(),ae=String(X.get("type")||"relates_to");if(!J)return;let K=Sn(J,s.id);if(!K){b.warning("Pick a ticket from the suggestions");return}try{await v(S("/api/relations"),{method:"POST",body:{source_ticket_id:s.id,target_ticket_id:K,type:ae}})}catch(Ee){b.error(Ee.message||"Failed to add relation");return}await ce(s.id)})}Zt()}function ao(e){return localStorage.getItem(`mab_ai_plan_open:${e}`)==="1"}function no(e,t=[]){let a=t.length?`<div class="attachment-grid">${t.map(n=>ro(e,n)).join("")}</div>`:'<p class="description">No images attached yet.</p>';return`
|
|
505
|
-
<div class="section attachments-section" data-attachment-section data-ticket-id="${l(e.id)}">
|
|
506
|
-
<div class="attachment-heading-row">
|
|
507
|
-
<h3>Images</h3>
|
|
508
|
-
<label class="ghost attachment-upload-button">
|
|
509
|
-
Upload
|
|
510
|
-
<input type="file" accept="image/*" multiple data-attachment-input />
|
|
511
|
-
</label>
|
|
512
|
-
</div>
|
|
513
|
-
<div class="attachment-dropzone" data-attachment-dropzone tabindex="0">
|
|
514
|
-
<strong>Paste, drag, or upload images</strong>
|
|
515
|
-
<span>PNG, JPG, GIF, WebP, SVG, BMP, or AVIF up to 10 MB each.</span>
|
|
516
|
-
</div>
|
|
517
|
-
${a}
|
|
518
|
-
</div>
|
|
519
|
-
`}function ro(e,t){let a=l(t.original_name||"image"),n=`${oo(t.size_bytes)} \xB7 ${l(t.mime_type||"image")}`;if(t.missing)return`
|
|
520
|
-
<article class="attachment-card is-missing">
|
|
521
|
-
<div class="attachment-missing-thumb">Missing</div>
|
|
522
|
-
<div class="attachment-card-meta"><strong>${a}</strong><span>${n}</span></div>
|
|
523
|
-
<button type="button" class="attachment-delete" data-delete-attachment="${l(t.id)}" aria-label="Remove missing image">\xD7</button>
|
|
524
|
-
</article>
|
|
525
|
-
`;let r=S(t.content_url||`/api/tickets/${e.id}/attachments/${t.id}/content`);return`
|
|
526
|
-
<article class="attachment-card">
|
|
527
|
-
<button type="button" class="attachment-thumb-button" data-open-lightbox="${l(r)}" data-lightbox-title="${a}">
|
|
528
|
-
<img src="${l(r)}" alt="${a}" loading="lazy" />
|
|
529
|
-
</button>
|
|
530
|
-
<div class="attachment-card-meta"><strong>${a}</strong><span>${n}</span></div>
|
|
531
|
-
<button type="button" class="attachment-delete" data-delete-attachment="${l(t.id)}" aria-label="Remove image">\xD7</button>
|
|
532
|
-
</article>
|
|
533
|
-
`}function oo(e){let t=Number(e||0);return t<1024?`${t} B`:t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/(1024*1024)).toFixed(1)} MB`}function Ln(e){return[...e||[]].filter(t=>t&&String(t.type||"").startsWith("image/"))}async function ga(e,t){let a=Ln(t);if(!a.length){b.warning("No image files found");return}for(let n of a){let r=await fetch(S(`/api/tickets/${e.id}/attachments?filename=${encodeURIComponent(n.name||"image")}`),{method:"POST",headers:{"Content-Type":n.type||"application/octet-stream","X-File-Name":n.name||"image"},body:n});if(!r.ok){let o={};try{o=await r.json()}catch{}throw new Error(o.error||`Upload failed (${r.status})`)}}await ce(e.id),b.success(a.length===1?"Image attached":`${a.length} images attached`)}function io(e){let t=k.querySelector("[data-attachment-section]");if(!t)return;let a=t.querySelector("[data-attachment-input]");a?.addEventListener("change",async()=>{try{await ga(e,a.files),a.value=""}catch(r){b.error(r.message||"Upload failed")}});let n=t.querySelector("[data-attachment-dropzone]");n?.addEventListener("dragover",r=>{r.preventDefault(),n.classList.add("is-dragover")}),n?.addEventListener("dragleave",()=>n.classList.remove("is-dragover")),n?.addEventListener("drop",async r=>{r.preventDefault(),n.classList.remove("is-dragover");try{await ga(e,r.dataTransfer?.files||[])}catch(o){b.error(o.message||"Upload failed")}}),k.onpaste=async r=>{if(r.target.closest("input, textarea, [contenteditable='true']"))return;let o=Ln(r.clipboardData?.files||[]);if(o.length){r.preventDefault();try{await ga(e,o)}catch(i){b.error(i.message||"Paste upload failed")}}},t.querySelectorAll("[data-delete-attachment]").forEach(r=>{r.addEventListener("click",async()=>{try{await v(S(`/api/tickets/${e.id}/attachments/${r.dataset.deleteAttachment}`),{method:"DELETE"}),await ce(e.id),b.success("Image removed")}catch(o){b.error(o.message||"Delete failed")}})}),t.querySelectorAll("[data-open-lightbox]").forEach(r=>{r.addEventListener("click",()=>so(r.dataset.openLightbox,r.dataset.lightboxTitle||"Image"))})}function so(e,t){document.querySelector(".attachment-lightbox")?.remove();let n=document.createElement("div");n.className="attachment-lightbox",n.innerHTML=`
|
|
534
|
-
<div class="attachment-lightbox-panel" role="dialog" aria-modal="true" aria-label="${l(t)}">
|
|
535
|
-
<button type="button" class="attachment-lightbox-close" aria-label="Close image">\xD7</button>
|
|
536
|
-
<img src="${l(e)}" alt="${l(t)}" />
|
|
537
|
-
<p>${l(t)}</p>
|
|
538
|
-
</div>
|
|
539
|
-
`;let r=()=>{document.removeEventListener("keydown",o),n.remove()};function o(i){i.key==="Escape"&&r()}n.addEventListener("click",i=>{(i.target===n||i.target.closest(".attachment-lightbox-close"))&&r()}),document.addEventListener("keydown",o),document.body.append(n)}function Sn(e,t){let a=e.trim(),n=a.match(/^#?(\d+)\b/),r=W().filter(i=>i.id!==t);if(n){let i=Number(n[1]),s=r.find(u=>u.number===i);if(s)return s.id}let o=r.find(i=>`#${i.number} \u2014 ${i.title}`===a);return o?o.id:""}function co(e){return!e||e.length===0?'<p class="description">No status changes yet.</p>':e.map(t=>{let a=t.body,n=a.from?l(a.from):"?",r=l(a.to||a.state||a.next_state||"?"),o=l(ba({actor:t.actor,actorType:a.actor_type||"human",actorId:a.actor_id}));return`<div class="comment status-change">
|
|
540
|
-
<div class="comment-meta">
|
|
541
|
-
<span class="status-route">${n} \u2192 ${r}</span>
|
|
542
|
-
<span>${o} \xB7 ${Ke(t.created_at)}</span>
|
|
543
|
-
</div>
|
|
544
|
-
</div>`}).join("")}function lo(e){return`
|
|
545
|
-
<div class="comment ${e.kind==="checkpoint"?"checkpoint":""}">
|
|
546
|
-
<div class="comment-meta">
|
|
547
|
-
<strong>${l($n(e))}</strong>
|
|
548
|
-
<span>${e.kind==="human_comment"?"":`${l(e.kind)} - `}${Ke(e.created_at)}</span>
|
|
549
|
-
</div>
|
|
550
|
-
<div class="comment-body preserved-text-body">${we(e.body)}</div>
|
|
551
|
-
</div>
|
|
552
|
-
`}function ya({fieldName:e,label:t,value:a,placeholder:n}){let r=a||"",o=!!r.trim(),i=o?we(r):l(n||""),s=o?"":"is-placeholder";return`
|
|
553
|
-
<div class="inline-md-field">
|
|
554
|
-
<span class="inline-md-field-label">${l(t)}</span>
|
|
555
|
-
<div
|
|
556
|
-
class="inline-md-field-body preserved-text-body editable-field ${s}"
|
|
557
|
-
data-edit-field="${l(e)}"
|
|
558
|
-
tabindex="0"
|
|
559
|
-
title="Click to edit"
|
|
560
|
-
role="button"
|
|
561
|
-
aria-label="Edit ${l(t)}"
|
|
562
|
-
>${i}</div>
|
|
563
|
-
</div>
|
|
564
|
-
`}function uo(e=[],t){let a=new Set(e.map(u=>u.other_ticket.id)),n=new Set;if(t?.parent_ticket_id&&n.add(t.parent_ticket_id),t?.id)for(let u of W())u.parent_ticket_id===t.id&&n.add(u.id);let o=W().filter(u=>u.id!==t?.id&&!a.has(u.id)&&!n.has(u.id)).map(u=>`<option value="#${u.number} \u2014 ${l(u.title)}"></option>`).join(""),i=e.length?`<div class="detail-card-grid">${e.map(u=>fo(u)).join("")}</div>`:"",s=`
|
|
565
|
-
<form id="relatedAddForm" class="related-add-row">
|
|
566
|
-
<div class="related-add-input-wrap">
|
|
567
|
-
<input type="text" name="target" class="related-add-input meta-inline-input" list="related-ticket-suggestions" placeholder="Add related ticket\u2026" aria-label="Add related ticket" autocomplete="off" />
|
|
568
|
-
<button type="button" class="related-add-clear" data-related-clear aria-label="Clear" tabindex="-1">\xD7</button>
|
|
569
|
-
<datalist id="related-ticket-suggestions">${o}</datalist>
|
|
570
|
-
</div>
|
|
571
|
-
<select name="type" class="related-add-type meta-inline" aria-label="Relation type">
|
|
572
|
-
<option value="relates_to">relates to</option>
|
|
573
|
-
<option value="blocks">blocks</option>
|
|
574
|
-
<option value="blocked_by">blocked by</option>
|
|
575
|
-
</select>
|
|
576
|
-
<button type="submit" class="ghost meta-add-btn">Add</button>
|
|
577
|
-
</form>
|
|
578
|
-
`;return i+s}function po(e,t){if(N(e.type)==="epic"){let n=t.child_tickets||[];return`
|
|
579
|
-
<div class="section">
|
|
580
|
-
<h3>Features inside this Epic</h3>
|
|
581
|
-
${n.length?`<div class="detail-card-grid">${n.map(o=>at(o,{detach:{mode:"child",id:o.id}})).join("")}</div>`:'<p class="description">No child features yet. Create a feature / task card and set this epic as its parent.</p>'}
|
|
582
|
-
</div>
|
|
583
|
-
`}return""}function mo(e,t){if(N(e.type)==="epic")return"";let n=W().filter(o=>o.type==="epic"&&o.id!==e.id).map(o=>`<option value="#${o.number} \u2014 ${l(o.title)}"></option>`).join("");return`
|
|
584
|
-
<div class="section">
|
|
585
|
-
<h3>Epic</h3>
|
|
586
|
-
${t.parent_ticket?`<div class="detail-card-grid detail-card-grid--single">${at(t.parent_ticket,{detach:{mode:"parent"}})}</div>`:`
|
|
587
|
-
<form id="parentEpicForm" class="related-add-row">
|
|
588
|
-
<div class="related-add-input-wrap">
|
|
589
|
-
<input type="text" name="target" class="related-add-input meta-inline-input" list="parent-epic-suggestions" placeholder="Set parent epic\u2026" aria-label="Set parent epic" autocomplete="off" />
|
|
590
|
-
<button type="button" class="related-add-clear" data-parent-epic-clear aria-label="Clear" tabindex="-1">\xD7</button>
|
|
591
|
-
<datalist id="parent-epic-suggestions">${n}</datalist>
|
|
592
|
-
</div>
|
|
593
|
-
<button type="submit" class="ghost meta-add-btn">Set</button>
|
|
594
|
-
</form>
|
|
595
|
-
`}
|
|
596
|
-
</div>
|
|
597
|
-
`}function at(e,t={}){let a=N(e.type),n=De(e.priority),r=Ye(e),o=Be(e.type).toUpperCase(),i=t.eyebrow?`<span class="card-eyebrow">${l(t.eyebrow)}</span>`:"",s="";if(t.detach){let{mode:m,id:p}=t.detach,f="",h="";m==="child"?(f=`data-detach-child="${l(p)}"`,h="Remove from epic"):m==="parent"?(f='data-detach-parent="1"',h="Remove parent epic"):m==="relation"&&(f=`data-detach-relation="${l(p)}"`,h="Remove relation"),s=`<button type="button" class="card-detach-btn" ${f} aria-label="${h}" title="${h}">\xD7</button>`}else if(t.archive){let m=l(t.archive.id);s=`
|
|
598
|
-
<div class="card-archive-actions">
|
|
599
|
-
<button type="button" class="card-archive-btn card-archive-btn--restore" data-archive-restore="${m}" aria-label="Restore to board" title="Restore to board"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12a9 9 0 1 0 3-6.7"/><path d="M3 4v5h5"/></svg></button>
|
|
600
|
-
<button type="button" class="card-archive-btn card-archive-btn--delete" data-archive-delete="${m}" aria-label="Delete permanently" title="Delete permanently"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg></button>
|
|
601
|
-
</div>
|
|
602
|
-
`}let u=xt(e),d=u===0?"":u===1?'<span class="card-unread-dot" role="status" aria-label="1 unread update"></span>':`<span class="card-unread-dot has-count" role="status" aria-label="${u} unread updates">${u>99?"99+":u}</span>`;return`
|
|
603
|
-
<article class="card card--detail type-${l(a)} priority-${l(n)}" data-variant="${l(a)}"${t.disableOpen?"":` data-open-ticket="${l(e.id)}"`}>
|
|
604
|
-
${d}
|
|
605
|
-
${s}
|
|
606
|
-
${i}
|
|
607
|
-
<h3>${l(e.title)}</h3>
|
|
608
|
-
<div class="card-meta">
|
|
609
|
-
<div class="card-expand-trigger card-expand-trigger--static">
|
|
610
|
-
<span class="ticket-number">${l(Q(e))}</span>
|
|
611
|
-
<div class="card-type-id type-pill-${l(a)}">
|
|
612
|
-
${Et(e.type)}
|
|
613
|
-
<span class="card-type-label">${l(o)}</span>
|
|
614
|
-
</div>
|
|
615
|
-
<span class="detail-card-state" data-variant="${l(r)}">${l(e.state_name||"State")}</span>
|
|
616
|
-
${c.showPriority?da(e.priority):""}
|
|
617
|
-
</div>
|
|
618
|
-
</div>
|
|
619
|
-
</article>
|
|
620
|
-
`}function fo(e){let t=String(e.type||"").replace(/_/g," "),a=e.direction==="incoming"?"from":"to";return at(e.other_ticket,{eyebrow:`${t} ${a}`,detach:{mode:"relation",id:e.id}})}var ma=C(()=>{z();ne();re();tt();gt();fe();Z();Oe();pe();vn();Ze();xn()});function va(){He&&(clearTimeout(He),He=null),c.eventSource&&(c.eventSource.close(),c.eventSource=null);let e=new URLSearchParams;c.boardId&&e.set("board_id",c.boardId);let t=`/api/events/stream${e.toString()?`?${e}`:""}`,a=new EventSource(t);c.eventSource=a,a.addEventListener("open",()=>{Mt=1e3,Pt&&(b.success("Live updates reconnected"),Pt=!1)}),a.addEventListener("board",async n=>{let r;try{r=JSON.parse(n.data)}catch{return}let o=r.type,i=()=>c.data?.tickets?.find(d=>d.id===r.ticket_id),s=c.data?.actor?.name,u=r.actor&&s&&r.actor===s;if(r.ticket_id&&(u?St(r.ticket_id,r.timestamp):o==="ticket_deleted"?on(r.ticket_id):ho.has(o)&&rn(r.ticket_id)),o==="ticket_created")b.success(`Card created: ${r.data.title||"New card"}`),ge();else if(o==="state_changed"){let d=i(),m=d?`#${d.number} ${d.title}`:"Card";b.info(`${m} \u2192 ${r.data.to}`),ge()}else if(o==="ticket_updated"){let d=i(),m=d?`#${d.number}`:"Card",p=r.data.fields?Object.keys(r.data.fields).join(", "):"";b.info(`${m} updated${p?`: ${p}`:""}`),ge()}else if(o==="comment_created"){let d=i(),m=d?`#${d.number}`:"ticket";b.info(`New comment on ${m}`),ge()}else if(o==="agent_claimed"){let d=i();b.info(`Agent claimed ${d?`#${d.number} ${d.title}`:"a ticket"}`),ge()}else if(o==="checkpoint_requested"){let d=i();b.warning(`Checkpoint on ${d?`#${d.number}`:"ticket"} \u2014 needs human input`),ge()}else if(o==="agent_completed"){let d=i();b.success(`Agent completed ${d?`#${d.number} ${d.title}`:"a ticket"}`),ge()}else(o==="ticket_deleted"||o==="states_reordered"||o==="state_created"||o==="state_deleted"||o==="board_updated"||o==="board_imported")&&ge()}),a.onerror=()=>{if(a.readyState!==EventSource.CLOSED||(Pt||(b.warning("Live updates disconnected \u2014 retrying\u2026"),Pt=!0),He))return;let n=Mt;Mt=Math.min(Mt*2,3e4),He=setTimeout(()=>{He=null,va()},n)}}var ho,Mt,He,Pt,ge,En=C(()=>{z();Z();pe();me();Ze();ho=new Set(["ticket_created","state_changed","ticket_updated","comment_created","agent_claimed","checkpoint_requested","agent_completed"]),Mt=1e3,He=null,Pt=!1;ge=mt(async()=>{let e=c.selectedTicketId,t=c.detailMode;c.data=await v(ft()),Pe(),c.selectedTicketId=e,c.detailMode=t,await At()},300)});var _n={};On(_n,{load:()=>_,render:()=>At,renderDetailOnly:()=>bo});async function _(){oe(),c.data=await v(ft()),Pe(),sn(c.boardId,c.data?.tickets||[]),await At(),H.sse&&c.boardId!==Tn&&(Tn=c.boardId,va())}async function At(){Pe(),wa(),Fe(),await Le()}async function bo(){Pe(),wa(),ln(),await Le()}var Tn,me=C(()=>{z();Z();tt();ma();nt();En();Ze();Oe();Tn=null});function $a(){return Te?.classList.contains("is-open")}function Tt(e){clearTimeout(ka),oe(),ht(),Nt.innerHTML=go(e||""),yo(),y("#createFlyoutClose")?.addEventListener("click",()=>F()),Te.hidden=!1,ve.hidden=!1,requestAnimationFrame(()=>{Te.classList.add("is-open"),ve.classList.add("is-visible")}),requestAnimationFrame(()=>{y("#createTicketTitle")?.focus()})}function F(){clearTimeout(ka),Te.classList.remove("is-open"),ve.classList.remove("is-visible"),ka=setTimeout(()=>{Te.hidden=!0,ve.hidden=!0,Nt.innerHTML=""},220)}function go(e){let t=Ae(),a=t.find(i=>i.is_default)||t[0],n=e&&t.some(i=>i.id===e)?e:a?.id||"",r=t.map(i=>`<option value="${l(i.id)}" ${i.id===n?"selected":""}>${l(i.name)}</option>`).join(""),o=W().filter(i=>i.type==="epic").map(i=>`<option value="${l(i.id)}">${l(Q(i))}</option>`).join("");return`
|
|
621
|
-
<div class="create-flyout-head">
|
|
622
|
-
<h2>New card</h2>
|
|
623
|
-
<button type="button" class="create-flyout-close ghost" id="createFlyoutClose" aria-label="Close">\xD7</button>
|
|
624
|
-
</div>
|
|
625
|
-
<form id="createTicketForm" class="create-flyout-form">
|
|
626
|
-
<input id="createTicketTitle" name="title" type="text" placeholder="Title \u2014 what is this card?" autocomplete="off" required />
|
|
627
|
-
<textarea id="createTicketDescription" name="description" placeholder="Description (optional)"></textarea>
|
|
628
|
-
<div class="create-flyout-grid">
|
|
629
|
-
<label>
|
|
630
|
-
Lane
|
|
631
|
-
<select id="createTicketState" name="state" class="select-chevron-field" aria-label="Lane">${r}</select>
|
|
632
|
-
</label>
|
|
633
|
-
<label>
|
|
634
|
-
Type
|
|
635
|
-
<select id="createTicketType" name="type" class="select-chevron-field" aria-label="Ticket type">
|
|
636
|
-
<option value="epic">Epic</option>
|
|
637
|
-
<option value="feature">Feature</option>
|
|
638
|
-
<option value="task" selected>Task</option>
|
|
639
|
-
<option value="bug">Bug</option>
|
|
640
|
-
</select>
|
|
641
|
-
</label>
|
|
642
|
-
<label>
|
|
643
|
-
Priority
|
|
644
|
-
<select id="createTicketPriority" name="priority" class="select-chevron-field" aria-label="Priority">
|
|
645
|
-
<option value="0">MAYBE</option>
|
|
646
|
-
<option value="1">LOW</option>
|
|
647
|
-
<option value="2" selected>MED</option>
|
|
648
|
-
<option value="3">HIGH</option>
|
|
649
|
-
<option value="4">URGENT</option>
|
|
650
|
-
</select>
|
|
651
|
-
</label>
|
|
652
|
-
</div>
|
|
653
|
-
<label>
|
|
654
|
-
Parent epic
|
|
655
|
-
<select id="createTicketParent" name="parent_ticket_id" class="select-chevron-field" aria-label="Parent epic">
|
|
656
|
-
<option value="">None</option>
|
|
657
|
-
${o}
|
|
658
|
-
</select>
|
|
659
|
-
</label>
|
|
660
|
-
<input id="createTicketLabels" name="labels" type="text" placeholder="Labels (comma-separated)" />
|
|
661
|
-
<button type="submit">Create card</button>
|
|
662
|
-
</form>
|
|
663
|
-
`}function yo(){let e=y("#createTicketForm");e&&e.addEventListener("submit",vo)}async function vo(e){e.preventDefault();let t=e.currentTarget,a=new FormData(t),n=String(a.get("labels")||"").split(",").map(o=>o.trim()).filter(Boolean),r=await v("/api/tickets",{method:"POST",body:{board_id:c.boardId,title:a.get("title"),description:a.get("description"),type:a.get("type"),parent_ticket_id:a.get("parent_ticket_id")||null,state_id:a.get("state"),priority:Number(a.get("priority")),labels:n}});F(),c.selectedTicketId=r.id,c.detailMode="ticket",await _(),qe()}var ka,Ne=C(()=>{ne();z();re();Z();fe();gt();nt();me();ka=0});function rt(){V&&(V.hidden=!0,V.innerHTML="",le?.setAttribute("aria-expanded","false"))}function oe(){rt()}function wa(){if(!c.data)return;let e=(c.data.boards||[]).find(t=>t.id===c.boardId);if(le){let t=e?.name||"Board";le.innerHTML=`${wo}<span class="topbar-chip-label">${l(t)}</span>`,le.title=e?`${e.name} \u2014 slug: ${e.slug}`:"Choose board"}}function ko(){let a=(c.data.boards||[]).map(r=>`
|
|
664
|
-
<button type="button" class="menu-flyout-item ${r.id===c.boardId?"is-current":""}" data-variant="menu-item" data-pick-board="${l(r.id)}" role="menuitem">
|
|
665
|
-
<span class="menu-flyout-item-title">${l(r.name)}</span>
|
|
666
|
-
<span class="menu-flyout-item-meta">${l(r.slug)}</span>
|
|
667
|
-
</button>`).join("")||'<p class="menu-flyout-hint menu-flyout-hint--empty">No boards yet. Create one below if you can.</p>',n=H.multiBoard?`
|
|
668
|
-
<div class="menu-flyout-section">
|
|
669
|
-
<h3 class="menu-flyout-heading">New board</h3>
|
|
670
|
-
<form id="createBoardForm" class="menu-flyout-form">
|
|
671
|
-
<div class="folder-picker-field">
|
|
672
|
-
<input id="repoPathInput" name="repo_path" type="text" required readonly placeholder="Choose a folder" autocomplete="off" />
|
|
673
|
-
<button type="button" id="pickRepoFolderBtn">Browse</button>
|
|
674
|
-
</div>
|
|
675
|
-
<p class="menu-flyout-hint menu-flyout-hint--intro">Board data is stored in <code>.orbit/</code> under this folder.</p>
|
|
676
|
-
<input name="name" type="text" required placeholder="Board name" autocomplete="off" />
|
|
677
|
-
<input name="slug" type="text" placeholder="Slug (optional)" autocomplete="off" />
|
|
678
|
-
<input name="default_branch" type="text" placeholder="Default branch (optional)" autocomplete="off" />
|
|
679
|
-
<input name="repo_url" type="text" placeholder="Repo URL (optional)" autocomplete="off" />
|
|
680
|
-
<button type="submit">Create board</button>
|
|
681
|
-
</form>
|
|
682
|
-
</div>`:"";return`
|
|
683
|
-
<div class="menu-flyout-head">Board</div>
|
|
684
|
-
<div class="menu-flyout-list">${a}</div>
|
|
685
|
-
${n}
|
|
686
|
-
`}function $o(){V.querySelectorAll("[data-pick-board]").forEach(t=>{t.addEventListener("click",async a=>{a.stopPropagation();let n=t.getAttribute("data-pick-board");rt(),n!==c.boardId&&(F(),await P({boardId:n,view:"board"}))})});let e=V.querySelector("#createBoardForm");if(e){let t=e.querySelector("#pickRepoFolderBtn"),a=e.querySelector("#repoPathInput");t?.addEventListener("click",async n=>{n.preventDefault(),t.disabled=!0;try{let r=await v("/api/system/pick-folder",{method:"POST"});r.path&&(a.value=r.path,a.dispatchEvent(new Event("input",{bubbles:!0})))}catch(r){b.error(r?.message||"Folder picker unavailable")}finally{t.disabled=!1}}),e.addEventListener("submit",async n=>{n.preventDefault();let r=new FormData(e),o=String(r.get("slug")||"").trim(),i=String(r.get("default_branch")||"").trim(),s=String(r.get("repo_url")||"").trim(),u=String(r.get("repo_path")||"").trim();if(!u){a?.reportValidity();return}let d=await v("/api/boards",{method:"POST",body:{name:r.get("name"),repo_path:u,...o?{slug:o}:{},...i?{default_branch:i}:{},...s?{repo_url:s}:{}}});rt(),c.boardId=d.id,c.selectedTicketId="",c.detailMode="settings",await _(),qe(),b.success("Board created")})}}function Cn(){F(),V.innerHTML=ko(),V.hidden=!1,le?.setAttribute("aria-expanded","true"),$o()}var wo,nt=C(()=>{ne();z();re();Z();fe();Ne();pe();me();Oe();wo=`
|
|
687
|
-
<svg
|
|
688
|
-
class="topbar-icon"
|
|
689
|
-
width="16"
|
|
690
|
-
height="16"
|
|
691
|
-
viewBox="0 0 24 24"
|
|
692
|
-
fill="none"
|
|
693
|
-
aria-hidden="true"
|
|
694
|
-
>
|
|
695
|
-
<rect x="4" y="4" width="6" height="16" rx="0.75" stroke="currentColor" stroke-width="2" />
|
|
696
|
-
<rect x="14" y="4" width="6" height="16" rx="0.75" stroke="currentColor" stroke-width="2" />
|
|
697
|
-
</svg>`});function In(){return{...xo}}function Sa(e=globalThis.location){if(typeof e=="string"){let t=new URL(e,"http://orbit.local");return{pathname:t.pathname||"/",search:t.search||"",hash:t.hash||""}}return{pathname:e?.pathname||"/",search:e?.search||"",hash:e?.hash||""}}function xa(e){try{return decodeURIComponent(e)}catch{return e}}function So(e){return String(e||"").replace(/^#/,"").replace(/^\/+/,"").replace(/\/+$/,"").split("/").filter(Boolean)}function Lo(e){if(!e.length)return null;let t=In(),a=0;if(e[a]==="b"){if(!e[a+1])return null;t.boardSlug=xa(e[a+1]),a+=2}else if(e[a]!=="t"&&e[a]!=="settings")return null;if(e[a]==="t"&&e[a+1])t.view="ticket",t.ticketId=xa(e[a+1]);else if(e[a]==="settings"){t.view="settings";let n=e[a+1]?xa(e[a+1]):"";n&&dt.has(n)&&(t.tab=n)}return t}function Mn(e){let t=String(e||"");return!t||t==="#"||t==="#/"?null:Lo(So(t))}function Bt(e=globalThis.location){let t=Sa(e);return Mn(t.hash)||In()}function ot(e=globalThis.location){let t=Sa(e);return!!Mn(t.hash)}function Ue({boardSlug:e="",view:t="board",ticketId:a="",tab:n=""}={}){let r=[];return e&&r.push("b",encodeURIComponent(e)),t==="ticket"&&a?r.push("t",encodeURIComponent(a)):t==="settings"&&(r.push("settings"),n&&dt.has(n)&&r.push(n)),"#/"+r.join("/")}function Rt(e=globalThis.location,t=Bt(e)){return Sa(e).hash===Ue(t)}var xo,La=C(()=>{ut();xo=Object.freeze({boardSlug:"",view:"board",ticketId:"",tab:""})});function Pn(e){if(!e)return null;let t=c.data?.boards||[];return t.find(a=>a.slug===e)||t.find(a=>a.id===e)||null}function Eo(e={}){return e.boardSlug?e.boardSlug:e.boardId?Pn(e.boardId)?.slug||e.boardId:Y()?.slug||c.boardSlug||""}function To(e={}){return{...e,boardSlug:Eo(e)}}function _o(){let e="board";return c.detailMode==="ticket"&&c.selectedTicketId?e="ticket":c.detailMode==="settings"&&(e="settings"),{boardSlug:Y()?.slug||c.boardSlug,view:e,ticketId:c.selectedTicketId,tab:e==="settings"?Ge(c.settingsTab):""}}function An(e){Dt=e}function Ea(){return Bt(location)}async function P(e,{replace:t=!1}={}){let a=To(e),n=Ue(a);if(Rt(location,a)){Dt&&await it();return}t?history.replaceState(null,"",n):history.pushState(null,"",n),Dt&&await it()}function qe({replace:e=!1}={}){let t=_o(),a=Ue(t);Rt(location,t)||(e?history.replaceState(null,"",a):history.pushState(null,"",a))}async function it(){let{load:e,renderDetailOnly:t}=await Promise.resolve().then(()=>(me(),_n)),a=Ea(),n=ot(location),r=c.boardId,i=Pn(a.boardSlug)||(a.boardSlug?Y():null);i?(c.boardId=i.id,c.boardSlug=i.slug):a.boardSlug&&(c.boardSlug=a.boardSlug);let s=i?{...a,boardSlug:i.slug}:a;if(n&&!Rt(location,s)&&history.replaceState(null,"",Ue(s)),a.view==="ticket")c.selectedTicketId=a.ticketId||"",c.detailMode=a.ticketId?"ticket":"empty";else if(a.view==="settings"){if(c.selectedTicketId="",c.detailMode="settings",a.tab){c.settingsTab=a.tab;try{localStorage.setItem("mab_settings_tab",a.tab)}catch{}}}else c.selectedTicketId="",c.detailMode="empty";if(oe(),F(),c.boardId&&c.boardId!==r){await e();return}await t()}var Dt,fe=C(()=>{z();ut();nt();Ne();La();La();Dt=!1;window.addEventListener("popstate",()=>{Dt&&it()})});ne();qt();Jt();Pa(),ze();var qn=Un();Kt();{let e=()=>{document.body.classList.toggle("is-scrolled",window.scrollY>4)};window.addEventListener("scroll",e,{passive:!0}),e()}Ba();Ta?.addEventListener("click",()=>{Aa()});window.addEventListener(lt,()=>{Ba(),qn.sync()});function Ba(){if(!Ot)return;let e=ue()==="dark";Ot.innerHTML=e?'<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" />':'<circle cx="12" cy="12" r="5" /><path d="M12 1v2m0 18v2M4.2 4.2l1.4 1.4m12.8 12.8l1.4 1.4M1 12h2m18 0h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4" />'}function Un(){let e=document.querySelector(".grain");if(!e)return{sync:()=>{}};zn(e);let t=document.createElement("canvas");t.className="starfield-canvas",t.setAttribute("aria-hidden","true");let a=e.querySelector(".milky-way");a&&a.nextSibling?e.insertBefore(t,a.nextSibling):a?e.appendChild(t):e.insertBefore(t,e.firstChild);let n=t.getContext("2d"),r=Math.min(window.devicePixelRatio||1,2),o=0,i=0,s=[],u=0,d=!1,m={x:0,y:0,tx:0,ty:0};function p(){o=window.innerWidth,i=window.innerHeight,t.width=Math.round(o*r),t.height=Math.round(i*r),t.style.width=o+"px",t.style.height=i+"px",n.setTransform(r,0,0,r,0,0),f()}function f(){let L=o*i/1296e3,x=[{count:Math.round(320*L),depth:.12,sizes:[[1,.9],[1.4,.1]],glow:0},{count:Math.round(160*L),depth:.3,sizes:[[1,.5],[1.6,.4],[2.4,.1]],glow:1.8},{count:Math.round(44*L),depth:.58,sizes:[[1.8,.5],[2.6,.38],[3.6,.12]],glow:2.4}],R=Ra(12648430);s=[];for(let j of x)for(let E=0;E<j.count;E++){let g=Jn(R(),j.sizes);s.push({x:R()*o,y:R()*i,size:g,depth:j.depth,glow:g>=2?j.glow:0,rgb:h(R()),period:2.2+R()*5.4,phase:R()*Math.PI*2,amp:.25+R()*.5,baseAlpha:g<1.5?.45+R()*.28:.55+R()*.22})}}function h(L){return L<.8?[255,255,255]:L<.93?[255,222,186]:[195,218,255]}function w(L){!o||!i||(m.tx=L.clientX/o-.5,m.ty=L.clientY/i-.5)}function $(L){m.x+=(m.tx-m.x)*.06,m.y+=(m.ty-m.y)*.06,n.clearRect(0,0,o,i),n.globalCompositeOperation="lighter";let x=L/1e3,R=70;for(let j=0;j<s.length;j++){let E=s[j],g=.5+.5*Math.sin(x*(2*Math.PI/E.period)+E.phase),I=E.baseAlpha*(1-E.amp+E.amp*g),D=E.x-m.x*E.depth*R,q=E.y-m.y*E.depth*R,[X,J,ae]=E.rgb;if(E.glow>0){let K=E.size*E.glow,Ee=n.createRadialGradient(D,q,0,D,q,K);Ee.addColorStop(0,`rgba(${X},${J},${ae},${I*.85})`),Ee.addColorStop(.35,`rgba(${X},${J},${ae},${I*.3})`),Ee.addColorStop(1,`rgba(${X},${J},${ae},0)`),n.fillStyle=Ee,n.beginPath(),n.arc(D,q,K,0,Math.PI*2),n.fill(),n.fillStyle=`rgba(${X},${J},${ae},${I})`,n.beginPath(),n.arc(D,q,E.size/2,0,Math.PI*2),n.fill()}else{let K=E.size/2;n.fillStyle=`rgba(${X},${J},${ae},${I})`,n.fillRect(D-K,q-K,E.size,E.size)}}n.globalCompositeOperation="source-over",d&&(u=requestAnimationFrame($))}function M(){n.clearRect(0,0,o,i),n.globalCompositeOperation="lighter";for(let L of s){let[x,R,j]=L.rgb;if(n.fillStyle=`rgba(${x},${R},${j},${L.baseAlpha})`,L.glow>0)n.beginPath(),n.arc(L.x,L.y,L.size/2,0,Math.PI*2),n.fill();else{let E=L.size/2;n.fillRect(L.x-E,L.y-E,L.size,L.size)}}n.globalCompositeOperation="source-over"}function A(){d||(d=!0,u=requestAnimationFrame($))}function te(){d=!1,cancelAnimationFrame(u)}function B(){let L=ue()==="dark",x=Ce();if(!L){te(),n.clearRect(0,0,o,i);return}s.length||p(),x||document.hidden?(te(),M()):A()}return window.addEventListener("resize",()=>{p(),B()},{passive:!0}),window.addEventListener("pointermove",w,{passive:!0}),document.addEventListener("visibilitychange",B),window.matchMedia("(prefers-reduced-motion: reduce)").addEventListener("change",()=>{ze(),B()}),window.addEventListener("orbit:motion-preference-change",B),p(),B(),{sync:B}}function zn(e){e.querySelectorAll(".signature-star").forEach(n=>n.remove());let t=Ra(6070757),a=7;for(let n=0;n<a;n++){let r=document.createElement("div");r.className="signature-star",r.setAttribute("aria-hidden","true"),r.style.left=(6+t()*88).toFixed(2)+"%",r.style.top=(8+t()*78).toFixed(2)+"%",r.style.animationDelay=(-t()*4.8).toFixed(2)+"s",e.appendChild(r)}}function Kt(){let e=14e3+Math.random()*28e3;setTimeout(Gn,e)}function Gn(){let e=ue()==="dark",t=Ce(),a=document.querySelector(".grain");if(!e||t||document.hidden||!a){Kt();return}let n=window.innerWidth,r=window.innerHeight,o=Math.random()*n*.6,i=Math.random()*r*.55,s=380+Math.random()*520,u=18+Math.random()*26,d=u*Math.PI/180,m=o+Math.cos(d)*s,p=i+Math.sin(d)*s,f=document.createElement("div");f.className="meteor",f.setAttribute("aria-hidden","true"),f.style.setProperty("--from-x",o+"px"),f.style.setProperty("--from-y",i+"px"),f.style.setProperty("--to-x",m+"px"),f.style.setProperty("--to-y",p+"px"),f.style.setProperty("--angle",u+"deg"),a.appendChild(f),f.addEventListener("animationend",()=>f.remove(),{once:!0}),Kt()}function Jn(e,t){let a=0;for(let[n,r]of t)if(a+=r,e<a)return n;return t[t.length-1][0]}function Ra(e){let t=e>>>0;return function(){t=t+1831565813>>>0;let a=t;return a=Math.imul(a^a>>>15,a|1),a^=a+Math.imul(a^a>>>7,a|61),((a^a>>>14)>>>0)/4294967296}}ne();z();Z();pe();fe();nt();Ne();tt();ne();z();Z();fe();re();Ne();async function Bn(){let e=st.value.trim();if(e.length<2){Ft();return}let t=Y();if(!t)return;let a=await v(`/api/search?q=${encodeURIComponent(e)}&board=${encodeURIComponent(t.slug)}`);if(a.results.length===0){de.hidden=!1,de.innerHTML='<div class="search-hit">No results</div>';return}de.hidden=!1,de.innerHTML=a.results.map(n=>`
|
|
698
|
-
<div class="search-hit" data-ticket-id="${n.id}">
|
|
699
|
-
<span class="search-hit-main">
|
|
700
|
-
<strong>${l(Q(n))}</strong>
|
|
701
|
-
<span class="search-hit-title">${l(n.title)}</span>
|
|
702
|
-
</span>
|
|
703
|
-
<span class="search-hit-state" data-variant="${l(Ye(n))}">${l(n.state_name||"State")}</span>
|
|
704
|
-
</div>
|
|
705
|
-
`).join(""),de.querySelectorAll("[data-ticket-id]").forEach(n=>{n.addEventListener("click",async()=>{F(),Ft(),P({boardId:c.boardId,view:"ticket",ticketId:n.dataset.ticketId})})})}function Ft(){de.hidden=!0,de.innerHTML=""}me();Oe();function Dn(e){e?.showModal()}function Rn(e){e?.open&&e.close()}function Fn(e){e&&(e.addEventListener("click",t=>{t.target===e&&Rn(e)}),e.querySelectorAll("[data-modal-close]").forEach(t=>{t.addEventListener("click",()=>Rn(e))}))}async function Co(){let e=document.getElementById("welcomeDialog");Fn(e),xe==="preview"&&Dn(e),y("#settingsBtn").addEventListener("click",async()=>{F(),oe(),P({boardId:c.boardId,view:"settings",tab:c.settingsTab||"lanes"})}),le?.addEventListener("click",a=>{if(a.stopPropagation(),!V.hidden){rt();return}Cn()}),y("#newTicketBtn").addEventListener("click",()=>{if($a()){F();return}Tt("")}),ve.addEventListener("click",()=>{F()}),y("#drawerCloseBtn").addEventListener("click",()=>{P({boardId:c.boardId,view:"board"})}),y("#drawerArchiveBtn").addEventListener("click",async()=>{let a=c.selectedTicketId;if(!a||c.detailMode!=="ticket")return;let r=c.data?.tickets?.find(o=>o.id===a)?.title||"card";await v(S(`/api/tickets/${encodeURIComponent(a)}/archive`),{method:"POST"}),b.error(`Archived: ${r}`),await _(),P({boardId:c.boardId,view:"board"})}),ye.addEventListener("click",()=>{P({boardId:c.boardId,view:"board"})}),document.addEventListener("keydown",a=>{if(a.key!=="Escape")return;if(!V.hidden){oe();return}if($a()){F();return}if(!U.classList.contains("is-open"))return;let n=document.activeElement;n?.classList?.contains("inline-title-editor")||n?.classList?.contains("inline-desc-editor")||P({boardId:c.boardId,view:"board"})}),st.addEventListener("input",mt(Bn,220)),document.addEventListener("click",a=>{a.target.closest("#topbarSearch")||Ft(),a.target.closest(".topbar-menu")||oe()}),un(),cn();let t=Ea();t.boardSlug&&(c.boardSlug=t.boardSlug),await _(),An(!0),ot()?await it():qe({replace:!0})}Co();
|
|
706
|
-
//# sourceMappingURL=app.js.map
|