@starlove/orbit 1.0.7 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +22 -7
  2. package/SKILL-ORBIT.md +26 -1
  3. package/dist/full/index.html +2 -2
  4. package/dist/full/js/app.a4f045fc.js +676 -0
  5. package/dist/full/js/app.a4f045fc.js.map +7 -0
  6. package/dist/full/{styles.14e9dbbd.css → styles.836872e7.css} +836 -39
  7. package/dist/full/version.json +2 -2
  8. package/docs/AGENT_PROTOCOL.md +23 -5
  9. package/docs/MCP_SETUP.md +5 -3
  10. package/docs/ORBIT_DISPATCH.md +185 -0
  11. package/docs/setup/orbit-init-wizard.md +174 -0
  12. package/package.json +2 -2
  13. package/public/js/actor-labels.js +65 -0
  14. package/public/js/app.js +8 -1
  15. package/public/js/card-actions.js +526 -0
  16. package/public/js/config.js +2 -1
  17. package/public/js/format.js +204 -10
  18. package/public/js/kanban.js +15 -0
  19. package/public/js/local-backend.js +24 -8
  20. package/public/js/router.js +3 -2
  21. package/public/js/search.js +6 -3
  22. package/public/js/settings.js +96 -26
  23. package/public/js/state.js +50 -0
  24. package/public/js/ticket-detail.js +428 -125
  25. package/public/styles.css +836 -39
  26. package/src/cli/orbit.js +217 -32
  27. package/src/core/agent.js +114 -24
  28. package/src/core/agents-md.js +15 -10
  29. package/src/core/attachments.js +238 -0
  30. package/src/core/auth.js +36 -0
  31. package/src/core/backups.js +9 -3
  32. package/src/core/board.js +32 -1
  33. package/src/core/boards.js +1 -1
  34. package/src/core/db.js +42 -2
  35. package/src/core/delete-board.js +87 -21
  36. package/src/core/dispatch.js +532 -0
  37. package/src/core/http.js +31 -11
  38. package/src/core/migrate.js +198 -0
  39. package/src/core/paths.js +6 -0
  40. package/src/core/registry.js +12 -9
  41. package/src/core/router.js +81 -7
  42. package/src/core/search.js +83 -10
  43. package/src/core/snapshots.js +14 -2
  44. package/src/mcp/orbit-client.js +109 -46
  45. package/src/mcp-server.js +238 -12
  46. package/src/server.js +20 -0
  47. package/dist/full/js/app.f4fd5e94.js +0 -604
  48. package/dist/full/js/app.f4fd5e94.js.map +0 -7
@@ -0,0 +1,676 @@
1
+ var An=Object.defineProperty;var T=(e,t)=>()=>(e&&(t=e(e=0)),t);var Bn=(e,t)=>{for(var a in t)An(e,a,{get:t[a],enumerable:!0})};var y,ie,V,L,j,w,ge,Le,Rt,ye,st,se,Ea,Ft,te=T(()=>{y=e=>document.querySelector(e),ie=y("#boardMenuBtn"),V=y("#boardFlyout"),L=y("#kanban"),j=y("#drawer"),w=y("#drawerInner"),ge=y("#drawerBackdrop"),Le=y("#createFlyout"),Rt=y("#createFlyoutInner"),ye=y("#createFlyoutBackdrop"),st=y("#searchInput"),se=y("#searchResults"),Ea=y("#themeToggle"),Ft=y("#themeIcon")});function Se(e){try{return e??globalThis.localStorage??null}catch{return null}}function Ot(e){return typeof window<"u"&&typeof window.matchMedia=="function"?window.matchMedia(e):{matches:!1}}function Nt(e=Se()){let a=Se(e)?.getItem?.(La);return a==="true"?!0:a==="false"?!1:null}function jt(e=Ot){try{return!!e("(prefers-reduced-motion: reduce)")?.matches}catch{return!1}}function Te({storage:e=Se(),mediaMatcher:t=Ot}={}){return Nt(e)??jt(t)}function Sa(e,t=Se()){let a=Se(t),n=e?"true":"false";return a?.setItem?.(La,n),e}function He({doc:e=globalThis.document,storage:t=Se(),mediaMatcher:a=Ot}={}){let n=Te({storage:t,mediaMatcher:a});return e?.documentElement?.setAttribute("data-reduced-motion",n?"reduce":"allow"),n}var La,qt=T(()=>{La="mab_reduce_motion"});function _e(e){try{return e??globalThis.localStorage??null}catch{return null}}function Ie(e){try{return e??globalThis.document??null}catch{return null}}function Ht(e){try{return e??globalThis.window??null}catch{return null}}function Dn(e){return Ut.has(e)?e:"light"}function _a(e=_e()){let a=_e(e)?.getItem?.(Ta);return Ut.has(a)?a:null}function ce({doc:e=Ie(),storage:t=_e()}={}){let a=Ie(e)?.documentElement?.getAttribute?.("data-theme");return Ut.has(a)?a:_a(t)??"light"}function Ia({doc:e=Ie(),storage:t=_e()}={}){let a=_a(t);return a&&Ie(e)?.documentElement?.setAttribute?.("data-theme",a),ce({doc:e,storage:t})}function zt(e,{doc:t=Ie(),storage:a=_e(),eventTarget:n=Ht(),notify:r=!0}={}){let o=Dn(e);return Ie(t)?.documentElement?.setAttribute?.("data-theme",o),_e(a)?.setItem?.(Ta,o),r&&Rn(o,n),o}function Ca(e={}){let t=ce(e)==="dark"?"light":"dark";return zt(t,e)}function Rn(e,t=Ht()){let a=Ht(t);if(typeof a?.dispatchEvent=="function"){if(typeof CustomEvent=="function"){a.dispatchEvent(new CustomEvent(ct,{detail:{theme:e}}));return}a.dispatchEvent({type:ct,detail:{theme:e}})}}var Ta,ct,Ut,Gt=T(()=>{Ta="mab_theme",ct="orbit:theme-change",Ut=new Set(["light","dark"])});function Ue(e){return!e||!dt.has(e)?"lanes":e}var dt,lt=T(()=>{dt=new Set(["lanes","appearance","ai","notes","journal","repository","archive"])});function Aa(){return Ue(c.settingsTab||localStorage.getItem("mab_settings_tab"))}function Ce(){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 K(){return(c.data?.boards||[]).find(e=>e.id===c.boardId)}function Ba(){return c.boardId?(c.data?.labels||[]).filter(e=>e.board_id===c.boardId):[]}function ze(){return c.data.states.filter(e=>e.board_id===c.boardId)}function Da(e){return e?.role==="ai_ready"||e?.name==="AI Ready"}function Me(){let e=ze();return K()?.ai_enabled!==0?e:e.filter(t=>!Da(t))}function Ra(e=""){let t=ze();return K()?.ai_enabled!==0?t:t.filter(a=>!Da(a)||a.id===e)}function W(){return c.data.tickets.filter(e=>e.board_id===c.boardId)}function Hn(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 Un(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?Hn(a.get(r.parent_ticket_id)):null,r.child_count=n.get(r.id)||0}function Fa(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))),Un(n.board_id||c.boardId)}var c,q=T(()=>{lt();c={data:null,boardId:"",boardSlug:"",selectedTicketId:"",expandedCardIds:new Set,detailMode:"empty",settingsTab:Ue(localStorage.getItem("mab_settings_tab")),showPriority:localStorage.getItem("mab_show_priority")!=="false",eventSource:null}});function l(e){return String(e??"").replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#039;")}function Gn(e){if(!e)return e;let t=e;for(let[a,n]of zn)t=t.split(a).join(n);return t}function de(e){return e?Gn(String(e)).replace(/\r\n?/g,`
2
+ `):""}function Pe(e){return l(de(e))}function Wn(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&&!Kn.has(`${n[1]}:`)?"":t}function Wt(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 Yt(e){let t=/\[([^\]\n]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g,a="",n=0,r;for(;r=t.exec(e);){a+=Wt(e.slice(n,r.index));let o=Wt(r[1]),i=Wn(r[2]);a+=i?`<a href="${l(i)}" target="_blank" rel="noopener noreferrer">${o}</a>`:o,n=r.index+r[0].length}return a+=Wt(e.slice(n)),a}function ut(e){let t=String(e??""),a="",n=0;for(;n<t.length;){let r=t.indexOf("`",n);if(r===-1){a+=Yt(t.slice(n));break}let o=t.indexOf("`",r+1);if(o===-1){a+=Yt(t.slice(n));break}a+=Yt(t.slice(n,r)),a+=`<code>${l(t.slice(r+1,o))}</code>`,n=o+1}return a}function Yn(e){return`<p>${ut(e.join(`
3
+ `)).replaceAll(`
4
+ `,"<br>")}</p>`}function Jn(e){let t=e.match(/^\[( |x|X)\]\s+(.*)$/);if(!t)return null;let a=t[1].toLowerCase()==="x",n=ut(t[2].trim()).replaceAll(`
5
+ `,"<br>");return{checked:a,html:`<input type="checkbox" disabled${a?" checked":""}> ${n}`}}function Jt(e){let t=de(e);if(!t.trim())return"";let a=[],n=t.split(`
6
+ `),r=[],o=null,i=null,s=()=>{r.length&&(a.push(Yn(r)),r=[])},u=()=>{if(!o)return;let p=o.taskList?' class="task-list"':"";a.push(`<${o.type}${p}>${o.items.map(h=>`<li${h.taskClass?` class="${h.taskClass}"`:""}>${h.html}</li>`).join("")}</${o.type}>`),o=null},d=()=>{i&&(a.push(`<blockquote>${Jt(i.join(`
7
+ `))}</blockquote>`),i=null)},m=()=>{s(),u(),d()};for(let p=0;p<n.length;p+=1){let h=n[p];if(/^\s*```/.test(h)){m();let x=[];for(p+=1;p<n.length&&!/^\s*```\s*$/.test(n[p]);)x.push(n[p]),p+=1;a.push(`<pre><code>${l(x.join(`
8
+ `))}</code></pre>`);continue}if(/^\s*(?:-\s*){3,}\s*$/.test(h)||/^\s*(?:\*\s*){3,}\s*$/.test(h)||/^\s*(?:_\s*){3,}\s*$/.test(h)){m(),a.push("<hr>");continue}let v=h.match(/^\s{0,3}(#{1,6})\s+(.+?)\s*#*\s*$/);if(v){m();let x=v[1].length;a.push(`<h${x}>${ut(v[2])}</h${x}>`);continue}let $=h.match(/^\s{0,3}>\s?(.*)$/);if($){s(),u(),i||(i=[]),i.push($[1]);continue}if(i&&!h.trim()){d();continue}if(d(),!h.trim()){s(),u();continue}let I=h.match(/^\s*\d+[.)]\s+(.+)$/),Y=h.match(/^\s*[-*+]\s+(.+)$/);if(I||Y){s();let x=I?"ol":"ul",he=(I||Y)[1].trim(),O=null;Y&&(O=Jn(he)),(!o||o.type!==x||!!o.taskList!=!!O)&&(u(),o={type:x,items:[],taskList:!!O}),O?o.items.push({html:O.html,taskClass:O.checked?"task-item task-item-done":"task-item"}):o.items.push({html:ut(he).replaceAll(`
9
+ `,"<br>")});continue}u(),r.push(h)}return m(),a.join("")}function Ge(e){return e?new Intl.DateTimeFormat(void 0,{month:"short",day:"numeric",hour:"numeric",minute:"2-digit"}).format(new Date(e)):""}function Oa(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 X(e){return`#${e.number}`}function Ke(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 B(e){return e==="epic"||e==="feature"||e==="bug"||e==="task"?e:"task"}function Ae(e){return{epic:"Epic",feature:"Feature",task:"Task",bug:"Bug"}[B(e)]||"Task"}function Na(e){return{epic:"Epic / Story",feature:"Feature",task:"Task",bug:"Bug"}[B(e)]||"Task"}function Be(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 ja(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 qa(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 zn,Kn,ae=T(()=>{zn=[["\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","'"]];Kn=new Set(["http:","https:","mailto:","tel:"])});function Xn(){return ve&&ve.isConnected||(ve=document.createElement("div"),ve.className="toast-container",document.body.append(ve)),ve}function b(e,t="info",a=4e3){let n=Xn(),r=document.createElement("div");r.className=`toast toast-${t}`,r.setAttribute("role","status");let o=Vn[t];r.innerHTML=`<span class="toast-icon">${o}</span><span class="toast-msg">${l(e)}</span><button class="toast-close" aria-label="Dismiss">&times;</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 Ha(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 pt(e,t){let a=0;return(...n)=>{clearTimeout(a),a=setTimeout(()=>e(...n),t)}}var Vn,ve,le=T(()=>{ae();Vn={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>'},ve=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 Ua({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 za=T(()=>{});function mt(){return`/api/bootstrap${c.boardId?`?board_id=${encodeURIComponent(c.boardId)}`:c.boardSlug?`?board=${encodeURIComponent(c.boardSlug)}`:""}`}function k(e){if(!c.boardId||e.includes("board_id="))return e;let t=e.includes("?")?"&":"?";return`${e}${t}board_id=${encodeURIComponent(c.boardId)}`}async function g(e,t={}){try{return await Ua({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 Q=T(()=>{q();le();za()});function Qt(){Xt+=1,j.hidden=!1,ge.hidden=!1,requestAnimationFrame(()=>{j.classList.add("is-open"),ge.classList.add("is-visible")})}function ft(){let e=++Xt,t=j.classList.contains("is-open");j.classList.remove("is-open"),ge.classList.remove("is-visible");let a=()=>{if(Xt!==e)return;j.hidden=!0,ge.hidden=!0,w.innerHTML="";let o=document.getElementById("drawerEyebrow"),i=document.getElementById("drawerTitle"),s=document.getElementById("drawerSubtitle"),u=document.getElementById("drawerTabs");if(o&&(o.textContent="",o.hidden=!0),i){for(let d of Array.from(i.attributes))d.name!=="id"&&i.removeAttribute(d.name);i.className="drawer-title-text",i.textContent=""}s&&(s.innerHTML="",s.hidden=!0),u&&(u.innerHTML="",u.hidden=!0),j.classList.remove("is-wide")};if(!t){a();return}let n=!1,r=()=>{n||(n=!0,a())};j.addEventListener("transitionend",r,{once:!0}),setTimeout(r,360)}function ht({eyebrow:e,title:t,titleAttrs:a,subtitleHtml:n,tabs:r,activeTab:o,onTabSelect:i,body:s,mode:u}){j.classList.toggle("is-settings",u==="settings"),j.classList.toggle("is-wide",u==="ticket"||u==="settings");let d=document.getElementById("drawerEyebrow"),m=document.getElementById("drawerTitle"),p=document.getElementById("drawerSubtitle"),h=document.getElementById("drawerTabs");e?(d.textContent=e,d.hidden=!1):(d.textContent="",d.hidden=!0),n?(p.innerHTML=n,p.hidden=!1):(p.innerHTML="",p.hidden=!0);for(let $ of Array.from(m.attributes))$.name!=="id"&&m.removeAttribute($.name);if(m.className="drawer-title-text",a)for(let[$,I]of Object.entries(a))$==="class"?m.className=`drawer-title-text ${I}`:m.setAttribute($,I);m.textContent=t??"",r&&r.length?(h.hidden=!1,h.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"&&h.querySelectorAll(".drawer-tab").forEach($=>{$.addEventListener("click",()=>i($.dataset.drawerTab))})):(h.hidden=!0,h.innerHTML="");let v=document.getElementById("drawerArchiveBtn");v&&(v.hidden=c.detailMode!=="ticket"),w.innerHTML=s??""}var Xt,bt=T(()=>{te();q();ae();Xt=0});function ar(e,t=Me()){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:Zn.map(a=>({id:`priority:${a}`,label:Be(a),action:"priority",value:a,checked:Number(e.priority)===a,disabled:Number(e.priority)===a}))},{id:"type",label:"Type",children:Qn.map(a=>({id:`type:${a}`,label:Ae(a),action:"type",value:a,checked:B(e.type)===a,disabled:B(e.type)===a}))},{id:"open",label:"Open",action:"open"},{id:"archive",label:"Archive",action:"archive",danger:!0}]:[]}function nr({x:e,y:t,menuWidth:a,menuHeight:n,viewportWidth:r,viewportHeight:o,margin:i=ea}){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 rr({triggerRect:e,submenuWidth:t,submenuHeight:a,viewportWidth:n,viewportHeight:r,margin:o=ea,gap:i=er}){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,h=m||!p?u:d,v=Math.max(0,r-o*2),$=Math.min(a,v),I=Math.max(o,r-$-o),Y=e.top-i;return{left:Math.min(Math.max(o,h),s),top:Math.min(Math.max(o,Y),I),maxHeight:v}}function wt({apiClient:e=g,navigator:t=null,confirmer:a=globalThis.confirm}={}){async function n(r,o){await e(k(`/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(k(`/api/tickets/${encodeURIComponent(r.id)}/archive`),{method:"POST"}),b.error(`Archived: ${r.title||"card"}`),!0)}}}function aa(e){ta=!!e}function Z({restoreFocus:e=!1}={}){re(),We&&(We.abort(),We=null);for(let t of Je)t.isConnected&&t.remove();Je.clear(),H&&(H.querySelectorAll(".card-action-menu-item.has-submenu").forEach(t=>{let a=we.get(t);a?.isConnected&&a.remove(),we.delete(t)}),H.remove(),H=null),e&&gt?.isConnected&&gt.focus({preventScroll:!0}),gt=null}function or({ticket:e,anchor:t,x:a,y:n,handlers:r=wt(),onBeforeOpen:o=null}){if(Z(),!e||ta)return null;typeof o=="function"&&o(),gt=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=Wa(ar(e)),document.body.append(i),H=i,i.querySelectorAll(".card-action-menu-item.has-submenu").forEach(p=>{let h=p.querySelector(":scope > .card-action-submenu");h&&(h.remove(),document.body.append(h),we.set(p,h),Zt.set(h,p),cr(h,e,r))});let s=i.getBoundingClientRect(),u=nr({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`,sr(i,e,r),ra(i)?.focus({preventScroll:!0}),We=new AbortController;let m=We.signal;return requestAnimationFrame(()=>{document.addEventListener("pointerdown",dr,{signal:m,capture:!0})}),document.addEventListener("keydown",lr,{signal:m,capture:!0}),document.addEventListener("scroll",ur,{signal:m,capture:!0,passive:!0}),window.addEventListener("resize",()=>Z(),{signal:m,passive:!0}),window.addEventListener("blur",()=>Z(),{signal:m}),i}function Ka(e,t,{handlers:a=wt(),onBeforeOpen:n=null}={}){e.addEventListener("contextmenu",r=>{if(r.preventDefault(),r.stopPropagation(),ta){Z();return}or({ticket:t,anchor:e,x:r.clientX,y:r.clientY,handlers:a,onBeforeOpen:n})})}function Wa(e){return`<ul class="card-action-menu-list" role="none">${e.map(ir).join("")}</ul>`}function ir(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">${Wa(e.children)}</div>`:""}
27
+ </li>
28
+ `}function sr(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")?(re(),yt(r)):pr(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")?(re(),yt(r)):(re(),Xa(e)))}),e.addEventListener("click",n=>Ya(n,e,t,a)),e.addEventListener("keydown",n=>Ja(n,e))}function cr(e,t,a){e.addEventListener("pointerenter",re),e.addEventListener("pointerover",n=>{n.target instanceof Element&&e.contains(n.target)&&re()},!0),e.addEventListener("click",n=>{H&&Ya(n,H,t,a,e)}),e.addEventListener("keydown",n=>{H&&Ja(n,H)})}function Ya(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")){re(),yt(i),ra(we.get(i))?.focus({preventScroll:!0});return}let s=o.dataset.action,u=o.dataset.value;if(Z(),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 Ja(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=Zt.get(n),u=s?.querySelector(":scope > .card-action-button");vt(s),u?.focus({preventScroll:!0})}else Z({restoreFocus:!0});return}if(e.key==="ArrowDown"||e.key==="ArrowUp"){e.preventDefault(),mr(o,a,e.key==="ArrowDown"?1:-1);return}if(e.key==="Home"||e.key==="End"){e.preventDefault();let s=na(o);s[e.key==="Home"?0:s.length-1]?.focus({preventScroll:!0});return}if(e.key==="ArrowRight"&&i?.classList.contains("has-submenu")){e.preventDefault(),re(),yt(i),ra(we.get(i))?.focus({preventScroll:!0});return}if(e.key==="ArrowLeft"&&n){let s=Zt.get(n),u=s?.querySelector(":scope > .card-action-button");u&&(e.preventDefault(),vt(s),u.focus({preventScroll:!0}))}}function dr(e){H&&(Va(e)||Z())}function lr(e){e.key==="Escape"&&H&&(e.preventDefault(),Z({restoreFocus:!0}))}function ur(e){H&&(Va(e)||Z())}function Va(e){let t=e.target;if(!(t instanceof Node))return!1;if(H?.contains(t))return!0;for(let a of Je)if(a.contains(t))return!0;return!1}function yt(e){let t=e.parentElement?.children||[];for(let n of t)n!==e&&n.classList?.contains("has-submenu")&&n.classList.contains("is-open")&&vt(n);let a=we.get(e);if(a){if(e.classList.contains("is-open")){Ga(e,a);return}e.classList.add("is-open"),e.querySelector(":scope > .card-action-button")?.setAttribute("aria-expanded","true"),a.classList.add("is-open"),Je.add(a),Ga(e,a)}}function vt(e){if(!e)return;e.classList.remove("is-open"),e.querySelector(":scope > .card-action-button")?.setAttribute("aria-expanded","false");let t=we.get(e);t&&(t.classList.remove("is-open"),t.style.removeProperty("left"),t.style.removeProperty("top"),t.style.removeProperty("max-height"),Je.delete(t))}function Xa(e){e.querySelectorAll(".card-action-menu-item.has-submenu.is-open").forEach(vt)}function pr(e){re(),Ye=setTimeout(()=>{Ye=null,H===e&&Xa(e)},tr)}function re(){Ye!=null&&(clearTimeout(Ye),Ye=null)}function Ga(e,t){let a=e.querySelector(":scope > .card-action-button");if(!t||!a)return;let n=Math.max(0,window.innerHeight-ea*2);t.style.maxHeight=`${n}px`,t.style.left="0px",t.style.top="0px";let r=t.getBoundingClientRect(),o=rr({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 na(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 ra(e){return na(e)[0]||null}function mr(e,t,a){let n=na(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 Qn,Zn,ea,er,tr,H,ta,We,gt,we,Zt,Je,Ye,Qa=T(()=>{Q();q();ae();le();Qn=["epic","feature","task","bug"],Zn=[0,1,2,3,4],ea=8,er=6,tr=180,H=null,ta=!1,We=null,gt=null,we=new WeakMap,Zt=new WeakMap,Je=new Set,Ye=null});function fr(e){return e?typeof e=="string"?{readAt:e,count:0}:{readAt:e.readAt||null,count:Number(e.count)||0}:{readAt:null,count:0}}function Ve(e){if(!e)return new Map;let t=ia.get(e);if(t)return t;t=new Map;try{let a=localStorage.getItem(Za(e));if(a){let n=JSON.parse(a);for(let[r,o]of Object.entries(n))t.set(r,fr(o))}}catch{}return ia.set(e,t),t}function kt(e){let t=ia.get(e);if(t)try{let a={};for(let[n,r]of t)a[n]=r;localStorage.setItem(Za(e),JSON.stringify(a))}catch{}}function en(e,t){let a=e.get(t);return a||(a={readAt:null,count:0},e.set(t,a)),a}function $t(e){let t=e?.board_id||c.boardId;if(!t||!e?.id)return 0;let n=Ve(t).get(e.id);return n?n.count:localStorage.getItem(oa(t))==="1"?1:0}function xt(e,t){let a=c.boardId;if(!a||!e)return;let n=Ve(a),r=en(n,e);r.count===0&&r.readAt&&t&&r.readAt>=t||(r.count=0,t&&(r.readAt=t),kt(a))}function tn(e){let t=c.boardId;if(!t||!e)return;let a=Ve(t),n=en(a,e);n.count=(n.count||0)+1,kt(t)}function an(e){let t=c.boardId;if(!t||!e)return;let a=Ve(t);a.has(e)&&(a.delete(e),kt(t))}function nn(e,t){if(!e||localStorage.getItem(oa(e))==="1")return;let a=Ve(e);for(let n of t||[])!a.has(n.id)&&n.updated_at&&a.set(n.id,{readAt:n.updated_at,count:0});kt(e);try{localStorage.setItem(oa(e),"1")}catch{}}var Za,oa,ia,Xe=T(()=>{q();Za=e=>`mab_card_read:${e}`,oa=e=>`mab_card_read_seeded:${e}`,ia=new Map});function Re(){let e=Me(),t=W();L.innerHTML=e.map(n=>br(n,t)).join(""),L.querySelectorAll(".column").forEach(n=>{n.addEventListener("dragover",r=>{r.preventDefault(),n.classList.add("drag-over")}),n.addEventListener("dragleave",()=>n.classList.remove("drag-over")),n.addEventListener("drop",async r=>{r.preventDefault(),n.classList.remove("drag-over");let o=r.dataTransfer.getData("text/plain");o&&(M(),await g(k(`/api/tickets/${o}`),{method:"PATCH",body:{state_id:n.dataset.stateId}}),await S())})}),L.querySelectorAll("[data-add-to-state]").forEach(n=>{n.addEventListener("click",r=>{r.stopPropagation(),Lt(n.dataset.addToState||"")})}),L.querySelectorAll(".epic-mini-header[data-ticket-id]").forEach(n=>{n.addEventListener("click",()=>{M(),P({boardId:c.boardId,view:"ticket",ticketId:n.dataset.ticketId})})});let a=wt({navigator:P});L.querySelectorAll(".card").forEach(n=>{let r=t.find(s=>s.id===n.dataset.ticketId);r&&Ka(n,r,{handlers:a,onBeforeOpen:M});let o=!1;n.addEventListener("click",async s=>{if((s.target instanceof Element?s.target:s.target?.parentElement)?.closest("a")){s.stopPropagation();return}if(o){o=!1,s.preventDefault(),s.stopPropagation();return}M(),P({boardId:c.boardId,view:"ticket",ticketId:n.dataset.ticketId})});let i=n.querySelector("button.card-expand-trigger");i&&i.addEventListener("click",s=>{s.stopPropagation(),s.preventDefault();let u=n.dataset.ticketId;c.expandedCardIds.has(u)?c.expandedCardIds.delete(u):c.expandedCardIds.add(u);let d=n.classList.toggle("is-expanded");i.setAttribute("aria-expanded",d?"true":"false")}),n.addEventListener("dragstart",s=>{Z(),aa(!0),s.dataTransfer.setData("text/plain",n.dataset.ticketId),s.dataTransfer.effectAllowed="move"}),n.addEventListener("dragend",()=>{aa(!1),o=!0,setTimeout(()=>{o=!1},0)})})}function rn(){L.querySelectorAll(".card.selected").forEach(e=>e.classList.remove("selected")),c.selectedTicketId&&L.querySelectorAll(`.card[data-ticket-id="${CSS.escape(c.selectedTicketId)}"]`).forEach(e=>e.classList.add("selected"))}function hr(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 br(e,t){let a=t.filter(i=>i.state_id===e.id),n=a.length===0,r=e.name==="AI Ready",o=["column"];return r&&o.push("column--ai-ready"),`
56
+ <section class="${o.join(" ")}" data-state-id="${e.id}" data-state-name="${l(e.name)}">
57
+ <div class="column-head">
58
+ <h2${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">
62
+ <button type="button" class="column-add-btn ghost" data-add-to-state="${l(e.id)}" title="New card">+</button>
63
+ <span class="count">${a.length}</span>
64
+ </div>
65
+ </div>
66
+ ${n?hr(e.name):gr(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 gr(e,t){let a=new Map(t.filter(s=>B(s.type)==="epic").map(s=>[s.id,s])),n=new Set(e.filter(s=>B(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(B(s.type)==="epic"){let u=r.get(s.id)||[];if(u.length){let d=u.map(m=>Qe(m,{asEpicChild:!0})).join("");i.push(`${Qe(s)}<div class="epic-children">${d}</div>`),u.forEach(m=>o.add(m.id))}else i.push(Qe(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=>Qe(p,{asEpicChild:!0})).join("");i.push(`${yr(u)}<div class="epic-children">${m}</div>`),d.forEach(p=>o.add(p.id));continue}i.push(Qe(s)),o.add(s.id)}}return i.join("")}function yr(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(X(e))}</span>
76
+ <span class="epic-mini-title">${l(e.title)}</span>
77
+ </div>
78
+ `}function sa(e){let t=De(e),a=Be(e);return`<div class="card-priority-id priority-pill priority-pill-${l(t)}"><span class="card-priority-label">${l(a)}</span></div>`}function Et(e){let t=B(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 Qe(e,{asEpicChild:t=!1}={}){let a=B(e.type),n=De(e.priority),r=Ae(e.type).toUpperCase(),o=c.expandedCardIds.has(e.id),i=!!String(e.description||"").trim(),s=i||e.labels&&e.labels.length||e.parent_ticket||e.child_count,u=`
83
+ <span class="ticket-number">${l(X(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?sa(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=$t(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":""}" 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-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?`
100
+ <div class="card-expandable">
101
+ ${i?`<div class="card-description markdown-body">${Jt(e.description)}</div>`:""}
102
+ ${ja(e.labels)}
103
+ ${e.parent_ticket?`<div class="parent-line">-> ${l(X(e.parent_ticket))}</div>`:""}
104
+ ${e.child_count?`<div class="child-count">${e.child_count} feature${e.child_count===1?"":"s"} inside</div>`:""}
105
+ </div>
106
+ `:""}
107
+ </article>
108
+ `}function on(){let a=()=>Math.max(L.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,h)=>h===WheelEvent.DOM_DELTA_LINE?p*16:h===WheelEvent.DOM_DELTA_PAGE?p*a():p;L.addEventListener("wheel",p=>{if(p.defaultPrevented||p.ctrlKey)return;let h=p.deltaX||(p.shiftKey?p.deltaY:0);!h||!(Math.abs(h)>=Math.abs(p.deltaY)||p.shiftKey)||L.scrollWidth<=L.clientWidth||(p.preventDefault(),L.scrollLeft+=d(h,p.deltaMode))},{passive:!1}),L.addEventListener("pointerdown",p=>{p.button===0&&(u(p.target)||(n=!0,r=!1,s=p.pointerId,o=p.clientX,i=L.scrollLeft,L.classList.add("is-grabbing")))}),L.addEventListener("pointermove",p=>{if(!n||p.pointerId!==s)return;let h=p.clientX-o;if(!r&&Math.abs(h)>4){r=!0;try{L.setPointerCapture(s)}catch{}}r&&(p.preventDefault(),L.scrollLeft=i-h)});let m=p=>{if(!(!n||p&&p.pointerId!==s)){n=!1,L.classList.remove("is-grabbing");try{L.releasePointerCapture(s)}catch{}}};L.addEventListener("pointerup",m),L.addEventListener("pointercancel",m),L.addEventListener("pointerleave",m),L.addEventListener("click",p=>{r&&(p.stopPropagation(),p.preventDefault(),r=!1)},!0)}var Ze=T(()=>{te();q();ae();Q();pe();Fe();ue();Qa();Xe()});var sn,ke,F,Oe=T(()=>{sn="full";sn="full";ke=sn,F={sse:ke==="full",ai:ke==="full",multiBoard:ke==="full",tokenAuth:ke==="full",attachments:ke==="full"}});async function $e(){let e=await g(`/api/boards/${c.boardId}/context?include_struck=true`),t=e.board,a=Aa(),n=[{id:"lanes",label:"Lanes"},{id:"appearance",label:"Appearance"},{id:"archive",label:"Card Archive"},{id:"notes",label:"Notes"},{id:"journal",label:"Journal"},...F.ai?[{id:"ai",label:"AI"}]:[],{id:"repository",label:"Repository"}],r=a==="ai"&&!F.ai?"lanes":a;ht({eyebrow:"SETTINGS",mode:"settings",title:t.name,tabs:n,activeTab:r,onTabSelect:async o=>{P({boardId:c.boardId,view:"settings",tab:o})},body:vr(e,r)}),Rr(e,r)}function vr(e,t){switch(t){case"notes":return Ar(e);case"lanes":return dn();case"appearance":return Br();case"ai":return F.ai?wr(e):dn();case"journal":return Dr(e);case"archive":return Ir();case"repository":default:return Cr(e)}}function wr(e){let t=e.board,a=t.ai_enabled!==0,n=a?'<div class="ai-section-divider" aria-hidden="true"></div>':"",r=a?kr(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 kr(e){return`
128
+ <div class="section ai-subsection ai-context-setup">
129
+ <div class="ai-context-title-row">
130
+ <span class="ai-context-inline-mark" aria-hidden="true">Context</span>
131
+ <h3>Agent Instructions</h3>
132
+ </div>
133
+ <p class="description">This project-level context is provided to agents when they work on tickets.</p>
134
+ <form id="agentInstructionsForm" class="field-form">
135
+ <textarea name="agent_instructions" placeholder="${l(`This project is about\u2026
136
+
137
+ How any AI agent should use this board and work in this repo.`)}">${l(e.agent_instructions||"")}</textarea>
138
+ <button type="submit">Save</button>
139
+ </form>
140
+ </div>
141
+ `}function $r(e){let t=St(),a="claude-code",n=Er(e,t),r=ca(a,n),o=da(t,n),i=_t[a];return`
142
+ <details class="section ai-subsection mcp-setup" data-mcp-setup>
143
+ <summary>
144
+ <div class="mcp-title-row">
145
+ <span class="mcp-inline-mark" aria-hidden="true">MCP</span>
146
+ <h3>Connect Your Agent to MCP</h3>
147
+ </div>
148
+ </summary>
149
+ <div>
150
+ <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>
151
+ <div class="mcp-flow">
152
+ <label>
153
+ <span>Operating system</span>
154
+ <select id="mcpOsSelect" class="select-chevron-field">
155
+ ${Object.entries(Tt).map(([s,u])=>`<option value="${s}" ${s===t?"selected":""}>${l(u.label)}</option>`).join("")}
156
+ </select>
157
+ </label>
158
+ <label>
159
+ <span>Agent app</span>
160
+ <select id="mcpClientSelect" class="select-chevron-field">
161
+ ${Object.entries(_t).map(([s,u])=>`<option value="${s}" ${s===a?"selected":""}>${l(u.label)}</option>`).join("")}
162
+ </select>
163
+ </label>
164
+ </div>
165
+ <div class="mcp-path-grid">
166
+ <label>
167
+ <span>Orbit MCP server file</span>
168
+ <input id="mcpServerPathInput" value="${l(n.serverPath)}" />
169
+ </label>
170
+ </div>
171
+ </div>
172
+
173
+ <div class="mcp-options-stack">
174
+ <div class="mcp-option-card">
175
+ <div class="mcp-option-card-head">
176
+ <div>
177
+ <strong data-mcp-output-title>${l(i.label)} setup</strong>
178
+ <p class="description" data-mcp-helper>${l(i.helper)}</p>
179
+ </div>
180
+ <button type="button" class="ghost" data-copy-mcp="config">Copy setup</button>
181
+ </div>
182
+ <div class="mcp-terminal">
183
+ <div class="mcp-terminal-chrome" aria-hidden="true"><span></span><span></span><span></span></div>
184
+ <pre class="mcp-snippet"><code data-mcp-config>${l(r)}</code></pre>
185
+ </div>
186
+ </div>
187
+
188
+ <div class="mcp-or-divider" aria-hidden="true"><span>OR</span></div>
189
+
190
+ <div class="mcp-option-card">
191
+ <div class="mcp-option-card-head">
192
+ <div>
193
+ <strong>Manual run command</strong>
194
+ <p class="description">Use this only when an app asks you to start the MCP server yourself.</p>
195
+ </div>
196
+ <button type="button" class="ghost" data-copy-mcp="command">Copy command</button>
197
+ </div>
198
+ <div class="mcp-terminal">
199
+ <div class="mcp-terminal-chrome" aria-hidden="true"><span></span><span></span><span></span></div>
200
+ <pre class="mcp-snippet mcp-snippet--compact"><code data-mcp-command>${l(o)}</code></pre>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ </details>
205
+ `}function St(){let e=String(navigator.userAgentData?.platform||navigator.platform||navigator.userAgent||"").toLowerCase();return e.includes("win")?"windows":e.includes("mac")?"macos":"linux"}function xr(e){return{serverPath:(Tt[e]||Tt.linux).serverPath}}function Er(e,t){let a=xr(t);return{serverPath:(e.deployment||{}).mcp_server_path||a.serverPath}}function cn(){return{serverPath:y("#mcpServerPathInput")?.value||""}}function ca(e,t){if(e==="claude-code")return Tr(t);if(e==="codex")return Sr(t);if(e==="opencode")return Lr(t);let a={mcpServers:{"starscape-orbit":{command:"node",args:[t.serverPath]}}};return JSON.stringify(a,null,2)}function Lr(e){return JSON.stringify({$schema:"https://opencode.ai/config.json",mcp:{"starscape-orbit":{type:"local",command:["node",e.serverPath],enabled:!0}}},null,2)}function Sr(e){return`[mcp_servers.starscape-orbit]
206
+ command = "node"
207
+ args = ["${_r(e.serverPath)}"]
208
+ enabled = true`}function Tr(e){return["claude mcp add starscape-orbit","--","node",un(e.serverPath)].join(" ")}function da(e,t){return`node ${un(t.serverPath)}`}function un(e){return`"${String(e||"").replace(/"/g,'\\"')}"`}function _r(e){return String(e||"").replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function Ir(){return`
209
+ <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>
210
+ <div class="section">
211
+ <div id="archiveList" class="archive-list">Loading\u2026</div>
212
+ </div>
213
+ `}function Cr(e){let t=e.board;return D.boardId!==t.id&&(D.boardId=t.id,D.step="idle",D.exported=!1),`
214
+ ${F.multiBoard?`
215
+ <div class="detail-head project-manual-head">
216
+ <div class="meta-grid">
217
+ <div class="meta meta--repo-path">
218
+ <span>Repo</span>
219
+ <strong>${l(qa(t.repo_url))}</strong>
220
+ <span>Path</span>
221
+ <strong>${l(t.system_path||e.deployment?.system_path||"Not available")}</strong>
222
+ </div>
223
+ </div>
224
+ </div>`:`
225
+ <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>`}
226
+
227
+ ${F.multiBoard?Mr(t):""}
228
+
229
+ <div class="section">
230
+ <h3>Board Snapshot</h3>
231
+ ${F.attachments?`
232
+ <label class="orbit-check">
233
+ <input type="checkbox" id="exportProjectImages" />
234
+ <span class="orbit-check-box" aria-hidden="true">
235
+ <svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
236
+ </span>
237
+ <span>Include attached images</span>
238
+ </label>
239
+ <p class="description">Image exports are embedded in the .orbit.json snapshot and may be much larger.</p>
240
+ `:""}
241
+ <div class="deployment-actions">
242
+ <button type="button" class="ghost" id="exportProject">Export</button>
243
+ <label class="import-button">
244
+ <input type="file" id="importSnapshotFile" accept=".orbit.json,.json,application/json" />
245
+ <span>Import</span>
246
+ </label>
247
+ </div>
248
+ </div>
249
+
250
+ ${F.multiBoard?Pr(t):""}
251
+ `}function Mr(e){return`
252
+ <div class="section board-rename-section">
253
+ <h3>Board Name</h3>
254
+ <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>
255
+ <form id="boardRenameForm" class="field-form">
256
+ <label>
257
+ <span>Display name</span>
258
+ <input name="name" value="${l(e.name||"")}" maxlength="120" required />
259
+ </label>
260
+ <button type="submit">Rename Board</button>
261
+ </form>
262
+ </div>
263
+ `}function Pr(e){let t=l(e.slug||"");if(D.step==="choice")return`
264
+ <div class="section repository-danger-zone">
265
+ <h3>Delete Board</h3>
266
+ <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>
267
+ <div class="repository-delete-actions">
268
+ <button type="button" class="ghost" id="deleteBoardExportFirst">Export first</button>
269
+ <button type="button" class="ghost danger-button" id="deleteBoardSkipExport">Delete without exporting</button>
270
+ <button type="button" class="ghost" id="deleteBoardCancel">Cancel</button>
271
+ </div>
272
+ </div>
273
+ `;if(D.step==="confirm"){let a=D.exported?"Snapshot exported. Type the board slug to confirm permanent deletion.":"Type the board slug to confirm permanent deletion.";return`
274
+ <div class="section repository-danger-zone is-confirming">
275
+ <h3>Delete Board</h3>
276
+ <p class="description">${l(a)}</p>
277
+ <label class="repository-confirm-field">
278
+ <span>Board slug</span>
279
+ <strong>${t}</strong>
280
+ <input id="deleteBoardConfirmInput" autocomplete="off" spellcheck="false" placeholder="${t}" autofocus />
281
+ </label>
282
+ <div class="repository-delete-actions">
283
+ <button type="button" class="danger-button" id="deleteBoardConfirm" disabled>Delete board permanently</button>
284
+ <button type="button" class="ghost" id="deleteBoardCancel">Cancel</button>
285
+ </div>
286
+ </div>
287
+ `}return`
288
+ <div class="section repository-danger-zone">
289
+ <h3>Delete Board</h3>
290
+ <p class="description">Remove this board from Orbit and delete its repo-local Orbit files. This cannot be undone without an exported snapshot.</p>
291
+ <button type="button" class="ghost danger-button" id="deleteBoardStart">Delete Board</button>
292
+ </div>
293
+ `}function Ar(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?Pe(n):l(a);return`
294
+ <div class="section ai-fields">
295
+ <div class="inline-md-field">
296
+ <span class="inline-md-field-label">Notes For You</span>
297
+ <div
298
+ class="inline-md-field-body preserved-text-body editable-field settings-notes-body ${o}"
299
+ data-edit-field="project_notes"
300
+ tabindex="0"
301
+ title="Click to edit"
302
+ role="button"
303
+ aria-label="Edit Notes For You"
304
+ >${i}</div>
305
+ </div>
306
+ </div>
307
+ `}function dn(){return`
308
+ <div class="section">
309
+ <label class="orbit-check" for="showPriorityToggle">
310
+ <input
311
+ type="checkbox"
312
+ id="showPriorityToggle"
313
+ ${c.showPriority?"checked":""}
314
+ />
315
+ <span class="orbit-check-box" aria-hidden="true">
316
+ <svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
317
+ </span>
318
+ <span class="orbit-check-label">Show priority label on cards</span>
319
+ </label>
320
+ <p class="description lanes-description">Rename, reorder, add, or remove lanes.</p>
321
+ ${qr()}
322
+ <form id="laneCreateForm" class="lane-create-form">
323
+ <input name="name" placeholder="New lane name..." required />
324
+ <button type="submit">Add Lane</button>
325
+ </form>
326
+ </div>
327
+ `}function Br(){let e=ce(),t=Te(),a=Nt(),n=jt(),r=a===null?`Using browser default (${n?"reduced motion":"motion allowed"}).`:"Using your saved preference.";return`
328
+ <div class="section appearance-theme-section">
329
+ <fieldset class="appearance-theme-field">
330
+ <legend>Theme</legend>
331
+ <div class="appearance-theme-options">
332
+ ${ln("light","Light",e)}
333
+ ${ln("dark","Dark",e)}
334
+ </div>
335
+ <p class="description">Applies the light/dark theme used by the top rail toggle.</p>
336
+ </fieldset>
337
+ </div>
338
+ <div class="section">
339
+ <label class="orbit-check" for="reduceMotionToggle">
340
+ <input
341
+ type="checkbox"
342
+ id="reduceMotionToggle"
343
+ ${t?"checked":""}
344
+ />
345
+ <span class="orbit-check-box" aria-hidden="true">
346
+ <svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
347
+ </span>
348
+ <span class="orbit-check-label">Reduce theme motion</span>
349
+ </label>
350
+ <p class="description">Turns off the animated starfield, meteors, and theme animations. ${l(r)}</p>
351
+ </div>
352
+ `}function ln(e,t,a){return`
353
+ <label class="appearance-theme-option">
354
+ <input type="radio" name="themePreference" value="${e}" ${a===e?"checked":""} />
355
+ <span class="appearance-theme-option-mark" aria-hidden="true">
356
+ <svg
357
+ class="appearance-theme-option-icon"
358
+ width="16"
359
+ height="16"
360
+ viewBox="0 0 24 24"
361
+ fill="none"
362
+ stroke="currentColor"
363
+ stroke-width="2"
364
+ stroke-linecap="round"
365
+ stroke-linejoin="round"
366
+ >${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>
367
+ </span>
368
+ <span class="appearance-theme-option-label">${t}</span>
369
+ </label>
370
+ `}function Dr(e){return`
371
+ ${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>':""}
372
+ <div class="section">
373
+ ${Or(e.entries)}
374
+ <form id="projectEntryForm" class="comment-form project-entry-form">
375
+ <select name="type" class="select-chevron-field" aria-label="Entry type">
376
+ <option value="decision">Decision</option>
377
+ <option value="lesson">Lesson</option>
378
+ </select>
379
+ <input name="title" placeholder="Entry title..." required />
380
+ <textarea name="body" placeholder="Decision rationale or lesson: do X instead of Y when working with Z." required></textarea>
381
+ <button type="submit">Add Entry</button>
382
+ </form>
383
+ </div>
384
+ `}function Rr(e,t){let a=e.board;if(t==="repository"){let n=async(i=!!y("#exportProjectImages")?.checked)=>{let s=i?"?include_attachments=true":"",u=await g(`/api/boards/${encodeURIComponent(a.id)}/export${s}`),d=i?`${a.slug}.with-images.orbit.json`:`${a.slug}.orbit.json`;Ha(d,u)};y("#exportProject")?.addEventListener("click",async()=>{await n()}),y("#boardRenameForm")?.addEventListener("submit",async i=>{i.preventDefault();let s=new FormData(i.currentTarget),u=String(s.get("name")||"").trim();!u||u===a.name||(await g(`/api/boards/${encodeURIComponent(a.id)}`,{method:"PATCH",body:{name:u}}),await S(),c.detailMode="settings",b.success(`Renamed board to ${u}. Canonical slug unchanged: ${a.slug}`))}),y("#importSnapshotFile")?.addEventListener("change",async i=>{let s=i.currentTarget.files?.[0];if(!s)return;let u=await s.text(),d=JSON.parse(u);await g("/api/admin/import",{method:"POST",body:{snapshot:d,replace_existing:!0,board_id:a.id}}),await S(),c.detailMode="settings",b.success("Board imported"),i.currentTarget.value=""}),y("#deleteBoardStart")?.addEventListener("click",async()=>{D.step="choice",D.exported=!1,await $e()}),y("#deleteBoardExportFirst")?.addEventListener("click",async()=>{await n(),D.step="confirm",D.exported=!0,await $e()}),y("#deleteBoardSkipExport")?.addEventListener("click",async()=>{D.step="confirm",D.exported=!1,await $e()}),y("#deleteBoardCancel")?.addEventListener("click",async()=>{D.step="idle",D.exported=!1,await $e()});let r=y("#deleteBoardConfirmInput"),o=y("#deleteBoardConfirm");r?.addEventListener("input",()=>{o&&(o.disabled=r.value.trim()!==a.slug)}),o?.addEventListener("click",async()=>{await g(`/api/boards/${encodeURIComponent(a.id)}`,{method:"DELETE",body:{confirm_slug:a.slug,delete_files:!0}}),D.step="idle",D.exported=!1,await S(),b.success(`Deleted board: ${a.name}`),P({boardId:c.boardId,view:"board"},{replace:!0})})}if(t==="notes"){let n=w.querySelector('[data-edit-field="project_notes"]');if(n){let r=()=>et(n,{fieldName:"project_notes",multiline:!0,initialValue:a.project_notes||"",ariaLabel:"Edit Notes For You",rerender:async()=>{c.detailMode="settings",await $e()},commit:async o=>{await g(`/api/boards/${a.id}`,{method:"PATCH",body:{project_notes:de(o)}}),await S(),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"&&(w.querySelectorAll("[data-lane-action]").forEach(n=>{n.addEventListener("click",async()=>{await Hr(n)})}),w.querySelectorAll(".lane-row input, .lane-row select").forEach(n=>{n.addEventListener("change",async()=>{await mn(n.closest(".lane-row"))})}),y("#laneCreateForm")?.addEventListener("submit",async n=>{n.preventDefault();let r=new FormData(n.currentTarget);await g(`/api/boards/${a.id}/states`,{method:"POST",body:Object.fromEntries(r.entries())}),await S(),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"),Re()})),t==="appearance"&&(w.querySelectorAll("input[name='themePreference']").forEach(n=>{n.addEventListener("change",r=>{if(!r.currentTarget.checked)return;let o=zt(r.currentTarget.value);b.success(o==="dark"?"Dark theme enabled":"Light theme enabled")})}),y("#reduceMotionToggle")?.addEventListener("change",n=>{let r=!!n.currentTarget.checked;Sa(r),He(),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 r=y("#mcpOsSelect")?.value||St(),o=y("#mcpClientSelect")?.value||"claude-code",i=_t[o]||_t.generic,s=cn(),u=ca(o,s),d=da(r,s),m=w.querySelector("[data-mcp-os-note]");m&&(m.textContent=`Detected: ${Tt[St()]?.label||"Linux"}`);let p=w.querySelector("[data-mcp-client-format]");p&&(p.textContent=i.format);let h=w.querySelector("[data-mcp-output-title]");h&&(h.textContent=`${i.label} setup`);let v=w.querySelector("[data-mcp-helper]");v&&(v.textContent=i.helper);let $=w.querySelector("[data-mcp-config]");$&&($.textContent=u);let I=w.querySelector("[data-mcp-command]");I&&(I.textContent=d)};y("#mcpOsSelect")?.addEventListener("change",()=>n()),y("#mcpClientSelect")?.addEventListener("change",()=>n()),["#mcpServerPathInput"].forEach(r=>{y(r)?.addEventListener("input",()=>n())}),w.querySelectorAll("[data-copy-mcp]").forEach(r=>{r.addEventListener("click",async()=>{let o=y("#mcpOsSelect")?.value||St(),i=y("#mcpClientSelect")?.value||"claude-code",s=cn(),u=r.dataset.copyMcp==="command"?da(o,s):ca(i,s);await Fr(u),b.success(r.dataset.copyMcp==="command"?"MCP command copied":"MCP config copied")})}),y("#agentInstructionsForm")?.addEventListener("submit",async r=>{r.preventDefault();let o=new FormData(r.currentTarget);await g(`/api/boards/${a.id}`,{method:"PATCH",body:Object.fromEntries(o.entries())}),await S(),c.detailMode="settings",b.success("Saved")}),y("#aiEnabledToggle")?.addEventListener("change",async r=>{let o=!!r.currentTarget.checked;await g(`/api/boards/${a.id}`,{method:"PATCH",body:{ai_enabled:o}}),await S(),c.detailMode="settings",b.success(o?"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 g(`/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=>tt(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 g(k(`/api/tickets/${encodeURIComponent(m)}/restore`),{method:"POST"}),b.success(`Restored: ${d}`),await S(),await r();return}if(s){let m=s.dataset.archiveDelete;if(!confirm(`Permanently delete "${d}"? This cannot be undone.`))return;await g(k(`/api/tickets/${encodeURIComponent(m)}`),{method:"DELETE"}),b.success(`Deleted: ${d}`),await r()}})}}t==="journal"&&(w.querySelectorAll("[data-entry-action='toggle-struck']").forEach(n=>{n.addEventListener("click",async()=>{await g(k(`/api/board-entries/${n.dataset.entryId}`),{method:"PATCH",body:{struck:n.dataset.struck!=="true"}}),await S(),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 g(`/api/boards/${a.id}/entries`,{method:"POST",body:Object.fromEntries(r.entries())}),await S(),c.detailMode="settings",b.success("Project entry added")}))}async function Fr(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 Or(e=[]){return e.length?e.map(t=>{let a=!!t.struck_at;return`
385
+ <div class="comment project-entry ${l(t.type)} ${a?"is-struck":""}">
386
+ <div class="comment-meta">
387
+ <div>
388
+ <strong>${l(t.type.toUpperCase())}: ${l(t.title)}</strong>
389
+ ${a?'<span class="project-entry-status">Excluded from agent context</span>':""}
390
+ </div>
391
+ <div class="project-entry-actions">
392
+ <span>${l(t.created_by)} - ${Ge(t.created_at)}</span>
393
+ <button
394
+ type="button"
395
+ class="ghost project-entry-strike"
396
+ data-entry-action="toggle-struck"
397
+ data-entry-id="${l(t.id)}"
398
+ data-struck="${a?"true":"false"}"
399
+ aria-pressed="${a?"true":"false"}"
400
+ >${a?"Restore":"Strike"}</button>
401
+ </div>
402
+ </div>
403
+ <div class="comment-body">${l(t.body)}</div>
404
+ </div>
405
+ `}).join(""):'<p class="description">No project entries yet.</p>'}function Nr(e,t,a){return!!(t.get(e.id)||e.is_default||pn.has(e.role)||a&&e.role==="ai_ready")}function jr(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)":pn.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 qr(){let e=ze(),t=K()?.ai_enabled!==0,a=new Map;for(let n of W())a.set(n.state_id,(a.get(n.state_id)||0)+1);return`
406
+ <div class="lane-list">
407
+ ${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`
408
+ <div class="lane-row${o?" lane-row--locked":""}" data-lane-id="${n.id}">
409
+ <button type="button" class="lane-icon-btn" data-lane-action="up" ${r===0?"disabled":""} title="Move lane left" aria-label="Move lane left">\u2191</button>
410
+ <button type="button" class="lane-icon-btn" data-lane-action="down" ${r===e.length-1?"disabled":""} title="Move lane right" aria-label="Move lane right">\u2193</button>
411
+ <input name="name" value="${l(n.name)}" aria-label="Lane name" ${o?`readonly title="${l(i)}"`:""} />
412
+ <button type="button" class="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">
413
+ <span class="lane-new-sun" aria-hidden="true"></span>
414
+ <span class="lane-new-label">New</span>
415
+ </button>
416
+ <button type="button" class="lane-delete-btn" data-lane-action="delete" ${Nr(n,a,t)?"disabled":""} title="${jr(n,a,t)}" aria-label="Delete lane">\xD7</button>
417
+ </div>
418
+ `}).join("")}
419
+ </div>
420
+ `}async function Hr(e){let t=e.closest(".lane-row"),a=t.dataset.laneId,n=e.dataset.laneAction;if(n==="delete"){await g(k(`/api/states/${a}`),{method:"DELETE"}),await S(),c.detailMode="settings",b.success("Lane deleted");return}if(n==="toggle-default"){if(e.getAttribute("aria-pressed")==="true")return;w.querySelectorAll('[data-lane-action="toggle-default"]').forEach(u=>{u.setAttribute("aria-pressed","false"),u.classList.remove("is-on")}),e.setAttribute("aria-pressed","true"),e.classList.add("is-on"),await mn(t,{silent:!0}),b.success("Default lane updated");return}let r=ze().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 g(`/api/boards/${c.boardId}/states`,{method:"PATCH",body:{state_ids:r}}),await S(),c.detailMode="settings",b.success("Lanes reordered"))}async function mn(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 g(k(`/api/states/${e.dataset.laneId}`),{method:"PATCH",body:{name:a,is_default:r}}),await S(),c.detailMode="settings",t||b.success("Lane updated")}var D,Tt,_t,pn,fn=T(()=>{q();te();ae();Q();le();bt();pe();Ze();la();ue();Oe();qt();Gt();D={boardId:"",step:"idle",exported:!1};Tt={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"}},_t={"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."}};pn=new Set(["in_progress","review"])});function xe(e){return String(e||"").trim()}function pa(e){let t=xe(e);return t?t.replace(/^@+/,"").replace(/[-_]+/g," ").replace(/\s+/g," ").trim().replace(/\b\w/g,a=>a.toUpperCase()):""}function bn({actor:e,actorId:t}={}){let a=xe(e),n=xe(t),r=a.toLowerCase(),o=n.toLowerCase();return a&&!ua.has(r)&&!hn.has(r)?pa(a):n&&!ua.has(o)&&!hn.has(o)?pa(n):""}function ma({actor:e,actorType:t,actorId:a}={}){let n=xe(t).toLowerCase(),r=xe(e),o=r.toLowerCase();if(n==="agent"){let i=bn({actor:r,actorId:a});return i?`Agent \xB7 ${i}`:"Agent"}return!r||ua.has(o)?"You":pa(r)}function gn(e={}){let t=xe(e.kind).toLowerCase(),a=xe(e.author);if(Ur.has(t)||t&&t!=="human_comment"){let r=bn({actor:a});return r?`Agent \xB7 ${r}`:"Agent"}return ma({actor:a,actorType:"human"})}var ua,hn,Ur,yn=T(()=>{ua=new Set(["local","local-owner","owner"]),hn=new Set(["agent","local-agent","orbit-agent"]),Ur=new Set(["agent_note","checkpoint","completion"])});async function oe(e,{renderBoardAfter:t=!1}={}){c.selectedTicketId=e,c.detailMode="ticket";let[a,n,r]=await Promise.all([g(k(`/api/tickets/${e}/context?depth=1`)),g(k(`/api/tickets/${e}/history`)),g(k(`/api/tickets/${e}/comments`))]);Fa(a.ticket),t&&Re(),await Ee({context:a,statusHistory:n,comments:r.comments||[]})}async function me(e,t){await g(k(`/api/tickets/${e}`),{method:"PATCH",body:t}),await oe(e,{renderBoardAfter:!0})}function et(e,t){if(!e||e.dataset.editing==="true")return;let{fieldName:a,multiline:n=!0,initialValue:r="",ariaLabel:o,emptyMessage:i,rerender:s,commit:u}=t;e.dataset.editing="true";let d=document.createElement(n?"textarea":"input");d.className=n?"inline-desc-editor":"inline-title-editor",d.value=r,a&&d.setAttribute("data-editing-field",a),o&&d.setAttribute("aria-label",o),n?d.rows=Math.max(3,String(r).split(`
421
+ `).length+1):(d.type="text",d.maxLength=500,d.autocomplete="off",d.spellcheck=!0),e.replaceWith(d),d.focus(),n?d.setSelectionRange(d.value.length,d.value.length):d.select();let m=!1,p=()=>{document.removeEventListener("pointerdown",$,!0)},h=()=>{d.isConnected&&d.replaceWith(e),delete e.dataset.editing},v=()=>{m=!0,p(),h()};function $(x){m||x.target===d||d.contains(x.target)||d.blur()}document.addEventListener("pointerdown",$,!0);let I=async()=>{if(m)return;let x=n?d.value:d.value.trim();if(v(),String(x)===String(r)){s&&await s();return}if(i&&!x.trim()){b.warning(i),s&&await s();return}u&&await u(x)},Y=async()=>{m||(v(),s&&await s())};d.addEventListener("blur",I),d.addEventListener("keydown",x=>{if(x.key==="Escape"){x.preventDefault(),Y();return}x.key==="Enter"&&(n?(x.metaKey||x.ctrlKey)&&(x.preventDefault(),I()):(x.preventDefault(),I()))})}function zr(e){let t=e.id,a=()=>et(n,{fieldName:"title",multiline:!1,initialValue:e.title||"",ariaLabel:"Edit ticket title",emptyMessage:"Title cannot be empty",rerender:()=>Ee(),commit:d=>me(t,{title:d})}),n=j.querySelector('[data-edit-field="title"]');n&&(n.addEventListener("click",a),n.addEventListener("keydown",d=>{(d.key==="Enter"||d.key===" ")&&(d.preventDefault(),a())}));let r=w.querySelector('[data-edit-field="description"]'),o=()=>et(r,{fieldName:"description",multiline:!0,initialValue:e.description||"",ariaLabel:"Edit ticket description",rerender:()=>Ee(),commit:d=>me(t,{description:de(d)})});r&&(r.addEventListener("click",d=>{d.preventDefault(),o()}),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=w.querySelector(`[data-edit-field="${d}"]`);if(!p)continue;let h=()=>et(p,{fieldName:d,multiline:!0,initialValue:e[d]||"",ariaLabel:`Edit ${m}`,rerender:()=>Ee(),commit:v=>me(t,{[d]:de(v)})});p.addEventListener("click",v=>{v.preventDefault(),h()}),p.addEventListener("keydown",v=>{(v.key==="Enter"||v.key===" ")&&(v.preventDefault(),h())})}j.querySelectorAll(".meta-select[data-meta-field]").forEach(d=>{d.addEventListener("change",async()=>{let m=d.dataset.metaField;if(m==="type"){await me(t,{type:d.value});return}if(m==="state_id"){await me(t,{state_id:d.value});return}if(m==="parent_ticket_id"){await me(t,{parent_ticket_id:d.value||null});return}m==="priority"&&await me(t,{priority:Number(d.value)})})});let s=async d=>{await me(t,{labels:d})};w.querySelectorAll("[data-remove-label]").forEach(d=>{d.addEventListener("click",async()=>{let m=d.getAttribute("data-remove-label"),p=(e.labels||[]).map(h=>h.name).filter(h=>h!==m);await s(p)})});let u=async()=>{let d=w.querySelector("[data-label-input]");if(!d)return;let m=d.value.trim();if(!m)return;let p=(e.labels||[]).map(h=>h.name);if(p.includes(m)){d.value="";return}p.push(m),d.value="",await s(p)};w.querySelector("[data-label-add]")?.addEventListener("click",()=>{u()}),w.querySelector("[data-label-input]")?.addEventListener("keydown",d=>{d.key==="Enter"&&(d.preventDefault(),u())})}async function Ee(e={}){if(c.detailMode==="settings"){await $e(),Qt();return}if(!c.selectedTicketId){ft();return}let t=f=>Object.prototype.hasOwnProperty.call(e,f),[a,n,r,o]=await Promise.all([t("context")?Promise.resolve(e.context):g(k(`/api/tickets/${c.selectedTicketId}/context?depth=1`)),t("statusHistory")?Promise.resolve(e.statusHistory):g(k(`/api/tickets/${c.selectedTicketId}/history`)),t("comments")?Promise.resolve(e.comments):g(k(`/api/tickets/${c.selectedTicketId}/comments`)).then(f=>f.comments||[]),t("attachmentList")?Promise.resolve(e.attachmentList):F.attachments?g(k(`/api/tickets/${c.selectedTicketId}/attachments`)).catch(()=>({attachments:[]})):Promise.resolve({attachments:[]})]),i=a.ticket;xt(i.id,i.updated_at),document.querySelector(`.card[data-ticket-id="${CSS.escape(i.id)}"] .card-unread-dot`)?.remove();let s=Ra(i.state_id),u=["epic","feature","task","bug"].map(f=>`<option value="${l(f)}" ${B(i.type)===f?"selected":""}>${l(Na(f))}</option>`).join(""),d=s.map(f=>`<option value="${l(f.id)}" ${f.id===i.state_id?"selected":""}>${l(f.name)}</option>`).join(""),m=[0,1,2,3,4].map(f=>`<option value="${f}" ${Number(i.priority)===f?"selected":""}>${l(Be(f))}</option>`).join(""),p=(i.labels||[]).map(f=>`
422
+ <span class="label-pill-removable" style="--label-color: ${l(f.color)}">
423
+ ${l(f.name)}
424
+ <button type="button" class="label-pill-remove" data-remove-label="${l(f.name)}" title="Remove label">\xD7</button>
425
+ </span>`).join(""),v=Ba().map(f=>f.name).filter(f=>!i.labels?.some(_=>_.name===f)).map(f=>`<option value="${l(f)}"></option>`).join(""),$=B(i.type),I=De(i.priority),Y=s.find(f=>f.id===i.state_id),x=Ke(Y),he=i.description?Pe(i.description):l("No description yet."),O=i.description?"description preserved-text-body editable-field":"description editable-field is-placeholder",E=`
426
+ <span class="ticket-number">${l(X(i))}</span>
427
+ <div class="detail-meta-badge-row" aria-label="Ticket metadata controls">
428
+ <select class="detail-meta-badge detail-state-badge state-pill-${l(x)} meta-select" data-meta-field="state_id" aria-label="Ticket state">${d}</select>
429
+ <select class="detail-meta-badge detail-type-badge type-pill-${l($)} meta-select" data-meta-field="type" aria-label="Ticket type">${u}</select>
430
+ <select class="detail-meta-badge detail-priority-badge priority-pill priority-pill-${l(I)} meta-select" data-meta-field="priority" aria-label="Ticket priority">${m}</select>
431
+ </div>
432
+ `;ht({mode:"ticket",title:i.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:E,body:`
433
+ <div class="detail-head">
434
+ <p class="detail-updated" title="Last saved change">Updated ${l(Oa(i.updated_at))}</p>
435
+ <div class="${O}" data-edit-field="description" tabindex="0" title="Click to edit">${he}</div>
436
+ <dl class="ticket-meta" id="ticketMetaGrid" data-ticket-id="${l(i.id)}">
437
+ <div class="ticket-meta-row ticket-meta-row--stack">
438
+ <dt>Labels</dt>
439
+ <dd>
440
+ <div class="label-pills-row" data-other-labels>${p}</div>
441
+ <div class="label-add-row">
442
+ <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" />
443
+ <datalist id="board-label-suggestions">${v}</datalist>
444
+ <button type="button" class="ghost meta-add-btn" data-label-add>Add</button>
445
+ </div>
446
+ </dd>
447
+ </div>
448
+ </dl>
449
+ </div>
450
+
451
+ ${eo(i,a)}
452
+
453
+ ${F.attachments?Kr(i,o.attachments||[]):""}
454
+
455
+ ${to(i,a)}
456
+
457
+ <div class="section">
458
+ <h3>Related</h3>
459
+ ${Zr(a.relations,i)}
460
+ </div>
461
+
462
+ ${K()?.ai_enabled!==0?`
463
+ <details class="section ai-fields" data-ai-plan-toggle data-ticket-id="${l(i.id)}"${Gr(i.id)?" open":""}>
464
+ <summary><h3>AI Plan / Implementation Record</h3></summary>
465
+ <div class="ai-fields-grid">
466
+ ${ha({fieldName:"ai_plan",label:"AI-Written Plan",value:i.ai_plan,placeholder:"Paste or let an AI write the plan..."})}
467
+ ${ha({fieldName:"implementation_summary",label:"Implementation Summary",value:i.implementation_summary,placeholder:"What changed, what shipped, what remains..."})}
468
+ ${ha({fieldName:"implementation_updates",label:"Implementation Updates / Lessons",value:i.implementation_updates,placeholder:"Progress notes, mistakes to avoid, discoveries..."})}
469
+ </div>
470
+ </details>`:""}
471
+
472
+ <div class="section">
473
+ <h3>Status History</h3>
474
+ ${Xr(n)}
475
+ </div>
476
+
477
+ <div class="section">
478
+ <h3>Comments</h3>
479
+ ${r.map(Qr).join("")||'<p class="description">No comments yet.</p>'}
480
+ <form class="comment-form" id="detailCommentForm">
481
+ <textarea name="body" placeholder="Add a comment, decision, or instruction..." required></textarea>
482
+ <button type="submit">Comment</button>
483
+ </form>
484
+ </div>
485
+ `}),zr(i),F.attachments&&Jr(i),y("#detailCommentForm").addEventListener("submit",async f=>{f.preventDefault();let _=de(new FormData(f.currentTarget).get("body")).trim();_&&(await g(k(`/api/tickets/${i.id}/comments`),{method:"POST",body:{body:_}}),await oe(i.id))}),w.querySelectorAll("[data-open-ticket]").forEach(f=>{f.addEventListener("click",async _=>{_.target.closest("[data-detach-child],[data-detach-parent],[data-detach-relation]")||P({boardId:c.boardId,view:"ticket",ticketId:f.dataset.openTicket})})}),w.querySelectorAll("[data-detach-child]").forEach(f=>{f.addEventListener("click",async _=>{_.stopPropagation();let A=f.dataset.detachChild;try{await g(k(`/api/tickets/${A}`),{method:"PATCH",body:{parent_ticket_id:null}})}catch(N){b.error(N.message||"Failed to remove child");return}await oe(i.id,{renderBoardAfter:!0}),b.success("Removed from epic")})}),w.querySelectorAll("[data-detach-parent]").forEach(f=>{f.addEventListener("click",async _=>{_.stopPropagation();try{await g(k(`/api/tickets/${i.id}`),{method:"PATCH",body:{parent_ticket_id:null}})}catch(A){b.error(A.message||"Failed to remove parent");return}await oe(i.id,{renderBoardAfter:!0}),b.success("Parent epic removed")})}),w.querySelectorAll("[data-detach-relation]").forEach(f=>{f.addEventListener("click",async _=>{_.stopPropagation();let A=f.dataset.detachRelation;try{await g(k(`/api/relations/${A}`),{method:"DELETE"})}catch(N){b.error(N.message||"Failed to remove relation");return}await oe(i.id),b.success("Relation removed")})});let U=w.querySelector("[data-ai-plan-toggle]");U&&U.addEventListener("toggle",()=>{let f=U.dataset.ticketId;U.open?localStorage.setItem(`mab_ai_plan_open:${f}`,"1"):localStorage.removeItem(`mab_ai_plan_open:${f}`)});let C=w.querySelector("#parentEpicForm");if(C){let f=C.querySelector("[name=target]"),_=C.querySelector("[data-parent-epic-clear]"),A=()=>{C.classList.toggle("has-value",f.value.length>0)};f.addEventListener("input",A),A(),_.addEventListener("click",()=>{f.value="",A(),f.focus()}),C.addEventListener("submit",async N=>{N.preventDefault();let J=String(new FormData(N.currentTarget).get("target")||"").trim();if(!J)return;let z=vn(J,i.id);if(!z){b.warning("Pick an epic from the suggestions");return}let ee=W().find(G=>G.id===z);if(!ee||B(ee.type)!=="epic"){b.warning("Parent must be an epic");return}try{await g(k(`/api/tickets/${i.id}`),{method:"PATCH",body:{parent_ticket_id:z}})}catch(G){b.error(G.message||"Failed to set parent epic");return}await oe(i.id,{renderBoardAfter:!0}),b.success("Parent epic set")})}let R=w.querySelector("#relatedAddForm");if(R){let f=R.querySelector("[name=target]"),_=R.querySelector("[data-related-clear]"),A=()=>{R.classList.toggle("has-value",f.value.length>0)};f.addEventListener("input",A),A(),_.addEventListener("click",()=>{f.value="",A(),f.focus()}),R.addEventListener("submit",async N=>{N.preventDefault();let J=new FormData(N.currentTarget),z=String(J.get("target")||"").trim(),ee=String(J.get("type")||"relates_to");if(!z)return;let G=vn(z,i.id);if(!G){b.warning("Pick a ticket from the suggestions");return}try{await g(k("/api/relations"),{method:"POST",body:{source_ticket_id:i.id,target_ticket_id:G,type:ee}})}catch(be){b.error(be.message||"Failed to add relation");return}await oe(i.id)})}Qt()}function Gr(e){return localStorage.getItem(`mab_ai_plan_open:${e}`)==="1"}function Kr(e,t=[]){let a=t.length?`<div class="attachment-grid">${t.map(n=>Wr(e,n)).join("")}</div>`:'<p class="description">No images attached yet.</p>';return`
486
+ <div class="section attachments-section" data-attachment-section data-ticket-id="${l(e.id)}">
487
+ <div class="attachment-heading-row">
488
+ <h3>Images</h3>
489
+ <label class="ghost attachment-upload-button">
490
+ Upload
491
+ <input type="file" accept="image/*" multiple data-attachment-input />
492
+ </label>
493
+ </div>
494
+ <div class="attachment-dropzone" data-attachment-dropzone tabindex="0">
495
+ <strong>Paste, drag, or upload images</strong>
496
+ <span>PNG, JPG, GIF, WebP, SVG, BMP, or AVIF up to 10 MB each.</span>
497
+ </div>
498
+ ${a}
499
+ </div>
500
+ `}function Wr(e,t){let a=l(t.original_name||"image"),n=`${Yr(t.size_bytes)} \xB7 ${l(t.mime_type||"image")}`;if(t.missing)return`
501
+ <article class="attachment-card is-missing">
502
+ <div class="attachment-missing-thumb">Missing</div>
503
+ <div class="attachment-card-meta"><strong>${a}</strong><span>${n}</span></div>
504
+ <button type="button" class="attachment-delete" data-delete-attachment="${l(t.id)}" aria-label="Remove missing image">\xD7</button>
505
+ </article>
506
+ `;let r=k(t.content_url||`/api/tickets/${e.id}/attachments/${t.id}/content`);return`
507
+ <article class="attachment-card">
508
+ <button type="button" class="attachment-thumb-button" data-open-lightbox="${l(r)}" data-lightbox-title="${a}">
509
+ <img src="${l(r)}" alt="${a}" loading="lazy" />
510
+ </button>
511
+ <div class="attachment-card-meta"><strong>${a}</strong><span>${n}</span></div>
512
+ <button type="button" class="attachment-delete" data-delete-attachment="${l(t.id)}" aria-label="Remove image">\xD7</button>
513
+ </article>
514
+ `}function Yr(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 wn(e){return[...e||[]].filter(t=>t&&String(t.type||"").startsWith("image/"))}async function fa(e,t){let a=wn(t);if(!a.length){b.warning("No image files found");return}for(let n of a){let r=await fetch(k(`/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 oe(e.id),b.success(a.length===1?"Image attached":`${a.length} images attached`)}function Jr(e){let t=w.querySelector("[data-attachment-section]");if(!t)return;let a=t.querySelector("[data-attachment-input]");a?.addEventListener("change",async()=>{try{await fa(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 fa(e,r.dataTransfer?.files||[])}catch(o){b.error(o.message||"Upload failed")}}),w.onpaste=async r=>{if(r.target.closest("input, textarea, [contenteditable='true']"))return;let o=wn(r.clipboardData?.files||[]);if(o.length){r.preventDefault();try{await fa(e,o)}catch(i){b.error(i.message||"Paste upload failed")}}},t.querySelectorAll("[data-delete-attachment]").forEach(r=>{r.addEventListener("click",async()=>{try{await g(k(`/api/tickets/${e.id}/attachments/${r.dataset.deleteAttachment}`),{method:"DELETE"}),await oe(e.id),b.success("Image removed")}catch(o){b.error(o.message||"Delete failed")}})}),t.querySelectorAll("[data-open-lightbox]").forEach(r=>{r.addEventListener("click",()=>Vr(r.dataset.openLightbox,r.dataset.lightboxTitle||"Image"))})}function Vr(e,t){document.querySelector(".attachment-lightbox")?.remove();let n=document.createElement("div");n.className="attachment-lightbox",n.innerHTML=`
515
+ <div class="attachment-lightbox-panel" role="dialog" aria-modal="true" aria-label="${l(t)}">
516
+ <button type="button" class="attachment-lightbox-close" aria-label="Close image">\xD7</button>
517
+ <img src="${l(e)}" alt="${l(t)}" />
518
+ <p>${l(t)}</p>
519
+ </div>
520
+ `;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 vn(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 Xr(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(ma({actor:t.actor,actorType:a.actor_type||"human",actorId:a.actor_id}));return`<div class="comment status-change">
521
+ <div class="comment-meta">
522
+ <span class="status-route">${n} \u2192 ${r}</span>
523
+ <span>${o} \xB7 ${Ge(t.created_at)}</span>
524
+ </div>
525
+ </div>`}).join("")}function Qr(e){return`
526
+ <div class="comment ${e.kind==="checkpoint"?"checkpoint":""}">
527
+ <div class="comment-meta">
528
+ <strong>${l(gn(e))}</strong>
529
+ <span>${e.kind==="human_comment"?"":`${l(e.kind)} - `}${Ge(e.created_at)}</span>
530
+ </div>
531
+ <div class="comment-body preserved-text-body">${Pe(e.body)}</div>
532
+ </div>
533
+ `}function ha({fieldName:e,label:t,value:a,placeholder:n}){let r=a||"",o=!!r.trim(),i=o?Pe(r):l(n||""),s=o?"":"is-placeholder";return`
534
+ <div class="inline-md-field">
535
+ <span class="inline-md-field-label">${l(t)}</span>
536
+ <div
537
+ class="inline-md-field-body preserved-text-body editable-field ${s}"
538
+ data-edit-field="${l(e)}"
539
+ tabindex="0"
540
+ title="Click to edit"
541
+ role="button"
542
+ aria-label="Edit ${l(t)}"
543
+ >${i}</div>
544
+ </div>
545
+ `}function Zr(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=>ao(u)).join("")}</div>`:"",s=`
546
+ <form id="relatedAddForm" class="related-add-row">
547
+ <div class="related-add-input-wrap">
548
+ <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" />
549
+ <button type="button" class="related-add-clear" data-related-clear aria-label="Clear" tabindex="-1">\xD7</button>
550
+ <datalist id="related-ticket-suggestions">${o}</datalist>
551
+ </div>
552
+ <select name="type" class="related-add-type meta-inline" aria-label="Relation type">
553
+ <option value="relates_to">relates to</option>
554
+ <option value="blocks">blocks</option>
555
+ <option value="blocked_by">blocked by</option>
556
+ </select>
557
+ <button type="submit" class="ghost meta-add-btn">Add</button>
558
+ </form>
559
+ `;return i+s}function eo(e,t){if(B(e.type)==="epic"){let n=t.child_tickets||[];return`
560
+ <div class="section">
561
+ <h3>Features inside this Epic</h3>
562
+ ${n.length?`<div class="detail-card-grid">${n.map(o=>tt(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>'}
563
+ </div>
564
+ `}return""}function to(e,t){if(B(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`
565
+ <div class="section">
566
+ <h3>Epic</h3>
567
+ ${t.parent_ticket?`<div class="detail-card-grid detail-card-grid--single">${tt(t.parent_ticket,{detach:{mode:"parent"}})}</div>`:`
568
+ <form id="parentEpicForm" class="related-add-row">
569
+ <div class="related-add-input-wrap">
570
+ <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" />
571
+ <button type="button" class="related-add-clear" data-parent-epic-clear aria-label="Clear" tabindex="-1">\xD7</button>
572
+ <datalist id="parent-epic-suggestions">${n}</datalist>
573
+ </div>
574
+ <button type="submit" class="ghost meta-add-btn">Set</button>
575
+ </form>
576
+ `}
577
+ </div>
578
+ `}function tt(e,t={}){let a=B(e.type),n=De(e.priority),r=Ke(e),o=Ae(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,h="",v="";m==="child"?(h=`data-detach-child="${l(p)}"`,v="Remove from epic"):m==="parent"?(h='data-detach-parent="1"',v="Remove parent epic"):m==="relation"&&(h=`data-detach-relation="${l(p)}"`,v="Remove relation"),s=`<button type="button" class="card-detach-btn" ${h} aria-label="${v}" title="${v}">\xD7</button>`}else if(t.archive){let m=l(t.archive.id);s=`
579
+ <div class="card-archive-actions">
580
+ <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>
581
+ <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>
582
+ </div>
583
+ `}let u=$t(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`
584
+ <article class="card card--detail type-${l(a)} priority-${l(n)}"${t.disableOpen?"":` data-open-ticket="${l(e.id)}"`}>
585
+ ${d}
586
+ ${s}
587
+ ${i}
588
+ <h3>${l(e.title)}</h3>
589
+ <div class="card-meta">
590
+ <div class="card-expand-trigger card-expand-trigger--static">
591
+ <span class="ticket-number">${l(X(e))}</span>
592
+ <div class="card-type-id type-pill-${l(a)}">
593
+ ${Et(e.type)}
594
+ <span class="card-type-label">${l(o)}</span>
595
+ </div>
596
+ <span class="detail-card-state state-pill-${l(r)}">${l(e.state_name||"State")}</span>
597
+ ${c.showPriority?sa(e.priority):""}
598
+ </div>
599
+ </div>
600
+ </article>
601
+ `}function ao(e){let t=String(e.type||"").replace(/_/g," "),a=e.direction==="incoming"?"from":"to";return tt(e.other_ticket,{eyebrow:`${t} ${a}`,detach:{mode:"relation",id:e.id}})}var la=T(()=>{q();te();ae();Ze();bt();pe();Q();Oe();le();fn();Xe();yn()});function ba(){Ne&&(clearTimeout(Ne),Ne=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",()=>{It=1e3,Ct&&(b.success("Live updates reconnected"),Ct=!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?xt(r.ticket_id,r.timestamp):o==="ticket_deleted"?an(r.ticket_id):no.has(o)&&tn(r.ticket_id)),o==="ticket_created")b.success(`Card created: ${r.data.title||"New card"}`),fe();else if(o==="state_changed"){let d=i(),m=d?`#${d.number} ${d.title}`:"Card";b.info(`${m} \u2192 ${r.data.to}`),fe()}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}`:""}`),fe()}else if(o==="comment_created"){let d=i(),m=d?`#${d.number}`:"ticket";b.info(`New comment on ${m}`),fe()}else if(o==="agent_claimed"){let d=i();b.info(`Agent claimed ${d?`#${d.number} ${d.title}`:"a ticket"}`),fe()}else if(o==="checkpoint_requested"){let d=i();b.warning(`Checkpoint on ${d?`#${d.number}`:"ticket"} \u2014 needs human input`),fe()}else if(o==="agent_completed"){let d=i();b.success(`Agent completed ${d?`#${d.number} ${d.title}`:"a ticket"}`),fe()}else(o==="ticket_deleted"||o==="states_reordered"||o==="state_created"||o==="state_deleted"||o==="board_updated"||o==="board_imported")&&fe()}),a.onerror=()=>{if(a.readyState!==EventSource.CLOSED||(Ct||(b.warning("Live updates disconnected \u2014 retrying\u2026"),Ct=!0),Ne))return;let n=It;It=Math.min(It*2,3e4),Ne=setTimeout(()=>{Ne=null,ba()},n)}}var no,It,Ne,Ct,fe,kn=T(()=>{q();Q();le();ue();Xe();no=new Set(["ticket_created","state_changed","ticket_updated","comment_created","agent_claimed","checkpoint_requested","agent_completed"]),It=1e3,Ne=null,Ct=!1;fe=pt(async()=>{let e=c.selectedTicketId,t=c.detailMode;c.data=await g(mt()),Ce(),c.selectedTicketId=e,c.detailMode=t,await Mt()},300)});var xn={};Bn(xn,{load:()=>S,render:()=>Mt,renderDetailOnly:()=>ro});async function S(){ne(),c.data=await g(mt()),Ce(),nn(c.boardId,c.data?.tickets||[]),await Mt(),F.sse&&c.boardId!==$n&&($n=c.boardId,ba())}async function Mt(){Ce(),ga(),Re(),await Ee()}async function ro(){Ce(),ga(),rn(),await Ee()}var $n,ue=T(()=>{q();Q();Ze();la();at();kn();Xe();Oe();$n=null});function va(){return Le?.classList.contains("is-open")}function Lt(e){clearTimeout(ya),ne(),ft(),Rt.innerHTML=oo(e||""),io(),y("#createFlyoutClose")?.addEventListener("click",()=>M()),Le.hidden=!1,ye.hidden=!1,requestAnimationFrame(()=>{Le.classList.add("is-open"),ye.classList.add("is-visible")}),requestAnimationFrame(()=>{y("#createTicketTitle")?.focus()})}function M(){clearTimeout(ya),Le.classList.remove("is-open"),ye.classList.remove("is-visible"),ya=setTimeout(()=>{Le.hidden=!0,ye.hidden=!0,Rt.innerHTML=""},220)}function oo(e){let t=Me(),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(X(i))}</option>`).join("");return`
602
+ <div class="create-flyout-head">
603
+ <h2>New card</h2>
604
+ <button type="button" class="create-flyout-close ghost" id="createFlyoutClose" aria-label="Close">\xD7</button>
605
+ </div>
606
+ <form id="createTicketForm" class="create-flyout-form">
607
+ <input id="createTicketTitle" name="title" type="text" placeholder="Title \u2014 what is this card?" autocomplete="off" required />
608
+ <textarea id="createTicketDescription" name="description" placeholder="Description (optional)"></textarea>
609
+ <div class="create-flyout-grid">
610
+ <label>
611
+ Lane
612
+ <select id="createTicketState" name="state" class="select-chevron-field" aria-label="Lane">${r}</select>
613
+ </label>
614
+ <label>
615
+ Type
616
+ <select id="createTicketType" name="type" class="select-chevron-field" aria-label="Ticket type">
617
+ <option value="epic">Epic / Story</option>
618
+ <option value="feature">Feature</option>
619
+ <option value="task" selected>Task</option>
620
+ <option value="bug">Bug</option>
621
+ </select>
622
+ </label>
623
+ <label>
624
+ Priority
625
+ <select id="createTicketPriority" name="priority" class="select-chevron-field" aria-label="Priority">
626
+ <option value="0">MAYBE</option>
627
+ <option value="1">LOW</option>
628
+ <option value="2" selected>MED</option>
629
+ <option value="3">HIGH</option>
630
+ <option value="4">URGENT</option>
631
+ </select>
632
+ </label>
633
+ </div>
634
+ <label>
635
+ Parent epic
636
+ <select id="createTicketParent" name="parent_ticket_id" class="select-chevron-field" aria-label="Parent epic">
637
+ <option value="">None</option>
638
+ ${o}
639
+ </select>
640
+ </label>
641
+ <input id="createTicketLabels" name="labels" type="text" placeholder="Labels (comma-separated)" />
642
+ <button type="submit">Create card</button>
643
+ </form>
644
+ `}function io(){let e=y("#createTicketForm");e&&e.addEventListener("submit",so)}async function so(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 g("/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}});M(),c.selectedTicketId=r.id,c.detailMode="ticket",await S(),je()}var ya,Fe=T(()=>{te();q();ae();Q();pe();bt();at();ue();ya=0});function nt(){V&&(V.hidden=!0,V.innerHTML="",ie?.setAttribute("aria-expanded","false"))}function ne(){nt()}function ga(){if(!c.data)return;let e=(c.data.boards||[]).find(t=>t.id===c.boardId);ie&&(ie.textContent=e?.name||"Board",ie.title=e?`${e.name} \u2014 slug: ${e.slug}`:"Choose board")}function co(){let a=(c.data.boards||[]).map(r=>`
645
+ <button type="button" class="menu-flyout-item ${r.id===c.boardId?"is-current":""}" data-pick-board="${l(r.id)}" role="menuitem">
646
+ <span class="menu-flyout-item-title">${l(r.name)}</span>
647
+ <span class="menu-flyout-item-meta">${l(r.slug)}</span>
648
+ </button>`).join("")||'<p class="menu-flyout-hint menu-flyout-hint--empty">No boards yet. Create one below if you can.</p>',n=F.multiBoard?`
649
+ <div class="menu-flyout-section">
650
+ <h3 class="menu-flyout-heading">New board</h3>
651
+ <form id="createBoardForm" class="menu-flyout-form">
652
+ <div class="folder-picker-field">
653
+ <input id="repoPathInput" name="repo_path" type="text" required readonly placeholder="Choose a folder" autocomplete="off" />
654
+ <button type="button" id="pickRepoFolderBtn">Browse</button>
655
+ </div>
656
+ <p class="menu-flyout-hint menu-flyout-hint--intro">Board data is stored in <code>.orbit/</code> under this folder.</p>
657
+ <input name="name" type="text" required placeholder="Board name" autocomplete="off" />
658
+ <input name="slug" type="text" placeholder="Slug (optional)" autocomplete="off" />
659
+ <input name="default_branch" type="text" placeholder="Default branch (optional)" autocomplete="off" />
660
+ <input name="repo_url" type="text" placeholder="Repo URL (optional)" autocomplete="off" />
661
+ <button type="submit">Create board</button>
662
+ </form>
663
+ </div>`:"";return`
664
+ <div class="menu-flyout-head">Board</div>
665
+ <div class="menu-flyout-list">${a}</div>
666
+ ${n}
667
+ `}function lo(){V.querySelectorAll("[data-pick-board]").forEach(t=>{t.addEventListener("click",async a=>{a.stopPropagation();let n=t.getAttribute("data-pick-board");nt(),n!==c.boardId&&(M(),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 g("/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 g("/api/boards",{method:"POST",body:{name:r.get("name"),repo_path:u,...o?{slug:o}:{},...i?{default_branch:i}:{},...s?{repo_url:s}:{}}});nt(),c.boardId=d.id,c.selectedTicketId="",c.detailMode="settings",await S(),je(),b.success("Board created")})}}function En(){M(),V.innerHTML=co(),V.hidden=!1,ie?.setAttribute("aria-expanded","true"),lo()}var at=T(()=>{te();q();ae();Q();pe();Fe();le();ue();Oe()});function Ln(){return{...uo}}function ka(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 wa(e){try{return decodeURIComponent(e)}catch{return e}}function po(e){return String(e||"").replace(/^#/,"").replace(/^\/+/,"").replace(/\/+$/,"").split("/").filter(Boolean)}function mo(e){if(!e.length)return null;let t=Ln(),a=0;if(e[a]==="b"){if(!e[a+1])return null;t.boardSlug=wa(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=wa(e[a+1]);else if(e[a]==="settings"){t.view="settings";let n=e[a+1]?wa(e[a+1]):"";n&&dt.has(n)&&(t.tab=n)}return t}function Sn(e){let t=String(e||"");return!t||t==="#"||t==="#/"?null:mo(po(t))}function Pt(e=globalThis.location){let t=ka(e);return Sn(t.hash)||Ln()}function rt(e=globalThis.location){let t=ka(e);return!!Sn(t.hash)}function qe({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 At(e=globalThis.location,t=Pt(e)){return ka(e).hash===qe(t)}var uo,$a=T(()=>{lt();uo=Object.freeze({boardSlug:"",view:"board",ticketId:"",tab:""})});function Tn(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 fo(e={}){return e.boardSlug?e.boardSlug:e.boardId?Tn(e.boardId)?.slug||e.boardId:K()?.slug||c.boardSlug||""}function ho(e={}){return{...e,boardSlug:fo(e)}}function bo(){let e="board";return c.detailMode==="ticket"&&c.selectedTicketId?e="ticket":c.detailMode==="settings"&&(e="settings"),{boardSlug:K()?.slug||c.boardSlug,view:e,ticketId:c.selectedTicketId,tab:e==="settings"?Ue(c.settingsTab):""}}function _n(e){Bt=e}function xa(){return Pt(location)}async function P(e,{replace:t=!1}={}){let a=ho(e),n=qe(a);if(At(location,a)){Bt&&await ot();return}t?history.replaceState(null,"",n):history.pushState(null,"",n),Bt&&await ot()}function je({replace:e=!1}={}){let t=bo(),a=qe(t);At(location,t)||(e?history.replaceState(null,"",a):history.pushState(null,"",a))}async function ot(){let{load:e,renderDetailOnly:t}=await Promise.resolve().then(()=>(ue(),xn)),a=xa(),n=rt(location),r=c.boardId,i=Tn(a.boardSlug)||(a.boardSlug?K():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&&!At(location,s)&&history.replaceState(null,"",qe(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(ne(),M(),c.boardId&&c.boardId!==r){await e();return}await t()}var Bt,pe=T(()=>{q();lt();at();Fe();$a();$a();Bt=!1;window.addEventListener("popstate",()=>{Bt&&ot()})});te();qt();Gt();Ia(),He();var Fn=On();Kt();{let e=()=>{document.body.classList.toggle("is-scrolled",window.scrollY>4)};window.addEventListener("scroll",e,{passive:!0}),e()}Ma();Ea?.addEventListener("click",()=>{Ca()});window.addEventListener(ct,()=>{Ma(),Fn.sync()});function Ma(){if(!Ft)return;let e=ce()==="dark";Ft.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 On(){let e=document.querySelector(".grain");if(!e)return{sync:()=>{}};Nn(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),h()}function h(){let E=o*i/1296e3,U=[{count:Math.round(320*E),depth:.12,sizes:[[1,.9],[1.4,.1]],glow:0},{count:Math.round(160*E),depth:.3,sizes:[[1,.5],[1.6,.4],[2.4,.1]],glow:1.8},{count:Math.round(44*E),depth:.58,sizes:[[1.8,.5],[2.6,.38],[3.6,.12]],glow:2.4}],C=Pa(12648430);s=[];for(let R of U)for(let f=0;f<R.count;f++){let _=qn(C(),R.sizes);s.push({x:C()*o,y:C()*i,size:_,depth:R.depth,glow:_>=2?R.glow:0,rgb:v(C()),period:2.2+C()*5.4,phase:C()*Math.PI*2,amp:.25+C()*.5,baseAlpha:_<1.5?.45+C()*.28:.55+C()*.22})}}function v(E){return E<.8?[255,255,255]:E<.93?[255,222,186]:[195,218,255]}function $(E){!o||!i||(m.tx=E.clientX/o-.5,m.ty=E.clientY/i-.5)}function I(E){m.x+=(m.tx-m.x)*.06,m.y+=(m.ty-m.y)*.06,n.clearRect(0,0,o,i),n.globalCompositeOperation="lighter";let U=E/1e3,C=70;for(let R=0;R<s.length;R++){let f=s[R],_=.5+.5*Math.sin(U*(2*Math.PI/f.period)+f.phase),A=f.baseAlpha*(1-f.amp+f.amp*_),N=f.x-m.x*f.depth*C,J=f.y-m.y*f.depth*C,[z,ee,G]=f.rgb;if(f.glow>0){let be=f.size*f.glow,it=n.createRadialGradient(N,J,0,N,J,be);it.addColorStop(0,`rgba(${z},${ee},${G},${A*.85})`),it.addColorStop(.35,`rgba(${z},${ee},${G},${A*.3})`),it.addColorStop(1,`rgba(${z},${ee},${G},0)`),n.fillStyle=it,n.beginPath(),n.arc(N,J,be,0,Math.PI*2),n.fill(),n.fillStyle=`rgba(${z},${ee},${G},${A})`,n.beginPath(),n.arc(N,J,f.size/2,0,Math.PI*2),n.fill()}else{let be=f.size/2;n.fillStyle=`rgba(${z},${ee},${G},${A})`,n.fillRect(N-be,J-be,f.size,f.size)}}n.globalCompositeOperation="source-over",d&&(u=requestAnimationFrame(I))}function Y(){n.clearRect(0,0,o,i),n.globalCompositeOperation="lighter";for(let E of s){let[U,C,R]=E.rgb;if(n.fillStyle=`rgba(${U},${C},${R},${E.baseAlpha})`,E.glow>0)n.beginPath(),n.arc(E.x,E.y,E.size/2,0,Math.PI*2),n.fill();else{let f=E.size/2;n.fillRect(E.x-f,E.y-f,E.size,E.size)}}n.globalCompositeOperation="source-over"}function x(){d||(d=!0,u=requestAnimationFrame(I))}function he(){d=!1,cancelAnimationFrame(u)}function O(){let E=ce()==="dark",U=Te();if(!E){he(),n.clearRect(0,0,o,i);return}s.length||p(),U||document.hidden?(he(),Y()):x()}return window.addEventListener("resize",()=>{p(),O()},{passive:!0}),window.addEventListener("pointermove",$,{passive:!0}),document.addEventListener("visibilitychange",O),window.matchMedia("(prefers-reduced-motion: reduce)").addEventListener("change",()=>{He(),O()}),window.addEventListener("orbit:motion-preference-change",O),p(),O(),{sync:O}}function Nn(e){e.querySelectorAll(".signature-star").forEach(n=>n.remove());let t=Pa(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(jn,e)}function jn(){let e=ce()==="dark",t=Te(),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,h=document.createElement("div");h.className="meteor",h.setAttribute("aria-hidden","true"),h.style.setProperty("--from-x",o+"px"),h.style.setProperty("--from-y",i+"px"),h.style.setProperty("--to-x",m+"px"),h.style.setProperty("--to-y",p+"px"),h.style.setProperty("--angle",u+"deg"),a.appendChild(h),h.addEventListener("animationend",()=>h.remove(),{once:!0}),Kt()}function qn(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 Pa(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}}te();q();Q();le();pe();at();Fe();Ze();te();q();Q();pe();ae();Fe();async function In(){let e=st.value.trim();if(e.length<2){Dt();return}let t=K();if(!t)return;let a=await g(`/api/search?q=${encodeURIComponent(e)}&board=${encodeURIComponent(t.slug)}`);if(a.results.length===0){se.hidden=!1,se.innerHTML='<div class="search-hit">No results</div>';return}se.hidden=!1,se.innerHTML=a.results.map(n=>`
668
+ <div class="search-hit" data-ticket-id="${n.id}">
669
+ <span class="search-hit-main">
670
+ <strong>${l(X(n))}</strong>
671
+ <span class="search-hit-title">${l(n.title)}</span>
672
+ </span>
673
+ <span class="search-hit-state state-pill-${l(Ke(n))}">${l(n.state_name||"State")}</span>
674
+ </div>
675
+ `).join(""),se.querySelectorAll("[data-ticket-id]").forEach(n=>{n.addEventListener("click",async()=>{M(),Dt(),P({boardId:c.boardId,view:"ticket",ticketId:n.dataset.ticketId})})})}function Dt(){se.hidden=!0,se.innerHTML=""}ue();Oe();function Mn(e){e?.showModal()}function Cn(e){e?.open&&e.close()}function Pn(e){e&&(e.addEventListener("click",t=>{t.target===e&&Cn(e)}),e.querySelectorAll("[data-modal-close]").forEach(t=>{t.addEventListener("click",()=>Cn(e))}))}async function go(){let e=document.getElementById("welcomeDialog");Pn(e),ke==="preview"&&Mn(e),y("#settingsBtn").addEventListener("click",async()=>{M(),ne(),P({boardId:c.boardId,view:"settings",tab:c.settingsTab||"lanes"})}),ie?.addEventListener("click",a=>{if(a.stopPropagation(),!V.hidden){nt();return}En()}),y("#newTicketBtn").addEventListener("click",()=>{if(va()){M();return}Lt("")}),ye.addEventListener("click",()=>{M()}),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 g(k(`/api/tickets/${encodeURIComponent(a)}/archive`),{method:"POST"}),b.error(`Archived: ${r}`),await S(),P({boardId:c.boardId,view:"board"})}),ge.addEventListener("click",()=>{P({boardId:c.boardId,view:"board"})}),document.addEventListener("keydown",a=>{if(a.key!=="Escape")return;if(!V.hidden){ne();return}if(va()){M();return}if(!j.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",pt(In,220)),document.addEventListener("click",a=>{a.target.closest("#topbarSearch")||Dt(),a.target.closest(".topbar-menu")||ne()}),on();let t=xa();t.boardSlug&&(c.boardSlug=t.boardSlug),await S(),_n(!0),rt()?await ot():je({replace:!0})}go();
676
+ //# sourceMappingURL=app.js.map