@starlove/orbit 1.4.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/Dockerfile +10 -8
  2. package/README.md +146 -164
  3. package/dist/full/csp.txt +1 -0
  4. package/dist/full/fonts/JetBrains_Mono/JetBrainsMono-subset.woff2 +0 -0
  5. package/dist/full/fonts/JetBrains_Mono/README.txt +5 -0
  6. package/dist/full/fonts/Mona_Sans/MonaSans-subset.woff2 +0 -0
  7. package/{public/fonts/Open_Sans → dist/full/fonts/Mona_Sans}/OFL.txt +93 -93
  8. package/dist/full/fonts/Mona_Sans/README.txt +10 -0
  9. package/{public/fonts/JetBrains_Mono → dist/full/fonts/Newsreader}/OFL.txt +93 -93
  10. package/dist/full/fonts/Newsreader/README.txt +8 -0
  11. package/dist/full/fonts/Newsreader/newsreader.woff2 +0 -0
  12. package/dist/full/index.html +36 -653
  13. package/dist/full/js/app.BmuponPJ.js +32 -0
  14. package/dist/full/js/app.BmuponPJ.js.map +1 -0
  15. package/dist/full/js/chunk.1fJGQGbW.js +2 -0
  16. package/dist/full/js/chunk.1fJGQGbW.js.map +1 -0
  17. package/dist/full/js/chunk.5D7JO2zp.js +2 -0
  18. package/dist/full/js/chunk.5D7JO2zp.js.map +1 -0
  19. package/dist/full/js/chunk.BrNn_D79.js +2 -0
  20. package/dist/full/js/chunk.BrNn_D79.js.map +1 -0
  21. package/dist/full/js/chunk.CokJXIqM.js +2 -0
  22. package/dist/full/js/chunk.CokJXIqM.js.map +1 -0
  23. package/dist/full/js/chunk.D_Haqy6R.js +2 -0
  24. package/dist/full/js/chunk.D_Haqy6R.js.map +1 -0
  25. package/dist/full/js/chunk.RO0jsH6I.js +15 -0
  26. package/dist/full/js/chunk.RO0jsH6I.js.map +1 -0
  27. package/dist/full/js/chunk.dqWOEtBM.js +2 -0
  28. package/dist/full/js/chunk.dqWOEtBM.js.map +1 -0
  29. package/dist/full/js/warm.f260bbbc.js +15 -0
  30. package/dist/full/orbits-favicon.png +0 -0
  31. package/dist/full/orbits-logo-bg-wide.png +0 -0
  32. package/dist/full/orbits-logo-bg.png +0 -0
  33. package/dist/full/orbits-logo.png +0 -0
  34. package/dist/full/styles.2b154ffe.css +1 -0
  35. package/dist/full/vendor/starlove-ui/theme-init.js +66 -0
  36. package/dist/full/version.json +2 -2
  37. package/docs/AGENT_GUIDE.md +114 -0
  38. package/docs/UPDATING.md +249 -64
  39. package/lib/adapters/indexeddb/blobs.d.ts +3 -0
  40. package/lib/adapters/indexeddb/blobs.js +54 -0
  41. package/lib/adapters/indexeddb/database.d.ts +60 -0
  42. package/lib/adapters/indexeddb/database.js +126 -0
  43. package/lib/adapters/indexeddb/index.d.ts +5 -0
  44. package/lib/adapters/indexeddb/index.js +3 -0
  45. package/lib/adapters/indexeddb/locks.d.ts +14 -0
  46. package/lib/adapters/indexeddb/locks.js +40 -0
  47. package/lib/adapters/indexeddb/store.d.ts +40 -0
  48. package/lib/adapters/indexeddb/store.js +424 -0
  49. package/lib/adapters/indexeddb/tables.d.ts +43 -0
  50. package/lib/adapters/indexeddb/tables.js +127 -0
  51. package/lib/adapters/memory/blobs.d.ts +3 -0
  52. package/lib/adapters/memory/blobs.js +44 -0
  53. package/lib/adapters/memory/board.d.ts +41 -0
  54. package/lib/adapters/memory/board.js +521 -0
  55. package/lib/adapters/memory/index.d.ts +2 -0
  56. package/lib/adapters/memory/index.js +2 -0
  57. package/lib/adapters/memory/search.d.ts +6 -0
  58. package/lib/adapters/memory/search.js +30 -0
  59. package/lib/adapters/memory/store.d.ts +34 -0
  60. package/lib/adapters/memory/store.js +266 -0
  61. package/lib/adapters/sqlite/backups.d.ts +53 -0
  62. package/lib/adapters/sqlite/backups.js +232 -0
  63. package/lib/adapters/sqlite/blobs.d.ts +14 -0
  64. package/lib/adapters/sqlite/blobs.js +109 -0
  65. package/lib/adapters/sqlite/board.d.ts +77 -0
  66. package/lib/adapters/sqlite/board.js +328 -0
  67. package/lib/adapters/sqlite/database.d.ts +67 -0
  68. package/lib/adapters/sqlite/database.js +285 -0
  69. package/lib/adapters/sqlite/index.d.ts +3 -0
  70. package/lib/adapters/sqlite/index.js +2 -0
  71. package/lib/adapters/sqlite/lock.d.ts +11 -0
  72. package/lib/adapters/sqlite/lock.js +34 -0
  73. package/lib/adapters/sqlite/records.d.ts +54 -0
  74. package/lib/adapters/sqlite/records.js +546 -0
  75. package/lib/adapters/sqlite/schema.d.ts +54 -0
  76. package/lib/adapters/sqlite/schema.js +276 -0
  77. package/lib/adapters/sqlite/store.d.ts +39 -0
  78. package/lib/adapters/sqlite/store.js +385 -0
  79. package/lib/browser/in-page.d.ts +31 -0
  80. package/lib/browser/in-page.js +46 -0
  81. package/lib/browser/old-preview.d.ts +13 -0
  82. package/lib/browser/old-preview.js +80 -0
  83. package/lib/browser/preview-board.d.ts +8 -0
  84. package/lib/browser/preview-board.js +69 -0
  85. package/lib/cli/main.d.ts +7 -0
  86. package/lib/cli/main.js +343 -0
  87. package/lib/cli/orbit.d.ts +8 -0
  88. package/lib/cli/orbit.js +25 -0
  89. package/lib/client/http.d.ts +38 -0
  90. package/lib/client/http.js +334 -0
  91. package/lib/client/local.d.ts +26 -0
  92. package/lib/client/local.js +190 -0
  93. package/lib/client/object-url.d.ts +8 -0
  94. package/lib/client/object-url.js +21 -0
  95. package/lib/client/types.d.ts +268 -0
  96. package/lib/client/types.js +8 -0
  97. package/lib/commands/agent-tools.d.ts +31 -0
  98. package/lib/commands/agent-tools.js +100 -0
  99. package/lib/commands/index.d.ts +56 -0
  100. package/lib/commands/index.js +298 -0
  101. package/lib/commands/parse.d.ts +73 -0
  102. package/lib/commands/parse.js +231 -0
  103. package/lib/commands/present.d.ts +134 -0
  104. package/lib/commands/present.js +198 -0
  105. package/lib/commands/session.d.ts +85 -0
  106. package/lib/commands/session.js +138 -0
  107. package/lib/commands/verbs.d.ts +35 -0
  108. package/lib/commands/verbs.js +842 -0
  109. package/lib/core/actors.d.ts +18 -0
  110. package/lib/core/actors.js +45 -0
  111. package/lib/core/agent.d.ts +128 -0
  112. package/lib/core/agent.js +323 -0
  113. package/lib/core/boards.d.ts +113 -0
  114. package/lib/core/boards.js +288 -0
  115. package/lib/core/bootstrap.d.ts +47 -0
  116. package/lib/core/bootstrap.js +100 -0
  117. package/lib/core/bytes.d.ts +10 -0
  118. package/lib/core/bytes.js +52 -0
  119. package/lib/core/card-paths.d.ts +25 -0
  120. package/lib/core/card-paths.js +44 -0
  121. package/lib/core/context.d.ts +60 -0
  122. package/lib/core/context.js +42 -0
  123. package/lib/core/cross-board.d.ts +81 -0
  124. package/lib/core/cross-board.js +228 -0
  125. package/lib/core/describe.d.ts +21 -0
  126. package/lib/core/describe.js +81 -0
  127. package/lib/core/errors.d.ts +159 -0
  128. package/lib/core/errors.js +160 -0
  129. package/lib/core/files.d.ts +79 -0
  130. package/lib/core/files.js +262 -0
  131. package/lib/core/history.d.ts +20 -0
  132. package/lib/core/history.js +53 -0
  133. package/lib/core/image-types.d.ts +16 -0
  134. package/lib/core/image-types.js +36 -0
  135. package/lib/core/model.d.ts +263 -0
  136. package/lib/core/model.js +34 -0
  137. package/lib/core/nebula-link.d.ts +14 -0
  138. package/lib/core/nebula-link.js +39 -0
  139. package/lib/core/relations.d.ts +58 -0
  140. package/lib/core/relations.js +112 -0
  141. package/lib/core/search.d.ts +38 -0
  142. package/lib/core/search.js +92 -0
  143. package/lib/core/sessions.d.ts +44 -0
  144. package/lib/core/sessions.js +158 -0
  145. package/lib/core/snapshot-schema.d.ts +295 -0
  146. package/lib/core/snapshot-schema.js +82 -0
  147. package/lib/core/snapshots.d.ts +75 -0
  148. package/lib/core/snapshots.js +409 -0
  149. package/lib/core/states.d.ts +25 -0
  150. package/lib/core/states.js +126 -0
  151. package/lib/core/text.d.ts +40 -0
  152. package/lib/core/text.js +113 -0
  153. package/lib/core/tickets.d.ts +69 -0
  154. package/lib/core/tickets.js +311 -0
  155. package/lib/core/trello.d.ts +17 -0
  156. package/lib/core/trello.js +269 -0
  157. package/lib/core/verdicts.d.ts +28 -0
  158. package/lib/core/verdicts.js +92 -0
  159. package/lib/core/views.d.ts +95 -0
  160. package/lib/core/views.js +186 -0
  161. package/lib/http/app.d.ts +46 -0
  162. package/lib/http/app.js +491 -0
  163. package/lib/mcp-server.d.ts +10 -0
  164. package/lib/mcp-server.js +120 -0
  165. package/lib/node/assets.d.ts +7 -0
  166. package/lib/node/assets.js +68 -0
  167. package/lib/node/data-folder.d.ts +21 -0
  168. package/lib/node/data-folder.js +34 -0
  169. package/lib/node/env.d.ts +38 -0
  170. package/lib/node/env.js +49 -0
  171. package/lib/node/host-check.d.ts +24 -0
  172. package/lib/node/host-check.js +53 -0
  173. package/lib/node/host-services.d.ts +9 -0
  174. package/lib/node/host-services.js +150 -0
  175. package/lib/node/local.d.ts +22 -0
  176. package/lib/node/local.js +61 -0
  177. package/lib/node/migrate.d.ts +53 -0
  178. package/lib/node/migrate.js +635 -0
  179. package/lib/node/package.d.ts +7 -0
  180. package/lib/node/package.js +14 -0
  181. package/lib/node/repo-board.d.ts +49 -0
  182. package/lib/node/repo-board.js +245 -0
  183. package/lib/node/serve.d.ts +8 -0
  184. package/lib/node/serve.js +106 -0
  185. package/lib/orbit.d.ts +47 -0
  186. package/lib/orbit.js +44 -0
  187. package/lib/quiet-sqlite-warning.d.ts +1 -0
  188. package/lib/quiet-sqlite-warning.js +18 -0
  189. package/lib/store/blob-path.d.ts +6 -0
  190. package/lib/store/blob-path.js +12 -0
  191. package/lib/store/conformance.d.ts +52 -0
  192. package/lib/store/conformance.js +1449 -0
  193. package/lib/store/search-words.d.ts +10 -0
  194. package/lib/store/search-words.js +19 -0
  195. package/lib/store/types.d.ts +452 -0
  196. package/lib/store/types.js +34 -0
  197. package/llms.txt +63 -0
  198. package/package.json +109 -25
  199. package/src/cli/orbit.js +11 -658
  200. package/src/mcp-server.js +16 -876
  201. package/SKILL-ORBIT.md +0 -151
  202. package/build.mjs +0 -210
  203. package/dist/full/js/app.62905fcb.js +0 -952
  204. package/dist/full/js/app.62905fcb.js.map +0 -7
  205. package/dist/full/styles.4e60d824.css +0 -1
  206. package/docs/AGENT_PROTOCOL.md +0 -424
  207. package/docs/MCP_SETUP.md +0 -54
  208. package/docs/ORBIT_DISPATCH.md +0 -280
  209. package/docs/SS-Orbit-Dark.png +0 -0
  210. package/docs/SS-Orbit-Light.png +0 -0
  211. package/docs/mcp-client.example.json +0 -13
  212. package/docs/setup/orbit-init-wizard.md +0 -174
  213. package/public/apple-touch-icon.png +0 -0
  214. package/public/cursors/live_arrow.cur +0 -0
  215. package/public/cursors/live_link.cur +0 -0
  216. package/public/cursors/live_move.cur +0 -0
  217. package/public/cursors/live_select.cur +0 -0
  218. package/public/cursors/live_unavail.cur +0 -0
  219. package/public/favicon-32x32.png +0 -0
  220. package/public/fonts/Chakra_Petch/ChakraPetch-Bold.ttf +0 -0
  221. package/public/fonts/Chakra_Petch/ChakraPetch-BoldItalic.ttf +0 -0
  222. package/public/fonts/Chakra_Petch/ChakraPetch-Italic.ttf +0 -0
  223. package/public/fonts/Chakra_Petch/ChakraPetch-Light.ttf +0 -0
  224. package/public/fonts/Chakra_Petch/ChakraPetch-LightItalic.ttf +0 -0
  225. package/public/fonts/Chakra_Petch/ChakraPetch-Medium.ttf +0 -0
  226. package/public/fonts/Chakra_Petch/ChakraPetch-MediumItalic.ttf +0 -0
  227. package/public/fonts/Chakra_Petch/ChakraPetch-Regular.ttf +0 -0
  228. package/public/fonts/Chakra_Petch/ChakraPetch-SemiBold.ttf +0 -0
  229. package/public/fonts/Chakra_Petch/ChakraPetch-SemiBoldItalic.ttf +0 -0
  230. package/public/fonts/Chakra_Petch/OFL.txt +0 -93
  231. package/public/fonts/Inter/Inter-Italic-VariableFont_opsz,wght.ttf +0 -0
  232. package/public/fonts/Inter/Inter-VariableFont_opsz,wght.ttf +0 -0
  233. package/public/fonts/Inter/OFL.txt +0 -93
  234. package/public/fonts/Inter/README.txt +0 -118
  235. package/public/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
  236. package/public/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf +0 -0
  237. package/public/fonts/JetBrains_Mono/README.txt +0 -79
  238. package/public/fonts/OpenRing-Regular.ttf +0 -0
  239. package/public/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  240. package/public/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  241. package/public/fonts/Open_Sans/README.txt +0 -100
  242. package/public/fonts/Orbitron/OFL.txt +0 -93
  243. package/public/fonts/Orbitron/Orbitron-VariableFont_wght.ttf +0 -0
  244. package/public/fonts/Orbitron/README.txt +0 -68
  245. package/public/fonts/Rajdhani/OFL.txt +0 -93
  246. package/public/fonts/Rajdhani/Rajdhani-Bold.ttf +0 -0
  247. package/public/fonts/Rajdhani/Rajdhani-Light.ttf +0 -0
  248. package/public/fonts/Rajdhani/Rajdhani-Medium.ttf +0 -0
  249. package/public/fonts/Rajdhani/Rajdhani-Regular.ttf +0 -0
  250. package/public/fonts/Rajdhani/Rajdhani-SemiBold.ttf +0 -0
  251. package/public/fonts/runescape-uf.ttf +0 -0
  252. package/public/index.html +0 -769
  253. package/public/js/actor-labels.js +0 -65
  254. package/public/js/api.js +0 -39
  255. package/public/js/app.js +0 -45
  256. package/public/js/artifact-drawer.js +0 -79
  257. package/public/js/board-export.js +0 -161
  258. package/public/js/board-menu.js +0 -374
  259. package/public/js/card-actions.js +0 -545
  260. package/public/js/config.js +0 -20
  261. package/public/js/create-card.js +0 -190
  262. package/public/js/dom.js +0 -19
  263. package/public/js/drawer.js +0 -273
  264. package/public/js/font-preference.js +0 -229
  265. package/public/js/format.js +0 -433
  266. package/public/js/help-menu.js +0 -31
  267. package/public/js/icons.js +0 -16
  268. package/public/js/kanban.js +0 -694
  269. package/public/js/local-backend.js +0 -1015
  270. package/public/js/main.js +0 -161
  271. package/public/js/modal.js +0 -33
  272. package/public/js/motion-preference.js +0 -64
  273. package/public/js/panel.js +0 -58
  274. package/public/js/router.js +0 -134
  275. package/public/js/search.js +0 -56
  276. package/public/js/settings-tabs.js +0 -7
  277. package/public/js/settings.js +0 -1634
  278. package/public/js/sse.js +0 -159
  279. package/public/js/starscape.js +0 -332
  280. package/public/js/state.js +0 -129
  281. package/public/js/text-editing.js +0 -128
  282. package/public/js/theme-preference.js +0 -95
  283. package/public/js/ticket-detail.js +0 -1516
  284. package/public/js/ticket-typeahead.js +0 -49
  285. package/public/js/toast.js +0 -89
  286. package/public/js/topbar-notes.js +0 -155
  287. package/public/js/transport.js +0 -22
  288. package/public/js/typeahead.js +0 -206
  289. package/public/js/unread.js +0 -121
  290. package/public/js/update-check.js +0 -58
  291. package/public/js/url-routes.js +0 -94
  292. package/public/orbits-favicon.png +0 -0
  293. package/public/orbits-logo-bg-wide.png +0 -0
  294. package/public/orbits-logo-bg.png +0 -0
  295. package/public/orbits-logo.png +0 -0
  296. package/public/styles.css +0 -7709
  297. package/src/core/agent.js +0 -387
  298. package/src/core/agents-md.js +0 -109
  299. package/src/core/artifacts.js +0 -149
  300. package/src/core/attachments.js +0 -238
  301. package/src/core/auth.js +0 -85
  302. package/src/core/backups.js +0 -142
  303. package/src/core/board.js +0 -148
  304. package/src/core/boards.js +0 -409
  305. package/src/core/bootstrap.js +0 -170
  306. package/src/core/css-bundle.js +0 -30
  307. package/src/core/db.js +0 -315
  308. package/src/core/delete-board.js +0 -163
  309. package/src/core/dispatch.js +0 -1007
  310. package/src/core/events.js +0 -314
  311. package/src/core/http.js +0 -115
  312. package/src/core/migrate.js +0 -198
  313. package/src/core/paths.js +0 -55
  314. package/src/core/provision-repo-board.js +0 -119
  315. package/src/core/queries.js +0 -246
  316. package/src/core/registry.js +0 -211
  317. package/src/core/relations.js +0 -163
  318. package/src/core/review-verdicts.js +0 -138
  319. package/src/core/router.js +0 -556
  320. package/src/core/search.js +0 -183
  321. package/src/core/seed.js +0 -234
  322. package/src/core/snapshots.js +0 -692
  323. package/src/core/states.js +0 -167
  324. package/src/core/system-picker.js +0 -109
  325. package/src/core/tickets.js +0 -400
  326. package/src/core/util.js +0 -106
  327. package/src/core/version-check.js +0 -154
  328. package/src/mcp/orbit-client.js +0 -287
  329. package/src/server.js +0 -115
@@ -1,952 +0,0 @@
1
- var oo=Object.defineProperty;var B=(e,t)=>()=>(e&&(t=e(e=0)),t);var It=(e,t)=>{for(var a in t)oo(e,a,{get:t[a],enumerable:!0})};var w,ke,V,L,X,T,_e,qe,ga,Ce,_t,xe,Tn,ya,pe=B(()=>{w=e=>document.querySelector(e),ke=w("#boardMenuBtn"),V=w("#boardFlyout"),L=w("#kanban"),X=w("#drawer"),T=w("#drawerInner"),_e=w("#drawerBackdrop"),qe=w("#createFlyout"),ga=w("#createFlyoutInner"),Ce=w("#createFlyoutBackdrop"),_t=w("#searchInput"),xe=w("#searchResults"),Tn=w("#themeToggle"),ya=w("#themeIcon")});function ze(e){try{return e??globalThis.localStorage??null}catch{return null}}function va(e){return typeof window<"u"&&typeof window.matchMedia=="function"?window.matchMedia(e):{matches:!1}}function Ct(e=ze()){let a=ze(e)?.getItem?.(In);return a==="true"?!0:a==="false"?!1:null}function wa(e=va){try{return!!e("(prefers-reduced-motion: reduce)")?.matches}catch{return!1}}function Ve({storage:e=ze(),mediaMatcher:t=va}={}){return Ct(e)??wa(t)}function _n(e,t=ze()){let a=ze(t),n=e?"true":"false";return a?.setItem?.(In,n),e}function lt({doc:e=globalThis.document,storage:t=ze(),mediaMatcher:a=va}={}){let n=e?.documentElement,r=Ct(t);return n&&(r===!0?n.setAttribute("data-motion","reduce"):r===!1?n.setAttribute("data-motion","full"):n.removeAttribute("data-motion")),Ve({storage:t,mediaMatcher:a})}var In,ka=B(()=>{In="mab_reduce_motion"});function Ge(e){try{return e??globalThis.localStorage??null}catch{return null}}function We(e){try{return e??globalThis.document??null}catch{return null}}function xa(e){try{return e??globalThis.window??null}catch{return null}}function io(e){return $a.has(e)?e:"light"}function Pt(e=Ge()){let a=Ge(e)?.getItem?.(Cn);return $a.has(a)?a:null}function An(){try{return globalThis.matchMedia?.("(prefers-color-scheme: dark)").matches?"dark":"light"}catch{return"light"}}function Ae({doc:e=We(),storage:t=Ge()}={}){let a=We(e)?.documentElement?.getAttribute?.("data-theme");return $a.has(a)?a:Pt(t)??An()}function Pn({doc:e=We(),storage:t=Ge()}={}){let a=Pt(t)??An();return We(e)?.documentElement?.setAttribute?.("data-theme",a),a}function Sa(e,{doc:t=We(),storage:a=Ge(),eventTarget:n=xa(),notify:r=!0}={}){let o=io(e);return We(t)?.documentElement?.setAttribute?.("data-theme",o),Ge(a)?.setItem?.(Cn,o),r&&so(o,n),o}function Mt(e={}){let t=Ae(e)==="dark"?"light":"dark";return Sa(t,e)}function so(e,t=xa()){let a=xa(t);if(typeof a?.dispatchEvent=="function"){if(typeof CustomEvent=="function"){a.dispatchEvent(new CustomEvent(At,{detail:{theme:e}}));return}a.dispatchEvent({type:At,detail:{theme:e}})}}var Cn,At,$a,Ea=B(()=>{Cn="mab_theme",At="orbit:theme-change",$a=new Set(["light","dark"])});function dt(e){return!e||!Bt.has(e)?"lanes":e}var Bt,Ft=B(()=>{Bt=new Set(["lanes","appearance","ai","notes","journal","data","archive"])});var Fn={};It(Fn,{boardLabelCatalog:()=>Ia,currentBoard:()=>W,getSettingsTab:()=>Ta,isAiReadyState:()=>_a,selectableStatesForProject:()=>Ca,state:()=>c,statesForProject:()=>Ke,syncBoardSelection:()=>Pe,ticketsForProject:()=>Z,upsertTicket:()=>Aa,visibleStatesForProject:()=>Me});function Ta(){return dt(c.settingsTab||localStorage.getItem("mab_settings_tab"))}function Pe(){if(!c.data)return;let e=c.data.boards||[],t=c.boardId?e.find(a=>a.id===c.boardId):null;if(!t&&c.boardSlug&&(t=e.find(a=>a.slug===c.boardSlug)||e.find(a=>a.id===c.boardSlug)),!t){let a=c.data.active_board_id;t=e.find(n=>n.id===a)||e[0]||null}c.boardId=t?.id||"",c.boardSlug=t?.slug||""}function W(){return(c.data?.boards||[]).find(e=>e.id===c.boardId)}function Ia(){return c.boardId?(c.data?.labels||[]).filter(e=>e.board_id===c.boardId):[]}function Ke(){return c.data.states.filter(e=>e.board_id===c.boardId)}function _a(e){return e?.role==="ai_ready"||e?.name==="AI Ready"}function Me(){let e=Ke();return W()?.ai_enabled!==0?e:e.filter(t=>!_a(t))}function Ca(e=""){let t=Ke();return W()?.ai_enabled!==0?t:t.filter(a=>!_a(a)||a.id===e)}function Z(){return c.data.tickets.filter(e=>e.board_id===c.boardId)}function mo(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 ho(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?mo(a.get(r.parent_ticket_id)):null,r.child_count=n.get(r.id)||0}function Aa(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))),ho(n.board_id||c.boardId)}var c,K=B(()=>{Ft();c={data:null,boardId:"",boardSlug:"",selectedTicketId:"",expandedCardIds:new Set,detailMode:"empty",settingsTab:dt(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 go(e){if(!e)return e;let t=e;for(let[a,n]of bo)t=t.split(a).join(n);return t}function re(e){return e?go(String(e)).replace(/\r\n?/g,`
2
- `):""}function me(e){return l(re(e))}function vo(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&&!yo.has(`${n[1]}:`)?"":t}function Pa(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 Ma(e){let t=/\[([^\]\n]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g,a="",n=0,r;for(;r=t.exec(e);){a+=Pa(e.slice(n,r.index));let o=Pa(r[1]),s=vo(r[2]);a+=s?`<a href="${l(s)}" target="_blank" rel="noopener noreferrer">${o}</a>`:o,n=r.index+r[0].length}return a+=Pa(e.slice(n)),a}function Dt(e){let t=String(e??""),a="",n=0;for(;n<t.length;){let r=t.indexOf("`",n);if(r===-1){a+=Ma(t.slice(n));break}let o=t.indexOf("`",r+1);if(o===-1){a+=Ma(t.slice(n));break}a+=Ma(t.slice(n,r)),a+=`<code>${l(t.slice(r+1,o))}</code>`,n=o+1}return a}function wo(e){return`<p>${Dt(e.join(`
3
- `)).replaceAll(`
4
- `,"<br>")}</p>`}function ko(e){let t=e.match(/^\[( |x|X)\]\s+(.*)$/);if(!t)return null;let a=t[1].toLowerCase()==="x",n=Dt(t[2].trim()).replaceAll(`
5
- `,"<br>");return{checked:a,html:`<input type="checkbox" disabled${a?" checked":""}> ${n}`}}function ut(e){let t=re(e);if(!t.trim())return"";let a=[],n=t.split(`
6
- `),r=[],o=null,s=null,i=()=>{r.length&&(a.push(wo(r)),r=[])},d=()=>{if(!o)return;let f=o.taskList?' class="task-list"':"";a.push(`<${o.type}${f}>${o.items.map(h=>`<li${h.taskClass?` class="${h.taskClass}"`:""}>${h.html}</li>`).join("")}</${o.type}>`),o=null},m=()=>{s&&(a.push(`<blockquote>${ut(s.join(`
7
- `))}</blockquote>`),s=null)},u=()=>{i(),d(),m()};for(let f=0;f<n.length;f+=1){let h=n[f];if(/^\s*```/.test(h)){u();let P=[];for(f+=1;f<n.length&&!/^\s*```\s*$/.test(n[f]);)P.push(n[f]),f+=1;a.push(`<pre><code>${l(P.join(`
8
- `))}</code></pre>`);continue}if(/^\s*(?:-\s*){3,}\s*$/.test(h)||/^\s*(?:\*\s*){3,}\s*$/.test(h)||/^\s*(?:_\s*){3,}\s*$/.test(h)){u(),a.push("<hr>");continue}let p=h.match(/^\s{0,3}(#{1,6})\s+(.+?)\s*#*\s*$/);if(p){u();let P=p[1].length;a.push(`<h${P}>${Dt(p[2])}</h${P}>`);continue}let g=h.match(/^\s{0,3}>\s?(.*)$/);if(g){i(),d(),s||(s=[]),s.push(g[1]);continue}if(s&&!h.trim()){m();continue}if(m(),!h.trim()){i(),d();continue}let x=h.match(/^\s*\d+[.)]\s+(.+)$/),E=h.match(/^\s*[-*+]\s+(.+)$/);if(x||E){i();let P=x?"ol":"ul",v=(x||E)[1].trim(),I=null;E&&(I=ko(v)),(!o||o.type!==P||!!o.taskList!=!!I)&&(d(),o={type:P,items:[],taskList:!!I}),I?o.items.push({html:I.html,taskClass:I.checked?"task-item task-item-done":"task-item"}):o.items.push({html:Dt(v).replaceAll(`
9
- `,"<br>")});continue}d(),r.push(h)}return u(),a.join("")}function pt(e){return e?new Intl.DateTimeFormat(void 0,{month:"short",day:"numeric",hour:"numeric",minute:"2-digit"}).format(new Date(e)):""}function Dn(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 ce(e){return`#${e.number}`}function Be(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 j(e){return e==="epic"||e==="feature"||e==="bug"||e==="task"?e:"task"}function $e(e){return{epic:"Epic",feature:"Feature",task:"Task",bug:"Bug"}[j(e)]||"Task"}function Rn(e){return{epic:"Epic",feature:"Feature",task:"Task",bug:"Bug"}[j(e)]||"Task"}function Je(e){return{0:"MAYBE",1:"LOW",2:"MED",3:"HIGH",4:"URGENT"}[Number(e)]||"MED"}function Ye(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 On(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
- `:""}var bo,yo,te=B(()=>{bo=[["\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","'"]];yo=new Set(["http:","https:","mailto:","tel:"])});var Q,Nn,ft=B(()=>{Q=`
14
- <svg class="ui-icon ui-icon-x" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
15
- <path d="M6 6l12 12M18 6 6 18" />
16
- </svg>
17
- `,Nn=`
18
- <svg class="ui-icon ui-icon-trash" viewBox="0 0 24 24" aria-hidden="true" focusable="false" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
19
- <path d="M3 6h18" />
20
- <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
21
- <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" />
22
- <path d="M10 11v6" />
23
- <path d="M14 11v6" />
24
- </svg>
25
- `});function $o(){return Fe&&Fe.isConnected||(Fe=document.createElement("div"),Fe.className="toast-container",document.body.append(Fe)),Fe}function y(e,t="info",a=4e3,n={}){let r=$o(),o=document.createElement("div");o.className=`toast toast-${t}`,o.setAttribute("role","status");let s=xo[t];o.innerHTML=`<span class="toast-icon">${s}</span><span class="toast-msg">${l(e)}</span><button class="toast-close" aria-label="Dismiss">${Q}</button>`,o.querySelector(".toast-close").addEventListener("click",d=>{d.stopPropagation(),Rt(o)}),typeof n.onClick=="function"&&(o.classList.add("toast-clickable"),o.addEventListener("click",()=>{n.onClick(),Rt(o)})),r.prepend(o),requestAnimationFrame(()=>requestAnimationFrame(()=>o.classList.add("toast-enter")));let i=setTimeout(()=>Rt(o),a);o.addEventListener("mouseenter",()=>clearTimeout(i)),o.addEventListener("mouseleave",()=>{let d=setTimeout(()=>Rt(o),1500);o.addEventListener("mouseenter",()=>clearTimeout(d),{once:!0})})}function Rt(e){e.classList.contains("toast-exit")||(e.classList.add("toast-exit"),e.addEventListener("animationend",()=>e.remove(),{once:!0}))}function jn(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 Ot(e,t){let a=0;return(...n)=>{clearTimeout(a),a=setTimeout(()=>e(...n),t)}}var xo,Fe,le=B(()=>{te();ft();xo={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>',removed:Nn},Fe=null;y.info=(e,t)=>y(e,"info",t);y.success=(e,t)=>y(e,"success",t);y.warning=(e,t)=>y(e,"warning",t);y.error=(e,t)=>y(e,"error",t);y.removed=(e,t)=>y(e,"removed",t)});async function Un({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}),s=await o.text(),i=s?JSON.parse(s):{};if(!o.ok){let d=new Error(i.error||o.statusText);throw d.payload=i,d.status=o.status,d}return i}var Hn=B(()=>{});var qn={};It(qn,{api:()=>k,bootstrapPath:()=>mt,withBoardQuery:()=>A});function mt(){return`/api/bootstrap${c.boardId?`?board_id=${encodeURIComponent(c.boardId)}`:c.boardSlug?`?board=${encodeURIComponent(c.boardSlug)}`:""}`}function A(e){if(!c.boardId||e.includes("board_id="))return e;let t=e.includes("?")?"&":"?";return`${e}${t}board_id=${encodeURIComponent(c.boardId)}`}async function k(e,t={}){try{return await Un({method:t.method||"GET",path:e,body:t.body,headers:t.headers})}catch(a){throw y.error(a.payload?.error||a.message||"Request failed"),a}}var ae=B(()=>{K();le();Hn()});function zn(e,t){let a=Number(t);return Number.isFinite(a)?Math.max(0,Math.min(e.length,a)):0}function So(e,t){return e.lastIndexOf(`
26
- `,Math.max(0,t-1))+1}function Eo(e,t,a){let n=So(e,t),r=a>t&&e[a-1]===`
27
- `?a-1:a,o=e.indexOf(`
28
- `,Math.max(n,r));return{lineStart:n,lineEnd:o===-1?e.length:o}}function Lo(e,t){let a=[],n=t;for(let r of e.split(`
29
- `))a.push(n),n+=r.length+1;return a}function To(e){return e.startsWith(" ")?1:(e.match(/^ {1,2}/)?.[0]||"").length}function Nt(e,t,a){let n=0;for(let r=0;r<t.length;r+=1)e<=t[r]||(n+=Math.min(a[r],e-t[r]));return n}function Io({value:e="",selectionStart:t=0,selectionEnd:a=t,shiftKey:n=!1,indent:r=" "}={}){let o=String(e),s=zn(o,t),i=zn(o,a),d=Math.min(s,i),m=Math.max(s,i),u=String(r||" ");if(!n&&d===m){let $=`${o.slice(0,d)}${u}${o.slice(m)}`,S=d+u.length;return{value:$,selectionStart:S,selectionEnd:S,handled:!0}}let{lineStart:f,lineEnd:h}=Eo(o,d,m),p=o.slice(f,h),g=p.split(`
30
- `),x=Lo(p,f);if(!n){let $=g.map(()=>u.length),S=g.map(M=>`${u}${M}`).join(`
31
- `),R=Nt(d,x,$),U=Nt(m,x,$);return{value:`${o.slice(0,f)}${S}${o.slice(h)}`,selectionStart:d+R,selectionEnd:m+U,handled:!0}}let E=g.map(To),P=g.map(($,S)=>$.slice(E[S])).join(`
32
- `),v=Nt(d,x,E),I=Nt(m,x,E);return{value:`${o.slice(0,f)}${P}${o.slice(h)}`,selectionStart:d-v,selectionEnd:m-I,handled:!0}}function ht(e,t=e?.currentTarget){if(!e||e.key!=="Tab"||e.altKey||e.ctrlKey||e.metaKey||!t||t.tagName!=="TEXTAREA")return!1;e.preventDefault();let a=Io({value:t.value,selectionStart:t.selectionStart,selectionEnd:t.selectionEnd,shiftKey:e.shiftKey});return t.value=a.value,t.setSelectionRange(a.selectionStart,a.selectionEnd),t.dispatchEvent(new Event("input",{bubbles:!0})),a.handled}var Ba=B(()=>{});function _o(e){let t=document.getElementById("drawerTabsShell");if(!e||!t)return;let a=e.scrollWidth-e.clientWidth;if(a<=1){t.classList.remove("can-scroll-left","can-scroll-right");return}t.classList.toggle("can-scroll-left",e.scrollLeft>1),t.classList.toggle("can-scroll-right",e.scrollLeft<a-1)}function Co(e){e?.querySelector(".drawer-tab.is-active")?.scrollIntoView({block:"nearest",inline:"nearest"})}function Ra(){Da?.abort(),Da=null;let e=document.getElementById("drawerTabsShell"),t=document.getElementById("drawerTabs");e?.classList.remove("can-scroll-left","can-scroll-right"),t?.classList.remove("is-grabbing")}function Ao(e){if(Ra(),!e)return;let t=new AbortController;Da=t;let{signal:a}=t,n=()=>_o(e);if(e.addEventListener("scroll",n,{passive:!0,signal:a}),typeof ResizeObserver<"u"){let p=new ResizeObserver(n);p.observe(e),a.addEventListener("abort",()=>p.disconnect(),{once:!0})}let r=16,o=(p,g)=>g===WheelEvent.DOM_DELTA_LINE?p*r:g===WheelEvent.DOM_DELTA_PAGE?p*Math.max(e.clientWidth,1):p;e.addEventListener("wheel",p=>{if(p.defaultPrevented||p.ctrlKey||e.scrollWidth<=e.clientWidth)return;let g=p.deltaX||p.deltaY;g&&(p.preventDefault(),e.scrollLeft+=o(g,p.deltaMode))},{passive:!1,signal:a});let s=4,i=!1,d=!1,m=0,u=0,f=0;e.addEventListener("pointerdown",p=>{p.button===0&&(e.scrollWidth<=e.clientWidth||(d=!1,i=!0,f=p.pointerId,m=p.clientX,u=e.scrollLeft))},{signal:a}),e.addEventListener("pointermove",p=>{if(!i||p.pointerId!==f)return;let g=p.clientX-m;if(!d&&Math.abs(g)>s){d=!0;try{e.setPointerCapture(f)}catch{}e.classList.add("is-grabbing")}d&&(p.preventDefault(),e.scrollLeft=u-g)},{signal:a});let h=p=>{if(!(!i||p&&p.pointerId!==f)){i=!1,e.classList.remove("is-grabbing");try{e.releasePointerCapture(f)}catch{}}};e.addEventListener("pointerup",h,{signal:a}),e.addEventListener("pointercancel",h,{signal:a}),e.addEventListener("pointerleave",h,{signal:a}),e.addEventListener("click",p=>{d&&(p.stopPropagation(),p.preventDefault(),d=!1)},{capture:!0,signal:a}),requestAnimationFrame(()=>{a.aborted||(n(),Co(e))})}function Oa(){Fa+=1,X.hidden=!1,_e.hidden=!1,requestAnimationFrame(()=>{X.classList.add("is-open"),_e.classList.add("is-visible")})}function jt(){let e=++Fa,t=X.classList.contains("is-open");X.classList.remove("is-open"),_e.classList.remove("is-visible");let a=()=>{if(Fa!==e)return;X.hidden=!0,_e.hidden=!0,T.innerHTML="";let o=document.getElementById("drawerTitleBlock"),s=document.getElementById("drawerTabs"),i=document.getElementById("drawerTabsShell");o&&(o.innerHTML=""),s&&(s.innerHTML=""),i&&(i.hidden=!0),Ra(),X.classList.remove("is-wide")};if(!t){a();return}let n=!1,r=()=>{n||(n=!0,a())};X.addEventListener("transitionend",r,{once:!0}),setTimeout(r,360)}function Ut({eyebrow:e,title:t,titleAttrs:a,subtitleHtml:n,tabs:r,activeTab:o,onTabSelect:s,body:i,mode:d}){X.classList.toggle("is-settings",d==="settings"),X.classList.toggle("is-wide",d==="ticket"||d==="settings");let m=document.getElementById("drawerTitleBlock"),u=document.getElementById("drawerTabs"),f=document.getElementById("drawerTabsShell"),p=` class="drawer-title drawer-title-text${a?.class?` ${a.class}`:""}"`;for(let[E,P]of Object.entries(a||{}))E!=="class"&&(p+=` ${E}="${l(String(P))}"`);let g=[];e&&g.push(`<span class="drawer-eyebrow">${l(e)}</span>`),t!=null&&String(t).trim()!==""&&g.push(`<h2${p}>${l(t)}</h2>`),n&&g.push(`<div class="drawer-subtitle">${n}</div>`),m.innerHTML=g.join(""),r&&r.length?(f&&(f.hidden=!1),u.innerHTML=r.map(E=>`<button type="button" role="tab" class="drawer-tab ${E.id===o?"is-active":""}" data-drawer-tab="${l(E.id)}" aria-selected="${E.id===o}">${l(E.label)}</button>`).join(""),typeof s=="function"&&u.querySelectorAll(".drawer-tab").forEach(E=>{E.addEventListener("click",()=>s(E.dataset.drawerTab))}),Ao(u)):(f&&(f.hidden=!0),u.innerHTML="",Ra());let x=document.getElementById("drawerArchiveBtn");x&&(x.hidden=c.detailMode!=="ticket"),T.innerHTML=i??""}var Fa,Da,Ht=B(()=>{pe();K();te();Fa=0,Da=null});function Do(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:Mo.map(a=>({id:`priority:${a}`,label:Je(a),action:"priority",value:a,checked:Number(e.priority)===a,disabled:Number(e.priority)===a}))},{id:"type",label:"Type",children:Po.map(a=>({id:`type:${a}`,label:$e(a),action:"type",value:a,checked:j(e.type)===a,disabled:j(e.type)===a}))},{id:"open",label:"Open",action:"open"},{id:"archive",label:"Archive",action:"archive",danger:!0}]:[]}function Ro({x:e,y:t,menuWidth:a,menuHeight:n,viewportWidth:r,viewportHeight:o,margin:s=ja}){let i=Math.max(s,r-a-s),d=Math.max(s,o-n-s),m=e+a+s>r?e-a:e,u=t+n+s>o?t-n:t;return{left:Math.min(Math.max(s,m),i),top:Math.min(Math.max(s,u),d)}}function Oo({triggerRect:e,submenuWidth:t,submenuHeight:a,viewportWidth:n,viewportHeight:r,margin:o=ja,gap:s=Bo}){let i=Math.max(o,n-t-o),d=e.right+s,m=e.left-t-s,u=d+t+o<=n,f=m>=o,h=u||!f?d:m,p=Math.max(0,r-o*2),g=Math.min(a,p),x=Math.max(o,r-g-o),E=e.top-s;return{left:Math.min(Math.max(o,h),i),top:Math.min(Math.max(o,E),x),maxHeight:p}}function Gt({apiClient:e=k,navigator:t=null,confirmer:a=globalThis.confirm}={}){async function n(r,o){await e(A(`/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(A(`/api/tickets/${encodeURIComponent(r.id)}/archive`),{method:"POST"}),y.removed(`Archived: ${r.title||"card"}`),!0)}}}function Ha(e){Ua=!!e}function de({restoreFocus:e=!1}={}){he(),bt&&(bt.abort(),bt=null);for(let t of yt)t.isConnected&&t.remove();yt.clear(),ne&&(ne.querySelectorAll(".card-action-menu-item.has-submenu").forEach(t=>{let a=De.get(t);a?.isConnected&&a.remove(),De.delete(t)}),ne.remove(),ne=null),e&&qt?.isConnected&&qt.focus({preventScroll:!0}),qt=null}function No({ticket:e,anchor:t,x:a,y:n,handlers:r=Gt(),onBeforeOpen:o=null}){if(de(),!e||Ua)return null;typeof o=="function"&&o(),qt=t||document.activeElement;let s=document.createElement("div");s.className="card-action-menu",s.setAttribute("role","menu"),s.setAttribute("aria-label",`Actions for #${e.number||""} ${e.title||"card"}`.trim()),s.dataset.ticketId=e.id,s.innerHTML=Wn(Do(e)),document.body.append(s),ne=s,s.querySelectorAll(".card-action-menu-item.has-submenu").forEach(f=>{let h=f.querySelector(":scope > .card-action-submenu");h&&(h.remove(),document.body.append(h),De.set(f,h),Na.set(h,f),Ho(h,e,r))});let i=s.getBoundingClientRect(),d=Ro({x:a,y:n,menuWidth:i.width,menuHeight:i.height,viewportWidth:window.innerWidth,viewportHeight:window.innerHeight});s.style.left=`${d.left}px`,s.style.top=`${d.top}px`,Uo(s,e,r),za(s)?.focus({preventScroll:!0}),bt=new AbortController;let u=bt.signal;return requestAnimationFrame(()=>{document.addEventListener("pointerdown",qo,{signal:u,capture:!0})}),document.addEventListener("keydown",zo,{signal:u,capture:!0}),document.addEventListener("scroll",Vo,{signal:u,capture:!0,passive:!0}),window.addEventListener("resize",()=>de(),{signal:u,passive:!0}),window.addEventListener("blur",()=>de(),{signal:u}),s}function Gn(e,t,{handlers:a=Gt(),onBeforeOpen:n=null}={}){e.addEventListener("contextmenu",r=>{let o=r.target instanceof Element?r.target.closest(".card"):null;if(!o)return;let s=t(o.dataset.ticketId);if(s){if(r.preventDefault(),r.stopPropagation(),Ua){de();return}No({ticket:s,anchor:o,x:r.clientX,y:r.clientY,handlers:a,onBeforeOpen:n})}})}function Wn(e){return`<ul class="card-action-menu-list" role="none">${e.map(jo).join("")}</ul>`}function jo(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`
33
- <li class="${a.join(" ")}" role="none">
34
- <button type="button"
35
- class="card-action-button"
36
- role="menuitem${e.checked?"checkbox":""}"
37
- data-action="${l(e.action||"")}" data-value="${l(e.value??"")}" data-menu-id="${l(e.id)}"
38
- ${e.disabled?"disabled":""}
39
- ${e.checked?'aria-checked="true"':""}
40
- ${t?'aria-haspopup="menu" aria-expanded="false"':""}>
41
- ${t?"":n}
42
- <span class="card-action-label">${l(e.label)}</span>
43
- ${r}
44
- </button>
45
- ${t?`<div class="card-action-submenu" role="menu">${Wn(e.children)}</div>`:""}
46
- </li>
47
- `}function Uo(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")?(he(),zt(r)):Go(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")?(he(),zt(r)):(he(),Xn(e)))}),e.addEventListener("click",n=>Kn(n,e,t,a)),e.addEventListener("keydown",n=>Jn(n,e))}function Ho(e,t,a){e.addEventListener("pointerenter",he),e.addEventListener("pointerover",n=>{n.target instanceof Element&&e.contains(n.target)&&he()},!0),e.addEventListener("click",n=>{ne&&Kn(n,ne,t,a,e)}),e.addEventListener("keydown",n=>{ne&&Jn(n,ne)})}function Kn(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 s=o.closest(".card-action-menu-item");if(s?.classList.contains("has-submenu")){he(),zt(s),za(De.get(s))?.focus({preventScroll:!0});return}let i=o.dataset.action,d=o.dataset.value;if(de(),i==="open")return n.open(a);if(i==="move")return n.move(a,d);if(i==="priority")return n.priority(a,Number(d));if(i==="type")return n.type(a,d);if(i==="archive")return n.archive(a)}function Jn(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"),s=a.closest(".card-action-menu-item");if(e.key==="Escape"){if(e.preventDefault(),n){let i=Na.get(n),d=i?.querySelector(":scope > .card-action-button");Vt(i),d?.focus({preventScroll:!0})}else de({restoreFocus:!0});return}if(e.key==="ArrowDown"||e.key==="ArrowUp"){e.preventDefault(),Wo(o,a,e.key==="ArrowDown"?1:-1);return}if(e.key==="Home"||e.key==="End"){e.preventDefault();let i=qa(o);i[e.key==="Home"?0:i.length-1]?.focus({preventScroll:!0});return}if(e.key==="ArrowRight"&&s?.classList.contains("has-submenu")){e.preventDefault(),he(),zt(s),za(De.get(s))?.focus({preventScroll:!0});return}if(e.key==="ArrowLeft"&&n){let i=Na.get(n),d=i?.querySelector(":scope > .card-action-button");d&&(e.preventDefault(),Vt(i),d.focus({preventScroll:!0}))}}function qo(e){ne&&(Yn(e)||de())}function zo(e){e.key==="Escape"&&ne&&(e.preventDefault(),de({restoreFocus:!0}))}function Vo(e){ne&&(Yn(e)||de())}function Yn(e){let t=e.target;if(!(t instanceof Node))return!1;if(ne?.contains(t))return!0;for(let a of yt)if(a.contains(t))return!0;return!1}function zt(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=De.get(e);if(a){if(e.classList.contains("is-open")){Vn(e,a);return}e.classList.add("is-open"),e.querySelector(":scope > .card-action-button")?.setAttribute("aria-expanded","true"),a.classList.add("is-open"),yt.add(a),Vn(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=De.get(e);t&&(t.classList.remove("is-open"),t.style.removeProperty("left"),t.style.removeProperty("top"),t.style.removeProperty("max-height"),yt.delete(t))}function Xn(e){e.querySelectorAll(".card-action-menu-item.has-submenu.is-open").forEach(Vt)}function Go(e){he(),gt=setTimeout(()=>{gt=null,ne===e&&Xn(e)},Fo)}function he(){gt!=null&&(clearTimeout(gt),gt=null)}function Vn(e,t){let a=e.querySelector(":scope > .card-action-button");if(!t||!a)return;let n=Math.max(0,window.innerHeight-ja*2);t.style.maxHeight=`${n}px`,t.style.left="0px",t.style.top="0px";let r=t.getBoundingClientRect(),o=Oo({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 qa(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 za(e){return qa(e)[0]||null}function Wo(e,t,a){let n=qa(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 Po,Mo,ja,Bo,Fo,ne,Ua,bt,qt,De,Na,yt,gt,Zn=B(()=>{ae();K();te();le();Po=["epic","feature","task","bug"],Mo=[0,1,2,3,4],ja=8,Bo=6,Fo=180,ne=null,Ua=!1,bt=null,qt=null,De=new WeakMap,Na=new WeakMap,yt=new Set,gt=null});function Ko(e){return e?typeof e=="string"?{readAt:e,count:0}:{readAt:e.readAt||null,count:Number(e.count)||0}:{readAt:null,count:0}}function vt(e){if(!e)return new Map;let t=Ga.get(e);if(t)return t;t=new Map;try{let a=localStorage.getItem(Qn(e));if(a){let n=JSON.parse(a);for(let[r,o]of Object.entries(n))t.set(r,Ko(o))}}catch{}return Ga.set(e,t),t}function Wt(e){let t=Ga.get(e);if(t)try{let a={};for(let[n,r]of t)a[n]=r;localStorage.setItem(Qn(e),JSON.stringify(a))}catch{}}function er(e,t){let a=e.get(t);return a||(a={readAt:null,count:0},e.set(t,a)),a}function Kt(e){let t=e?.board_id||c.boardId;if(!t||!e?.id)return 0;let n=vt(t).get(e.id);return n?n.count:localStorage.getItem(Va(t))==="1"?1:0}function Jt(e,t){let a=c.boardId;if(!a||!e)return;let n=vt(a),r=er(n,e);r.count===0&&r.readAt&&t&&r.readAt>=t||(r.count=0,t&&(r.readAt=t),Wt(a))}function tr(e){let t=c.boardId;if(!t||!e)return;let a=vt(t),n=er(a,e);n.count=(n.count||0)+1,Wt(t)}function ar(e){let t=c.boardId;if(!t||!e)return;let a=vt(t);a.has(e)&&(a.delete(e),Wt(t))}function nr(e,t){if(!e||localStorage.getItem(Va(e))==="1")return;let a=vt(e);for(let n of t||[])!a.has(n.id)&&n.updated_at&&a.set(n.id,{readAt:n.updated_at,count:0});Wt(e);try{localStorage.setItem(Va(e),"1")}catch{}}var Qn,Va,Ga,wt=B(()=>{K();Qn=e=>`mab_card_read:${e}`,Va=e=>`mab_card_read_seeded:${e}`,Ga=new Map});function Xe(){let e=Me(),t=Z();Ka=new Map(t.map(a=>[a.id,a])),L.innerHTML=e.map(a=>Xo(a,t)).join("")}function Jo(e){if(!e)return;let t=e.dataset.ticketId,a=!c.expandedCardIds.has(t);if(a){if(c.expandedCardIds.add(t),!e.querySelector(".card-expandable")){let r=Ka.get(t),o=e.querySelector(".card-meta");r&&o&&o.insertAdjacentHTML("afterend",ir(r))}}else c.expandedCardIds.delete(t);e.classList.toggle("is-expanded",a);let n=e.querySelector("button.card-expand-trigger");n&&n.setAttribute("aria-expanded",a?"true":"false")}function rr(){Wa=Wa||Gt({navigator:O});let e=null,t=a=>{e!==a&&(e&&e.classList.remove("drag-over"),e=a,e&&e.classList.add("drag-over"))};L.addEventListener("dragstart",a=>{let n=a.target instanceof Element?a.target.closest(".card"):null;n&&(de(),Ha(!0),a.dataTransfer.setData("text/plain",n.dataset.ticketId),a.dataTransfer.effectAllowed="move")}),L.addEventListener("dragover",a=>{let n=a.target instanceof Element?a.target.closest(".column"):null;n&&(a.preventDefault(),t(n))}),L.addEventListener("dragend",()=>{Ha(!1),t(null),Yt=!0,setTimeout(()=>{Yt=!1},0)}),L.addEventListener("drop",async a=>{let n=a.target instanceof Element?a.target.closest(".column"):null;if(!n)return;a.preventDefault(),t(null);let r=a.dataTransfer.getData("text/plain");r&&(G(),await k(A(`/api/tickets/${r}`),{method:"PATCH",body:{state_id:n.dataset.stateId}}),await F())}),L.addEventListener("click",a=>{let n=a.target instanceof Element?a.target:a.target?.parentElement;if(!n)return;let r=n.closest("[data-add-to-state]");if(r){a.stopPropagation(),Zt(r.dataset.addToState||"");return}let o=n.closest("button.card-expand-trigger");if(o){a.stopPropagation(),a.preventDefault(),Jo(o.closest(".card"));return}if(n.closest("a")){a.stopPropagation();return}let s=n.closest(".epic-mini-header[data-ticket-id]");if(s){G(),O({boardId:c.boardId,view:"ticket",ticketId:s.dataset.ticketId});return}let i=n.closest(".card");if(i){if(Yt){Yt=!1,a.preventDefault(),a.stopPropagation();return}G(),O({boardId:c.boardId,view:"ticket",ticketId:i.dataset.ticketId})}}),Gn(L,a=>Ka.get(a),{handlers:Wa,onBeforeOpen:G})}function or(){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 Yo(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">
48
- <path d="M12 3L14.5 9L21 11.5L14.5 14L12 20L9.5 14L3 11.5L9.5 9L12 3Z" fill="currentColor" opacity="0.65" />
49
- <circle cx="12" cy="11.5" r="1.8" fill="#fff" />
50
- </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">
51
- <circle cx="50" cy="50" r="10" fill="currentColor" opacity="0.9" />
52
- <circle class="ring" cx="50" cy="50" r="30" stroke="currentColor" stroke-dasharray="4 6" />
53
- <circle class="satellite" cx="80" cy="50" r="4" fill="currentColor" />
54
- </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">
55
- <path d="M12 2L14.5 9.5L22 12L14.5 14.5L12 22L9.5 14.5L2 12L9.5 9.5L12 2Z" fill="currentColor" />
56
- </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">
57
- <path d="M12 3L14.5 9L21 11.5L14.5 14L12 20L9.5 14L3 11.5L9.5 9L12 3Z" fill="currentColor" opacity="0.5" />
58
- <path d="M19 8L20 10L21 8L20 6L19 8ZM19 16L20 18L21 16L20 14L19 16ZM5 8L6 10L7 8L6 6L5 8Z" fill="currentColor" />
59
- </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">
60
- <circle cx="12" cy="12" r="10" />
61
- <path d="M8 12L11 15L16 9" />
62
- </svg>`,title:"Done",subtitle:"Move completed tickets here"}:t.includes("cancel")&&(a={icon:`<svg class="empty-state-icon" viewBox="0 0 24 24">
63
- <circle cx="12" cy="12" r="10" opacity="0.2" />
64
- <path d="M15 9L9 15M9 9l6 6" />
65
- </svg>`,title:"Not moving forward",subtitle:"Parked or replaced"}),`
66
- <div class="column-empty-state">
67
- <div class="empty-state-visual">
68
- <div class="empty-state-glow"></div>
69
- ${a.icon}
70
- </div>
71
- <div class="empty-state-title">${l(a.title)}</div>
72
- <div class="empty-state-subtitle">${l(a.subtitle)}</div>
73
- </div>
74
- `}function Xo(e,t){let a=t.filter(i=>i.state_id===e.id),n=a.length===0,r=e.name==="AI Ready",o=["column","lane"];r&&o.push("column--ai-ready");let s=r?' data-accent="ai-ready"':"";return`
75
- <section class="${o.join(" ")}" data-flat${s} data-state-id="${e.id}" data-state-name="${l(e.name)}">
76
- <div class="column-head lane-head">
77
- <h2 class="lane-title"${r?' title="Agent staging \u2014 cards here are picked up by the claim-next tool when unblocked"':""}>
78
- ${r?'<span class="agent-dot" aria-hidden="true"></span>':""}${l(e.name)}
79
- </h2>
80
- <div class="column-head-actions lane-actions">
81
- <button type="button" class="column-add-btn" data-variant="plus" data-add-to-state="${l(e.id)}" title="New card">+</button>
82
- <span class="count">${a.length}</span>
83
- </div>
84
- </div>
85
- ${n?Yo(e.name):Zo(a,t)}
86
- <button type="button" class="add-card-phantom" data-add-to-state="${l(e.id)}">+ Add a card</button>
87
- </section>
88
- `}function Zo(e,t){let a=new Map(t.filter(i=>j(i.type)==="epic").map(i=>[i.id,i])),n=new Set(e.filter(i=>j(i.type)==="epic").map(i=>i.id)),r=new Map;for(let i of e)if(i.parent_ticket_id&&a.has(i.parent_ticket_id)){let d=r.get(i.parent_ticket_id)||[];d.push(i),r.set(i.parent_ticket_id,d)}let o=new Set,s=[];for(let i of e)if(!o.has(i.id)){if(j(i.type)==="epic"){let d=r.get(i.id)||[];if(d.length){let m=d.map(u=>kt(u,{asEpicChild:!0})).join("");s.push(`${kt(i)}<div class="epic-children">${m}</div>`),d.forEach(u=>o.add(u.id))}else s.push(kt(i));o.add(i.id);continue}if(!(i.parent_ticket_id&&n.has(i.parent_ticket_id))){if(i.parent_ticket_id&&a.has(i.parent_ticket_id)&&!n.has(i.parent_ticket_id)){let d=a.get(i.parent_ticket_id),m=r.get(i.parent_ticket_id)||[],u=m.map(f=>kt(f,{asEpicChild:!0})).join("");s.push(`${Qo(d)}<div class="epic-children">${u}</div>`),m.forEach(f=>o.add(f.id));continue}s.push(kt(i)),o.add(i.id)}}return s.join("")}function Qo(e){return`
89
- <div class="epic-mini-header" data-ticket-id="${l(e.id)}" title="Open epic">
90
- <div class="card-type-id type-pill-epic">
91
- ${Xt("epic")}
92
- <span class="card-type-label">EPIC</span>
93
- </div>
94
- <span class="ticket-number">${l(ce(e))}</span>
95
- <span class="epic-mini-title">${l(e.title)}</span>
96
- </div>
97
- `}function Ja(e){let t=Ye(e),a=Je(e);return`<div class="card-priority-id priority-pill" data-variant="${l(t)}"><span class="card-priority-label">${l(a)}</span></div>`}function Xt(e){let t=j(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`
98
- <svg class="card-type-icon" viewBox="0 0 24 24" style="color: ${n}">
99
- ${a}
100
- </svg>
101
- `}function kt(e,{asEpicChild:t=!1}={}){let a=j(e.type),n=Ye(e.priority),r=$e(e.type).toUpperCase(),o=c.expandedCardIds.has(e.id),i=!!String(e.description||"").trim()||e.labels&&e.labels.length||e.parent_ticket||e.child_count,d=`
102
- <span class="ticket-number">${l(ce(e))}</span>
103
- <div class="card-type-id type-pill-${l(a)}">
104
- ${Xt(e.type)}
105
- <span class="card-type-label">${l(r)}</span>
106
- </div>
107
- ${c.showPriority?Ja(e.priority):""}
108
- ${i?'<svg class="card-expand-chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6" /></svg>':""}
109
- `,m=Kt(e),u=m===0?"":m===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="${m} unread updates">${m>99?"99+":m}</span>`;return`
110
- <article class="card type-${l(a)} priority-${l(n)}${o?" is-expanded":""}${t?" is-epic-child":""} ${e.id===c.selectedTicketId?"selected":""}" data-variant="${l(a)}" draggable="true" data-ticket-id="${e.id}">
111
- ${u}
112
- <h3>${l(e.title)}</h3>
113
-
114
- <div class="card-meta">
115
- ${i?`<button type="button" class="card-expand-trigger" data-variant="card-accordion" data-card-toggle aria-expanded="${o?"true":"false"}">${d}</button>`:`<div class="card-expand-trigger card-expand-trigger--static">${d}</div>`}
116
- </div>
117
-
118
- ${i&&o?ir(e):""}
119
- </article>
120
- `}function ir(e){return`
121
- <div class="card-expandable">
122
- ${!!String(e.description||"").trim()?`<div class="card-description markdown-body">${ut(e.description)}</div>`:""}
123
- ${On(e.labels)}
124
- ${e.parent_ticket?`<div class="parent-line">-> ${l(ce(e.parent_ticket))}</div>`:""}
125
- ${e.child_count?`<div class="child-count">${e.child_count} feature${e.child_count===1?"":"s"} inside</div>`:""}
126
- </div>
127
- `}function sr(){let a=()=>Math.max(L.clientWidth,1),n=!1,r=!1,o=0,s=0,i=0,d=v=>v instanceof Element?!!(v.closest(".card")||v.closest("a, button, input, select, textarea, [draggable='true']")):!1,m=(v,I)=>I===WheelEvent.DOM_DELTA_LINE?v*16:I===WheelEvent.DOM_DELTA_PAGE?v*a():v;L.addEventListener("wheel",v=>{if(v.defaultPrevented||v.ctrlKey)return;let I=v.deltaX||(v.shiftKey?v.deltaY:0);!I||!(Math.abs(I)>=Math.abs(v.deltaY)||v.shiftKey)||L.scrollWidth<=L.clientWidth||(v.preventDefault(),L.scrollLeft+=m(I,v.deltaMode))},{passive:!1}),L.addEventListener("pointerdown",v=>{v.button===0&&(r=!1,!d(v.target)&&(n=!0,i=v.pointerId,o=v.clientX,s=L.scrollLeft,L.classList.add("is-grabbing")))}),L.addEventListener("pointermove",v=>{if(!n||v.pointerId!==i)return;let I=v.clientX-o;if(!r&&Math.abs(I)>4){r=!0;try{L.setPointerCapture(i)}catch{}}r&&(v.preventDefault(),L.scrollLeft=s-I)});let u=v=>{if(!(!n||v&&v.pointerId!==i)){n=!1,L.classList.remove("is-grabbing");try{L.releasePointerCapture(i)}catch{}}};L.addEventListener("pointerup",u),L.addEventListener("pointercancel",u),L.addEventListener("pointerleave",u),L.addEventListener("click",v=>{r&&(v.stopPropagation(),v.preventDefault(),r=!1)},!0);let f=!1,h=0,p=!1;L.addEventListener("pointerdown",v=>{v.pointerType!=="mouse"||(v.buttons&3)!==3||(f=!0,h=v.clientX,p=!0,n=!1,L.classList.add("is-grabbing"))}),L.addEventListener("pointermove",v=>{if(v.pointerType!=="mouse")return;if((v.buttons&3)===3){if(v.preventDefault(),!f){f=!0,h=v.clientX,p=!0,n=!1,L.classList.add("is-grabbing");return}let $=v.clientX-h;$&&(L.scrollLeft-=$,h=v.clientX,r=!0)}else f&&(f=!1,L.classList.remove("is-grabbing"))}),L.addEventListener("pointerup",v=>{v.pointerType==="mouse"&&f&&(v.buttons&3)!==3&&(f=!1,L.classList.remove("is-grabbing"))}),L.addEventListener("contextmenu",v=>{if(p){v.preventDefault(),v.stopPropagation(),p=!1;return}v.target instanceof Element&&v.target.closest(".card")||(v.preventDefault(),v.stopPropagation())});let g=!1,x=0,E=v=>(v[0].clientX+v[1].clientX)/2;L.addEventListener("touchstart",v=>{r=!1,v.touches.length===2?(g=!0,x=E(v.touches),n=!1,L.classList.add("is-grabbing")):g=!1},{passive:!0}),L.addEventListener("touchmove",v=>{if(!g||v.touches.length!==2)return;let I=E(v.touches),$=I-x;$&&(v.preventDefault(),L.scrollLeft-=$,x=I,r=!0)},{passive:!1});let P=v=>{v.touches.length<2&&g&&(g=!1,L.classList.remove("is-grabbing"))};L.addEventListener("touchend",P,{passive:!0}),L.addEventListener("touchcancel",P,{passive:!0})}var Ka,Wa,Yt,xt=B(()=>{pe();K();te();ae();be();Ze();Se();Zn();wt();Ka=new Map,Wa=null,Yt=!1});var cr,ge,z,Re=B(()=>{cr="full";cr="full";ge=cr,z={sse:ge==="full",ai:ge==="full",multiBoard:ge==="full",tokenAuth:ge==="full",attachments:ge==="full"}});function Ee(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function pr(e){let t=Number(e);return Number.isFinite(t)?Math.min(1.3,Math.max(.85,t)):1}function Ya(e){let t=e&&typeof e=="object"?e:{},a={...Qt},n=new Set;for(let r of Object.keys(et))typeof t[r]=="string"&&Ee(ue,t[r])&&(a[r]=t[r],n.add(r));for(let[r,o]of Object.entries(ai))!n.has(o)&&typeof t[r]=="string"&&Ee(ue,t[r])&&(a[o]=t[r]);return typeof t.site=="string"&&Ee(ue,t.site)&&(a.site=t.site),t.userScale!==void 0&&(a.userScale=pr(t.userScale)),!Ee(t,"badge")&&Ee(t,"label")&&t.label!==Qe&&(a.badge=a.label),a}function Oe(e=globalThis.localStorage){if(!e)return{...Qt};try{let t=e.getItem(dr);return t?Ya(JSON.parse(t)):{...Qt}}catch{return{...Qt}}}function lr(e){return ue[e]?.stack||ue[Qe].stack}function fr(e=Oe(),{doc:t=globalThis.document}={}){let a=Ya(e),n=t?.documentElement;if(!n)return a;for(let[r,o]of Object.entries(et)){let s=a[r];n.style?.setProperty?.(o.cssVar,lr(s)),n.setAttribute?.(`data-${o.dataAttr.replace(/[A-Z]/g,i=>`-${i.toLowerCase()}`)}`,s)}return n.style?.setProperty?.("--orbit-font-body",lr(a.site)),n.style?.setProperty?.("--type-user-scale",String(a.userScale)),n.setAttribute?.("data-font-site",a.site),a}function Xa(e,{doc:t=globalThis.document,storage:a=globalThis.localStorage,eventTarget:n=globalThis.window}={}){let r=Ya(e);return a?.setItem?.(dr,JSON.stringify(r)),fr(r,{doc:t}),n?.dispatchEvent?.(new CustomEvent(ei,{detail:{preferences:r}})),r}function mr(e,t,a={}){if(!Ee(et,e))throw new Error(`Unknown font target: ${e}`);if(!Ee(ue,t))throw new Error(`Unknown font option: ${t}`);return Xa({...Oe(a.storage),[e]:t},a)}function hr(e,t={}){if(!Ee(ue,e))throw new Error(`Unknown font option: ${e}`);let a={...Oe(t.storage)};for(let n of Object.keys(et))a[n]=e;return a.site=e,Xa(a,t)}function br(e,t={}){return Xa({...Oe(t.storage),userScale:pr(e)},t)}function gr(e={}){return fr(Oe(e.storage),e)}var dr,ei,Qe,ti,ur,ue,et,ai,ni,Qt,Za=B(()=>{dr="orbit_font_preferences",ei="orbit:font-preference-change",Qe="inter",ti="orbitron",ur=Object.freeze(["inter","openSans","jetBrainsMono","chakraPetch","orbitron","rajdhani","runescapeUF","openRing"]),ue=Object.freeze({inter:{label:"Inter",stack:'"Orbit Inter", "Inter", "Segoe UI Variable", "Segoe UI", Arial, sans-serif',className:"font-inter"},openSans:{label:"Open Sans",stack:'"Orbit Open Sans", "Open Sans", "Segoe UI Variable", "Segoe UI", Arial, sans-serif',className:"font-open-sans"},jetBrainsMono:{label:"JetBrains Mono",stack:'"Orbit JetBrains Mono", "JetBrains Mono", "Cascadia Mono", "SFMono-Regular", Consolas, monospace',className:"font-jetbrains-mono"},chakraPetch:{label:"Chakra Petch",stack:'"Orbit Chakra Petch", "Chakra Petch", "Segoe UI Variable", "Segoe UI", Arial, sans-serif',className:"font-chakra-petch"},orbitron:{label:"Orbitron",stack:'"Orbit Orbitron", "Orbitron", "Segoe UI Variable", "Segoe UI", Arial, sans-serif',className:"font-orbitron"},rajdhani:{label:"Rajdhani",stack:'"Orbit Rajdhani", "Rajdhani", "Segoe UI Variable", "Segoe UI", Arial, sans-serif',className:"font-rajdhani"},runescapeUF:{label:"Runescape UF",stack:'"Orbit Runescape UF", "Cascadia Mono", monospace',className:"font-runescape-uf"},openRing:{label:"Open Ring",stack:'"Orbit Open Ring", "Segoe UI Variable", "Segoe UI", Arial, sans-serif',className:"font-open-ring"}}),et=Object.freeze({heading:{label:"Headings",cssVar:"--orbit-font-heading",dataAttr:"fontHeading"},control:{label:"Controls",cssVar:"--orbit-font-control",dataAttr:"fontControl"},label:{label:"Labels",cssVar:"--orbit-font-label",dataAttr:"fontLabel"},badge:{label:"Badges",cssVar:"--orbit-font-badge",dataAttr:"fontBadge"}}),ai=Object.freeze({ui:"control"}),ni=Object.freeze({control:Qe,heading:Qe,label:Qe,badge:ti}),Qt=Object.freeze({...ni,site:Qe,userScale:1})});function oi(e){let t=4294967295;for(let a of e)t=ri[(t^a)&255]^t>>>8;return(t^4294967295)>>>0}function N(e,t){e.push(t&255,t>>>8&255)}function oe(e,t){e.push(t&255,t>>>8&255,t>>>16&255,t>>>24&255)}function ii(e=new Date){return{time:(e.getHours()&31)<<11|(e.getMinutes()&63)<<5|Math.floor(e.getSeconds()/2)&31,date:(e.getFullYear()-1980&127)<<9|(e.getMonth()+1&15)<<5|e.getDate()&31}}function si(e){let t=new TextEncoder,a=[],n=[],r=0,o=ii();e.forEach(m=>{let u=t.encode(m.name),f=t.encode(m.content),h=oi(f),p=[];oe(p,67324752),N(p,20),N(p,2048),N(p,0),N(p,o.time),N(p,o.date),oe(p,h),oe(p,f.length),oe(p,f.length),N(p,u.length),N(p,0),a.push(new Uint8Array(p),u,f);let g=[];oe(g,33639248),N(g,20),N(g,20),N(g,2048),N(g,0),N(g,o.time),N(g,o.date),oe(g,h),oe(g,f.length),oe(g,f.length),N(g,u.length),N(g,0),N(g,0),N(g,0),N(g,0),oe(g,0),oe(g,r),n.push(new Uint8Array(g),u),r+=p.length+u.length+f.length});let s=r,i=n.reduce((m,u)=>m+u.length,0),d=[];return oe(d,101010256),N(d,0),N(d,0),N(d,e.length),N(d,e.length),oe(d,i),oe(d,s),N(d,0),new Blob([...a,...n,new Uint8Array(d)],{type:"application/zip"})}function ci(e,t){let a=URL.createObjectURL(t),n=document.createElement("a");n.href=a,n.download=e,document.body.append(n),n.click(),n.remove(),URL.revokeObjectURL(a)}function li(e,t){let a=String(e?.slug||e?.name||e?.id||"board").trim().toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"")||"board",n=`${a}.orbit.json`,r=2;for(;t.has(n);)n=`${a}-${r}.orbit.json`,r+=1;return t.add(n),n}async function ea({includeAttachments:e=!1}={}){let t=c.data?.boards||[];if(!t.length)return y.error("No boards to export"),!1;let a=e?"?include_attachments=true":"",n=new Set,r=[];for(let i of t){let d=await k(`/api/boards/${encodeURIComponent(i.id)}/export${a}`);r.push({name:li(i,n),content:JSON.stringify(d,null,2)})}let o=si(r),s=new Date().toISOString().slice(0,10);return ci(`orbit-boards-${s}${e?".with-images":""}.zip`,o),y.success(`Exported ${r.length} board${r.length===1?"":"s"}`),!0}var ri,Qa=B(()=>{K();ae();le();ri=Array.from({length:256},(e,t)=>{let a=t;for(let n=0;n<8;n+=1)a=a&1?3988292384^a>>>1:a>>>1;return a>>>0})});function vr(){return ta}async function en({force:e=!1}={}){try{let t=await fetch(`./api/version${e?"?refresh=1":""}`,{cache:"no-store"});return t.ok?(ta=await t.json(),ta):null}catch{return null}}function tn(){let e=!!ta?.updateAvailable,t=document.getElementById("settingsBtn");if(!t)return;t.classList.toggle("has-update",e);let a=t.querySelector(".topbar-update-dot");a&&(a.hidden=!e)}async function wr(){if(ge!=="full")return;let e=await en();tn(),!(!e?.updateAvailable||!e.latest)&&localStorage.getItem(yr)!==e.latest&&(localStorage.setItem(yr,e.latest),y(`Orbit v${e.latest} is available \u2192`,"info",8e3,{onClick:()=>O({boardId:c.boardId,view:"settings",tab:"data"})}))}var yr,ta,an=B(()=>{le();be();K();Re();yr="orbit:updateToastSeen",ta=null});function kr(e){let t=Number(e.min)||0,a=Number(e.max)||100,n=Number(e.value),r=a-t,o=r<=0?0:(n-t)/r*100;e.style.setProperty("--value",String(o))}function fi(e){e.querySelectorAll('input[type="range"].slider').forEach(t=>{kr(t),t.addEventListener("input",()=>kr(t))})}async function ye(){let e=await k(`/api/boards/${c.boardId}/context?include_struck=true`),t=W(),a={...e.board,...t?.id===e.board?.id?{project_notes:t.project_notes||""}:{}};e.board=a;let n=Ta(),r=[{id:"lanes",label:"Lanes"},{id:"appearance",label:"Appearance"},{id:"archive",label:"Card Archive"},{id:"notes",label:"Notes"},{id:"journal",label:"Journal"},...z.ai?[{id:"ai",label:"AI"}]:[],{id:"data",label:"Data"}],o=n==="ai"&&!z.ai?"lanes":n;Ut({eyebrow:"SETTINGS",mode:"settings",tabs:r,activeTab:o,onTabSelect:async s=>{O({boardId:c.boardId,view:"settings",tab:s})},body:mi(e,o)}),Ni(e,o)}function mi(e,t){switch(t){case"notes":return Ci(e);case"lanes":return $r();case"appearance":return Bi();case"ai":return z.ai?hi(e):$r();case"journal":return Ri(e);case"archive":return Ei();case"data":default:return Li(e)}}function hi(e){let t=e.board,a=t.ai_enabled!==0,n=a?bi(t):"",r=a&&z.multiBoard?Ti(t):"",o=a?gi(e):"";return`
128
- <div class="settings-stack">
129
- <section class="settings-card">
130
- <header class="settings-card-head">
131
- <h3 class="settings-card-title">AI</h3>
132
- </header>
133
- <div class="settings-card-body">
134
- <label class="orbit-check settings-toggle" for="aiEnabledToggle">
135
- <input
136
- type="checkbox"
137
- id="aiEnabledToggle"
138
- ${a?"checked":""}
139
- />
140
- <span class="orbit-check-box" aria-hidden="true">
141
- <svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
142
- </span>
143
- <span class="settings-toggle-text">
144
- <span class="settings-row-title">Enable AI</span>
145
- <span 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.</span>
146
- </span>
147
- </label>
148
- </div>
149
- </section>
150
- ${n}
151
- ${r}
152
- ${o}
153
- </div>
154
- `}function bi(e){let t=`This project is about\u2026
155
-
156
- How any AI agent should use this board and work in this repo.`,a=e.agent_instructions||"",n=!!a.trim(),r=n?"":"is-placeholder",o=n?me(a):l(t);return`
157
- <section class="settings-card ai-context-setup">
158
- <header class="settings-card-head">
159
- <h3 class="settings-card-title">Agent Instructions</h3>
160
- </header>
161
- <div class="settings-card-body">
162
- <div class="settings-row">
163
- <p class="description">This project-level context is provided to agents when they work on tickets.</p>
164
- <div class="inline-md-field">
165
- <div
166
- class="inline-md-field-body preserved-text-body editable-field settings-agent-instructions-body ${r}"
167
- data-edit-field="agent_instructions"
168
- tabindex="0"
169
- title="Click to edit"
170
- role="button"
171
- aria-label="Edit Agent Instructions"
172
- >${o}</div>
173
- </div>
174
- </div>
175
- </div>
176
- </section>
177
- `}function gi(e){let t=aa(),a="claude-code",n=vi(e,t),r=nn(a,n),o=rn(t,n),s=ra[a];return`
178
- <details class="settings-card settings-card--collapsible mcp-setup" data-mcp-setup>
179
- <summary class="settings-card-head">
180
- <h3 class="settings-card-title">Connect Your Agent to MCP</h3>
181
- <svg class="settings-card-chevron" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6" /></svg>
182
- </summary>
183
- <div class="settings-card-body">
184
- <div class="settings-row">
185
- <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>
186
- <div class="control-grid">
187
- <label class="control-field">
188
- <span class="control-field-label">Operating system</span>
189
- <select id="mcpOsSelect" class="select-chevron-field">
190
- ${Object.entries(na).map(([i,d])=>`<option value="${i}" ${i===t?"selected":""}>${l(d.label)}</option>`).join("")}
191
- </select>
192
- </label>
193
- <label class="control-field">
194
- <span class="control-field-label">Agent app</span>
195
- <select id="mcpClientSelect" class="select-chevron-field">
196
- ${Object.entries(ra).map(([i,d])=>`<option value="${i}" ${i===a?"selected":""}>${l(d.label)}</option>`).join("")}
197
- </select>
198
- </label>
199
- </div>
200
- <div class="control-grid control-grid--single mcp-path-grid">
201
- <label class="control-field">
202
- <span class="control-field-label">Orbit MCP server file</span>
203
- <input id="mcpServerPathInput" value="${l(n.serverPath)}" />
204
- </label>
205
- </div>
206
- </div>
207
-
208
- <div class="settings-row">
209
- <div class="mcp-options-stack">
210
- <div class="mcp-option-card">
211
- <div class="mcp-option-card-head">
212
- <div>
213
- <strong data-mcp-output-title>${l(s.label)} setup</strong>
214
- <p class="description" data-mcp-helper>${l(s.helper)}</p>
215
- </div>
216
- <button type="button" data-variant="secondary" data-copy-mcp="config">Copy setup</button>
217
- </div>
218
- <div class="mcp-terminal">
219
- <div class="mcp-terminal-chrome" aria-hidden="true"><span></span><span></span><span></span></div>
220
- <pre class="mcp-snippet"><code data-mcp-config>${l(r)}</code></pre>
221
- </div>
222
- </div>
223
-
224
- <div class="mcp-or-divider" aria-hidden="true"><span>OR</span></div>
225
-
226
- <div class="mcp-option-card">
227
- <div class="mcp-option-card-head">
228
- <div>
229
- <strong>Manual run command</strong>
230
- <p class="description">Use this only when an app asks you to start the MCP server yourself.</p>
231
- </div>
232
- <button type="button" data-variant="secondary" data-copy-mcp="command">Copy command</button>
233
- </div>
234
- <div class="mcp-terminal">
235
- <div class="mcp-terminal-chrome" aria-hidden="true"><span></span><span></span><span></span></div>
236
- <pre class="mcp-snippet mcp-snippet--compact"><code data-mcp-command>${l(o)}</code></pre>
237
- </div>
238
- </div>
239
- </div>
240
- </div>
241
- </div>
242
- </details>
243
- `}function aa(){let e=String(navigator.userAgentData?.platform||navigator.platform||navigator.userAgent||"").toLowerCase();return e.includes("win")?"windows":e.includes("mac")?"macos":"linux"}function yi(e){return{serverPath:(na[e]||na.linux).serverPath}}function vi(e,t){let a=yi(t);return{serverPath:(e.deployment||{}).mcp_server_path||a.serverPath}}function xr(){return{serverPath:w("#mcpServerPathInput")?.value||""}}function nn(e,t){if(e==="claude-code")return $i(t);if(e==="codex")return xi(t);if(e==="opencode")return wi(t);if(e==="vscode")return ki(t);let a={mcpServers:{"starscape-orbit":{command:"node",args:[t.serverPath]}}};return JSON.stringify(a,null,2)}function wi(e){return JSON.stringify({$schema:"https://opencode.ai/config.json",mcp:{"starscape-orbit":{type:"local",command:["node",e.serverPath],enabled:!0}}},null,2)}function ki(e){return JSON.stringify({servers:{starscapeOrbit:{type:"stdio",command:"node",args:[e.serverPath]}}},null,2)}function xi(e){return`[mcp_servers.starscape-orbit]
244
- command = "node"
245
- args = ["${Si(e.serverPath)}"]
246
- enabled = true`}function $i(e){return["claude mcp add starscape-orbit","--","node",Er(e.serverPath)].join(" ")}function rn(e,t){return`node ${Er(t.serverPath)}`}function Er(e){return`"${String(e||"").replace(/"/g,'\\"')}"`}function Si(e){return String(e||"").replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function Ei(){return`
247
- <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>
248
- <div class="section">
249
- <div id="archiveList" class="archive-list">Loading\u2026</div>
250
- </div>
251
- `}function Li(e){let t=e.board;J.boardId!==t.id&&(J.boardId=t.id,J.step="idle",J.exported=!1);let a=z.multiBoard?"":`
252
- <section class="settings-card">
253
- <header class="settings-card-head">
254
- <h3 class="settings-card-title">Data</h3>
255
- </header>
256
- <div class="settings-card-body">
257
- <div class="settings-row">
258
- <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>
259
- </div>
260
- </div>
261
- </section>`;return`
262
- <div class="settings-stack">
263
- ${Mi()}
264
-
265
- ${a}
266
-
267
- ${z.multiBoard?Ii(t):""}
268
-
269
- <section class="settings-card">
270
- <header class="settings-card-head">
271
- <h3 class="settings-card-title">Board Snapshot</h3>
272
- </header>
273
- <div class="settings-card-body">
274
- <div class="settings-row">
275
- <p class="description">Import accepts Orbit snapshots or Trello board JSON exports. The default import creates a separate board and leaves this board untouched.</p>
276
- ${z.attachments?`
277
- <label class="orbit-check settings-toggle">
278
- <input type="checkbox" id="exportProjectImages" />
279
- <span class="orbit-check-box" aria-hidden="true">
280
- <svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
281
- </span>
282
- <span class="settings-toggle-text">
283
- <span class="settings-row-title">Include attached images</span>
284
- </span>
285
- </label>
286
- `:""}
287
- <div class="deployment-actions">
288
- <button type="button" data-variant="primary" id="exportProject">Export</button>
289
- <input type="file" id="importSnapshotAsNewFile" class="import-file-input" accept=".orbit.json,.json,application/json" />
290
- <button type="button" data-variant="primary" id="importSnapshotAsNewButton">Import as New Board</button>
291
- </div>
292
- </div>
293
- </div>
294
- </section>
295
-
296
- <section class="settings-card settings-card--danger">
297
- <header class="settings-card-head">
298
- <h3 class="settings-card-title">Replace Current Board</h3>
299
- </header>
300
- <div class="settings-card-body">
301
- <div class="settings-row">
302
- <p class="description">Advanced: replace this board from an Orbit snapshot or Trello export after making the normal pre-import backup. Type <strong>${l(t.slug||"")}</strong> to enable replacement.</p>
303
- <label class="control-field repository-confirm-field">
304
- <span class="control-field-label">Board slug</span>
305
- <input id="replaceImportConfirmInput" autocomplete="off" spellcheck="false" placeholder="${l(t.slug||"")}" />
306
- </label>
307
- <div class="deployment-actions">
308
- <input type="file" id="replaceCurrentBoardImportFile" class="import-file-input" accept=".orbit.json,.json,application/json" disabled />
309
- <button type="button" data-variant="primary" data-arc="danger" id="replaceCurrentBoardImportButton" disabled>Replace Current Board</button>
310
- </div>
311
- </div>
312
- </div>
313
- </section>
314
-
315
- ${z.multiBoard?_i(t):""}
316
- </div>
317
- `}function Ti(e){let t=!!e.system_path,a=!!e.manage_helper_files;return`
318
- <section class="settings-card helper-files-section">
319
- <header class="settings-card-head">
320
- <h3 class="settings-card-title">AI Helper Files</h3>
321
- </header>
322
- <div class="settings-card-body">
323
- <div class="settings-row">
324
- <p class="description">Orbit can keep <code>SKILL-ORBIT.md</code> and <code>AGENTS.md</code> in this board's coding project folder so your AI agent knows how to use Orbit.${t?"":" Link a folder to enable this."}</p>
325
- <div class="folder-picker-field helper-files-folder">
326
- <input id="helperFolderInput" type="text" readonly placeholder="No folder linked" value="${l(e.system_path||"")}" />
327
- <button type="button" id="helperPickFolderBtn">${t?"Change":"Browse"}</button>
328
- </div>
329
- <label class="orbit-check settings-toggle">
330
- <input type="checkbox" id="manageHelperFilesToggle" ${a?"checked":""} ${t?"":"disabled"} />
331
- <span class="orbit-check-box" aria-hidden="true">
332
- <svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
333
- </span>
334
- <span class="settings-toggle-text">
335
- <span class="settings-row-title">Generate &amp; maintain helper files in this folder</span>
336
- </span>
337
- </label>
338
- </div>
339
- </div>
340
- </section>
341
- `}function Ii(e){return`
342
- <section class="settings-card board-rename-section">
343
- <header class="settings-card-head">
344
- <h3 class="settings-card-title">Board Name</h3>
345
- </header>
346
- <div class="settings-card-body">
347
- <div class="settings-row">
348
- <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>
349
- <form id="boardRenameForm" class="field-form">
350
- <label class="control-field">
351
- <span class="control-field-label">Display name</span>
352
- <input name="name" value="${l(e.name||"")}" maxlength="120" required />
353
- </label>
354
- <button type="submit" data-variant="primary">Rename Board</button>
355
- </form>
356
- </div>
357
- </div>
358
- </section>
359
- `}function _i(e){let t=l(e.slug||"");if(J.step==="choice")return`
360
- <section class="settings-card settings-card--danger">
361
- <header class="settings-card-head">
362
- <h3 class="settings-card-title">Delete Board</h3>
363
- </header>
364
- <div class="settings-card-body">
365
- <div class="settings-row">
366
- <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>
367
- <div class="repository-delete-actions">
368
- <button type="button" data-variant="primary" id="deleteBoardExportFirst">Export first</button>
369
- <button type="button" data-variant="primary" data-arc="danger" id="deleteBoardSkipExport">Delete without exporting</button>
370
- <button type="button" data-variant="ghost" id="deleteBoardCancel">Cancel</button>
371
- </div>
372
- </div>
373
- </div>
374
- </section>
375
- `;if(J.step==="confirm"){let a=J.exported?"Snapshot exported. Type the board slug to confirm permanent deletion.":"Type the board slug to confirm permanent deletion.";return`
376
- <section class="settings-card settings-card--danger is-confirming">
377
- <header class="settings-card-head">
378
- <h3 class="settings-card-title">Delete Board</h3>
379
- </header>
380
- <div class="settings-card-body">
381
- <div class="settings-row">
382
- <p class="description">${l(a)}</p>
383
- <label class="control-field repository-confirm-field">
384
- <span class="control-field-label">Board slug</span>
385
- <strong>${t}</strong>
386
- <input id="deleteBoardConfirmInput" autocomplete="off" spellcheck="false" placeholder="${t}" autofocus />
387
- </label>
388
- <div class="repository-delete-actions">
389
- <button type="button" data-variant="primary" data-arc="danger" id="deleteBoardConfirm" disabled>Delete board permanently</button>
390
- <button type="button" data-variant="ghost" id="deleteBoardCancel">Cancel</button>
391
- </div>
392
- </div>
393
- </div>
394
- </section>
395
- `}return`
396
- <section class="settings-card settings-card--danger">
397
- <header class="settings-card-head">
398
- <h3 class="settings-card-title">Delete Board</h3>
399
- </header>
400
- <div class="settings-card-body">
401
- <div class="settings-row">
402
- <p class="description">Remove this board from Orbit and delete its repo-local Orbit files. This cannot be undone without an exported snapshot.</p>
403
- <button type="button" data-variant="primary" data-arc="danger" id="deleteBoardStart">Delete Board</button>
404
- </div>
405
- </div>
406
- </section>
407
- `}function Ci(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",s=r?me(n):l(a);return`
408
- <div class="section settings-notes-section">
409
- <div class="inline-md-field">
410
- <span class="inline-md-field-label font-orbitron">Notes For You</span>
411
- <div
412
- class="inline-md-field-body preserved-text-body editable-field settings-notes-body ${o}"
413
- data-edit-field="project_notes"
414
- tabindex="0"
415
- title="Click to edit"
416
- role="button"
417
- aria-label="Edit Notes For You"
418
- >${s}</div>
419
- </div>
420
- </div>
421
- `}function $r(){return`
422
- <div class="section">
423
- <label class="orbit-check settings-toggle" for="showPriorityToggle">
424
- <input
425
- type="checkbox"
426
- id="showPriorityToggle"
427
- ${c.showPriority?"checked":""}
428
- />
429
- <span class="orbit-check-box" aria-hidden="true">
430
- <svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
431
- </span>
432
- <span class="settings-toggle-text">
433
- <span class="settings-row-title">Show priority label on cards</span>
434
- </span>
435
- </label>
436
- <p class="description lanes-description">Rename, reorder, add, or remove lanes.</p>
437
- ${zi()}
438
- <form id="laneCreateForm" class="lane-create-form">
439
- <input name="name" placeholder="New lane name..." required />
440
- <button type="submit" data-variant="primary">Add Lane</button>
441
- </form>
442
- </div>
443
- `}function Mi(){let e=vr(),t=e?.deployMode||"source",a=!!e?.updateAvailable,n=e?.current?`v${l(e.current)}`:"\u2014",r=!!(e?.selfUpdate&&a),o=e?e.error?`<span class="update-status-chip update-status-chip--warn">Couldn't check</span>`:a?'<span class="update-status-chip update-status-chip--available"><span class="update-status-dot" aria-hidden="true"></span>Update available</span>':`<span class="update-status-chip update-status-chip--current">${Pi} Up to date</span>`:'<span class="update-status-chip update-status-chip--idle">Not checked</span>',s=e?e.error?`Couldn't reach GitHub (${l(e.error)}). Try again in a moment.`:a?"Back up your boards, then update with the steps below.":"You have the latest version of Orbit.":"Check whether a newer version of Orbit has shipped.",i=a?`<span class="update-version-now">${n}</span>
444
- <span class="update-version-arrow" aria-hidden="true">\u2192</span>
445
- <span class="update-version-next">v${l(e.latest)}</span>`:`<span class="update-version-now">${n}</span>`,d=a?`
446
- <details class="update-howto" open>
447
- <summary>How to update <span class="update-howto-mode">${l(Ai[t]||t)}</span></summary>
448
- <pre class="update-commands"><code id="updateCommands">${l(Sr[t]||Sr.source)}</code></pre>
449
- <div class="update-howto-foot">
450
- <button type="button" data-variant="ghost" id="updateCopyCommands">Copy commands</button>
451
- ${e.changesUrl?`<a class="update-changes-link" href="${l(e.changesUrl)}" target="_blank" rel="noopener">View changes \u2197</a>`:""}
452
- </div>
453
- </details>`:"";return`
454
- <section class="settings-card update-card${a?" update-card--available":""}">
455
- <header class="settings-card-head">
456
- <h3 class="settings-card-title">Version &amp; Updates</h3>
457
- ${o}
458
- </header>
459
- <div class="settings-card-body">
460
- <div class="settings-row update-row">
461
- <div class="update-version-line">${i}</div>
462
- <p class="description" id="updateStatusLine">${s}</p>
463
- <div class="deployment-actions update-actions">
464
- <button type="button" data-variant="${a?"secondary":"primary"}" id="updateCheckNow">Check now</button>
465
- <button type="button" data-variant="secondary" id="updateBackupBoards">Back up all boards</button>
466
- ${r?'<button type="button" data-variant="primary" id="updateRunNow">Update now</button>':""}
467
- </div>
468
- ${d}
469
- <p class="description update-docs-hint">Full guide: <a href="https://github.com/brucestarlove/ss-orbit/blob/master/docs/UPDATING.md" target="_blank" rel="noopener">docs/UPDATING.md</a></p>
470
- </div>
471
- </div>
472
- </section>`}function Bi(){let e=Ae(),t=Ve(),a=Ct(),n=wa(),r=a===null?`Using browser default (${n?"reduced motion":"motion allowed"}).`:"Using your saved preference.";return`
473
- <div class="settings-stack appearance-settings">
474
- <section class="settings-card">
475
- <header class="settings-card-head">
476
- <h3 class="settings-card-title">Theme</h3>
477
- </header>
478
- <div class="settings-card-body">
479
- <div class="settings-toggle">
480
- <button
481
- type="button"
482
- class="topbar-btn topbar-ctl-shrink"
483
- data-variant="theme"
484
- id="appearanceThemeToggle"
485
- aria-label="Toggle theme"
486
- >
487
- <svg class="topbar-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">${Lr(e)}</svg>
488
- </button>
489
- <span class="settings-toggle-text">
490
- <span class="settings-row-title" id="appearanceThemeLabel">${e==="dark"?"Dark":"Light"}</span>
491
- <span class="description">Tap to switch between the light and dark theme used across the app.</span>
492
- </span>
493
- </div>
494
- <div class="settings-toggle">
495
- <button
496
- type="button"
497
- class="topbar-btn topbar-ctl-shrink motion-toggle"
498
- data-variant="theme"
499
- id="appearanceMotionToggle"
500
- aria-pressed="${t?"true":"false"}"
501
- aria-label="Toggle reduced theme motion"
502
- >
503
- ${Di()}
504
- </button>
505
- <span class="settings-toggle-text">
506
- <span class="settings-row-title">Reduce theme motion</span>
507
- <span class="description">Turns off the animated starfield, meteors, and theme animations. ${l(r)}</span>
508
- </span>
509
- </div>
510
- </div>
511
- </section>
512
- ${Fi()}
513
- </div>
514
- `}function Fi(){let e=Oe(),t=ur.map(n=>`<option value="${n}"${n===e.site?" selected":""}>${l(ue[n]?.label||n)}</option>`).join(""),a=Object.entries(et).map(([n,r])=>{let o=e[n],s=Object.entries(ue).map(([i,d])=>`<option value="${i}"${i===o?" selected":""}>${l(d.label)}</option>`).join("");return`
515
- <label class="control-field">
516
- <span class="control-field-label">${l(r.label)}</span>
517
- <select class="select-chevron-field" data-font-target="${n}">${s}</select>
518
- </label>
519
- `}).join("");return`
520
- <section class="settings-card appearance-typography-card">
521
- <header class="settings-card-head">
522
- <h3 class="settings-card-title">Typography</h3>
523
- </header>
524
- <div class="settings-card-body">
525
- <div class="settings-row settings-slider-row">
526
- <span class="control-field-label">Text size</span>
527
- <input type="range" class="slider" id="fontScaleSlider" min="${.85}" max="${1.3}" step="${.05}" value="${e.userScale}" aria-label="Text size" />
528
- <output id="fontScaleValue" class="control-value">${Math.round(e.userScale*100)}%</output>
529
- </div>
530
- <div class="settings-row">
531
- <label class="control-field">
532
- <span class="control-field-label">Primary Font</span>
533
- <select class="select-chevron-field" id="siteFontSelect">${t}</select>
534
- </label>
535
- </div>
536
- <div class="settings-row">
537
- <div class="control-grid typography-overrides">${a}</div>
538
- </div>
539
- </div>
540
- </section>
541
- `}function Lr(e){return 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" />'}function Di(){return`<svg class="topbar-icon motion-icon" width="16" height="16" viewBox="0 0 24 24" fill="currentColor" stroke="none" aria-hidden="true">${[{cx:6,cy:7,r:1.1,d:"0s"},{cx:17.5,cy:6,r:1.5,d:"0.5s"},{cx:12,cy:12,r:1,d:"0.9s"},{cx:7.5,cy:17,r:1.4,d:"0.25s"},{cx:18,cy:16.5,r:1.1,d:"0.7s"}].map(t=>`<circle class="motion-star" cx="${t.cx}" cy="${t.cy}" r="${t.r}" style="animation-delay:${t.d}" />`).join("")}</svg>`}function Ri(e){let t=e.board?.ai_enabled!==0;return`
542
- ${t?'<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>':""}
543
- <div class="section">
544
- ${Ui(e.entries,t)}
545
- <form id="projectEntryForm" class="comment-form project-entry-form">
546
- <div class="project-entry-form-header">Add entry</div>
547
- <select name="type" class="select-chevron-field" aria-label="Entry type">
548
- <option value="decision">Decision</option>
549
- <option value="lesson">Lesson</option>
550
- </select>
551
- <input name="title" placeholder="Entry title..." required />
552
- <textarea name="body" placeholder="Decision rationale or lesson: do X instead of Y when working with Z." required></textarea>
553
- <button type="submit" data-variant="primary">Add Entry</button>
554
- </form>
555
- </div>
556
- `}function Oi(e){if(String(e||"").trimStart().startsWith("<"))throw new Error("That file looks like an HTML page instead of JSON. In Trello, use Export JSON/download the board JSON, not Save Page As.");try{return JSON.parse(e)}catch(a){throw new Error(a?.message||"invalid JSON")}}function Ni(e,t){let a=e.board;if(t==="data"){let n=async(f=!!w("#exportProjectImages")?.checked)=>{let h=f?"?include_attachments=true":"",p=await k(`/api/boards/${encodeURIComponent(a.id)}/export${h}`),g=f?`${a.slug}.with-images.orbit.json`:`${a.slug}.orbit.json`;jn(g,p)};w("#exportProject")?.addEventListener("click",async()=>{await n()}),w("#boardRenameForm")?.addEventListener("submit",async f=>{f.preventDefault();let h=new FormData(f.currentTarget),p=String(h.get("name")||"").trim();!p||p===a.name||(await k(`/api/boards/${encodeURIComponent(a.id)}`,{method:"PATCH",body:{name:p}}),await F(),c.detailMode="settings",y.success(`Renamed board to ${p}. Canonical slug unchanged: ${a.slug}`))});let r=async(f,h)=>{let p=await f.text(),g=Oi(p);return k("/api/admin/import",{method:"POST",body:h(g)})},o=w("#importSnapshotAsNewFile");w("#importSnapshotAsNewButton")?.addEventListener("click",()=>{o?.click()}),o?.addEventListener("change",async f=>{let h=f.currentTarget,p=h.files?.[0];if(p)try{let g=await r(p,P=>({snapshot:P,create_new:!0,board_id:a.id}));c.boardId=g.imported_board_id,c.boardSlug=g.imported_board_slug,c.detailMode="empty",await F(),await O({boardId:g.imported_board_id,view:"board"});let x=g.imported_counts||{},E=Number.isFinite(Number(x.tickets))?` (${x.tickets} cards)`:"";y.success(g.source_format==="trello-board-export"?`Trello board imported as new board${E}`:"Board imported as new board")}catch(g){y.error(`Import failed: ${g?.message||"invalid JSON"}`)}finally{h.value=""}});let s=w("#replaceImportConfirmInput"),i=w("#replaceCurrentBoardImportFile"),d=w("#replaceCurrentBoardImportButton");s?.addEventListener("input",()=>{let f=s.value.trim()!==a.slug;i&&(i.disabled=f),d&&(d.disabled=f)}),d?.addEventListener("click",()=>{d.disabled||i?.disabled||i?.click()}),i?.addEventListener("change",async f=>{let h=f.currentTarget,p=h.files?.[0];if(p){if(s.value.trim()!==a.slug){y.error("Type the board slug before replacing this board."),h.value="";return}try{let g=await r(p,P=>({snapshot:P,replace_existing:!0,board_id:a.id}));await F(),c.detailMode="settings";let x=g.imported_counts||{},E=Number.isFinite(Number(x.tickets))?` (${x.tickets} cards)`:"";y.success(g.source_format==="trello-board-export"?`Trello board replaced from Trello import${E}`:"Board replaced from snapshot")}catch(g){y.error(`Import failed: ${g?.message||"invalid JSON"}`)}finally{h.value=""}}}),w("#deleteBoardStart")?.addEventListener("click",async()=>{J.step="choice",J.exported=!1,await ye()}),w("#deleteBoardExportFirst")?.addEventListener("click",async()=>{await n(),J.step="confirm",J.exported=!0,await ye()}),w("#deleteBoardSkipExport")?.addEventListener("click",async()=>{J.step="confirm",J.exported=!1,await ye()}),w("#deleteBoardCancel")?.addEventListener("click",async()=>{J.step="idle",J.exported=!1,await ye()});let m=w("#deleteBoardConfirmInput"),u=w("#deleteBoardConfirm");m?.addEventListener("input",()=>{u&&(u.disabled=m.value.trim()!==a.slug)}),u?.addEventListener("click",async()=>{await k(`/api/boards/${encodeURIComponent(a.id)}`,{method:"DELETE",body:{confirm_slug:a.slug,delete_files:!0}}),J.step="idle",J.exported=!1,await F(),y.success(`Deleted board: ${a.name}`),O({boardId:c.boardId,view:"board"},{replace:!0})}),w("#updateCheckNow")?.addEventListener("click",async()=>{let f=w("#updateCheckNow");f.disabled=!0,f.textContent="Checking\u2026",await en({force:!0}),tn(),c.detailMode="settings",await ye()}),w("#updateBackupBoards")?.addEventListener("click",async()=>{let f=w("#updateBackupBoards"),h=f.textContent;f.disabled=!0,f.textContent="Backing up\u2026";try{await ea()}catch(p){y.error(`Backup failed: ${p?.message||"request failed"}`)}finally{f.disabled=!1,f.textContent=h}}),w("#updateCopyCommands")?.addEventListener("click",async()=>{let f=w("#updateCommands")?.textContent||"";try{await navigator.clipboard.writeText(f),y.success("Update commands copied")}catch{y.error("Couldn't copy \u2014 select the commands manually")}}),w("#updateRunNow")?.addEventListener("click",async()=>{if(!window.confirm("Update Orbit now? It will pull the latest code, rebuild, and restart. Back up your boards first."))return;let h=w("#updateRunNow");h.disabled=!0,h.textContent="Updating\u2026";try{await k("/api/update",{method:"POST"}),y.success("Update started \u2014 Orbit will restart shortly. Reload in a minute.",9e3)}catch(p){y.error(`Update failed to start: ${p?.message||"request failed"}`),h.disabled=!1,h.textContent="Update now"}})}if(t==="notes"){let n=T.querySelector('[data-edit-field="project_notes"]');if(n){let r=()=>Ne(n,{fieldName:"project_notes",multiline:!0,initialValue:a.project_notes||"",ariaLabel:"Edit Notes For You",rerender:async()=>{c.detailMode="settings",await ye()},commit:async o=>{await k(`/api/boards/${a.id}`,{method:"PATCH",body:{project_notes:re(o)}}),await F(),c.detailMode="settings",y.success("Saved")}});n.addEventListener("click",o=>{o.preventDefault(),r()}),n.addEventListener("keydown",o=>{(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),r())})}}if(t==="lanes"&&(T.querySelectorAll("[data-lane-action]").forEach(n=>{n.addEventListener("click",async()=>{await Vi(n)})}),T.querySelectorAll(".lane-row input, .lane-row select").forEach(n=>{n.addEventListener("change",async()=>{await Ir(n.closest(".lane-row"))})}),w("#laneCreateForm")?.addEventListener("submit",async n=>{n.preventDefault();let r=new FormData(n.currentTarget);await k(`/api/boards/${a.id}/states`,{method:"POST",body:Object.fromEntries(r.entries())}),await F(),c.detailMode="settings",y.success("Lane added")}),w("#showPriorityToggle")?.addEventListener("change",n=>{c.showPriority=!!n.currentTarget.checked,localStorage.setItem("mab_show_priority",c.showPriority?"true":"false"),Xe()})),t==="appearance"){let n=w("#appearanceThemeToggle");if(n){let s=w("#appearanceThemeLabel"),i=n.querySelector(".topbar-icon");n.addEventListener("click",()=>{let d=Mt();i&&(i.innerHTML=Lr(d)),s&&(s.textContent=d==="dark"?"Dark":"Light"),y.success(d==="dark"?"Dark theme enabled":"Light theme enabled")})}let r=w("#appearanceMotionToggle");r?.addEventListener("click",()=>{let s=r.getAttribute("aria-pressed")!=="true";r.setAttribute("aria-pressed",s?"true":"false"),_n(s),lt(),window.dispatchEvent(new CustomEvent("orbit:motion-preference-change",{detail:{reduce:s}})),y.success(s?"Theme motion reduced":"Theme motion enabled")}),w("#siteFontSelect")?.addEventListener("change",s=>{let i=hr(s.currentTarget.value);T.querySelectorAll("select[data-font-target]").forEach(d=>{d.value=i[d.dataset.fontTarget]}),y.success(`Primary font: ${ue[i.site]?.label||"updated"}`)});let o=w("#fontScaleSlider");if(o){let s=w("#fontScaleValue");o.addEventListener("input",i=>{let d=br(i.currentTarget.value);s&&(s.textContent=`${Math.round(d.userScale*100)}%`)})}fi(T),T.querySelectorAll("select[data-font-target]").forEach(s=>{s.addEventListener("change",i=>{mr(i.currentTarget.dataset.fontTarget,i.currentTarget.value)})})}if(t==="ai"){let n=()=>{let i=w("#mcpOsSelect")?.value||aa(),d=w("#mcpClientSelect")?.value||"claude-code",m=ra[d]||ra.generic,u=xr(),f=nn(d,u),h=rn(i,u),p=T.querySelector("[data-mcp-os-note]");p&&(p.textContent=`Detected: ${na[aa()]?.label||"Linux"}`);let g=T.querySelector("[data-mcp-client-format]");g&&(g.textContent=m.format);let x=T.querySelector("[data-mcp-output-title]");x&&(x.textContent=`${m.label} setup`);let E=T.querySelector("[data-mcp-helper]");E&&(E.textContent=m.helper);let P=T.querySelector("[data-mcp-config]");P&&(P.textContent=f);let v=T.querySelector("[data-mcp-command]");v&&(v.textContent=h)};w("#mcpOsSelect")?.addEventListener("change",()=>n()),w("#mcpClientSelect")?.addEventListener("change",()=>n()),["#mcpServerPathInput"].forEach(i=>{w(i)?.addEventListener("input",()=>n())}),T.querySelectorAll("[data-copy-mcp]").forEach(i=>{i.addEventListener("click",async()=>{let d=w("#mcpOsSelect")?.value||aa(),m=w("#mcpClientSelect")?.value||"claude-code",u=xr(),f=i.dataset.copyMcp==="command"?rn(d,u):nn(m,u);await ji(f),y.success(i.dataset.copyMcp==="command"?"MCP command copied":"MCP config copied")})});let r=T.querySelector('[data-edit-field="agent_instructions"]');if(r){let i=()=>Ne(r,{fieldName:"agent_instructions",multiline:!0,initialValue:a.agent_instructions||"",ariaLabel:"Edit Agent Instructions",rerender:async()=>{c.detailMode="settings",await ye()},commit:async d=>{await k(`/api/boards/${a.id}`,{method:"PATCH",body:{agent_instructions:re(d)}}),await F(),c.detailMode="settings",y.success("Saved")}});r.addEventListener("click",d=>{d.preventDefault(),i()}),r.addEventListener("keydown",d=>{(d.key==="Enter"||d.key===" ")&&(d.preventDefault(),i())})}w("#aiEnabledToggle")?.addEventListener("change",async i=>{let d=!!i.currentTarget.checked;await k(`/api/boards/${a.id}`,{method:"PATCH",body:{ai_enabled:d}}),await F(),c.detailMode="settings",y.success(d?"AI features enabled \u2014 agent lanes provisioned":"AI features disabled \u2014 AI Ready hidden")});let o=w("#helperPickFolderBtn");o?.addEventListener("click",async()=>{o.disabled=!0;try{let i=await k("/api/system/pick-folder",{method:"POST"});if(!i.path)return;await k(`/api/boards/${encodeURIComponent(a.id)}`,{method:"PATCH",body:{system_path:i.path}}),await F(),c.detailMode="settings",y.success("Linked coding project folder updated")}catch(i){y.error(i?.message||"Folder picker unavailable")}finally{o.disabled=!1}});let s=w("#manageHelperFilesToggle");s?.addEventListener("change",async()=>{let i=s.checked;s.disabled=!0;try{await k(`/api/boards/${encodeURIComponent(a.id)}`,{method:"PATCH",body:{manage_helper_files:i}}),await F(),c.detailMode="settings",y.success(i?"Generated SKILL-ORBIT.md and AGENTS.md \u2014 Orbit will keep them in sync":"Stopped maintaining helper files (existing files left in place)")}catch(d){s.checked=!i,s.disabled=!1,y.error(d?.message||"Could not update helper files setting")}})}if(t==="archive"){let n=w("#archiveList");if(n){let r=async()=>{try{let s=(await k(`/api/boards/${encodeURIComponent(a.id)}/archive`)).tickets||[];if(s.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">${s.map(i=>tt(i,{archive:{id:i.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 s=o.target.closest("[data-archive-restore]"),i=o.target.closest("[data-archive-delete]");if(!s&&!i)return;o.stopPropagation();let m=(s||i).closest("article")?.querySelector("h3")?.textContent||"card";if(s){let u=s.dataset.archiveRestore;await k(A(`/api/tickets/${encodeURIComponent(u)}/restore`),{method:"POST"}),y.success(`Restored: ${m}`),await F(),await r();return}if(i){let u=i.dataset.archiveDelete;if(!confirm(`Permanently delete "${m}"? This cannot be undone.`))return;await k(A(`/api/tickets/${encodeURIComponent(u)}`),{method:"DELETE"}),y.removed(`Deleted: ${m}`),await r()}})}}t==="journal"&&(T.querySelectorAll("[data-entry-action='toggle-struck']").forEach(n=>{n.addEventListener("click",async()=>{await k(A(`/api/board-entries/${n.dataset.entryId}`),{method:"PATCH",body:{struck:n.dataset.struck!=="true"}}),await F(),c.detailMode="settings",y.info(n.dataset.struck==="true"?"Project entry restored":"Project entry struck")})}),w("#projectEntryForm")?.addEventListener("submit",async n=>{n.preventDefault();let r=new FormData(n.currentTarget);await k(`/api/boards/${a.id}/entries`,{method:"POST",body:Object.fromEntries(r.entries())}),await F(),c.detailMode="settings",y.success("Project entry added")}))}async function ji(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 Ui(e=[],t=!1){return e.length?e.map(a=>{let n=!!a.struck_at,r=t&&n;return`
557
- <div class="comment project-entry ${l(a.type)} ${r?"is-struck":""}">
558
- <div class="comment-head">
559
- <div class="comment-head-main">
560
- <div class="comment-head-title">
561
- <span class="entry-type-pill ${l(a.type)}">${l(a.type)}</span>
562
- <strong class="entry-title">${l(a.title)}</strong>
563
- </div>
564
- ${r?'<span class="project-entry-status">Excluded from agent context</span>':""}
565
- </div>
566
- </div>
567
- <div class="comment-body">${l(a.body)}</div>
568
- <div class="comment-footer">
569
- <span class="comment-footer-meta">${l(a.created_by)} \xB7 ${pt(a.created_at)}</span>
570
- ${t?`<button
571
- type="button"
572
- data-variant="ghost"
573
- class="project-entry-strike detail-action-btn"
574
- data-entry-action="toggle-struck"
575
- data-entry-id="${l(a.id)}"
576
- data-struck="${n?"true":"false"}"
577
- aria-pressed="${n?"true":"false"}"
578
- >${n?"Restore":"Strike"}</button>`:""}
579
- </div>
580
- </div>
581
- `}).join(""):'<p class="description">No project entries yet.</p>'}function Hi(e,t,a){return!!(t.get(e.id)||e.is_default||Tr.has(e.role)||a&&e.role==="ai_ready")}function qi(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)":Tr.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 zi(){let e=Ke(),t=W()?.ai_enabled!==0,a=new Map;for(let n of Z())a.set(n.state_id,(a.get(n.state_id)||0)+1);return`
582
- <div class="lane-list">
583
- ${e.map((n,r)=>{let o=n.role==="ai_ready",s=o?"AI Ready lane name is locked \u2014 disable AI to hide this column":"";return`
584
- <div class="lane-row${o?" lane-row--locked":""}" data-lane-id="${n.id}">
585
- <button type="button" data-variant="ghost" class="settings-lane-icon-btn" data-lane-action="up" ${r===0?"disabled":""} title="Move lane left" aria-label="Move lane left">\u2191</button>
586
- <button type="button" data-variant="ghost" class="settings-lane-icon-btn" data-lane-action="down" ${r===e.length-1?"disabled":""} title="Move lane right" aria-label="Move lane right">\u2193</button>
587
- <input name="name" value="${l(n.name)}" aria-label="Lane name" ${o?`readonly title="${l(s)}"`:""} />
588
- <button type="button" data-variant="${n.is_default?"secondary":"ghost"}" class="settings-lane-new-toggle ${n.is_default?"is-on":""}" data-lane-action="toggle-default" aria-pressed="${n.is_default?"true":"false"}" title="${n.is_default?"Default lane for new cards \u2014 click another lane to move it":"Make this the default lane for new cards"}" aria-label="Default lane for new cards">
589
- <span class="lane-new-sun" aria-hidden="true"></span>
590
- <span class="lane-new-label">New</span>
591
- </button>
592
- <button type="button" data-variant="ghost" class="settings-lane-delete-btn danger-button" data-lane-action="delete" ${Hi(n,a,t)?"disabled":""} title="${qi(n,a,t)}" aria-label="Delete lane">${Q}</button>
593
- </div>
594
- `}).join("")}
595
- </div>
596
- `}async function Vi(e){let t=e.closest(".lane-row"),a=t.dataset.laneId,n=e.dataset.laneAction;if(n==="delete"){await k(A(`/api/states/${a}`),{method:"DELETE"}),await F(),c.detailMode="settings",y.success("Lane deleted");return}if(n==="toggle-default"){if(e.getAttribute("aria-pressed")==="true")return;T.querySelectorAll('[data-lane-action="toggle-default"]').forEach(d=>{d.setAttribute("aria-pressed","false"),d.setAttribute("data-variant","ghost"),d.classList.remove("is-on")}),e.setAttribute("aria-pressed","true"),e.setAttribute("data-variant","secondary"),e.classList.add("is-on"),await Ir(t,{silent:!0}),y.success("Default lane updated");return}let r=Ke().map(i=>i.id),o=r.indexOf(a),s=n==="up"?o-1:o+1;s<0||s>=r.length||([r[o],r[s]]=[r[s],r[o]],await k(`/api/boards/${c.boardId}/states`,{method:"PATCH",body:{state_ids:r}}),await F(),c.detailMode="settings",y.success("Lanes reordered"))}async function Ir(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 k(A(`/api/states/${e.dataset.laneId}`),{method:"PATCH",body:{name:a,is_default:r}}),await F(),c.detailMode="settings",t||y.success("Lane updated")}var J,na,ra,Sr,Ai,Pi,Tr,_r=B(()=>{K();pe();te();ae();le();Ht();be();xt();oa();Se();Re();ka();Ea();Za();ft();Qa();an();J={boardId:"",step:"idle",exported:!1};na={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"}},ra={"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."},vscode:{label:"VS Code",format:"mcp.json",helper:"Paste this JSON into VS Code's user or workspace MCP configuration."},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."}};Sr={source:`git pull
597
- pnpm install
598
- pnpm run build:bundle
599
- # then restart Orbit`,docker:`git pull
600
- docker compose build
601
- docker compose up -d`,dist:`npm install -g @starlove/orbit@latest
602
- # then restart Orbit`},Ai={source:"local / server",docker:"Docker",dist:"npm"},Pi='<svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" d="M16.7 5.3a1 1 0 0 1 0 1.4l-7.5 7.5a1 1 0 0 1-1.4 0L4.3 10.7a1 1 0 1 1 1.4-1.4l2.8 2.8 6.8-6.8a1 1 0 0 1 1.4 0z" clip-rule="evenodd"/></svg>';Tr=new Set(["in_progress","review"])});function je(e){return String(e||"").trim()}function sn(e){let t=je(e);return t?t.replace(/^@+/,"").replace(/[-_]+/g," ").replace(/\s+/g," ").trim().replace(/\b\w/g,a=>a.toUpperCase()):""}function Ar({actor:e,actorId:t}={}){let a=je(e),n=je(t),r=a.toLowerCase(),o=n.toLowerCase();return a&&!on.has(r)&&!Cr.has(r)?sn(a):n&&!on.has(o)&&!Cr.has(o)?sn(n):""}function cn({actor:e,actorType:t,actorId:a}={}){let n=je(t).toLowerCase(),r=je(e),o=r.toLowerCase();if(n==="agent"){let s=Ar({actor:r,actorId:a});return s?`Agent \xB7 ${s}`:"Agent"}return!r||on.has(o)?"You":sn(r)}function Pr(e={}){let t=je(e.kind).toLowerCase(),a=je(e.author);if(Gi.has(t)||t&&t!=="human_comment"){let r=Ar({actor:a});return r?`Agent \xB7 ${r}`:"Agent"}return cn({actor:a,actorType:"human"})}var on,Cr,Gi,Mr=B(()=>{on=new Set(["local","local-owner","owner"]),Cr=new Set(["agent","local-agent","orbit-agent"]),Gi=new Set(["agent_note","checkpoint","completion"])});function Br(e){!Le||!$t||(Le.hidden=!1,$t.hidden=!1,requestAnimationFrame(()=>{Le.classList.toggle("is-open",e),$t.classList.toggle("is-visible",e)}),e||setTimeout(()=>{Le.classList.contains("is-open")||(Le.hidden=!0,$t.hidden=!0,at&&(at.innerHTML=""))},260))}function un(){Br(!1)}function Ki(e,t){let a=t?.payload?.error||t?.message||"Unable to load artifact",n=[l(a)];(a==="artifact_path_outside_allowed_roots"||a==="artifact_not_found")&&n.push("<br><br>The Orbit server could not read that artifact path. If this webapp is hosted on another machine, the file needs to be stored on the hosted Orbit server or linked as a server-readable/repo-relative path.");let r=String(e||"").match(/^\/mnt\/([a-z])\/(.+)$/i),o=String(e||"").match(/^\/([a-z])\/Users\/(.+)$/i);if(r){let s=r[1].toUpperCase();n.push(`<br><br>Windows spelling for this WSL path: <code>${l(`${s}:\\${r[2].replace(/\//g,"\\")}`)}</code>`)}else if(o){let s=o[1].toUpperCase();n.push(`<br><br>Windows spelling for this path: <code>${l(`${s}:\\Users\\${o[2].replace(/\//g,"\\")}`)}</code>`)}return n.join("")}async function Fr({ticketId:e,path:t,title:a="Linked artifact"}){if(!e||!t||!Le||!at||!ln||!dn)return!1;ln.textContent=a,dn.textContent=t,at.innerHTML='<p class="artifact-drawer-loading">Loading artifact\u2026</p>',Br(!0);try{let n=await k(A(`/api/tickets/${encodeURIComponent(e)}/artifacts/markdown?path=${encodeURIComponent(t)}`));return ln.textContent=a,dn.textContent=n.path||t,at.innerHTML=`<article class="markdown-body artifact-markdown-body">${ut(n.content||"")||`<p>${l("Empty artifact.")}</p>`}</article>`,!0}catch(n){return at.innerHTML=`<p class="artifact-drawer-error">${Ki(t,n)}</p>`,y.error("Artifact could not be opened"),!1}}var $t,Le,ln,dn,at,Wi,Dr=B(()=>{ae();te();le();$t=document.getElementById("artifactDrawerBackdrop"),Le=document.getElementById("artifactDrawer"),ln=document.getElementById("artifactDrawerTitle"),dn=document.getElementById("artifactDrawerMeta"),at=document.getElementById("artifactDrawerBody"),Wi=document.getElementById("artifactDrawerCloseBtn");Wi?.addEventListener("click",un);$t?.addEventListener("click",un);document.addEventListener("keydown",e=>{e.key==="Escape"&&Le&&!Le.hidden&&un()})});function Te({title:e,body:t="",className:a="",attrs:n="",headExtra:r=""}={}){let o=a?` ${a}`:"",s=n?` ${n}`:"";return`
603
- <section class="settings-card${o}"${s}>
604
- <header class="settings-card-head">
605
- <h3 class="settings-card-title">${l(e)}</h3>
606
- ${r}
607
- </header>
608
- <div class="settings-card-body">${t}</div>
609
- </section>
610
- `}function Rr({title:e,body:t="",open:a=!1,className:n="",attrs:r="",headExtra:o=""}={}){let s=n?` ${n}`:"",i=r?` ${r}`:"";return`
611
- <details class="settings-card settings-card--collapsible${s}"${a?" open":""}${i}>
612
- <summary class="settings-card-head">
613
- <h3 class="settings-card-title">${l(e)}</h3>
614
- ${o}
615
- ${Ji}
616
- </summary>
617
- <div class="settings-card-body">${t}</div>
618
- </details>
619
- `}var Ji,Or=B(()=>{te();Ji='<svg class="settings-card-chevron" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M6 9l6 6 6-6" /></svg>'});function ia(e,{getItems:t,match:a,renderItem:n,valueOf:r,onSelect:o,emptyText:s="No matches",minChars:i=0,maxItems:d=8}){if(!e)return()=>{};let m=document.createElement("div");m.className="search-results typeahead-results",m.hidden=!0,m.setAttribute("role","listbox"),m._ownerInput=e,document.body.appendChild(m);for(let b of document.querySelectorAll(".typeahead-results"))b!==m&&b._ownerInput&&!b._ownerInput.isConnected&&b.remove();let u=[],f=-1,h=0,p=()=>{let b=e.getBoundingClientRect();m.style.left=`${b.left}px`,m.style.width=`${b.width}px`,m.style.top=`${b.bottom+4}px`},g=()=>{m.hidden||p()},x=()=>{m.hidden=!0,m.innerHTML="",u=[],f=-1,window.removeEventListener("scroll",g,!0),window.removeEventListener("resize",g)},E=()=>{m.hidden&&(m.hidden=!1,window.addEventListener("scroll",g,!0),window.addEventListener("resize",g))},P=()=>{let b=e.value.trim().toLowerCase();if(b.length<i){x();return}let _=t()||[];if(u=(b?_.filter(C=>a(C,b)):_).slice(0,d),u.length===0){b?(E(),m.innerHTML=`<div class="search-hit typeahead-empty">${s}</div>`,p()):x();return}f=-1,E(),m.innerHTML=u.map((C,D)=>`<div class="search-hit" role="option" data-index="${D}">${n(C)}</div>`).join(""),p()},v=b=>{let _=m.querySelectorAll("[data-index]");_.length&&(f=(b+_.length)%_.length,_.forEach((C,D)=>{let H=D===f;C.classList.toggle(Yi,H),H&&C.scrollIntoView({block:"nearest"})}))},I=b=>{b&&(e.value=r(b),e.dispatchEvent(new Event("input",{bubbles:!0})),x(),e.focus(),o&&o(b))},$=()=>P(),S=()=>P(),R=b=>{m.hidden||(b.key==="ArrowDown"?(b.preventDefault(),v(f+1)):b.key==="ArrowUp"?(b.preventDefault(),v(f-1)):b.key==="Enter"?f>=0&&u[f]&&(b.preventDefault(),b.stopImmediatePropagation(),I(u[f])):b.key==="Escape"&&(m.hidden||(b.stopPropagation(),x())))},U=()=>{h=window.setTimeout(x,120)},M=b=>{let _=b.target.closest("[data-index]");_&&(b.preventDefault(),window.clearTimeout(h),I(u[Number(_.dataset.index)]))};return e.addEventListener("input",$),e.addEventListener("focus",S),e.addEventListener("keydown",R),e.addEventListener("blur",U),m.addEventListener("mousedown",M),()=>{window.clearTimeout(h),window.removeEventListener("scroll",g,!0),window.removeEventListener("resize",g),e.removeEventListener("input",$),e.removeEventListener("focus",S),e.removeEventListener("keydown",R),e.removeEventListener("blur",U),m.removeEventListener("mousedown",M),m.remove()}}var Yi,pn=B(()=>{Yi="is-active"});function fn(e){return`#${e.number} \u2014 ${e.title}`}function Xi(e){let t=j(e.type);return`
620
- <span class="search-hit-main">
621
- <strong>${l(ce(e))}</strong>
622
- <span class="search-hit-title">${l(e.title)}</span>
623
- </span>
624
- <span class="search-hit-type type-pill-${l(t)}">${l($e(e.type))}</span>
625
- <span class="search-hit-state" data-variant="${l(Be(e))}">${l(e.state_name||"State")}</span>
626
- `}function Zi(e,t){let a=t.replace(/^#/,"");return String(e.number).startsWith(a)||e.title.toLowerCase().includes(t)}function nt(e,t,{onSelect:a}={}){e&&ia(e,{getItems:t,match:Zi,renderItem:Xi,valueOf:fn,onSelect:a,emptyText:"No matching tickets"})}var mn=B(()=>{te();pn()});var Ur={};It(Ur,{renderDetail:()=>Ie,renderDetailCard:()=>tt,renderInlinePreservedTextField:()=>sa,startInlineEdit:()=>Ne});async function ie(e,{renderBoardAfter:t=!1}={}){let[a,n,r]=await Promise.all([k(A(`/api/tickets/${e}/context?depth=1`)),k(A(`/api/tickets/${e}/history`)),k(A(`/api/tickets/${e}/comments`))]);a.ticket?.id===e&&(Aa(a.ticket),t&&Xe(),!(c.detailMode!=="ticket"||c.selectedTicketId!==e)&&await Ie({context:a,statusHistory:n,comments:r.comments||[]}))}async function ve(e,t){await k(A(`/api/tickets/${e}`),{method:"PATCH",body:t}),await ie(e,{renderBoardAfter:!0})}function Qi(e,t,a){if(!e||!t||typeof t.clientX!="number"||typeof t.clientY!="number")return null;let n=null;if(document.caretPositionFromPoint){let r=document.caretPositionFromPoint(t.clientX,t.clientY);r?.offsetNode&&(r.offsetNode===e||e.contains(r.offsetNode))&&(n=document.createRange(),n.setStart(e,0),n.setEnd(r.offsetNode,r.offset))}else if(document.caretRangeFromPoint){let r=document.caretRangeFromPoint(t.clientX,t.clientY);r?.startContainer&&(r.startContainer===e||e.contains(r.startContainer))&&(n=document.createRange(),n.setStart(e,0),n.setEnd(r.startContainer,r.startOffset))}return n?Math.max(0,Math.min(a,n.toString().length)):null}function es(e){let t=e?.closest?.(".drawer-inner");return t?{scrollContainer:t,nodeRect:e.getBoundingClientRect(),scrollTop:t.scrollTop}:null}function ts(e,t){if(!e?.scrollContainer||!t?.isConnected)return;let a=t.getBoundingClientRect(),n=Math.max(0,e.scrollContainer.scrollTop+(a.top-e.nodeRect.top));Number.isFinite(n)?e.scrollContainer.scrollTop=n:e.scrollContainer.scrollTop=e.scrollTop}function as(e){try{e.focus({preventScroll:!0})}catch{e.focus()}}function Ne(e,t){if(!e||e.dataset.editing==="true")return;let{fieldName:a,multiline:n=!0,initialValue:r="",ariaLabel:o,emptyMessage:s,sourceEvent:i,rerender:d,commit:m}=t,u=n?es(e):null,f=String(r),h=n?Qi(e,i,f.length):null;e.dataset.editing="true";let p=document.createElement(n?"textarea":"input");if(p.className=n?"inline-desc-editor":"inline-title-editor",p.value=f,a&&p.setAttribute("data-editing-field",a),o&&p.setAttribute("aria-label",o),!n)p.type="text",p.maxLength=500,p.autocomplete="off",p.spellcheck=!0;else{p.rows=Math.max(3,f.split(`
627
- `).length+1);let S=e.getBoundingClientRect().height;Number.isFinite(S)&&S>0&&(p.style.minHeight=`${Math.ceil(S)}px`)}if(e.replaceWith(p),as(p),!n)p.select();else{let S=h??0;p.setSelectionRange(S,S),ts(u,p)}let g=!1,x=()=>{document.removeEventListener("pointerdown",v,!0)},E=()=>{p.isConnected&&p.replaceWith(e),delete e.dataset.editing},P=()=>{g=!0,x(),E()};function v(S){g||S.target===p||p.contains(S.target)||p.blur()}document.addEventListener("pointerdown",v,!0);let I=async()=>{if(g)return;let S=n?p.value:p.value.trim();if(P(),String(S)===String(r)){d&&await d();return}if(s&&!S.trim()){y.warning(s),d&&await d();return}m&&await m(S)},$=async()=>{g||(P(),d&&await d())};p.addEventListener("blur",I),p.addEventListener("keydown",S=>{if(!(n&&ht(S,p))){if(S.key==="Escape"){S.preventDefault(),$();return}S.key==="Enter"&&(n?(S.metaKey||S.ctrlKey)&&(S.preventDefault(),I()):(S.preventDefault(),I()))}})}function ns({currentPath:e="",label:t="Artifact"}={}){let a=window.prompt(`Local markdown/text path for ${t} (example: docs/test.md or ./docs/test.md)`,e||"docs/");return a===null?null:re(a).trim()}async function Nr({ticketId:e,fieldName:t,currentPath:a,label:n}){if(!e||!t)return null;let r=ns({currentPath:a,label:n});return r===null?null:(await ve(e,{[t]:r}),await ie(e),y.success(r?"Artifact path updated":"Artifact path cleared"),r)}function rs(e,t){let a=e.id,n=()=>Ne(r,{fieldName:"title",multiline:!1,initialValue:e.title||"",ariaLabel:"Edit ticket title",emptyMessage:"Title cannot be empty",rerender:()=>Ie(),commit:u=>ve(a,{title:u})}),r=X.querySelector('[data-edit-field="title"]');r&&(r.addEventListener("click",n),r.addEventListener("keydown",u=>{(u.key==="Enter"||u.key===" ")&&(u.preventDefault(),n())}));let o=T.querySelector('[data-edit-field="description"]'),s=u=>Ne(o,{fieldName:"description",multiline:!0,initialValue:e.description||"",ariaLabel:"Edit ticket description",sourceEvent:u,rerender:()=>Ie(),commit:f=>ve(a,{description:re(f)})});o&&(o.addEventListener("click",u=>{u.preventDefault(),s(u)}),o.addEventListener("keydown",u=>{(u.key==="Enter"||u.key===" ")&&(u.preventDefault(),s())}));let i=[{key:"ai_plan",label:"AI Plan"},{key:"implementation_summary",label:"Work Summary"},{key:"implementation_updates",label:"Revisions"}];for(let{key:u,label:f}of i){let h=T.querySelector(`[data-edit-field="${u}"]`);if(!h)continue;let p=g=>Ne(h,{fieldName:u,multiline:!0,initialValue:e[u]||"",ariaLabel:`Edit ${f}`,sourceEvent:g,rerender:()=>Ie(),commit:x=>ve(a,{[u]:re(x)})});h.addEventListener("click",g=>{g.preventDefault(),p(g)}),h.addEventListener("keydown",g=>{(g.key==="Enter"||g.key===" ")&&(g.preventDefault(),p())})}X.querySelectorAll(".meta-select[data-meta-field]").forEach(u=>{u.addEventListener("change",async()=>{let f=u.dataset.metaField;if(f==="type"){await ve(a,{type:u.value});return}if(f==="state_id"){await ve(a,{state_id:u.value});return}if(f==="parent_ticket_id"){await ve(a,{parent_ticket_id:u.value||null});return}f==="priority"&&await ve(a,{priority:Number(u.value)})})});let d=async u=>{await ve(a,{labels:u})};T.querySelectorAll("[data-remove-label]").forEach(u=>{u.addEventListener("click",async()=>{let f=u.getAttribute("data-remove-label"),h=(e.labels||[]).map(p=>p.name).filter(p=>p!==f);await d(h)})});let m=async()=>{let u=T.querySelector("[data-label-input]");if(!u)return;let f=u.value.trim();if(!f)return;let h=(e.labels||[]).map(p=>p.name);if(h.includes(f)){u.value="";return}h.push(f),u.value="",await d(h)};T.querySelector("[data-label-add]")?.addEventListener("click",()=>{m()}),ws(T.querySelector("[data-label-input]"),e),T.querySelector("[data-label-input]")?.addEventListener("keydown",u=>{u.key==="Enter"&&(u.preventDefault(),m())})}async function Ie(e={}){if(c.detailMode==="settings"){await ye(),Oa();return}if(!c.selectedTicketId){jt();return}let t=c.selectedTicketId,a=b=>Object.prototype.hasOwnProperty.call(e,b),[n,r,o,s]=await Promise.all([a("context")?Promise.resolve(e.context):k(A(`/api/tickets/${t}/context?depth=1`)),a("statusHistory")?Promise.resolve(e.statusHistory):k(A(`/api/tickets/${t}/history`)),a("comments")?Promise.resolve(e.comments):k(A(`/api/tickets/${t}/comments`)).then(b=>b.comments||[]),a("attachmentList")?Promise.resolve(e.attachmentList):z.attachments?k(A(`/api/tickets/${t}/attachments`)).catch(()=>({attachments:[]})):Promise.resolve({attachments:[]})]);if(c.detailMode!=="ticket"||c.selectedTicketId!==t||n.ticket?.id!==t)return;let i=n.ticket;Jt(i.id,i.updated_at),document.querySelector(`.card[data-ticket-id="${CSS.escape(i.id)}"] .card-unread-dot`)?.remove();let d=Ca(i.state_id),m=["epic","feature","task","bug"].map(b=>`<option value="${l(b)}" ${j(i.type)===b?"selected":""}>${l(Rn(b))}</option>`).join(""),u=d.map(b=>`<option value="${l(b.id)}" ${b.id===i.state_id?"selected":""}>${l(b.name)}</option>`).join(""),f=[4,3,2,1,0].map(b=>`<option value="${b}" ${Number(i.priority)===b?"selected":""}>${l(Je(b))}</option>`).join(""),h=(i.labels||[]).map(b=>`
628
- <span class="label-pill-removable" style="--label-color: ${l(b.color)}">
629
- ${l(b.name)}
630
- <button type="button" class="label-pill-remove" data-remove-label="${l(b.name)}" title="Remove label" aria-label="Remove label">${Q}</button>
631
- </span>`).join(""),p=j(i.type),g=Ye(i.priority),x=d.find(b=>b.id===i.state_id),E=Be(x),P=i.description?me(i.description):l("No description yet."),v=i.description?"description preserved-text-body editable-field detail-description-body":"description editable-field detail-description-body is-placeholder",I=`
632
- <span class="ticket-number">${l(ce(i))}</span>
633
- <div class="detail-meta-badge-row" aria-label="Ticket metadata controls">
634
- <select class="detail-meta-badge detail-state-badge meta-select" data-variant="${l(E)}" data-meta-field="state_id" aria-label="Ticket state">${u}</select>
635
- <select class="detail-meta-badge detail-type-badge type-pill-${l(p)} meta-select" data-meta-field="type" aria-label="Ticket type">${m}</select>
636
- <select class="detail-meta-badge detail-priority-badge priority-pill meta-select" data-variant="${l(g)}" data-meta-field="priority" aria-label="Ticket priority">${f}</select>
637
- </div>
638
- `;Ut({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:I,body:`
639
- <div class="detail-head">
640
- <p class="detail-updated" title="Last saved change">Updated ${l(Dn(i.updated_at))}</p>
641
- <div class="${v}" data-edit-field="description" tabindex="0" title="Click to edit">${P}</div>
642
- </div>
643
-
644
- <div class="settings-stack detail-section-stack">
645
- ${Te({title:"Labels",body:`
646
- <div class="settings-row">
647
- <div class="label-pills-row" data-other-labels>${h}</div>
648
- <div class="label-add-row">
649
- <div class="label-add-input-wrap">
650
- <input type="text" class="label-add-input meta-inline-input" data-label-input autocomplete="off" placeholder="Add label\u2026" aria-label="Add label" />
651
- </div>
652
- <button type="button" class="meta-add-btn detail-action-btn" data-variant="ghost" data-label-add>Add</button>
653
- </div>
654
- </div>
655
- `})}
656
-
657
- ${hs(i,n)}
658
-
659
- ${z.attachments?is(i,s.attachments||[]):""}
660
-
661
- ${ks(i,n)}
662
-
663
- ${Te({title:"Related",body:`<div class="settings-row">${ms(n.relations,i)}</div>`})}
664
-
665
- ${W()?.ai_enabled!==0?Rr({title:"AI Plan / Implementation Record",open:os(i.id),attrs:`data-ai-plan-toggle data-ticket-id="${l(i.id)}"`,body:`
666
- <div class="ai-fields-grid">
667
- ${sa({fieldName:"ai_plan",label:"AI Plan",value:i.ai_plan,placeholder:"What to do: ",ticketId:i.id,artifactPath:i.plan_artifact_path,artifactFieldName:"plan_artifact_path",artifactLabel:"Open full handoff"})}
668
- ${sa({fieldName:"implementation_summary",label:"Work Summary",value:i.implementation_summary,placeholder:"What was done: ",ticketId:i.id,artifactPath:i.implementation_artifact_path,artifactFieldName:"implementation_artifact_path",artifactLabel:"Open full report"})}
669
- ${sa({fieldName:"implementation_updates",label:"Revisions",value:i.implementation_updates,placeholder:"Changes since initial implementation: "})}
670
- </div>`}):""}
671
-
672
- ${Te({title:"Status History",body:`<div class="settings-row">${us(r)}</div>`})}
673
-
674
- ${Te({title:"Comments",body:`
675
- <div class="settings-row">
676
- ${o.map(ps).join("")||'<p class="description">No comments yet.</p>'}
677
- <form class="comment-form" id="detailCommentForm">
678
- <textarea name="body" placeholder="Add a comment, decision, or instruction..." required></textarea>
679
- <button type="submit" class="detail-action-btn" data-variant="ghost">Comment</button>
680
- </form>
681
- </div>
682
- `})}
683
- </div>
684
- `}),rs(i,n),z.attachments&&ls(i);let $=w("#detailCommentForm textarea");$?.addEventListener("keydown",b=>{ht(b,$)}),w("#detailCommentForm").addEventListener("submit",async b=>{b.preventDefault();let _=re(new FormData(b.currentTarget).get("body")).trim();_&&(await k(A(`/api/tickets/${i.id}/comments`),{method:"POST",body:{body:_}}),await ie(i.id))}),T.querySelectorAll("[data-comment-action='toggle-ai-omission']").forEach(b=>{b.addEventListener("click",async()=>{let _=b.dataset.commentId;if(!_)return;let C=b.dataset.omittedForAi!=="true";await k(A(`/api/tickets/${i.id}/comments/${_}`),{method:"PATCH",body:{omitted_for_ai:C}}),y.info(C?"Comment omitted from AI context":"Comment included in AI context"),await ie(i.id)})}),T.querySelectorAll("[data-open-ticket]").forEach(b=>{b.addEventListener("click",async _=>{_.target.closest("[data-detach-child],[data-detach-parent],[data-detach-relation]")||O({boardId:c.boardId,view:"ticket",ticketId:b.dataset.openTicket})})}),T.querySelectorAll("[data-detach-child]").forEach(b=>{b.addEventListener("click",async _=>{_.stopPropagation();let C=b.dataset.detachChild;try{await k(A(`/api/tickets/${C}`),{method:"PATCH",body:{parent_ticket_id:null}})}catch(D){y.error(D.message||"Failed to remove child");return}await ie(i.id,{renderBoardAfter:!0}),y.success("Removed from epic")})}),T.querySelectorAll("[data-detach-parent]").forEach(b=>{b.addEventListener("click",async _=>{_.stopPropagation();try{await k(A(`/api/tickets/${i.id}`),{method:"PATCH",body:{parent_ticket_id:null}})}catch(C){y.error(C.message||"Failed to remove parent");return}await ie(i.id,{renderBoardAfter:!0}),y.success("Parent epic removed")})}),T.querySelectorAll("[data-detach-relation]").forEach(b=>{b.addEventListener("click",async _=>{_.stopPropagation();let C=b.dataset.detachRelation;try{await k(A(`/api/relations/${C}`),{method:"DELETE"})}catch(D){y.error(D.message||"Failed to remove relation");return}await ie(i.id),y.success("Relation removed")})});let S=T.querySelector("[data-ai-plan-toggle]");S&&S.addEventListener("toggle",()=>{let b=S.dataset.ticketId;S.open?localStorage.setItem(`mab_ai_plan_open:${b}`,"1"):localStorage.removeItem(`mab_ai_plan_open:${b}`)}),T.querySelectorAll("[data-open-artifact]").forEach(b=>{b.addEventListener("click",async _=>{_.stopPropagation();let C=b.dataset.artifactField,D=b.dataset.artifactTitle||"Linked artifact",H=b.dataset.artifactTicketId||i.id,q=String(b.dataset.openArtifact||"").trim();if(!q){await Nr({ticketId:H,fieldName:C,currentPath:q,label:D});return}await Fr({ticketId:H,path:q,title:D})===!1&&C&&await Nr({ticketId:H,fieldName:C,currentPath:q,label:D})})});let R=T.querySelector("#parentEpicForm");if(R){let b=R.querySelector("[name=target]"),_=R.querySelector("[data-parent-epic-clear]");nt(b,()=>bs(i));let C=()=>{R.classList.toggle("has-value",b.value.length>0)};b.addEventListener("input",C),C(),_.addEventListener("click",()=>{b.value="",C(),b.focus()}),R.addEventListener("submit",async D=>{D.preventDefault();let H=String(new FormData(D.currentTarget).get("target")||"").trim();if(!H)return;let q=bn(H,i.id);if(!q){y.warning("Pick an epic from the suggestions");return}let ee=Z().find(Y=>Y.id===q);if(!ee||j(ee.type)!=="epic"){y.warning("Parent must be an epic");return}try{await k(A(`/api/tickets/${i.id}`),{method:"PATCH",body:{parent_ticket_id:q}})}catch(Y){y.error(Y.message||"Failed to set parent epic");return}await ie(i.id,{renderBoardAfter:!0}),y.success("Parent epic set")})}let U=T.querySelector("#relatedAddForm");if(U){let b=U.querySelector("[name=target]"),_=U.querySelector("[data-related-clear]");nt(b,()=>fs(n.relations,i));let C=()=>{U.classList.toggle("has-value",b.value.length>0)};b.addEventListener("input",C),C(),_.addEventListener("click",()=>{b.value="",C(),b.focus()}),U.addEventListener("submit",async D=>{D.preventDefault();let H=new FormData(D.currentTarget),q=String(H.get("target")||"").trim(),ee=String(H.get("type")||"relates_to");if(!q)return;let Y=bn(q,i.id);if(!Y){y.warning("Pick a ticket from the suggestions");return}try{await k(A("/api/relations"),{method:"POST",body:{source_ticket_id:i.id,target_ticket_id:Y,type:ee}})}catch(He){y.error(He.message||"Failed to add relation");return}await ie(i.id)})}let M=T.querySelector("#epicChildForm");if(M){let b=M.querySelector("[name=target]"),_=M.querySelector("[data-epic-child-clear]"),C=()=>{M.classList.toggle("has-value",b.value.length>0)};b.addEventListener("input",C),C(),_.addEventListener("click",()=>{b.value="",C(),b.focus()}),nt(b,()=>gs(i)),M.addEventListener("submit",async D=>{D.preventDefault();let H=String(new FormData(D.currentTarget).get("target")||"").trim();if(!H)return;let q=bn(H,i.id);if(!q){y.warning("Pick a ticket from the suggestions");return}let ee=Z().find(Y=>Y.id===q);if(!ee||j(ee.type)==="epic"){y.warning("Epics can't be nested inside an epic");return}try{await k(A(`/api/tickets/${q}`),{method:"PATCH",body:{parent_ticket_id:i.id}})}catch(Y){y.error(Y.message||"Failed to add feature");return}await ie(i.id,{renderBoardAfter:!0}),y.success("Feature added to epic")})}Oa()}function os(e){return localStorage.getItem(`mab_ai_plan_open:${e}`)==="1"}function is(e,t=[]){let a=t.length?`<div class="attachment-grid">${t.map(n=>ss(e,n)).join("")}</div>`:'<p class="description attachment-empty-state">No images attached yet.</p>';return Te({title:"Images",className:"attachments-panel",attrs:`data-attachment-section data-ticket-id="${l(e.id)}"`,body:`
685
- <div class="settings-row">
686
- <div class="attachment-dropzone" data-attachment-dropzone tabindex="0">
687
- <strong>Paste, drag, or upload images</strong>
688
- <span>PNG, JPG, GIF, WebP, SVG, BMP, or AVIF up to 10 MB each.</span>
689
- </div>
690
- ${a}
691
- <label class="attachment-upload-button detail-action-btn" role="button" data-variant="ghost" tabindex="0">
692
- Upload
693
- <input type="file" accept="image/*" multiple data-attachment-input />
694
- </label>
695
- </div>
696
- `})}function ss(e,t){let a=l(t.original_name||"image"),n=`${cs(t.size_bytes)} \xB7 ${l(t.mime_type||"image")}`;if(t.missing)return`
697
- <article class="attachment-card is-missing">
698
- <div class="attachment-missing-thumb">Missing</div>
699
- <div class="attachment-card-meta"><strong>${a}</strong><span>${n}</span></div>
700
- <button type="button" class="attachment-delete" data-delete-attachment="${l(t.id)}" aria-label="Remove missing image">${Q}</button>
701
- </article>
702
- `;let r=A(t.content_url||`/api/tickets/${e.id}/attachments/${t.id}/content`);return`
703
- <article class="attachment-card">
704
- <button type="button" class="attachment-thumb-button" data-no-arc data-open-lightbox="${l(r)}" data-lightbox-title="${a}">
705
- <img src="${l(r)}" alt="${a}" loading="lazy" />
706
- </button>
707
- <div class="attachment-card-meta"><strong>${a}</strong><span>${n}</span></div>
708
- <button type="button" class="attachment-delete" data-delete-attachment="${l(t.id)}" aria-label="Remove image">${Q}</button>
709
- </article>
710
- `}function cs(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 jr(e){return[...e||[]].filter(t=>t&&String(t.type||"").startsWith("image/"))}async function hn(e,t){let a=jr(t);if(!a.length){y.warning("No image files found");return}for(let n of a){let r=await fetch(A(`/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 ie(e.id),y.success(a.length===1?"Image attached":`${a.length} images attached`)}function ls(e){let t=T.querySelector("[data-attachment-section]");if(!t)return;let a=t.querySelector("[data-attachment-input]");a?.addEventListener("change",async()=>{try{await hn(e,a.files),a.value=""}catch(r){y.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 hn(e,r.dataTransfer?.files||[])}catch(o){y.error(o.message||"Upload failed")}}),T.onpaste=async r=>{if(r.target.closest("input, textarea, [contenteditable='true']"))return;let o=jr(r.clipboardData?.files||[]);if(o.length){r.preventDefault();try{await hn(e,o)}catch(s){y.error(s.message||"Paste upload failed")}}},t.querySelectorAll("[data-delete-attachment]").forEach(r=>{r.addEventListener("click",async()=>{try{await k(A(`/api/tickets/${e.id}/attachments/${r.dataset.deleteAttachment}`),{method:"DELETE"}),await ie(e.id),y.success("Image removed")}catch(o){y.error(o.message||"Delete failed")}})}),t.querySelectorAll("[data-open-lightbox]").forEach(r=>{r.addEventListener("click",()=>ds(r.dataset.openLightbox,r.dataset.lightboxTitle||"Image"))})}function ds(e,t){document.querySelector(".attachment-lightbox")?.remove();let n=document.createElement("div");n.className="attachment-lightbox",n.innerHTML=`
711
- <div class="attachment-lightbox-panel" role="dialog" aria-modal="true" aria-label="${l(t)}">
712
- <button type="button" class="attachment-lightbox-close" aria-label="Close image">${Q}</button>
713
- <img src="${l(e)}" alt="${l(t)}" />
714
- <p>${l(t)}</p>
715
- </div>
716
- `;let r=()=>{document.removeEventListener("keydown",o),n.remove()};function o(s){s.key==="Escape"&&r()}n.addEventListener("click",s=>{(s.target===n||s.target.closest(".attachment-lightbox-close"))&&r()}),document.addEventListener("keydown",o),document.body.append(n)}function bn(e,t){let a=e.trim(),n=a.match(/^#?(\d+)\b/),r=Z().filter(s=>s.id!==t);if(n){let s=Number(n[1]),i=r.find(d=>d.number===s);if(i)return i.id}let o=r.find(s=>`#${s.number} \u2014 ${s.title}`===a);return o?o.id:""}function us(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(cn({actor:t.actor,actorType:a.actor_type||"human",actorId:a.actor_id}));return`<div class="comment status-change">
717
- <div class="comment-meta">
718
- <span class="status-route">${n} \u2192 ${r}</span>
719
- <span>${o} \xB7 ${pt(t.created_at)}</span>
720
- </div>
721
- </div>`}).join("")}function ps(e){let t=W()?.ai_enabled!==0,a=!!e.omitted_for_ai,n=e.kind==="human_comment"?"":l(e.kind.replace(/_/g," "));return`
722
- <div class="comment ${e.kind==="checkpoint"?"checkpoint":""} ${t&&a?"is-ai-omitted":""}">
723
- <div class="comment-head">
724
- <div class="comment-head-main">
725
- <div class="comment-head-title">
726
- <strong>${l(Pr(e))}</strong>
727
- ${n?`<span class="comment-kind-pill">${n}</span>`:""}
728
- </div>
729
- </div>
730
- </div>
731
- <div class="comment-body preserved-text-body">${me(e.body)}</div>
732
- <div class="comment-footer">
733
- <span class="comment-footer-meta">${pt(e.created_at)}</span>
734
- ${t?`<button
735
- type="button"
736
- class="comment-ai-toggle detail-action-btn"
737
- data-variant="ghost"
738
- data-comment-action="toggle-ai-omission"
739
- data-comment-id="${l(e.id)}"
740
- data-omitted-for-ai="${a?"true":"false"}"
741
- >${a?"AI Include":"AI Ignore"}</button>`:""}
742
- </div>
743
- </div>
744
- `}function sa({fieldName:e,label:t,value:a,placeholder:n,ticketId:r,artifactPath:o,artifactFieldName:s,artifactLabel:i}){let d=a||"",m=!!d.trim(),u=String(o||"").trim(),f=!!(i||s),h=m?me(d):l(n||""),p=m?"":"is-placeholder",g=f?`<button
745
- type="button"
746
- class="detail-action-btn"
747
- data-variant="ghost"
748
- data-open-artifact="${l(u)}"
749
- data-artifact-field="${l(s||"")}"
750
- data-artifact-ticket-id="${l(r||"")}"
751
- data-artifact-title="${l(t)}"
752
- >${l(i||"Open full artifact")}</button>`:"";return`
753
- <div class="inline-md-field">
754
- <div class="inline-md-field-head">
755
- <span class="inline-md-field-label">${l(t)}</span>
756
- ${g}
757
- </div>
758
- <div
759
- class="inline-md-field-body preserved-text-body editable-field ${p}"
760
- data-edit-field="${l(e)}"
761
- tabindex="0"
762
- title="Click to edit"
763
- role="button"
764
- aria-label="Edit ${l(t)}"
765
- >${h}</div>
766
- </div>
767
- `}function fs(e=[],t){let a=new Set(e.map(r=>r.other_ticket.id)),n=new Set;if(t?.parent_ticket_id&&n.add(t.parent_ticket_id),t?.id)for(let r of Z())r.parent_ticket_id===t.id&&n.add(r.id);return Z().filter(r=>r.id!==t?.id&&!a.has(r.id)&&!n.has(r.id))}function ms(e=[],t){let a=e.length?`<div class="detail-card-grid">${e.map(r=>xs(r)).join("")}</div>`:"",n=`
768
- <form id="relatedAddForm" class="related-add-row">
769
- <div class="related-add-input-wrap">
770
- <input type="text" name="target" class="related-add-input meta-inline-input" placeholder="Add related ticket\u2026" aria-label="Add related ticket" autocomplete="off" />
771
- <button type="button" class="related-add-clear" data-related-clear aria-label="Clear" tabindex="-1">${Q}</button>
772
- </div>
773
- <select name="type" class="related-add-type meta-inline" aria-label="Relation type">
774
- <option value="relates_to">relates to</option>
775
- <option value="blocks">blocks</option>
776
- <option value="blocked_by">blocked by</option>
777
- </select>
778
- <button type="submit" class="meta-add-btn detail-action-btn" data-variant="ghost">Add</button>
779
- </form>
780
- `;return a+n}function hs(e,t){if(!(j(e.type)==="epic"))return"";let n=t.child_tickets||[],r=n.length?`<div class="detail-card-grid">${n.map(s=>tt(s,{detach:{mode:"child",id:s.id}})).join("")}</div>`:"",o=`
781
- <form id="epicChildForm" class="related-add-row epic-child-form">
782
- <div class="related-add-input-wrap">
783
- <input type="text" name="target" class="related-add-input meta-inline-input" placeholder="Add feature to epic\u2026" aria-label="Add feature to epic" autocomplete="off" />
784
- <button type="button" class="related-add-clear" data-epic-child-clear aria-label="Clear" tabindex="-1">${Q}</button>
785
- </div>
786
- <button type="submit" class="meta-add-btn detail-action-btn" data-variant="ghost">Add</button>
787
- </form>
788
- `;return Te({title:"Features inside this Epic",body:`<div class="settings-row">${r}${o}</div>`})}function bs(e){return Z().filter(t=>t.type==="epic"&&t.id!==e.id)}function gs(e){return Z().filter(t=>t.id!==e.id&&j(t.type)!=="epic"&&t.parent_ticket_id!==e.id)}function ys(e){let t=new Set((e.labels||[]).map(a=>a.name));return Ia().filter(a=>!t.has(a.name))}function vs(e){return`
789
- <span class="search-hit-main">
790
- <span class="typeahead-swatch" style="--label-color: ${l(e.color||"var(--muted)")}"></span>
791
- <strong>${l(e.name)}</strong>
792
- </span>
793
- `}function ws(e,t){e&&ia(e,{getItems:()=>ys(t),match:(a,n)=>a.name.toLowerCase().includes(n),renderItem:vs,valueOf:a=>a.name,emptyText:"No matching labels"})}function ks(e,t){if(j(e.type)==="epic")return"";let a=t.parent_ticket?`<div class="detail-card-grid detail-card-grid--single">${tt(t.parent_ticket,{detach:{mode:"parent"}})}</div>`:`
794
- <form id="parentEpicForm" class="related-add-row parent-epic-form">
795
- <div class="related-add-input-wrap">
796
- <input type="text" name="target" class="related-add-input meta-inline-input" placeholder="Set parent epic\u2026" aria-label="Set parent epic" autocomplete="off" />
797
- <button type="button" class="related-add-clear" data-parent-epic-clear aria-label="Clear" tabindex="-1">${Q}</button>
798
- </div>
799
- <button type="submit" class="meta-add-btn detail-action-btn" data-variant="ghost">Set</button>
800
- </form>
801
- `;return Te({title:"Epic",body:`<div class="settings-row">${a}</div>`})}function tt(e,t={}){let a=j(e.type),n=Ye(e.priority),r=Be(e),o=$e(e.type).toUpperCase(),s=t.eyebrow?`<span class="card-eyebrow">${l(t.eyebrow)}</span>`:"",i="";if(t.detach){let{mode:u,id:f}=t.detach,h="",p="";u==="child"?(h=`data-detach-child="${l(f)}"`,p="Remove from epic"):u==="parent"?(h='data-detach-parent="1"',p="Remove parent epic"):u==="relation"&&(h=`data-detach-relation="${l(f)}"`,p="Remove relation"),i=`<button type="button" class="card-detach-btn" ${h} aria-label="${p}" title="${p}">${Q}</button>`}else if(t.archive){let u=l(t.archive.id);i=`
802
- <div class="card-archive-actions">
803
- <button type="button" class="card-archive-btn card-archive-btn--restore" data-archive-restore="${u}" 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>
804
- <button type="button" class="card-archive-btn card-archive-btn--delete" data-archive-delete="${u}" 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>
805
- </div>
806
- `}let d=Kt(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`
807
- <article class="card card--detail type-${l(a)} priority-${l(n)}" data-variant="${l(a)}"${t.disableOpen?"":` data-open-ticket="${l(e.id)}"`}>
808
- ${m}
809
- ${s}
810
- <h3>${l(e.title)}</h3>
811
- <div class="card-meta">
812
- <div class="card-expand-trigger card-expand-trigger--static">
813
- <span class="ticket-number">${l(ce(e))}</span>
814
- <div class="card-type-id type-pill-${l(a)}">
815
- ${Xt(e.type)}
816
- <span class="card-type-label">${l(o)}</span>
817
- </div>
818
- <span class="detail-card-state" data-variant="${l(r)}">${l(e.state_name||"State")}</span>
819
- ${c.showPriority?Ja(e.priority):""}
820
- ${i}
821
- </div>
822
- </div>
823
- </article>
824
- `}function xs(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 oa=B(()=>{K();pe();te();xt();Ht();be();ae();Re();le();_r();wt();Mr();Dr();ft();Ba();Or();pn();mn()});function gn(){rt&&(clearTimeout(rt),rt=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",()=>{ca=1e3,la&&(y.success("Live updates reconnected"),la=!1)}),a.addEventListener("board",async n=>{let r;try{r=JSON.parse(n.data)}catch{return}let o=r.type,s=()=>c.data?.tickets?.find(m=>m.id===r.ticket_id),i=c.data?.actor?.name,d=r.actor&&i&&r.actor===i;if(r.ticket_id&&(d?Jt(r.ticket_id,r.timestamp):o==="ticket_deleted"?ar(r.ticket_id):$s.has(o)&&tr(r.ticket_id)),o==="ticket_created")y.success(`Card created: ${r.data.title||"New card"}`),we();else if(o==="state_changed"){let m=s(),u=m?`#${m.number} ${m.title}`:"Card";y.info(`${u} \u2192 ${r.data.to}`),we()}else if(o==="ticket_updated"){let m=s(),u=m?`#${m.number}`:"Card",f=r.data.fields?Object.keys(r.data.fields).join(", "):"";y.info(`${u} updated${f?`: ${f}`:""}`),we()}else if(o==="comment_created"){let m=s(),u=m?`#${m.number}`:"ticket";y.info(`New comment on ${u}`),we()}else if(o==="comment_ai_omission_changed")we();else if(o==="agent_claimed"){let m=s();y.info(`Agent claimed ${m?`#${m.number} ${m.title}`:"a ticket"}`),we()}else if(o==="checkpoint_requested"){let m=s();y.warning(`Checkpoint on ${m?`#${m.number}`:"ticket"} \u2014 needs human input`),we()}else if(o==="agent_completed"){let m=s();y.success(`Agent completed ${m?`#${m.number} ${m.title}`:"a ticket"}`),we()}else(o==="ticket_deleted"||o==="states_reordered"||o==="state_created"||o==="state_deleted"||o==="board_updated"||o==="board_imported")&&we()}),a.onerror=()=>{if(a.readyState!==EventSource.CLOSED||(la||(y.warning("Live updates disconnected \u2014 retrying\u2026"),la=!0),rt))return;let n=ca;ca=Math.min(ca*2,3e4),rt=setTimeout(()=>{rt=null,gn()},n)}}var $s,ca,rt,la,we,Hr=B(()=>{K();ae();le();Se();wt();$s=new Set(["ticket_created","state_changed","ticket_updated","comment_created","comment_ai_omission_changed","agent_claimed","checkpoint_requested","agent_completed"]),ca=1e3,rt=null,la=!1;we=Ot(async()=>{let e=c.selectedTicketId,t=c.detailMode;c.data=await k(mt()),Pe(),c.selectedTicketId=e,c.detailMode=t,await da()},300)});function Es(e){return re(e||"").split(`
825
- `).map(t=>t.trim()).filter(Boolean).join(" ").replace(/\s+/g," ").trim()}function Ls(e={}){return Es(e.project_notes)||yn}function Ts(e,t){if(!e?.id||!t?.data?.boards)return;let a=t.data.boards.findIndex(n=>n.id===e.id);a>=0&&t.data.boards.splice(a,1,{...t.data.boards[a],...e})}function Is(e){let t=e?.project_notes||"",a=!!t.trim(),n=a?"":" is-placeholder",r=a?me(t):l(Ss);return`
826
- <div
827
- class="inline-md-field-body preserved-text-body editable-field settings-notes-body brand-focus-notes-edit${n}"
828
- data-edit-field="project_notes"
829
- tabindex="0"
830
- title="Click to edit"
831
- role="button"
832
- aria-label="Edit Notes For You"
833
- >${r}</div>
834
- `}function ot(e=se){se=e||{};let t=Ls(se),a=document.getElementById("brandNotesBtn"),n=document.getElementById("brandNotesText"),r=document.getElementById("brandFocusNotes");a&&(a.hidden=!1,a.dataset.empty=t===yn?"true":"false",a.setAttribute("aria-label",`Show board notes: ${t}`)),n&&(n.textContent=t===yn?"":t),r&&(r.innerHTML=Is(se))}function St(e){let t=[document.getElementById("brandFocusBtn"),document.getElementById("brandNotesBtn")].filter(Boolean),a=document.getElementById("brandFocusPopover");if(a){for(let n of t)n.setAttribute("aria-expanded",e?"true":"false"),n.dataset.state=e?"open":"closed";a.hidden=!e}}function qr(){let e=document.getElementById("brandFocusPopover");ot(),St(!!e?.hidden)}async function zr(e){let t=document.querySelector('#brandFocusPopover [data-edit-field="project_notes"]');if(!t||!se?.id)return;let{startInlineEdit:a}=await Promise.resolve().then(()=>(oa(),Ur));a(t,{fieldName:"project_notes",multiline:!0,initialValue:se.project_notes||"",ariaLabel:"Edit Notes For You",sourceEvent:e,rerender:async()=>{ot(se),St(!0)},commit:async n=>{let[{api:r},{state:o}]=await Promise.all([Promise.resolve().then(()=>(ae(),qn)),Promise.resolve().then(()=>(K(),Fn))]),s=await r(`/api/boards/${encodeURIComponent(se.id||o.boardId)}`,{method:"PATCH",body:{project_notes:re(n)}});se={...se,...s},Ts(se,o),ot(se),St(!0)}})}function Vr(){let e=document.getElementById("brandFocusBtn"),t=document.getElementById("brandNotesBtn"),a=document.getElementById("brandFocusPopover");!e||!t||!a||(e.addEventListener("click",n=>{n.stopPropagation(),qr()}),t.addEventListener("click",n=>{n.stopPropagation(),qr()}),a.addEventListener("click",n=>{n.stopPropagation(),n.target.closest('[data-edit-field="project_notes"]')&&zr(n)}),a.addEventListener("keydown",n=>{n.key!=="Enter"&&n.key!==" "||n.target.closest('[data-edit-field="project_notes"]')&&(n.preventDefault(),zr(n))}),document.addEventListener("click",n=>{n.target.closest("#brandFocusBtn")||n.target.closest("#brandNotesBtn")||n.target.closest("#brandFocusPopover")||St(!1)}),document.addEventListener("keydown",n=>{n.key==="Escape"&&St(!1)}))}var yn,Ss,se,vn=B(()=>{te();yn="No board notes yet",Ss="Add board notes for yourself and your agents\u2026";se={}});var Wr={};It(Wr,{load:()=>F,render:()=>da,renderDetailOnly:()=>_s});async function F(){fe(),c.data=await k(mt()),Pe(),nr(c.boardId,c.data?.tickets||[]),await da(),z.sse&&c.boardId!==Gr&&(Gr=c.boardId,gn())}async function da(){Pe(),wn(),ot(W()),Xe(),await Ie()}async function _s(){Pe(),wn(),ot(W()),or(),await Ie()}var Gr,Se=B(()=>{K();ae();xt();oa();Et();Hr();wt();Re();vn();Gr=null});function xn(){return qe?.classList.contains("is-open")}function Zt(e){clearTimeout(kn),fe(),jt(),ga.innerHTML=Cs(e||""),As(),w("#createFlyoutClose")?.addEventListener("click",()=>G()),qe.hidden=!1,Ce.hidden=!1,requestAnimationFrame(()=>{qe.classList.add("is-open"),Ce.classList.add("is-visible")}),requestAnimationFrame(()=>{w("#createTicketTitle")?.focus()})}function G(){clearTimeout(kn),qe.classList.remove("is-open"),Ce.classList.remove("is-visible"),kn=setTimeout(()=>{qe.hidden=!0,Ce.hidden=!0,ga.innerHTML=""},220)}function Cs(e){let t=Me(),a=t.find(o=>o.is_default)||t[0],n=e&&t.some(o=>o.id===e)?e:a?.id||"",r=t.map(o=>`<option value="${l(o.id)}" ${o.id===n?"selected":""}>${l(o.name)}</option>`).join("");return`
835
- <div class="create-flyout-head">
836
- <h2>New card</h2>
837
- <button type="button" class="create-flyout-close ghost" id="createFlyoutClose" aria-label="Close">${Q}</button>
838
- </div>
839
- <form id="createTicketForm" class="create-flyout-form">
840
- <input id="createTicketTitle" name="title" type="text" placeholder="Title \u2014 what is this card?" autocomplete="off" required />
841
- <textarea id="createTicketDescription" name="description" placeholder="Description (optional)"></textarea>
842
- <div class="control-grid">
843
- <label class="control-field">
844
- <span class="control-field-label">LANE</span>
845
- <select id="createTicketState" name="state" class="select-chevron-field" aria-label="Lane">${r}</select>
846
- </label>
847
- <label class="control-field">
848
- <span class="control-field-label">TYPE</span>
849
- <select id="createTicketType" name="type" class="select-chevron-field" aria-label="Ticket type">
850
- <option value="epic">Epic</option>
851
- <option value="feature">Feature</option>
852
- <option value="task" selected>Task</option>
853
- <option value="bug">Bug</option>
854
- </select>
855
- </label>
856
- <label class="control-field">
857
- <span class="control-field-label">PRIORITY</span>
858
- <select id="createTicketPriority" name="priority" class="select-chevron-field" aria-label="Priority">
859
- <option value="4">URGENT</option>
860
- <option value="3">HIGH</option>
861
- <option value="2" selected>MED</option>
862
- <option value="1">LOW</option>
863
- <option value="0">MAYBE</option>
864
- </select>
865
- </label>
866
- </div>
867
- <label class="control-field" id="createTicketParentField">
868
- <span class="control-field-label">Parent epic</span>
869
- <input id="createTicketParent" name="parent_target" type="text" placeholder="Search epic\u2026 (optional)" autocomplete="off" aria-label="Parent epic" />
870
- <input type="hidden" name="parent_ticket_id" id="createTicketParentId" value="" />
871
- </label>
872
- <input id="createTicketLabels" name="labels" type="text" placeholder="Labels (comma-separated)" />
873
- <button type="submit">Create card</button>
874
- </form>
875
- `}function As(){let e=w("#createTicketForm");if(!e)return;let t=e.querySelector("#createTicketDescription");t?.addEventListener("keydown",i=>{ht(i,t)});let a=e.querySelector("#createTicketParent"),n=e.querySelector("#createTicketParentId");a.addEventListener("input",()=>{n.value=""}),nt(a,()=>Z().filter(i=>i.type==="epic"),{onSelect:i=>{n.value=i.id,a.value=fn(i)}});let r=e.querySelector("#createTicketType"),o=e.querySelector("#createTicketParentField"),s=()=>{let i=r.value==="epic";o.hidden=i,i&&(a.value="",n.value="")};r.addEventListener("change",s),s(),e.addEventListener("submit",Ps)}async function Ps(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 k("/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}});G(),c.selectedTicketId=r.id,c.detailMode="ticket",await F(),it()}var kn,Ze=B(()=>{pe();K();te();ae();be();Ba();Ht();Et();Se();ft();mn();kn=0});function st(){V&&(V.hidden=!0,V.innerHTML="",Ue="list",ke?.setAttribute("aria-expanded","false"))}function fe(){st()}function wn(){if(!c.data)return;let e=(c.data.boards||[]).find(t=>t.id===c.boardId);if(ke){let t=e?.name||"Board";ke.innerHTML=`${Ms}<span class="topbar-chip-label">${l(t)}</span>`,ke.title=e?`${e.name} \u2014 slug: ${e.slug}`:"Choose board"}}async function Bs(e){if(typeof DecompressionStream>"u")throw new Error("Compressed ZIP entries are not supported in this browser.");let t=new Blob([e]).stream().pipeThrough(new DecompressionStream("deflate-raw"));return new Uint8Array(await new Response(t).arrayBuffer())}function Kr(e,t="snapshot"){if(String(e||"").trimStart().startsWith("<"))throw new Error(`${t} looks like HTML instead of JSON.`);try{let n=JSON.parse(e);if(!n?.board)throw new Error("snapshot_missing_board");return n}catch(n){throw new Error(`${t}: ${n?.message||"invalid JSON"}`)}}async function Fs(e){let t=await e.arrayBuffer(),a=new DataView(t),n=new Uint8Array(t),r=new TextDecoder,o=[],s=0;for(;s+30<=n.length;){let i=a.getUint32(s,!0);if(i===33639248||i===101010256)break;if(i!==67324752)throw new Error("Invalid ZIP archive.");let d=a.getUint16(s+6,!0),m=a.getUint16(s+8,!0),u=a.getUint32(s+18,!0),f=a.getUint16(s+26,!0),h=a.getUint16(s+28,!0),p=s+30,g=p+f+h,x=g+u;if(d&8)throw new Error("ZIP entries with data descriptors are not supported.");if(x>n.length)throw new Error("Invalid ZIP archive.");let E=r.decode(n.slice(p,p+f));if(!E.endsWith("/")){if(!E.toLowerCase().endsWith(".orbit.json"))throw new Error("ZIP imports may contain only .orbit.json files.");let P=n.slice(g,x),v=m===0?P:m===8?await Bs(P):null;if(!v)throw new Error("Unsupported ZIP compression method.");o.push(Kr(r.decode(v),E))}s=x}if(!o.length)throw new Error("ZIP archive did not contain any .orbit.json snapshots.");return o}async function Ds(e){let t=String(e?.name||"").toLowerCase();if(t.endsWith(".zip"))return Fs(e);if(t.endsWith(".orbit.json"))return[Kr(await e.text(),e.name)];throw new Error("Choose a .zip archive or a single .orbit.json snapshot.")}function Rs(){let e=Ue!=="list",t=e?"List":"New";return`
876
- <div class="menu-flyout-head menu-flyout-head--actions">
877
- <span>Board</span>
878
- <span class="menu-flyout-actions">
879
- ${z.multiBoard?`<button type="button" data-variant="ghost" data-board-view="${e?"list":"new"}">${t}</button>`:""}
880
- <button type="button" data-variant="ghost" data-board-view="export">Export</button>
881
- </span>
882
- </div>`}function Jr(){let n=`<div class="menu-flyout-list">${(c.data.boards||[]).map(i=>`
883
- <button type="button" class="menu-flyout-item ${i.id===c.boardId?"is-current":""}" data-variant="menu-item" data-pick-board="${l(i.id)}" role="menuitem">
884
- <span class="menu-flyout-item-title">${l(i.name)}</span>
885
- <span class="menu-flyout-item-meta">${l(i.slug)}</span>
886
- </button>`).join("")||'<p class="menu-flyout-hint menu-flyout-hint--empty">No boards yet. Create one below if you can.</p>'}</div>`,r=`
887
- <div class="menu-flyout-section">
888
- <h3 class="menu-flyout-heading">New Board</h3>
889
- <form id="createBoardForm" class="menu-flyout-form">
890
- <input name="name" type="text" required placeholder="Board name" autocomplete="off" />
891
- <div class="folder-picker-field">
892
- <input id="repoPathInput" name="repo_path" type="text" readonly placeholder="Coding project folder (optional)" autocomplete="off" />
893
- <button type="button" id="pickRepoFolderBtn">Browse</button>
894
- </div>
895
- <div id="helperFilesReveal" hidden>
896
- <p class="menu-flyout-hint menu-flyout-hint--intro">Orbit writes <code>SKILL-ORBIT.md</code> and <code>AGENTS.md</code> into this folder for your AI coding agent.</p>
897
- <label class="menu-flyout-check">
898
- <input id="helperFilesCheckbox" name="manage_helper_files" type="checkbox" checked />
899
- <span>Generate AI helper files (<code>SKILL-ORBIT.md</code>, <code>AGENTS.md</code>)</span>
900
- </label>
901
- </div>
902
- <input name="slug" type="text" placeholder="Slug (optional)" autocomplete="off" />
903
- <button type="submit">Create board</button>
904
- </form>
905
- </div>`,o=`
906
- <div class="menu-flyout-section menu-flyout-section--export">
907
- <p class="menu-flyout-copy">Export every board as Orbit snapshots in a ZIP archive.</p>
908
- ${z.attachments?`
909
- <label class="orbit-check menu-flyout-toggle">
910
- <input type="checkbox" id="exportAllBoardImages" />
911
- <span class="orbit-check-box" aria-hidden="true">
912
- <svg viewBox="0 0 16 16" class="orbit-check-tick"><path d="M3.5 8.5l3 3 6-7" /></svg>
913
- </span>
914
- <span class="menu-flyout-toggle-text">Include attached images</span>
915
- </label>
916
- `:""}
917
- <div class="menu-flyout-actions-row">
918
- <button type="button" id="exportAllBoards">Export All</button>
919
- </div>
920
- <div class="menu-flyout-divider" aria-hidden="true"></div>
921
- <section class="menu-flyout-danger-section" aria-label="Import All">
922
- <h3 class="menu-flyout-danger-heading">Import All</h3>
923
- <p class="menu-flyout-danger-copy">Imports fully overwrite each matching board. Snapshots without a matching board create new boards.</p>
924
- <div class="menu-flyout-actions-row">
925
- <input type="file" id="importAllBoardsFile" class="import-file-input" accept=".zip,.orbit.json,application/zip" />
926
- <button type="button" data-arc="danger" id="importAllBoards">Import All</button>
927
- </div>
928
- </section>
929
- </div>`,s=Ue==="export"?o:Ue==="new"&&z.multiBoard?r:n;return`
930
- ${Rs()}
931
- ${s}
932
- `}function Os(e){Ue=e,V.innerHTML=Jr(),Yr(),e==="new"&&V.querySelector('input[name="name"]')?.focus()}function Yr(){V.querySelectorAll("[data-board-view]").forEach(a=>{a.addEventListener("click",n=>{n.stopPropagation();let r=a.getAttribute("data-board-view")||"list";Os(r)})}),V.querySelectorAll("[data-pick-board]").forEach(a=>{a.addEventListener("click",async n=>{n.stopPropagation();let r=a.getAttribute("data-pick-board");st(),r!==c.boardId&&(G(),await O({boardId:r,view:"board"}))})});let e=V.querySelector("#createBoardForm");if(e){let a=e.querySelector("#pickRepoFolderBtn"),n=e.querySelector("#repoPathInput"),r=e.querySelector("#helperFilesReveal"),o=()=>{r&&(r.hidden=!n.value.trim())};o(),a?.addEventListener("click",async s=>{s.preventDefault(),a.disabled=!0;try{let i=await k("/api/system/pick-folder",{method:"POST"});i.path&&(n.value=i.path,n.dispatchEvent(new Event("input",{bubbles:!0})),o())}catch(i){y.error(i?.message||"Folder picker unavailable")}finally{a.disabled=!1}}),e.addEventListener("submit",async s=>{s.preventDefault();let i=new FormData(e),d=String(i.get("slug")||"").trim(),m=String(i.get("repo_path")||"").trim(),u=await k("/api/boards",{method:"POST",body:{name:i.get("name"),...m?{repo_path:m,manage_helper_files:i.get("manage_helper_files")==="on"}:{},...d?{slug:d}:{}}});st(),c.boardId=u.id,c.selectedTicketId="",c.detailMode="settings",await F(),it(),y.success("Board created")})}V.querySelector("#exportAllBoards")?.addEventListener("click",async a=>{let n=a.currentTarget,r=!!V.querySelector("#exportAllBoardImages")?.checked;n.disabled=!0;let o=n.textContent;n.textContent="Exporting...";try{await ea({includeAttachments:r})}catch(s){y.error(`Export failed: ${s?.message||"request failed"}`)}finally{n.disabled=!1,n.textContent=o}});let t=V.querySelector("#importAllBoardsFile");V.querySelector("#importAllBoards")?.addEventListener("click",()=>{t?.click()}),t?.addEventListener("change",async a=>{let n=a.currentTarget,r=n.files?.[0];if(!r)return;let o=V.querySelector("#importAllBoards"),s=o?.textContent;o&&(o.disabled=!0,o.textContent="Importing...");try{let i=await Ds(r),d=new Map((c.data.boards||[]).map(h=>[h.id,h])),m=new Map((c.data.boards||[]).map(h=>[h.slug,h])),u=0,f=0;for(let h of i){let p=h.board||{},g=m.get(p.slug)||d.get(p.id);if(g){await k("/api/admin/import",{method:"POST",body:{board_id:g.id,replace_existing:!0,snapshot:h}}),u+=1;continue}let x=await k("/api/admin/import",{method:"POST",body:{...c.boardId?{board_id:c.boardId}:{},create_new:!0,snapshot:h}});f+=1,x.imported_board_id&&d.set(x.imported_board_id,{id:x.imported_board_id,slug:x.imported_board_slug}),x.imported_board_slug&&m.set(x.imported_board_slug,{id:x.imported_board_id,slug:x.imported_board_slug})}await F(),y.success(`Imported ${i.length} board${i.length===1?"":"s"} (${u} replaced, ${f} created)`),st(),await O({boardId:c.boardId,view:"board"},{replace:!0})}catch(i){y.error(`Import failed: ${i?.message||"invalid import file"}`)}finally{n.value="",o&&(o.disabled=!1,o.textContent=s)}})}function Xr(){G(),Ue==="new"&&!z.multiBoard&&(Ue="list"),V.innerHTML=Jr(),V.hidden=!1,ke?.setAttribute("aria-expanded","true"),Yr()}var Ue,Ms,Et=B(()=>{pe();K();te();ae();be();Ze();le();Se();Re();Qa();Ue="list";Ms=`
933
- <svg
934
- class="topbar-icon"
935
- width="16"
936
- height="16"
937
- viewBox="0 0 24 24"
938
- fill="none"
939
- aria-hidden="true"
940
- >
941
- <rect x="4" y="2" width="6" height="20" rx="0.75" stroke="currentColor" stroke-width="2" />
942
- <rect x="14" y="2" width="6" height="20" rx="0.75" stroke="currentColor" stroke-width="2" />
943
- </svg>`});function Zr(){return{...Ns}}function Sn(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 $n(e){try{return decodeURIComponent(e)}catch{return e}}function js(e){return String(e||"").replace(/^#/,"").replace(/^\/+/,"").replace(/\/+$/,"").split("/").filter(Boolean)}function Us(e){if(!e.length)return null;let t=Zr(),a=0;if(e[a]==="b"){if(!e[a+1])return null;t.boardSlug=$n(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=$n(e[a+1]);else if(e[a]==="settings"){t.view="settings";let n=e[a+1]?$n(e[a+1]):"";n&&Bt.has(n)&&(t.tab=n)}return t}function Qr(e){let t=String(e||"");return!t||t==="#"||t==="#/"?null:Us(js(t))}function ua(e=globalThis.location){let t=Sn(e);return Qr(t.hash)||Zr()}function Lt(e=globalThis.location){let t=Sn(e);return!!Qr(t.hash)}function ct({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&&Bt.has(n)&&r.push(n)),"#/"+r.join("/")}function pa(e=globalThis.location,t=ua(e)){return Sn(e).hash===ct(t)}var Ns,En=B(()=>{Ft();Ns=Object.freeze({boardSlug:"",view:"board",ticketId:"",tab:""})});function eo(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 Hs(e={}){return e.boardSlug?e.boardSlug:e.boardId?eo(e.boardId)?.slug||e.boardId:W()?.slug||c.boardSlug||""}function qs(e={}){return{...e,boardSlug:Hs(e)}}function zs(){let e="board";return c.detailMode==="ticket"&&c.selectedTicketId?e="ticket":c.detailMode==="settings"&&(e="settings"),{boardSlug:W()?.slug||c.boardSlug,view:e,ticketId:c.selectedTicketId,tab:e==="settings"?dt(c.settingsTab):""}}function to(e){fa=e}function Ln(){return ua(location)}async function O(e,{replace:t=!1}={}){let a=qs(e),n=ct(a);if(pa(location,a)){fa&&await Tt();return}t?history.replaceState(null,"",n):history.pushState(null,"",n),fa&&await Tt()}function it({replace:e=!1}={}){let t=zs(),a=ct(t);pa(location,t)||(e?history.replaceState(null,"",a):history.pushState(null,"",a))}async function Tt(){let{load:e,renderDetailOnly:t}=await Promise.resolve().then(()=>(Se(),Wr)),a=Ln(),n=Lt(location),r=c.boardId,s=eo(a.boardSlug)||(a.boardSlug?W():null);s?(c.boardId=s.id,c.boardSlug=s.slug):a.boardSlug&&(c.boardSlug=a.boardSlug);let i=s?{...a,boardSlug:s.slug}:a;if(n&&!pa(location,i)&&history.replaceState(null,"",ct(i)),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(fe(),G(),c.boardId&&c.boardId!==r){await e();return}await t()}var fa,be=B(()=>{K();Ft();Et();Ze();En();En();fa=!1;window.addEventListener("popstate",()=>{fa&&Tt()})});pe();ka();Ea();Pn(),lt();var co=lo();La();{let e=()=>{document.body.classList.toggle("is-scrolled",window.scrollY>4)};window.addEventListener("scroll",e,{passive:!0}),e()}Mn();Tn?.addEventListener("click",()=>{Mt()});window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",e=>{Pt()||Sa(e.matches?"dark":"light",{notify:!0})});window.addEventListener(At,()=>{Mn(),co.sync()});function Mn(){if(!ya)return;let e=Ae()==="dark";ya.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 lo(){let e=document.querySelector(".grain");if(!e)return{sync:()=>{}};uo(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,s=0,i=[],d=0,m=!1,u={x:0,y:0,tx:0,ty:0};function f(){o=window.innerWidth,s=window.innerHeight,t.width=Math.round(o*r),t.height=Math.round(s*r),t.style.width=o+"px",t.style.height=s+"px",n.setTransform(r,0,0,r,0,0),h()}function h(){let $=o*s/1296e3,S=[{count:Math.round(320*$),depth:.12,sizes:[[1,.9],[1.4,.1]],glow:0},{count:Math.round(160*$),depth:.3,sizes:[[1,.5],[1.6,.4],[2.4,.1]],glow:1.8},{count:Math.round(44*$),depth:.58,sizes:[[1.8,.5],[2.6,.38],[3.6,.12]],glow:2.4}],R=Bn(12648430);i=[];for(let U of S)for(let M=0;M<U.count;M++){let b=fo(R(),U.sizes);i.push({x:R()*o,y:R()*s,size:b,depth:U.depth,glow:b>=2?U.glow:0,rgb:p(R()),period:2.2+R()*5.4,phase:R()*Math.PI*2,amp:.25+R()*.5,baseAlpha:b<1.5?.45+R()*.28:.55+R()*.22})}}function p($){return $<.8?[255,255,255]:$<.93?[255,222,186]:[195,218,255]}function g($){!o||!s||(u.tx=$.clientX/o-.5,u.ty=$.clientY/s-.5)}function x($){u.x+=(u.tx-u.x)*.06,u.y+=(u.ty-u.y)*.06,n.clearRect(0,0,o,s),n.globalCompositeOperation="lighter";let S=$/1e3,R=70;for(let U=0;U<i.length;U++){let M=i[U],b=.5+.5*Math.sin(S*(2*Math.PI/M.period)+M.phase),_=M.baseAlpha*(1-M.amp+M.amp*b),C=M.x-u.x*M.depth*R,D=M.y-u.y*M.depth*R,[H,q,ee]=M.rgb;if(M.glow>0){let Y=M.size*M.glow,He=n.createRadialGradient(C,D,0,C,D,Y);He.addColorStop(0,`rgba(${H},${q},${ee},${_*.85})`),He.addColorStop(.35,`rgba(${H},${q},${ee},${_*.3})`),He.addColorStop(1,`rgba(${H},${q},${ee},0)`),n.fillStyle=He,n.beginPath(),n.arc(C,D,Y,0,Math.PI*2),n.fill(),n.fillStyle=`rgba(${H},${q},${ee},${_})`,n.beginPath(),n.arc(C,D,M.size/2,0,Math.PI*2),n.fill()}else{let Y=M.size/2;n.fillStyle=`rgba(${H},${q},${ee},${_})`,n.fillRect(C-Y,D-Y,M.size,M.size)}}n.globalCompositeOperation="source-over",m&&(d=requestAnimationFrame(x))}function E(){n.clearRect(0,0,o,s),n.globalCompositeOperation="lighter";for(let $ of i){let[S,R,U]=$.rgb;if(n.fillStyle=`rgba(${S},${R},${U},${$.baseAlpha})`,$.glow>0)n.beginPath(),n.arc($.x,$.y,$.size/2,0,Math.PI*2),n.fill();else{let M=$.size/2;n.fillRect($.x-M,$.y-M,$.size,$.size)}}n.globalCompositeOperation="source-over"}function P(){m||(m=!0,d=requestAnimationFrame(x))}function v(){m=!1,cancelAnimationFrame(d)}function I(){let $=Ae()==="dark",S=Ve();if(!$){v(),n.clearRect(0,0,o,s);return}i.length||f(),S||document.hidden?(v(),E()):P()}return window.addEventListener("resize",()=>{f(),I()},{passive:!0}),window.addEventListener("pointermove",g,{passive:!0}),document.addEventListener("visibilitychange",I),window.matchMedia("(prefers-reduced-motion: reduce)").addEventListener("change",()=>{lt(),I()}),window.addEventListener("orbit:motion-preference-change",I),f(),I(),{sync:I}}function uo(e){e.querySelectorAll(".signature-star").forEach(n=>n.remove());let t=Bn(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 La(){let e=14e3+Math.random()*28e3;setTimeout(po,e)}function po(){let e=Ae()==="dark",t=Ve(),a=document.querySelector(".grain");if(!e||t||document.hidden||!a){La();return}let n=window.innerWidth,r=window.innerHeight,o=Math.random()*n*.6,s=Math.random()*r*.55,i=380+Math.random()*520,d=18+Math.random()*26,m=d*Math.PI/180,u=o+Math.cos(m)*i,f=s+Math.sin(m)*i,h=document.createElement("div");h.className="meteor",h.setAttribute("aria-hidden","true"),h.style.setProperty("--from-x",o+"px"),h.style.setProperty("--from-y",s+"px"),h.style.setProperty("--to-x",u+"px"),h.style.setProperty("--to-y",f+"px"),h.style.setProperty("--angle",d+"deg"),a.appendChild(h),h.addEventListener("animationend",()=>h.remove(),{once:!0}),La()}function fo(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 Bn(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}}pe();K();ae();le();be();Et();Ze();xt();pe();K();ae();be();te();Ze();async function ao(){let e=_t.value.trim();if(e.length<2){ma();return}let t=W();if(!t)return;let a=await k(`/api/search?q=${encodeURIComponent(e)}&board=${encodeURIComponent(t.slug)}`);if(a.results.length===0){xe.hidden=!1,xe.innerHTML='<div class="search-hit">No results</div>';return}xe.hidden=!1,xe.innerHTML=a.results.map(n=>`
944
- <div class="search-hit" data-ticket-id="${n.id}">
945
- <span class="search-hit-main">
946
- <strong>${l(ce(n))}</strong>
947
- <span class="search-hit-title">${l(n.title)}</span>
948
- </span>
949
- <span class="search-hit-state" data-variant="${l(Be(n))}">${l(n.state_name||"State")}</span>
950
- </div>
951
- `).join(""),xe.querySelectorAll("[data-ticket-id]").forEach(n=>{n.addEventListener("click",async()=>{G(),ma(),O({boardId:c.boardId,view:"ticket",ticketId:n.dataset.ticketId})})})}function ma(){xe.hidden=!0,xe.innerHTML=""}Se();Re();function ha(e){e&&(e.showModal(),e.querySelector("[data-modal-initial-focus]")?.focus({preventScroll:!0}))}function no(e){e?.open&&e.close()}function ba(e){e&&(e.addEventListener("click",t=>{t.target===e&&no(e)}),e.querySelectorAll("[data-modal-close]").forEach(t=>{t.addEventListener("click",()=>no(e))}))}vn();async function Vs(e){if(e)try{let t=await fetch("./version.json",{cache:"no-store"});if(!t.ok)return;let a=await t.json();a?.version&&(e.textContent=`v${a.version}`)}catch{}}function ro(){let e=document.getElementById("helpDialog"),t=document.getElementById("helpBtn");!e||!t||(ba(e),Vs(document.getElementById("helpDialogVersion")),t.addEventListener("click",()=>ha(e)))}an();Za();async function Gs(){let e=document.getElementById("welcomeDialog");gr(),ba(e),Vr(),ro(),ge==="preview"&&ha(e),w("#settingsBtn").addEventListener("click",async()=>{G(),fe(),O({boardId:c.boardId,view:"settings",tab:c.settingsTab||"lanes"})}),ke?.addEventListener("click",a=>{if(a.stopPropagation(),!V.hidden){st();return}Xr()}),w("#newTicketBtn").addEventListener("click",()=>{if(xn()){G();return}Zt("")}),Ce.addEventListener("click",()=>{G()}),w("#drawerCloseBtn").addEventListener("click",()=>{O({boardId:c.boardId,view:"board"})}),w("#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 k(A(`/api/tickets/${encodeURIComponent(a)}/archive`),{method:"POST"}),y.removed(`Archived: ${r}`),await F(),O({boardId:c.boardId,view:"board"})}),_e.addEventListener("click",()=>{O({boardId:c.boardId,view:"board"})}),document.addEventListener("keydown",a=>{if(a.key!=="Escape")return;if(!V.hidden){fe();return}if(xn()){G();return}if(!X.classList.contains("is-open"))return;let n=document.activeElement;n?.classList?.contains("inline-title-editor")||n?.classList?.contains("inline-desc-editor")||O({boardId:c.boardId,view:"board"})}),_t.addEventListener("input",Ot(ao,220)),document.addEventListener("click",a=>{a.target.closest("#topbarSearch")||ma(),a.target.closest(".topbar-menu")||fe()}),sr(),rr();let t=Ln();t.boardSlug&&(c.boardSlug=t.boardSlug),await F(),to(!0),Lt()?await Tt():it({replace:!0}),wr()}Gs();
952
- //# sourceMappingURL=app.js.map