aicodeman 1.40.0 → 1.41.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 (211) hide show
  1. package/README.md +25 -7
  2. package/dist/cli-agent.d.ts +225 -0
  3. package/dist/cli-agent.d.ts.map +1 -0
  4. package/dist/cli-agent.js +767 -0
  5. package/dist/cli-agent.js.map +1 -0
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +6 -30
  8. package/dist/cli.js.map +1 -1
  9. package/dist/codeman-credentials.d.ts +27 -0
  10. package/dist/codeman-credentials.d.ts.map +1 -0
  11. package/dist/codeman-credentials.js +66 -0
  12. package/dist/codeman-credentials.js.map +1 -0
  13. package/dist/config/cli-registry/schema.d.ts +2 -0
  14. package/dist/config/cli-registry/schema.d.ts.map +1 -1
  15. package/dist/config/cli-registry/schema.js +3 -0
  16. package/dist/config/cli-registry/schema.js.map +1 -1
  17. package/dist/config/cli-registry/stock.d.ts.map +1 -1
  18. package/dist/config/cli-registry/stock.js +5 -0
  19. package/dist/config/cli-registry/stock.js.map +1 -1
  20. package/dist/config/cli-registry/types.d.ts +12 -1
  21. package/dist/config/cli-registry/types.d.ts.map +1 -1
  22. package/dist/git-clone.d.ts.map +1 -1
  23. package/dist/git-clone.js +4 -0
  24. package/dist/git-clone.js.map +1 -1
  25. package/dist/image-watcher.d.ts.map +1 -1
  26. package/dist/image-watcher.js +6 -2
  27. package/dist/image-watcher.js.map +1 -1
  28. package/dist/mcp-sync-targets.d.ts +34 -0
  29. package/dist/mcp-sync-targets.d.ts.map +1 -0
  30. package/dist/mcp-sync-targets.js +55 -0
  31. package/dist/mcp-sync-targets.js.map +1 -0
  32. package/dist/mcp-sync.d.ts +2 -1
  33. package/dist/mcp-sync.d.ts.map +1 -1
  34. package/dist/mcp-sync.js +62 -1
  35. package/dist/mcp-sync.js.map +1 -1
  36. package/dist/session.d.ts.map +1 -1
  37. package/dist/session.js +10 -4
  38. package/dist/session.js.map +1 -1
  39. package/dist/subagent-watcher.d.ts.map +1 -1
  40. package/dist/subagent-watcher.js +29 -6
  41. package/dist/subagent-watcher.js.map +1 -1
  42. package/dist/templates/case-template.md +2 -0
  43. package/dist/tmux-manager.d.ts +5 -0
  44. package/dist/tmux-manager.d.ts.map +1 -1
  45. package/dist/tmux-manager.js +47 -22
  46. package/dist/tmux-manager.js.map +1 -1
  47. package/dist/tui/tui-client.d.ts +3 -17
  48. package/dist/tui/tui-client.d.ts.map +1 -1
  49. package/dist/tui/tui-client.js +3 -44
  50. package/dist/tui/tui-client.js.map +1 -1
  51. package/dist/utils/index.d.ts +1 -0
  52. package/dist/utils/index.d.ts.map +1 -1
  53. package/dist/utils/index.js +1 -0
  54. package/dist/utils/index.js.map +1 -1
  55. package/dist/utils/process-exit-wait.d.ts +40 -0
  56. package/dist/utils/process-exit-wait.d.ts.map +1 -0
  57. package/dist/utils/process-exit-wait.js +68 -0
  58. package/dist/utils/process-exit-wait.js.map +1 -0
  59. package/dist/web/paste-image-gc.d.ts +46 -6
  60. package/dist/web/paste-image-gc.d.ts.map +1 -1
  61. package/dist/web/paste-image-gc.js +106 -32
  62. package/dist/web/paste-image-gc.js.map +1 -1
  63. package/dist/web/public/admin-ui.js.gz +0 -0
  64. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  65. package/dist/web/public/app.6f8552e1.js +43 -0
  66. package/dist/web/public/app.6f8552e1.js.br +0 -0
  67. package/dist/web/public/app.6f8552e1.js.gz +0 -0
  68. package/dist/web/public/approvals-ui.js.gz +0 -0
  69. package/dist/web/public/{constants.c0f8f9de.js → constants.bc9c1f63.js} +295 -28
  70. package/dist/web/public/constants.bc9c1f63.js.br +0 -0
  71. package/dist/web/public/constants.bc9c1f63.js.gz +0 -0
  72. package/dist/web/public/cron-ui.js.gz +0 -0
  73. package/dist/web/public/entrance-animations.js +490 -43
  74. package/dist/web/public/entrance-animations.js.br +0 -0
  75. package/dist/web/public/entrance-animations.js.gz +0 -0
  76. package/dist/web/public/git-status-ui.js.gz +0 -0
  77. package/dist/web/public/home-sessions.js +2 -1
  78. package/dist/web/public/home-sessions.js.br +0 -0
  79. package/dist/web/public/home-sessions.js.gz +0 -0
  80. package/dist/web/public/host-wake-ui.js.gz +0 -0
  81. package/dist/web/public/i18n.a9840ea2.js +2 -0
  82. package/dist/web/public/i18n.a9840ea2.js.br +0 -0
  83. package/dist/web/public/i18n.a9840ea2.js.gz +0 -0
  84. package/dist/web/public/{image-input.898a538b.js → image-input.e00471c6.js} +3 -1
  85. package/dist/web/public/image-input.e00471c6.js.br +0 -0
  86. package/dist/web/public/image-input.e00471c6.js.gz +0 -0
  87. package/dist/web/public/index.html +128 -33
  88. package/dist/web/public/index.html.br +0 -0
  89. package/dist/web/public/index.html.gz +0 -0
  90. package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
  91. package/dist/web/public/keyboard-accessory.c1e7c7b7.js.gz +0 -0
  92. package/dist/web/public/mobile-handlers.0ef9c311.js.gz +0 -0
  93. package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
  94. package/dist/web/public/mobile-overview.js.gz +0 -0
  95. package/dist/web/public/mobile.111b187d.css.gz +0 -0
  96. package/dist/web/public/{notification-manager.50bb87d8.js → notification-manager.feb82c04.js} +19 -2
  97. package/dist/web/public/notification-manager.feb82c04.js.br +0 -0
  98. package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
  99. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  100. package/dist/web/public/{panels-ui.4735dfc2.js → panels-ui.71846fcb.js} +1 -1
  101. package/dist/web/public/panels-ui.71846fcb.js.br +0 -0
  102. package/dist/web/public/panels-ui.71846fcb.js.gz +0 -0
  103. package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
  104. package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
  105. package/dist/web/public/readmymind-ui.js.gz +0 -0
  106. package/dist/web/public/reboot-restore-ui.js.gz +0 -0
  107. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  108. package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
  109. package/dist/web/public/session-lineage.js.gz +0 -0
  110. package/dist/web/public/session-ui.f731602c.js.gz +0 -0
  111. package/dist/web/public/settings-ui.4e33390f.js +97 -0
  112. package/dist/web/public/settings-ui.4e33390f.js.br +0 -0
  113. package/dist/web/public/settings-ui.4e33390f.js.gz +0 -0
  114. package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
  115. package/dist/web/public/spreadsheet-preview.af82fa2f.js.gz +0 -0
  116. package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
  117. package/dist/web/public/styles.81a72906.css +1 -0
  118. package/dist/web/public/styles.81a72906.css.br +0 -0
  119. package/dist/web/public/styles.81a72906.css.gz +0 -0
  120. package/dist/web/public/{subagent-windows.e6ca799f.js → subagent-windows.07277a58.js} +7 -5
  121. package/dist/web/public/subagent-windows.07277a58.js.br +0 -0
  122. package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
  123. package/dist/web/public/sw.js +2 -2
  124. package/dist/web/public/sw.js.br +0 -0
  125. package/dist/web/public/sw.js.gz +0 -0
  126. package/dist/web/public/tab-layout-browser.065fcba4.js.gz +0 -0
  127. package/dist/web/public/tab-rail-resize.a5867793.js.gz +0 -0
  128. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
  129. package/dist/web/public/terminal-split.js.gz +0 -0
  130. package/dist/web/public/terminal-tile.js +92 -15
  131. package/dist/web/public/terminal-tile.js.br +0 -0
  132. package/dist/web/public/terminal-tile.js.gz +0 -0
  133. package/dist/web/public/terminal-ui.b7267005.js +2 -0
  134. package/dist/web/public/terminal-ui.b7267005.js.br +0 -0
  135. package/dist/web/public/terminal-ui.b7267005.js.gz +0 -0
  136. package/dist/web/public/tile-grid.js +330 -119
  137. package/dist/web/public/tile-grid.js.br +0 -0
  138. package/dist/web/public/tile-grid.js.gz +0 -0
  139. package/dist/web/public/ultracode-panel.js.gz +0 -0
  140. package/dist/web/public/ultracode-windows.js +9 -6
  141. package/dist/web/public/ultracode-windows.js.br +0 -0
  142. package/dist/web/public/ultracode-windows.js.gz +0 -0
  143. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  144. package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
  145. package/dist/web/public/vendor/fflate.min.js.gz +0 -0
  146. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  147. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  148. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  149. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  150. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  151. package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
  152. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js +4 -0
  153. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.br +0 -0
  154. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
  155. package/dist/web/public/vendor/xterm.css.gz +0 -0
  156. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  157. package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
  158. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  159. package/dist/web/public/webview-tabs.js.gz +0 -0
  160. package/dist/web/routes/cli-registry-routes.d.ts +23 -4
  161. package/dist/web/routes/cli-registry-routes.d.ts.map +1 -1
  162. package/dist/web/routes/cli-registry-routes.js +92 -7
  163. package/dist/web/routes/cli-registry-routes.js.map +1 -1
  164. package/dist/web/routes/mcp-sync-routes.d.ts +7 -2
  165. package/dist/web/routes/mcp-sync-routes.d.ts.map +1 -1
  166. package/dist/web/routes/mcp-sync-routes.js +10 -3
  167. package/dist/web/routes/mcp-sync-routes.js.map +1 -1
  168. package/dist/web/routes/reboot-restore-routes.js +1 -1
  169. package/dist/web/routes/reboot-restore-routes.js.map +1 -1
  170. package/dist/web/routes/session-routes.d.ts.map +1 -1
  171. package/dist/web/routes/session-routes.js +68 -35
  172. package/dist/web/routes/session-routes.js.map +1 -1
  173. package/dist/web/schemas.d.ts +3 -0
  174. package/dist/web/schemas.d.ts.map +1 -1
  175. package/dist/web/schemas.js +9 -0
  176. package/dist/web/schemas.js.map +1 -1
  177. package/dist/web/server.d.ts +1 -1
  178. package/dist/web/server.d.ts.map +1 -1
  179. package/dist/web/server.js +15 -10
  180. package/dist/web/server.js.map +1 -1
  181. package/package.json +4 -4
  182. package/skills/codeman/SKILL.md +6 -0
  183. package/README.zh-CN.md +0 -1147
  184. package/dist/web/public/app.abadc970.js +0 -43
  185. package/dist/web/public/app.abadc970.js.br +0 -0
  186. package/dist/web/public/app.abadc970.js.gz +0 -0
  187. package/dist/web/public/constants.c0f8f9de.js.br +0 -0
  188. package/dist/web/public/constants.c0f8f9de.js.gz +0 -0
  189. package/dist/web/public/i18n.a31747a9.js +0 -2
  190. package/dist/web/public/i18n.a31747a9.js.br +0 -0
  191. package/dist/web/public/i18n.a31747a9.js.gz +0 -0
  192. package/dist/web/public/image-input.898a538b.js.br +0 -0
  193. package/dist/web/public/image-input.898a538b.js.gz +0 -0
  194. package/dist/web/public/notification-manager.50bb87d8.js.br +0 -0
  195. package/dist/web/public/notification-manager.50bb87d8.js.gz +0 -0
  196. package/dist/web/public/panels-ui.4735dfc2.js.br +0 -0
  197. package/dist/web/public/panels-ui.4735dfc2.js.gz +0 -0
  198. package/dist/web/public/settings-ui.a97ab16f.js +0 -97
  199. package/dist/web/public/settings-ui.a97ab16f.js.br +0 -0
  200. package/dist/web/public/settings-ui.a97ab16f.js.gz +0 -0
  201. package/dist/web/public/styles.8ee12666.css +0 -1
  202. package/dist/web/public/styles.8ee12666.css.br +0 -0
  203. package/dist/web/public/styles.8ee12666.css.gz +0 -0
  204. package/dist/web/public/subagent-windows.e6ca799f.js.br +0 -0
  205. package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
  206. package/dist/web/public/terminal-ui.5059b30e.js +0 -2
  207. package/dist/web/public/terminal-ui.5059b30e.js.br +0 -0
  208. package/dist/web/public/terminal-ui.5059b30e.js.gz +0 -0
  209. package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js +0 -4
  210. package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js.br +0 -0
  211. package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js.gz +0 -0
@@ -16,7 +16,7 @@
16
16
  <link rel="manifest" href="manifest.json">
17
17
  <title>Codeman</title>
18
18
  <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%2360a5fa'/%3E%3Cstop offset='100%25' stop-color='%233b82f6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='32' height='32' rx='6' fill='%230a0a0a'/%3E%3Cpath d='M18 4L8 18h6l-2 10 10-14h-6z' fill='url(%23g)'/%3E%3C/svg%3E">
19
- <link rel="stylesheet" href="styles.8ee12666.css">
19
+ <link rel="stylesheet" href="styles.81a72906.css">
20
20
  <link rel="stylesheet" href="mobile.111b187d.css" media="(max-width: 1023px)">
21
21
  <!-- xterm.css loaded async — terminal won't display until xterm.js runs anyway -->
22
22
  <link rel="preload" href="vendor/xterm.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
@@ -27,8 +27,8 @@
27
27
  <!-- Symbols font before the terminal first paints — a late-loading icon font
28
28
  leaves tofu in xterm's glyph atlas until something forces a re-render. -->
29
29
  <link rel="preload" href="fonts/symbols-nerd-font-mono.woff2" as="font" type="font/woff2" crossorigin>
30
- <link rel="preload" href="constants.c0f8f9de.js" as="script">
31
- <link rel="preload" href="app.abadc970.js" as="script">
30
+ <link rel="preload" href="constants.bc9c1f63.js" as="script">
31
+ <link rel="preload" href="app.6f8552e1.js" as="script">
32
32
  <!-- Self-hosted xterm.js — eliminates CDN DNS/TLS latency (~100ms).
33
33
  'defer' preserves execution order (xterm loads before fit addon). -->
34
34
  <script defer src="vendor/xterm.min.js"></script>
@@ -40,7 +40,7 @@
40
40
  <script defer src="vendor/xterm-addon-serialize.min.js"></script>
41
41
  <!-- WebGL addon lazy-loaded by app.js on desktop only (skipped on mobile, saving 244KB) -->
42
42
  <script defer src="vendor/xterm-addon-unicode11.min.js"></script>
43
- <script defer src="vendor/xterm-zerolag-input.bbb5f84c.js"></script>
43
+ <script defer src="vendor/xterm-zerolag-input.0b69623c.js"></script>
44
44
  <script defer src="vendor/xterm-predictive-echo.ae5e3d5d.js"></script>
45
45
  <script defer src="vendor/marked.min.js"></script>
46
46
  <!-- DOMPurify (allowlist HTML sanitizer for rendered markdown).
@@ -65,7 +65,7 @@
65
65
  app.js, NOT the handheld storage-key test `m`. Use a different predicate
66
66
  here and boot will contradict this value, animating the drawer open by
67
67
  itself on every load between 768 and 1023px. -->
68
- <script>try{var m=window.innerWidth<768||(('ontouchstart' in window||navigator.maxTouchPoints>0)&&window.innerWidth<1024);var k=m?'codeman-app-settings-mobile':'codeman-app-settings';var A=JSON.parse(localStorage.getItem(k)||'{}');var L=A.sessionListLayout;var F=Number(A.sessionSidebarFontSize);var solo=/^\/session\//.test(location.pathname);var C=localStorage.getItem('codeman-sidebar-collapsed');var S=(L==='sidebar'||L==='sidebar-rich')&&!solo;document.documentElement.dataset.sessionList=S?'sidebar':'header';document.documentElement.dataset.sidebarDetail=(S&&L==='sidebar-rich')?'rich':'simple';document.documentElement.dataset.sidebar=(C===null?window.innerWidth<1024:C==='1')?'collapsed':'expanded';var V=A.tabOrientation==='vertical'&&!S&&!solo&&window.innerWidth>=768;document.documentElement.dataset.tabOrientation=V?'vertical':'horizontal';document.documentElement.dataset.tabRailDetail=(A.tabRailDetail==='simple')?'simple':'rich';document.documentElement.dataset.tabRailSort=(A.tabRailSort==='manual')?'manual':'activity';var T=A.tabArrangement;document.documentElement.dataset.tabArrangement=(T==='state'||T==='case'||T==='ledger')?T:'classic';document.documentElement.dataset.tabStateOrder=(A.tabStateOrder==='urgent-last')?'urgent-last':'urgent-first';var H=A.headerStatsStyle;document.documentElement.dataset.headerStats=(window.innerWidth<768||solo)?'classic':(H==='classic'||H==='tiles')?H:'compact';var W=Number(A.tabRailWidth);if(V){if(Number.isInteger(W)&&W>=208&&W<=360)document.documentElement.style.setProperty('--tab-rail-width',W+'px');else if(document.documentElement.dataset.tabRailDetail!=='simple')document.documentElement.style.setProperty('--tab-rail-width','320px');}if(Number.isInteger(F)&&F>=11&&F<=18)document.documentElement.style.setProperty('--session-sidebar-name-font-size',F+'px');}catch(e){document.documentElement.dataset.sessionList='header';document.documentElement.dataset.sidebarDetail='simple';document.documentElement.dataset.sidebar='expanded';document.documentElement.dataset.tabOrientation='horizontal';document.documentElement.dataset.tabRailDetail='rich';document.documentElement.dataset.tabRailSort='activity';document.documentElement.dataset.tabArrangement='classic';document.documentElement.dataset.tabStateOrder='urgent-first';document.documentElement.dataset.headerStats='classic';}</script>
68
+ <script>try{var m=window.innerWidth<768||(('ontouchstart' in window||navigator.maxTouchPoints>0)&&window.innerWidth<1024);var k=m?'codeman-app-settings-mobile':'codeman-app-settings';var A=JSON.parse(localStorage.getItem(k)||'{}');var L=A.sessionListLayout;var F=Number(A.sessionSidebarFontSize);var solo=/^\/session\//.test(location.pathname);var C=localStorage.getItem('codeman-sidebar-collapsed');var S=(L==='sidebar'||L==='sidebar-rich')&&!solo;document.documentElement.dataset.sessionList=S?'sidebar':'header';document.documentElement.dataset.sidebarDetail=(S&&L==='sidebar-rich')?'rich':'simple';document.documentElement.dataset.sidebar=(C===null?window.innerWidth<1024:C==='1')?'collapsed':'expanded';var V=A.tabOrientation==='vertical'&&!S&&!solo&&window.innerWidth>=768;document.documentElement.dataset.tabOrientation=V?'vertical':'horizontal';document.documentElement.dataset.tabRailDetail=(A.tabRailDetail==='simple')?'simple':'rich';document.documentElement.dataset.tabRailSort=(A.tabRailSort==='manual')?'manual':'activity';var T=A.tabArrangement;document.documentElement.dataset.tabArrangement=(T==='state'||T==='case'||T==='ledger')?T:'classic';document.documentElement.dataset.tabStateOrder=(A.tabStateOrder==='urgent-last')?'urgent-last':'urgent-first';document.documentElement.dataset.tabLogos=(A.showTabCliLogos===false)?'off':'on';var H=A.headerStatsStyle;document.documentElement.dataset.headerStats=(window.innerWidth<768||solo)?'classic':(H==='classic'||H==='tiles')?H:'compact';var W=Number(A.tabRailWidth);if(V){if(Number.isInteger(W)&&W>=208&&W<=360)document.documentElement.style.setProperty('--tab-rail-width',W+'px');else if(document.documentElement.dataset.tabRailDetail!=='simple')document.documentElement.style.setProperty('--tab-rail-width','320px');}if(Number.isInteger(F)&&F>=11&&F<=18)document.documentElement.style.setProperty('--session-sidebar-name-font-size',F+'px');}catch(e){document.documentElement.dataset.sessionList='header';document.documentElement.dataset.sidebarDetail='simple';document.documentElement.dataset.sidebar='expanded';document.documentElement.dataset.tabOrientation='horizontal';document.documentElement.dataset.tabRailDetail='rich';document.documentElement.dataset.tabRailSort='activity';document.documentElement.dataset.tabArrangement='classic';document.documentElement.dataset.tabStateOrder='urgent-first';document.documentElement.dataset.headerStats='classic';document.documentElement.dataset.tabLogos='on';}</script>
69
69
  <!-- Inline critical CSS for instant skeleton paint (before styles.css loads) -->
70
70
  <style>
71
71
  .loading-skeleton{display:flex;flex-direction:column;height:100vh;height:100dvh;background:var(--bg-dark,#11151c)}
@@ -394,6 +394,19 @@
394
394
  <!-- Main Terminal Area -->
395
395
  <main class="main">
396
396
  <aside class="tab-rail" id="tabRail" aria-label="Session navigation">
397
+ <!-- Session-name search (app.js setTabRailSearch): a view filter over the
398
+ rows below, in memory only. Shown with the rail, never elsewhere. -->
399
+ <div class="session-sidebar-filter tab-rail-search">
400
+ <input type="search" id="tabRailSearch" class="session-sidebar-filter-input"
401
+ placeholder="Search sessions" aria-label="Search sessions"
402
+ autocomplete="off" spellcheck="false"
403
+ oninput="app.setTabRailSearch(this.value)"
404
+ onkeydown="app.handleTabRailSearchKeydown(event)">
405
+ <button type="button" id="tabRailSearchClear" class="tab-rail-search-clear"
406
+ aria-label="Clear search" title="Clear search"
407
+ onclick="app.clearTabRailSearch()" hidden>&times;</button>
408
+ </div>
409
+ <div id="tabRailSearchEmpty" class="tab-rail-search-empty" role="status" hidden>No sessions match</div>
397
410
  <div
398
411
  id="tabRailResizeHandle"
399
412
  class="tab-rail-resize-handle"
@@ -1635,6 +1648,7 @@
1635
1648
  Save; row-reverse keeps Save to the left of it on phones. -->
1636
1649
  <div class="set-head-actions">
1637
1650
  <button class="modal-close" onclick="app.closeAppSettings()" aria-label="Close app settings">&times;</button>
1651
+ <button class="set-head-save set-head-apply" onclick="app.applyAppSettings()" title="Save and keep Settings open">Apply</button>
1638
1652
  <button class="set-head-save" onclick="app.saveAppSettings()">Save</button>
1639
1653
  </div>
1640
1654
  </div>
@@ -1663,6 +1677,10 @@
1663
1677
  <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 3a9 9 0 0 0 0 18 4.5 4.5 0 0 0 0-9 4.5 4.5 0 0 1 0-9z"/></svg>
1664
1678
  <span>Appearance</span>
1665
1679
  </button>
1680
+ <button type="button" class="set-rail-item" data-section="settings-animations">
1681
+ <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l1.8 4.7L18.5 9.5l-4.7 1.8L12 16l-1.8-4.7L5.5 9.5l4.7-1.8z"/><path d="M19 15l.8 2.2L22 18l-2.2.8L19 21l-.8-2.2L16 18l2.2-.8z"/><path d="M3 19h6"/></svg>
1682
+ <span>Animations</span>
1683
+ </button>
1666
1684
  <button type="button" class="set-rail-item" data-section="settings-models">
1667
1685
  <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9L12 3z"/><path d="M12 12l8-4.5M12 12v9M12 12L4 7.5"/></svg>
1668
1686
  <span>Models</span>
@@ -2053,7 +2071,7 @@
2053
2071
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 3a9 9 0 0 0 0 18 4.5 4.5 0 0 0 0-9 4.5 4.5 0 0 1 0-9z"/></svg>
2054
2072
  <h2>Appearance</h2>
2055
2073
  </div>
2056
- <p class="set-section-blurb">Theme, motion, and what this install calls itself.</p>
2074
+ <p class="set-section-blurb">Theme, and what this install calls itself.</p>
2057
2075
 
2058
2076
  <div class="set-group">
2059
2077
  <div class="set-group-head"><h4>Theme</h4><span class="set-scope">device</span></div>
@@ -2077,21 +2095,6 @@
2077
2095
  </optgroup>
2078
2096
  </select>
2079
2097
  </div>
2080
- <div class="set-row has-field" data-search="entrance animations motion tabs windows">
2081
- <div class="set-row-text">
2082
- <span class="set-row-label">Entrance Animations</span>
2083
- <span class="set-row-desc">How new tabs, panes and agent windows arrive. Add ?animlab=1 to the URL for per-surface control.</span>
2084
- </div>
2085
- <select id="appSettingsEntranceAnim" class="set-select">
2086
- <option value="legacy">Off (default)</option>
2087
- <option value="terminal">Terminal (CRT)</option>
2088
- <option value="beamdown">Beam down</option>
2089
- <option value="softfocus">Soft focus (blur)</option>
2090
- <option value="quiet">Quiet</option>
2091
- <option value="playful">Playful</option>
2092
- <option value="custom">Custom (set in the lab)</option>
2093
- </select>
2094
- </div>
2095
2098
  </div>
2096
2099
  </div>
2097
2100
 
@@ -2216,6 +2219,13 @@
2216
2219
  </div>
2217
2220
  <label class="switch switch-sm"><input type="checkbox" id="appSettingsTabTwoRows"><span class="slider"></span></label>
2218
2221
  </div>
2222
+ <div class="set-row" data-search="cli logos on tabs logo icon harness agent cli tab hide">
2223
+ <div class="set-row-text">
2224
+ <span class="set-row-label">CLI Logos on Tabs</span>
2225
+ <span class="set-row-desc">Show each agent's CLI logo before the session name on tabs and the home screen's tab list. Off leaves the status dot and the shell's SH badge. Tiles, split headers and the Run menus keep their logos.</span>
2226
+ </div>
2227
+ <label class="switch switch-sm"><input type="checkbox" id="appSettingsShowTabCliLogos" checked><span class="slider"></span></label>
2228
+ </div>
2219
2229
  <div class="set-row" data-search="pop out detach tab window">
2220
2230
  <div class="set-row-text">
2221
2231
  <span class="set-row-label">Pop-out Button on Tabs</span>
@@ -2248,6 +2258,70 @@
2248
2258
  </div>
2249
2259
  </section>
2250
2260
 
2261
+ <!-- ══ Animations ═══════════════════════════════════════════════
2262
+ Every entrance animation in one place (entrance-animations.js).
2263
+ All per-device localStorage keys, applied as they are picked,
2264
+ never part of PUT /api/settings. -->
2265
+ <section class="set-section" id="settings-animations" data-label="Animations">
2266
+ <div class="set-section-head">
2267
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l1.8 4.7L18.5 9.5l-4.7 1.8L12 16l-1.8-4.7L5.5 9.5l4.7-1.8z"/><path d="M19 15l.8 2.2L22 18l-2.2.8L19 21l-.8-2.2L16 18l2.2-.8z"/><path d="M3 19h6"/></svg>
2268
+ <h2>Animations</h2>
2269
+ </div>
2270
+ <p class="set-section-blurb">How tabs, terminal panes, agent windows and tiles arrive. All off by default, applied as you pick them.</p>
2271
+
2272
+ <div class="set-group">
2273
+ <div class="set-group-head"><h4>Entrances</h4><span class="set-scope">device</span></div>
2274
+ <div class="set-group-body">
2275
+ <div class="set-row has-field" data-search="entrance animations theme motion tabs windows panes lines">
2276
+ <div class="set-row-text">
2277
+ <span class="set-row-label">Entrance Theme</span>
2278
+ <span class="set-row-desc">One look for how new tabs, terminal panes, agent windows and their lines arrive.</span>
2279
+ </div>
2280
+ <select id="appSettingsEntranceAnim" class="set-select">
2281
+ <option value="legacy">Off (default)</option>
2282
+ <option value="terminal">Terminal (CRT)</option>
2283
+ <option value="beamdown">Beam down</option>
2284
+ <option value="launch">Launch (tiles fly from tabs)</option>
2285
+ <option value="softfocus">Soft focus (blur)</option>
2286
+ <option value="quiet">Quiet</option>
2287
+ <option value="playful">Playful</option>
2288
+ <option value="custom">Custom (set in the lab)</option>
2289
+ </select>
2290
+ </div>
2291
+ <div class="set-row has-field" data-search="tile animations tiles grid motion entrance fly deal crt beam">
2292
+ <div class="set-row-text">
2293
+ <span class="set-row-label">Tile Animations</span>
2294
+ <span class="set-row-desc">How tiles arrive when the grid opens and leave when it closes. A theme above presets it.</span>
2295
+ </div>
2296
+ <select id="appSettingsTileAnim" class="set-select">
2297
+ <option value="settle">Off (default)</option>
2298
+ <option value="fly">Fly from tab</option>
2299
+ <option value="deal">Deal</option>
2300
+ <option value="crt">CRT</option>
2301
+ <option value="beam">Beam down</option>
2302
+ <option value="cascade">Cascade</option>
2303
+ <option value="pop">Pop</option>
2304
+ <option value="soft">Soft</option>
2305
+ <option value="off">None (tiles just appear)</option>
2306
+ </select>
2307
+ </div>
2308
+ </div>
2309
+ </div>
2310
+
2311
+ <div class="set-group">
2312
+ <div class="set-group-head"><h4>Lab</h4></div>
2313
+ <div class="set-group-body">
2314
+ <div class="set-row has-field" data-search="animation lab compare replay styles per surface animlab">
2315
+ <div class="set-row-text">
2316
+ <span class="set-row-label">Animation Lab</span>
2317
+ <span class="set-row-desc">Closes settings and opens every style per surface side by side, with replay and speed. Same as adding ?animlab=1 to the URL.</span>
2318
+ </div>
2319
+ <button type="button" id="appSettingsOpenAnimLab" class="btn-toolbar btn-sm">Open lab</button>
2320
+ </div>
2321
+ </div>
2322
+ </div>
2323
+ </section>
2324
+
2251
2325
  <!-- ══ Models ═══════════════════════════════════════════════════ -->
2252
2326
  <section class="set-section" id="settings-models" data-label="Models">
2253
2327
  <div class="set-section-head">
@@ -2644,17 +2718,17 @@
2644
2718
  <div class="set-group" id="mcpSyncGroup">
2645
2719
  <div class="set-group-head"><h4>MCP servers</h4><span class="set-scope">synced</span></div>
2646
2720
  <div class="set-group-body">
2647
- <div class="set-row" data-search="mcp server sync enable claude codex gemini opencode antigravity">
2721
+ <div class="set-row" data-search="mcp server sync enable claude codex gemini opencode antigravity copilot github">
2648
2722
  <div class="set-row-text">
2649
2723
  <span class="set-row-label">Enable MCP server sync</span>
2650
- <span class="set-row-desc">Adds a control that copies MCP servers between your enabled CLIs by writing their own config files. Off by default: this changes other tools' configuration, not just Codeman's.</span>
2724
+ <span class="set-row-desc">Adds a control that copies MCP servers between your enabled CLIs, and GitHub Copilot CLI if it is installed, by writing their own config files. Off by default: this changes other tools' configuration, not just Codeman's.</span>
2651
2725
  </div>
2652
2726
  <label class="switch switch-sm"><input type="checkbox" id="appSettingsMcpSync" onchange="app.applyMcpSyncVisibility()"><span class="slider"></span></label>
2653
2727
  </div>
2654
2728
  <div class="set-row" id="mcpSyncActionRow" style="display:none" data-search="mcp server sync preview">
2655
2729
  <div class="set-row-text">
2656
2730
  <span class="set-row-label">Sync MCP servers across CLIs</span>
2657
- <span class="set-row-desc">Copies each installed, enabled CLI's MCP servers into the others. Only adds missing servers; never edits, removes or copies a server you switched off. Env values and headers are copied too, so a file that receives them is left readable by you only. The previous file is kept as <code>.codeman-bak</code> (overwritten by each sync). A config dir moved by the CLI's own env var (<code>CODEX_HOME</code>, <code>CLAUDE_CONFIG_DIR</code>, <code>XDG_CONFIG_HOME</code>, <code>GEMINI_CLI_HOME</code>) is followed as Codeman's server sees it; a per-session override is not.</span>
2731
+ <span class="set-row-desc">Copies each installed, enabled CLI's MCP servers (and GitHub Copilot CLI's) into the others. Only adds missing servers; never edits, removes or copies a server you switched off. Env values and headers are copied too, so a file that receives them is left readable by you only. The previous file is kept as <code>.codeman-bak</code> (overwritten by each sync). A config dir moved by the CLI's own env var (<code>CODEX_HOME</code>, <code>CLAUDE_CONFIG_DIR</code>, <code>XDG_CONFIG_HOME</code>, <code>GEMINI_CLI_HOME</code>, <code>COPILOT_HOME</code>) is followed as Codeman's server sees it; a per-session override is not.</span>
2658
2732
  </div>
2659
2733
  <span>
2660
2734
  <button class="btn-toolbar btn-sm" id="mcpSyncPreviewBtn" onclick="app.mcpSync(false)">Preview</button>
@@ -2723,6 +2797,26 @@
2723
2797
  <span class="set-unit">min</span>
2724
2798
  </div>
2725
2799
  </div>
2800
+ <div class="set-row" data-search="toast display time popup dismiss seconds notification">
2801
+ <div class="set-row-text">
2802
+ <span class="set-row-label">Toast display time</span>
2803
+ <span class="set-row-desc">How long the corner pop-ups stay on screen.</span>
2804
+ </div>
2805
+ <div class="set-row-actions">
2806
+ <input type="number" id="appSettingsNotifToastSecs" class="set-num" value="3" min="1" max="300">
2807
+ <span class="set-unit">sec</span>
2808
+ </div>
2809
+ </div>
2810
+ <div class="set-row" data-search="browser notification auto close dismiss seconds">
2811
+ <div class="set-row-text">
2812
+ <span class="set-row-label">Browser notification display time</span>
2813
+ <span class="set-row-desc">How long a desktop notification stays up before Codeman closes it. Your OS may close it sooner.</span>
2814
+ </div>
2815
+ <div class="set-row-actions">
2816
+ <input type="number" id="appSettingsNotifBrowserSecs" class="set-num" value="8" min="1" max="300">
2817
+ <span class="set-unit">sec</span>
2818
+ </div>
2819
+ </div>
2726
2820
  </div>
2727
2821
  </div>
2728
2822
 
@@ -3037,6 +3131,7 @@
3037
3131
  </div>
3038
3132
  <div class="form-actions set-foot">
3039
3133
  <button class="btn-toolbar" onclick="app.closeAppSettings()">Cancel</button>
3134
+ <button class="btn-toolbar" onclick="app.applyAppSettings()" title="Save and keep Settings open">Apply</button>
3040
3135
  <button class="btn-toolbar btn-primary" onclick="app.saveAppSettings()">Save</button>
3041
3136
  </div>
3042
3137
  </div>
@@ -3994,11 +4089,11 @@
3994
4089
  <div class="approvals-list"></div>
3995
4090
  </div>
3996
4091
 
3997
- <script defer src="constants.c0f8f9de.js"></script>
3998
- <script defer src="i18n.a31747a9.js"></script>
4092
+ <script defer src="constants.bc9c1f63.js"></script>
4093
+ <script defer src="i18n.a9840ea2.js"></script>
3999
4094
  <script defer src="mobile-handlers.0ef9c311.js"></script>
4000
4095
  <script defer src="voice-input.2342c37b.js"></script>
4001
- <script defer src="notification-manager.50bb87d8.js"></script>
4096
+ <script defer src="notification-manager.feb82c04.js"></script>
4002
4097
  <script defer src="keyboard-accessory.c1e7c7b7.js"></script>
4003
4098
  <script defer src="input-cjk.8bc46081.js"></script>
4004
4099
  <!-- Shows iOS Safari IME composition text inside the terminal. Must precede terminal-ui.js. -->
@@ -4009,9 +4104,9 @@
4009
4104
  <script defer src="sanitize-html.e7147e76.js"></script>
4010
4105
  <!-- Owner tab layout projection (grouped vertical rail); pure, read by app.js. -->
4011
4106
  <script defer src="tab-layout-browser.065fcba4.js"></script>
4012
- <script defer src="app.abadc970.js"></script>
4107
+ <script defer src="app.6f8552e1.js"></script>
4013
4108
  <script defer src="tab-rail-resize.a5867793.js"></script>
4014
- <script defer src="terminal-ui.5059b30e.js"></script>
4109
+ <script defer src="terminal-ui.b7267005.js"></script>
4015
4110
  <script defer src="terminal-tile.js"></script>
4016
4111
  <script defer src="terminal-split.js"></script>
4017
4112
  <script defer src="tile-grid.js"></script>
@@ -4019,8 +4114,8 @@
4019
4114
  <script defer src="ralph-panel.b350e2ef.js"></script>
4020
4115
  <script defer src="orchestrator-panel.js"></script>
4021
4116
  <script defer src="cron-ui.js"></script>
4022
- <script defer src="settings-ui.a97ab16f.js"></script>
4023
- <script defer src="panels-ui.4735dfc2.js"></script>
4117
+ <script defer src="settings-ui.4e33390f.js"></script>
4118
+ <script defer src="panels-ui.71846fcb.js"></script>
4024
4119
  <script defer src="readmymind-ui.js"></script>
4025
4120
  <script defer src="ultracode-panel.js"></script>
4026
4121
  <script defer src="approvals-ui.js"></script>
@@ -4035,10 +4130,10 @@
4035
4130
  <script defer src="entrance-animations.js"></script>
4036
4131
  <script defer src="ralph-wizard.b6c0dd19.js"></script>
4037
4132
  <script defer src="api-client.c9b1cddc.js"></script>
4038
- <script defer src="subagent-windows.e6ca799f.js"></script>
4133
+ <script defer src="subagent-windows.07277a58.js"></script>
4039
4134
  <script defer src="ultracode-windows.js"></script>
4040
4135
  <script defer src="session-lineage.js"></script>
4041
- <script defer src="image-input.898a538b.js"></script>
4136
+ <script defer src="image-input.e00471c6.js"></script>
4042
4137
  <script defer src="spreadsheet-preview.af82fa2f.js"></script>
4043
4138
  </body>
4044
4139
  </html>
Binary file
Binary file
@@ -4,7 +4,7 @@
4
4
  * The NotificationManager class implements five notification layers:
5
5
  * 1. In-app notification drawer (slide-out panel with grouped notifications)
6
6
  * 2. Tab title flash (alternating "⚠️ (N) codeman:<host>" / "codeman:<host>" when tab is hidden; uses this.originalTitle so it tracks any per-host title)
7
- * 3. Browser Notification API (desktop push with auto-close after 8s)
7
+ * 3. Browser Notification API (desktop push; auto-closes after 8s by default, configurable per device in Settings → Notifications)
8
8
  * 4. Web Push via service worker (OS-level notifications when tab is closed)
9
9
  * 5. Audio alerts (Web Audio API beep, user-opt-in)
10
10
  *
@@ -93,6 +93,10 @@ class NotificationManager {
93
93
  browserNotifications: !isMobile,
94
94
  audioAlerts: false,
95
95
  stuckThresholdMs: STUCK_THRESHOLD_DEFAULT_MS,
96
+ // How long a corner toast stays on screen, and how long a browser notification
97
+ // stays up before Codeman closes it (ms; per-device like the rest of these)
98
+ toastDurationMs: DEFAULT_TOAST_DURATION_MS,
99
+ browserAutoCloseMs: AUTO_CLOSE_NOTIFICATION_MS,
96
100
  // Legacy urgency muting (keep for backwards compat)
97
101
  muteCritical: false,
98
102
  muteWarning: false,
@@ -167,11 +171,24 @@ class NotificationManager {
167
171
  return {
168
172
  ...defaults,
169
173
  ...prefs,
174
+ toastDurationMs: this.clampDuration(prefs.toastDurationMs, defaults.toastDurationMs),
175
+ browserAutoCloseMs: this.clampDuration(prefs.browserAutoCloseMs, defaults.browserAutoCloseMs),
170
176
  eventTypes: { ...defaults.eventTypes, ...prefs.eventTypes },
171
177
  _version: 5,
172
178
  };
173
179
  }
174
180
 
181
+ /** A display time in ms kept within [1s, 5min]; anything unusable falls back to the default. */
182
+ clampDuration(value, fallback) {
183
+ if (typeof value !== 'number' || !Number.isFinite(value)) return fallback;
184
+ return Math.min(MAX_NOTIFICATION_DURATION_MS, Math.max(MIN_NOTIFICATION_DURATION_MS, Math.round(value)));
185
+ }
186
+
187
+ /** Display time for corner toasts that do not set their own `duration`. */
188
+ getToastDurationMs() {
189
+ return this.clampDuration(this.preferences?.toastDurationMs, DEFAULT_TOAST_DURATION_MS);
190
+ }
191
+
175
192
  loadPreferences() {
176
193
  try {
177
194
  const storageKey = this.getStorageKey();
@@ -403,7 +420,7 @@ class NotificationManager {
403
420
  };
404
421
 
405
422
  // Auto-close
406
- setTimeout(() => notif.close(), AUTO_CLOSE_NOTIFICATION_MS);
423
+ setTimeout(() => notif.close(), this.clampDuration(this.preferences.browserAutoCloseMs, AUTO_CLOSE_NOTIFICATION_MS));
407
424
  }
408
425
 
409
426
  async requestPermission() {
@@ -358,4 +358,4 @@ OK overwrites it with your version; Cancel keeps your draft open.`)&&this.saveFi
358
358
  ${o.status!=="completed"?`<button class="btn-toolbar btn-sm btn-danger" onclick="app.killSubagent(${escapeHtml(JSON.stringify(o.agentId))})" title="Kill agent">Kill</button>`:""}
359
359
  </div>
360
360
  </div>
361
- `}e.innerHTML=i},async killMuxSession(e){if(confirm("Kill this mux session?")){try{await this.closeSession(e,!0)}catch{try{await fetch(`/api/mux-sessions/${e}`,{method:"DELETE"})}catch{}this.showToast("Tmux session killed","success")}this.muxSessions=this.muxSessions.filter(t=>t.sessionId!==e),this.renderMuxSessions()}},async reconcileMuxSessions(){try{const t=await(await fetch("/api/mux-sessions/reconcile",{method:"POST"})).json();t.data?.dead&&t.data.dead.length>0?(this.showToast(`Found ${t.data.dead.length} dead mux session(s)`,"warning"),await this.loadMuxSessions()):this.showToast("All mux sessions are alive","success")}catch{this.showToast("Failed to reconcile mux sessions","error")}},toggleNotifications(){this.notificationManager?.toggleDrawer()},async launchMultiMonitor(){try{const e=await fetch("/api/system/span-displays",{method:"POST"}),t=await e.json().catch(()=>({}));e.ok&&t.success?this.showToast("Opening Codeman across all displays\u2026","success"):this.showToast(t.error||"Could not open spanning window","error")}catch(e){this.showToast("Could not open spanning window: "+(e?.message||e),"error")}},toast(e,t="info"){return this.showToast(e,t)},showToast(e,t="info",s={}){const{duration:n=3e3,action:i}=s,o=document.createElement("div");o.className=`toast toast-${t}`;const r=document.createElement("span");if(r.className="toast-message",r.textContent=e,o.appendChild(r),i){const h=document.createElement("button");h.textContent=i.label,h.style.cssText="margin-left:12px;padding:2px 10px;background:rgba(255,255,255,0.15);border:1px solid rgba(255,255,255,0.3);border-radius:3px;color:inherit;cursor:pointer;font-size:12px",h.onclick=d=>{d.stopPropagation(),i.onClick(),o.remove()},o.appendChild(h)}let a=null;const l=()=>{a&&clearTimeout(a),o.classList.remove("show"),setTimeout(()=>o.remove(),200)},c=document.createElement("button");return c.className="toast-close",c.textContent="\xD7",c.setAttribute("aria-label","Dismiss"),c.onclick=h=>{h.stopPropagation(),l()},o.appendChild(c),this._toastContainer||(this._toastContainer=document.querySelector(".toast-container"),this._toastContainer||(this._toastContainer=document.createElement("div"),this._toastContainer.className="toast-container",document.body.appendChild(this._toastContainer))),this._toastContainer.appendChild(o),requestAnimationFrame(()=>o.classList.add("show")),n>0&&(a=setTimeout(l,n)),{dismiss:l,setMessage:h=>{r.textContent=h}}},_showCenterStatus(e,t={}){const{type:s="info",onCancel:n}=t;let i=document.getElementById("customModelCenterStatus");i||(i=document.createElement("div"),i.id="customModelCenterStatus",document.body.appendChild(i)),i._hideTimer&&(clearTimeout(i._hideTimer),i._hideTimer=null),i.className=`center-status-banner center-status-${s}`,i.innerHTML="";const o=()=>{i.classList.remove("show"),i._hideTimer=setTimeout(()=>{i.hidden=!0,i._hideTimer=null},200)};if(s!=="error"){const a=document.createElement("span");a.className="center-status-spinner",a.setAttribute("aria-hidden","true"),i.appendChild(a)}const r=document.createElement("span");if(r.className="center-status-text",r.textContent=e,i.appendChild(r),s==="error"){const a=document.createElement("button");a.className="center-status-close",a.textContent="\xD7",a.setAttribute("aria-label","Dismiss"),a.onclick=l=>{l.stopPropagation(),o()},i.appendChild(a)}else if(n){const a=document.createElement("button");a.className="center-status-cancel",a.textContent="Cancel",a.onclick=l=>{l.stopPropagation(),n()},i.appendChild(a)}return i.hidden=!1,requestAnimationFrame(()=>i.classList.add("show")),{dismiss:o,setMessage:a=>{const l=i.querySelector(".center-status-text");l&&(l.textContent=a)}}},startSystemStatsPolling(){this.stopSystemStatsPolling(),this.fetchSystemStats(),this.systemStatsInterval=setInterval(()=>{this.fetchSystemStats()},2e3)},stopSystemStatsPolling(){this.systemStatsInterval&&(clearInterval(this.systemStatsInterval),this.systemStatsInterval=null)},async fetchSystemStats(){const e=document.getElementById("headerSystemStats");if(!(!e||e.style.display==="none"))try{const s=await(await fetch("/api/system/stats")).json();this.updateSystemStatsDisplay(s.data)}catch{}},updateSystemStatsDisplay(e){const t=this.$("statCpu"),s=this.$("statCpuBar"),n=this.$("statMem"),i=this.$("statMemBar");if(t&&s&&(t.textContent=`${e.cpu}%`,s.style.width=`${Math.min(100,e.cpu)}%`,s.classList.remove("medium","high"),t.classList.remove("high"),e.cpu>80?(s.classList.add("high"),t.classList.add("high")):e.cpu>50&&s.classList.add("medium")),this._setStatRing("statCpuRing",e.cpu),this._setStatRing("statMemRing",e.memory?.percent),n&&i){const o=(e.memory.usedMB/1024).toFixed(1);n.textContent=`${o}G`,i.style.width=`${Math.min(100,e.memory.percent)}%`,i.classList.remove("medium","high"),n.classList.remove("high"),e.memory.percent>80?(i.classList.add("high"),n.classList.add("high")):e.memory.percent>50&&i.classList.add("medium")}},_setStatRing(e,t){const s=this.$(e);if(!s)return;const n=Number(t),i=Number.isFinite(n)?Math.round(Math.min(100,Math.max(0,n))):0;s.style.setProperty("--pu",String(i)),s.classList.toggle("high",i>80)},async _onClipboardWrite(e){const t=e?.text;if(typeof t=="string")try{await navigator.clipboard.writeText(t),this.showToast(`Copied to clipboard (${t.length} chars)`,"success")}catch{this._showClipboardFallback(t)}},_showClipboardFallback(e){const t=document.createElement("div");t.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:10000;display:flex;align-items:center;justify-content:center";const s=document.createElement("div");s.style.cssText="background:#1e1e2e;border:1px solid #444;border-radius:8px;padding:16px;max-width:600px;width:90%;max-height:60vh;display:flex;flex-direction:column;gap:12px";const n=document.createElement("div");n.style.cssText="display:flex;justify-content:space-between;align-items:center";const i=document.createElement("span");i.style.cssText="color:#cdd6f4;font-weight:600",i.textContent="Clipboard (browser blocked auto-copy)";const o=document.createElement("button");o.style.cssText="background:none;border:none;color:#cdd6f4;font-size:18px;cursor:pointer",o.textContent="\xD7",n.appendChild(i),n.appendChild(o);const r=document.createElement("textarea");r.readOnly=!0,r.style.cssText="background:#181825;color:#cdd6f4;border:1px solid #555;border-radius:4px;padding:8px;font-family:monospace;font-size:13px;resize:none;height:200px;width:100%",r.value=e;const a=document.createElement("button");a.style.cssText="background:#89b4fa;color:#1e1e2e;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-weight:600",a.textContent="Copy to Clipboard",s.appendChild(n),s.appendChild(r),s.appendChild(a),t.appendChild(s),document.body.appendChild(t),a.onclick=async()=>{try{await navigator.clipboard.writeText(e),this.showToast("Copied to clipboard","success"),t.remove()}catch{r.select(),document.execCommand("copy"),this.showToast("Copied (fallback)","success"),t.remove()}};const l=()=>t.remove();o.onclick=l,t.onclick=c=>{c.target===t&&l()}}});
361
+ `}e.innerHTML=i},async killMuxSession(e){if(confirm("Kill this mux session?")){try{await this.closeSession(e,!0)}catch{try{await fetch(`/api/mux-sessions/${e}`,{method:"DELETE"})}catch{}this.showToast("Tmux session killed","success")}this.muxSessions=this.muxSessions.filter(t=>t.sessionId!==e),this.renderMuxSessions()}},async reconcileMuxSessions(){try{const t=await(await fetch("/api/mux-sessions/reconcile",{method:"POST"})).json();t.data?.dead&&t.data.dead.length>0?(this.showToast(`Found ${t.data.dead.length} dead mux session(s)`,"warning"),await this.loadMuxSessions()):this.showToast("All mux sessions are alive","success")}catch{this.showToast("Failed to reconcile mux sessions","error")}},toggleNotifications(){this.notificationManager?.toggleDrawer()},async launchMultiMonitor(){try{const e=await fetch("/api/system/span-displays",{method:"POST"}),t=await e.json().catch(()=>({}));e.ok&&t.success?this.showToast("Opening Codeman across all displays\u2026","success"):this.showToast(t.error||"Could not open spanning window","error")}catch(e){this.showToast("Could not open spanning window: "+(e?.message||e),"error")}},toast(e,t="info"){return this.showToast(e,t)},showToast(e,t="info",s={}){const{duration:n=this.notificationManager?.getToastDurationMs?.()??3e3,action:i}=s,o=document.createElement("div");o.className=`toast toast-${t}`;const r=document.createElement("span");if(r.className="toast-message",r.textContent=e,o.appendChild(r),i){const h=document.createElement("button");h.textContent=i.label,h.style.cssText="margin-left:12px;padding:2px 10px;background:rgba(255,255,255,0.15);border:1px solid rgba(255,255,255,0.3);border-radius:3px;color:inherit;cursor:pointer;font-size:12px",h.onclick=d=>{d.stopPropagation(),i.onClick(),o.remove()},o.appendChild(h)}let a=null;const l=()=>{a&&clearTimeout(a),o.classList.remove("show"),setTimeout(()=>o.remove(),200)},c=document.createElement("button");return c.className="toast-close",c.textContent="\xD7",c.setAttribute("aria-label","Dismiss"),c.onclick=h=>{h.stopPropagation(),l()},o.appendChild(c),this._toastContainer||(this._toastContainer=document.querySelector(".toast-container"),this._toastContainer||(this._toastContainer=document.createElement("div"),this._toastContainer.className="toast-container",document.body.appendChild(this._toastContainer))),this._toastContainer.appendChild(o),requestAnimationFrame(()=>o.classList.add("show")),n>0&&(a=setTimeout(l,n)),{dismiss:l,setMessage:h=>{r.textContent=h}}},_showCenterStatus(e,t={}){const{type:s="info",onCancel:n}=t;let i=document.getElementById("customModelCenterStatus");i||(i=document.createElement("div"),i.id="customModelCenterStatus",document.body.appendChild(i)),i._hideTimer&&(clearTimeout(i._hideTimer),i._hideTimer=null),i.className=`center-status-banner center-status-${s}`,i.innerHTML="";const o=()=>{i.classList.remove("show"),i._hideTimer=setTimeout(()=>{i.hidden=!0,i._hideTimer=null},200)};if(s!=="error"){const a=document.createElement("span");a.className="center-status-spinner",a.setAttribute("aria-hidden","true"),i.appendChild(a)}const r=document.createElement("span");if(r.className="center-status-text",r.textContent=e,i.appendChild(r),s==="error"){const a=document.createElement("button");a.className="center-status-close",a.textContent="\xD7",a.setAttribute("aria-label","Dismiss"),a.onclick=l=>{l.stopPropagation(),o()},i.appendChild(a)}else if(n){const a=document.createElement("button");a.className="center-status-cancel",a.textContent="Cancel",a.onclick=l=>{l.stopPropagation(),n()},i.appendChild(a)}return i.hidden=!1,requestAnimationFrame(()=>i.classList.add("show")),{dismiss:o,setMessage:a=>{const l=i.querySelector(".center-status-text");l&&(l.textContent=a)}}},startSystemStatsPolling(){this.stopSystemStatsPolling(),this.fetchSystemStats(),this.systemStatsInterval=setInterval(()=>{this.fetchSystemStats()},2e3)},stopSystemStatsPolling(){this.systemStatsInterval&&(clearInterval(this.systemStatsInterval),this.systemStatsInterval=null)},async fetchSystemStats(){const e=document.getElementById("headerSystemStats");if(!(!e||e.style.display==="none"))try{const s=await(await fetch("/api/system/stats")).json();this.updateSystemStatsDisplay(s.data)}catch{}},updateSystemStatsDisplay(e){const t=this.$("statCpu"),s=this.$("statCpuBar"),n=this.$("statMem"),i=this.$("statMemBar");if(t&&s&&(t.textContent=`${e.cpu}%`,s.style.width=`${Math.min(100,e.cpu)}%`,s.classList.remove("medium","high"),t.classList.remove("high"),e.cpu>80?(s.classList.add("high"),t.classList.add("high")):e.cpu>50&&s.classList.add("medium")),this._setStatRing("statCpuRing",e.cpu),this._setStatRing("statMemRing",e.memory?.percent),n&&i){const o=(e.memory.usedMB/1024).toFixed(1);n.textContent=`${o}G`,i.style.width=`${Math.min(100,e.memory.percent)}%`,i.classList.remove("medium","high"),n.classList.remove("high"),e.memory.percent>80?(i.classList.add("high"),n.classList.add("high")):e.memory.percent>50&&i.classList.add("medium")}},_setStatRing(e,t){const s=this.$(e);if(!s)return;const n=Number(t),i=Number.isFinite(n)?Math.round(Math.min(100,Math.max(0,n))):0;s.style.setProperty("--pu",String(i)),s.classList.toggle("high",i>80)},async _onClipboardWrite(e){const t=e?.text;if(typeof t=="string")try{await navigator.clipboard.writeText(t),this.showToast(`Copied to clipboard (${t.length} chars)`,"success")}catch{this._showClipboardFallback(t)}},_showClipboardFallback(e){const t=document.createElement("div");t.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:10000;display:flex;align-items:center;justify-content:center";const s=document.createElement("div");s.style.cssText="background:#1e1e2e;border:1px solid #444;border-radius:8px;padding:16px;max-width:600px;width:90%;max-height:60vh;display:flex;flex-direction:column;gap:12px";const n=document.createElement("div");n.style.cssText="display:flex;justify-content:space-between;align-items:center";const i=document.createElement("span");i.style.cssText="color:#cdd6f4;font-weight:600",i.textContent="Clipboard (browser blocked auto-copy)";const o=document.createElement("button");o.style.cssText="background:none;border:none;color:#cdd6f4;font-size:18px;cursor:pointer",o.textContent="\xD7",n.appendChild(i),n.appendChild(o);const r=document.createElement("textarea");r.readOnly=!0,r.style.cssText="background:#181825;color:#cdd6f4;border:1px solid #555;border-radius:4px;padding:8px;font-family:monospace;font-size:13px;resize:none;height:200px;width:100%",r.value=e;const a=document.createElement("button");a.style.cssText="background:#89b4fa;color:#1e1e2e;border:none;border-radius:4px;padding:8px 16px;cursor:pointer;font-weight:600",a.textContent="Copy to Clipboard",s.appendChild(n),s.appendChild(r),s.appendChild(a),t.appendChild(s),document.body.appendChild(t),a.onclick=async()=>{try{await navigator.clipboard.writeText(e),this.showToast("Copied to clipboard","success"),t.remove()}catch{r.select(),document.execCommand("copy"),this.showToast("Copied (fallback)","success"),t.remove()}};const l=()=>t.remove();o.onclick=l,t.onclick=c=>{c.target===t&&l()}}});
Binary file